diff --git a/files/.config/configstore/update-notifier-npm.json b/files/.config/configstore/update-notifier-npm.json index dae46b6..b2a6374 100644 --- a/files/.config/configstore/update-notifier-npm.json +++ b/files/.config/configstore/update-notifier-npm.json @@ -1,4 +1,4 @@ { "optOut": false, - "lastUpdateCheck": 1592553121962 + "lastUpdateCheck": 1592658266558 } \ No newline at end of file diff --git a/files/.config/nitrogen/bg-saved.cfg b/files/.config/nitrogen/bg-saved.cfg index 9167c42..88943f7 100644 --- a/files/.config/nitrogen/bg-saved.cfg +++ b/files/.config/nitrogen/bg-saved.cfg @@ -4,11 +4,11 @@ mode=5 bgcolor=#000000 [xin_0] -file=/home/leon/Bilder/wallpapers/abstract-barb-wires-blur-close-up-116021.jpg +file=/home/leon/Bilder/wallpapers/green_leaves.jpg mode=5 bgcolor=#000000 [xin_1] -file=/home/leon/Bilder/wallpapers/abstract-barb-wires-blur-close-up-116021.jpg +file=/home/leon/Bilder/wallpapers/green_leaves.jpg mode=5 bgcolor=#000000 diff --git a/files/.config/nitrogen/nitrogen.cfg b/files/.config/nitrogen/nitrogen.cfg index 75661fa..72c30c0 100644 --- a/files/.config/nitrogen/nitrogen.cfg +++ b/files/.config/nitrogen/nitrogen.cfg @@ -1,8 +1,8 @@ [geometry] posx=20 posy=50 -sizex=1432 -sizey=841 +sizex=1297 +sizey=696 [nitrogen] view=icon diff --git a/files/.xmonad/.stack-work/stack.sqlite3 b/files/.xmonad/.stack-work/stack.sqlite3 index d9b2a7d..28aea47 100644 Binary files a/files/.xmonad/.stack-work/stack.sqlite3 and b/files/.xmonad/.stack-work/stack.sqlite3 differ diff --git a/files/.xmonad/lib/Config.hs b/files/.xmonad/lib/Config.hs index 59e626c..d814212 100644 --- a/files/.xmonad/lib/Config.hs +++ b/files/.xmonad/lib/Config.hs @@ -23,9 +23,9 @@ import qualified XMonad.Util.ExtensibleState as XS import qualified Rofi import qualified DescribedSubmap import qualified TiledDragging ---import qualified WindowSwallowing +import qualified WindowSwallowing -import XMonad.Hooks.WindowSwallowing as WindowSwallowing +--import XMonad.Hooks.WindowSwallowing as WindowSwallowing import Data.Foldable ( for_ ) @@ -444,6 +444,8 @@ myKeys = concat [ zoomRowBindings, tabbedBindings, multiMonitorBindings, program , ("Kill all other copies", killAllOtherCopies) , ("toggle polybar", sendMessage ToggleStruts >> safeSpawn "polybar-msg" ["cmd", "toggle"]) , ("get debug data", debugStackFullString >>= (\str -> safeSpawn "xmessage" [str])) + , ("get full stackset", withWindowSet (\ws -> spawn $ "echo '" ++ show (W.floating ws) ++ "\n" ++ show (W.current ws) ++ "' | xclip -in -selection clipboard")) + , ("asdf", windows (\ws -> ws {W.floating = M.empty })) ] @@ -522,7 +524,7 @@ main = do -- }}} -mySwallowEventHook = WindowSwallowing.swallowEventHook [className =? "Alacritty", className =? "Termite", className =? "Thunar"] [return True] +mySwallowEventHook = WindowSwallowing.swallowEventHook (className =? "Alacritty" <||> className =? "Termite" <||> className =? "Thunar") (return True) activateWindowEventHook :: Event -> X All @@ -530,8 +532,12 @@ activateWindowEventHook (ClientMessageEvent { ev_message_type = messageType, ev_ activateWindowAtom <- getAtom "_NET_ACTIVE_WINDOW" when (messageType == activateWindowAtom) $ if window `elem` (concatMap (W.integrate' . W.stack . W.workspace) (W.current ws : W.visible ws)) - then windows (W.focusWindow window) - else windows (W.shiftWin (W.tag $ W.workspace $ W.current ws) window) + then windows (W.focusWindow window) + else do + shouldRaise <- runQuery (className =? "discord" <||> className =? "web.whatsapp.com") window + if shouldRaise + then windows (W.shiftWin (W.tag $ W.workspace $ W.current ws) window) + else windows (W.focusWindow window) return $ All True activateWindowEventHook _ = return $ All True diff --git a/files/.xmonad/lib/WindowSwallowing.hs b/files/.xmonad/lib/WindowSwallowing.hs index c5b862a..a05a10d 100644 --- a/files/.xmonad/lib/WindowSwallowing.hs +++ b/files/.xmonad/lib/WindowSwallowing.hs @@ -1,4 +1,42 @@ {-# LANGUAGE NamedFieldPuns #-} +----------------------------------------------------------------------------- +-- | +-- Module : XMonad.Hooks.WindowSwallowing +-- Copyright : (c) 2020 Leon Kowarschick +-- License : BSD3-style (see LICENSE) +-- +-- Maintainer : Leon Kowarschick. +-- Stability : unstable +-- Portability : unportable +-- +-- Provides a handleEventHook that implements window swallowing. +-- +-- If you open a GUI-window (i.e. feh) from the terminal, +-- the terminal will normally still be shown on screen, unnecessarily +-- taking up space on the screen. +-- With window swallowing, can detect that you opened a window from within another +-- window, and allows you "swallow" that parent window for the time the new +-- window is running. +-- +-- __NOTE__: This module depends on @pstree@ to analyze the process hierarchy, so make +-- sure that is on your @$PATH@. +-- +-- __NOTE__ that this does not always work perfectly: +-- +-- - Because window swallowing needs to check the process hierarchy, it requires +-- both the child and the parent to be distinct processes. This means that +-- applications which implement instance sharing cannot be supported by window swallowing. +-- Most notably, this excludes some terminal emulators as well as tmux +-- from functioning as the parent process. It also excludes a good amount of +-- child programs, because many graphical applications do implement instance sharing. +-- For example, window swallowing will probably not work with your browser. +-- +-- - To check the process hierarchy, we need to be able to get the process ID +-- by looking at the window. This requires the @_NET_WM_PID@ X-property to be set. +-- If any application you want to use this with does not provide the @_NET_WM_PID@, +-- there is not much you can do except for reaching out to the author of that +-- application and asking them to set that property. +----------------------------------------------------------------------------- module WindowSwallowing ( swallowEventHook ) @@ -12,24 +50,94 @@ import Data.Semigroup ( All(..) ) import qualified Data.Map.Strict as M import Data.List ( isInfixOf ) import Control.Monad ( when ) -import qualified XMonad.Layout.Hidden as Hidden + +-- $usage +-- You can use this module by including the following in your @~\/.xmonad/xmonad.hs@: +-- +-- > import XMonad.Hooks.WindowSwallowing +-- +-- and using 'swallowEventHook' somewhere in your 'handleEventHook', for example: +-- +-- > myHandleEventHook = swallowEventHook [className =? "Alacritty", className =? "Termite"] [return True] +-- +-- For more information on editing your handleEventHook and key bindings, +-- see "XMonad.Doc.Extending". -swallowEventHook :: [Query Bool] -> [Query Bool] -> Event -> X All +-- | handleEventHook that will swallow child windows when they are +-- opened from another window. +swallowEventHook + :: Query Bool -- ^ query the parent window has to match for window swallowing to occur. + -- Set this to @return True@ to run swallowing for every parent. + -> Query Bool -- ^ query the child window has to match for window swallowing to occur. + -- Set this to @return True@ to run swallowing for every child + -> Event -- ^ The event to handle. + -> X All swallowEventHook parentQueries childQueries event = do case event of + -- This is called right before a window gets opened. We intercept that + -- call to possibly open the window ourselves, swapping out + -- it's parent processes window for the new window in the stack. + MapRequestEvent { ev_window = childWindow } -> + -- For a window to be opened from within another window, that other window + -- must be focused. Thus the parent window that would be swallowed has to be + -- the currently focused window. + withFocused $ \parentWindow -> do + -- First verify that both windows match the given queries + parentMatches <- runQuery parentQueries parentWindow + childMatches <- runQuery childQueries childWindow + when (parentMatches && childMatches) $ do + -- read the windows _NET_WM_PID properties + childWindowPid <- getProp32s "_NET_WM_PID" childWindow + parentWindowPid <- getProp32s "_NET_WM_PID" parentWindow + case (parentWindowPid, childWindowPid) of + (Just (parentPid : _), Just (childPid : _)) -> do + -- check if the new window is a child process of the last focused window + -- using the process ids. + isChild <- liftIO $ fi childPid `isChildOf` fi parentPid + when isChild $ do + -- We set the newly opened window as the focused window, replacing the parent window. + -- If the parent window was floating, we transfer that data to the child, + -- such that it shows up at the same position, with the same dimensions. + + windows + ( W.modify' (\x -> x { W.focus = childWindow }) + . copyFloatingState parentWindow childWindow + ) + windows + (\ws -> + ws { W.floating = M.delete parentWindow (W.floating ws) } + ) + XS.modify (addSwallowedParent parentWindow childWindow) + _ -> return () + return () + + + -- This is called in many circumstances, most notably for us: + -- right before a window gets closed. We store the current + -- state of the window stack here, such that we know where the + -- child window was on the screen when restoring the swallowed parent process. ConfigureEvent{} -> withWindowSet $ \ws -> do XS.modify . setStackBeforeWindowClosing . currentStack $ ws XS.modify . setFloatingBeforeWindowClosing . W.floating $ ws + -- This is called right after any window closes. DestroyWindowEvent { ev_event = eventId, ev_window = childWindow } -> + -- Because DestroyWindowEvent is emitted a lot more often then you think, + -- this check verifies that the event is /actually/ about closing a window. when (eventId == childWindow) $ do + -- we get some data from the extensible state, most notably we ask for + -- the \"parent\" window of the now closed window. maybeSwallowedParent <- XS.gets (getSwallowedParent childWindow) maybeOldStack <- XS.gets stackBeforeWindowClosing oldFloating <- XS.gets floatingBeforeClosing case (maybeSwallowedParent, maybeOldStack) of (Just parent, Just oldStack) -> do - --Hidden.popHiddenWindow parent + -- If there actually is a corresponding swallowed parent window for this window, + -- we will restore and place it where the closed window was. + -- For this, we look at the stack-state that was stored /before/ the window was closed, + -- and replace the focused window with the now restored parent. + -- we do this to make sure the parent is restored in the exact position the child was at. windows (\ws -> updateCurrentStack @@ -37,50 +145,36 @@ swallowEventHook parentQueries childQueries event = do $ copyFloatingState childWindow parent $ ws { W.floating = oldFloating } ) + windows + (\ws -> ws { W.floating = M.delete childWindow (W.floating ws) }) + + -- after restoring, we remove the information about the swallowing from the state. XS.modify $ removeSwallowed childWindow XS.modify $ setStackBeforeWindowClosing Nothing _ -> return () return () - - MapRequestEvent { ev_window = childWindow } -> - withFocused $ \parentWindow -> do - parentMatches <- mapM (`runQuery` parentWindow) parentQueries - childMatches <- mapM (`runQuery` childWindow) childQueries - when (or parentMatches && or childMatches) $ do - childWindowPid <- getProp32s "_NET_WM_PID" childWindow - parentWindowPid <- getProp32s "_NET_WM_PID" parentWindow - case (parentWindowPid, childWindowPid) of - (Just (parentPid : _), Just (childPid : _)) -> do - isChild <- liftIO $ fi childPid `isChildOf` fi parentPid - when isChild $ do - windows - (updateCurrentStack (fmap (\x -> x { W.focus = childWindow })) - . copyFloatingState parentWindow childWindow - ) - XS.modify (addSwallowedParent parentWindow childWindow) - --Hidden.hideWindow parentWindow - _ -> return () - return () _ -> return () return $ All True +-- | run a pure transformation on the Stack of the currently focused workspace. updateCurrentStack :: (Maybe (W.Stack a) -> Maybe (W.Stack a)) -> W.StackSet i l a sid sd -> W.StackSet i l a sid sd -updateCurrentStack f ws = ws - { W.current = (W.current ws) - { W.workspace = currentWsp { W.stack = f $ currentStack ws } - } - } - where currentWsp = W.workspace $ W.current ws +updateCurrentStack f = W.modify (f Nothing) (f . Just) currentStack :: W.StackSet i l a sid sd -> Maybe (W.Stack a) currentStack = W.stack . W.workspace . W.current + +-- | copy the floating related state of one window to another window in a StackSet. copyFloatingState - :: Ord a => a -> a -> W.StackSet i l a s sd -> W.StackSet i l a s sd + :: Ord a + => a -- ^ window to copy from + -> a -- ^ window to copy to + -> W.StackSet i l a s sd + -> W.StackSet i l a s sd copyFloatingState from to ws = ws { W.floating = maybe (M.delete to (W.floating ws)) (\r -> M.insert to r (W.floating ws)) @@ -101,9 +195,9 @@ isChildOf child parent = do data SwallowingState = SwallowingState - { currentlySwallowed :: M.Map Window Window -- ^ mapping from child window window to the currently swallowed parent window - , stackBeforeWindowClosing :: Maybe (W.Stack Window) -- ^ current stack state right before DestroyWindowEvent is sent - , floatingBeforeClosing :: M.Map Window W.RationalRect -- ^ floating map of the stackset right before DestroyWindowEvent is sent + { currentlySwallowed :: M.Map Window Window -- ^ mapping from child window window to the currently swallowed parent window + , stackBeforeWindowClosing :: Maybe (W.Stack Window) -- ^ current stack state right before DestroyWindowEvent is sent + , floatingBeforeClosing :: M.Map Window W.RationalRect -- ^ floating map of the stackset right before DestroyWindowEvent is sent } deriving (Typeable, Show) getSwallowedParent :: Window -> SwallowingState -> Maybe Window @@ -135,3 +229,4 @@ instance ExtensionClass SwallowingState where fi :: (Integral a, Num b) => a -> b fi = fromIntegral +