ENH add exit event program to handle empty dynamic workspaces

This commit is contained in:
Nathan Dwarshuis 2020-03-05 21:22:26 -05:00
parent 19999bb3b9
commit e934ceaa10
5 changed files with 85 additions and 14 deletions

4
.gitignore vendored
View File

@ -8,9 +8,7 @@ xmonad-*
# files used by stack # files used by stack
.stack-work/ .stack-work/
stack.yaml.lock
# directory created by `build` script
bin/
# files automatically created by xmonad # files automatically created by xmonad
xmonad.state xmonad.state

52
bin/xit-event.hs Normal file
View File

@ -0,0 +1,52 @@
-- | Send a special event as a signal to the window manager
-- Specifically, this is meant to be run after applications exit which
-- will allow xmonad to react to processes closing. It takes two
-- arguments: a string called the "magic string" up to 5 characters
-- and a string up to 15 characters called the "tag." These will be
-- concatenated and sent to xmonad in a ClientRequest event of type
-- BITMAP (which hopefully will never do anything) to the root window.
-- Operationally, the magic string is meant to be used to
-- differentiate this event and the tag is meant to be a signal to be
-- read by xmonad.
import Graphics.X11.Types
import Graphics.X11.Xlib.Atom
import Graphics.X11.Xlib.Display
import Graphics.X11.Xlib.Event
import Graphics.X11.Xlib.Extras
import System.Environment
import System.Exit
main :: IO ()
main = getArgs >>= parse
parse :: [String] -> IO ()
parse [magic, tag] = send magic tag >> exitSuccess
parse _ = exitFailure
send :: String -> String -> IO ()
send magic tag = do
dpy <- openDisplay ""
root <- rootWindow dpy $ defaultScreen dpy
allocaXEvent $ \e -> do
setEventType e clientMessage
-- NOTE: This function is written such that the penultimate
-- argument represents the first 40 bits of the 160 bit data
-- field, and it also only takes a decimal digit, which means the
-- string to be stored in the data field needs to be converted to
-- its decimal equivalent. The penultimate argument will be used
-- for the magic string and the last will be used for the tag.
setClientMessageEvent e root bITMAP 8 m t
sendEvent dpy root False substructureNotifyMask e
flush dpy
where
m = str2digit magic
t = str2digit tag
str2digit :: (Num a) => String -> a
str2digit = fromIntegral
. sum
. map (\(p, n) -> n * 256 ^ p)
. zip [0 :: Int ..]
. map fromEnum

View File

@ -3,13 +3,22 @@ version: 0.1.0.0
build-type: Simple build-type: Simple
cabal-version: >=1.10 cabal-version: >=1.10
executable my-xmonad executable xmonad
main-is: ../xmonad.hs main-is: ../xmonad.hs
-- other-modules lists custom modules in my ~/.xmonad/lib/ directory
other-modules: other-modules:
build-depends: base build-depends: base
, xmonad >= 0.13 , xmonad >= 0.13
, xmonad-contrib >= 0.13 , xmonad-contrib >= 0.13
, xmonad-extras >= 0.15.2
, X11 >= 1.9.1
hs-source-dirs: lib hs-source-dirs: lib
default-language: Haskell2010 default-language: Haskell2010
ghc-options: -Wall -Werror -fno-warn-missing-signatures -threaded ghc-options: -Wall -Werror -fno-warn-missing-signatures -threaded
executable xit-event
main-is: bin/xit-event.hs
other-modules:
build-depends: base
, X11 >= 1.9.1
default-language: Haskell2010
ghc-options: -Wall -Werror -fno-warn-missing-signatures -threaded

View File

@ -39,7 +39,7 @@ packages:
# - git: https://github.com/commercialhaskell/stack.git # - git: https://github.com/commercialhaskell/stack.git
# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a # commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
# #
# extra-deps: [] extra-deps: []
# Override default flag values for local packages and extra-deps # Override default flag values for local packages and extra-deps
# flags: {} # flags: {}

View File

@ -12,8 +12,8 @@ import System.IO
import Data.Char import Data.Char
import Data.List (sortBy) import Data.List (sortBy)
import Data.Maybe (fromMaybe, isJust) import Data.Maybe (fromMaybe, isJust)
import Data.Ord (comparing)
import Data.Monoid (All(..)) import Data.Monoid (All(..))
import Data.Ord (comparing)
import Graphics.X11.Xlib.Atom import Graphics.X11.Xlib.Atom
import Graphics.X11.Xlib.Extras import Graphics.X11.Xlib.Extras
@ -210,13 +210,18 @@ myManageHook = composeOne
-- data field that can be intercepted here. When this event is -- data field that can be intercepted here. When this event is
-- registered here, close the dynamic workspaces that are empty. -- registered here, close the dynamic workspaces that are empty.
myEventHook (ClientMessageEvent { ev_message_type = t, ev_data = d }) myEventHook (ClientMessageEvent { ev_message_type = t, ev_data = d })
| t == bITMAP && magicstring == data2string d = do | t == bITMAP = do
mapM_ removeEmptyWorkspaceByTag [myVMWorkspace, myGimpWorkspace] let (magic, tag) = splitAt 5 $ map (chr . fromInteger . toInteger) d
io $ putStrLn magic
if magic == magicString then do
let tag' = filter isAlphaNum tag
io $ putStrLn (show tag')
-- TODO this actually won't remove an empty workspace if
-- there are the same number of active workspaces as screens
removeEmptyWorkspaceByTag tag'
else return ()
return (All True) return (All True)
| otherwise = return (All True) | otherwise = return (All True)
where
magicstring = "xxxxxxxxxxxxxxxxxxxx"
data2string = map (chr . fromInteger . toInteger)
myEventHook _ = do myEventHook _ = do
return (All True) return (All True)
@ -314,7 +319,6 @@ myModMask = mod4Mask
_myRofi = "rofi -m -4" -- show rofi always with the focused window _myRofi = "rofi -m -4" -- show rofi always with the focused window
myTerm = "urxvt" myTerm = "urxvt"
myBrowser = "brave" myBrowser = "brave"
myVBox = "vbox-start win8raw"
myEditor = "emacsclient -c -e \"(select-frame-set-input-focus (selected-frame))\"" myEditor = "emacsclient -c -e \"(select-frame-set-input-focus (selected-frame))\""
myCalc = "urxvt -e R" myCalc = "urxvt -e R"
myFileManager = "pcmanfm" myFileManager = "pcmanfm"
@ -333,6 +337,14 @@ myScreenCap = "flameshot gui" --external script
-- myWindowCap = "screencap -w" --external script -- myWindowCap = "screencap -w" --external script
myScreenLock = "screenlock" --external script myScreenLock = "screenlock" --external script
removeWorkspaceOnExit cmd ws =
unwords [cmd, "&&", "xit-event", magicString, ws]
magicString = "%%%%%"
myVBox = removeWorkspaceOnExit "vbox-start win8raw" myVMWorkspace
myGimp = removeWorkspaceOnExit "gimp" myGimpWorkspace
showVBox = windows $ W.view myVMWorkspace showVBox = windows $ W.view myVMWorkspace
showKeybindings :: [((KeyMask, KeySym), NamedAction)] -> NamedAction showKeybindings :: [((KeyMask, KeySym), NamedAction)] -> NamedAction
@ -405,7 +417,7 @@ myKeys c =
, ("M-C-q", addName "launch calc" $ spawn myCalc) , ("M-C-q", addName "launch calc" $ spawn myCalc)
, ("M-C-f", addName "launch file manager" $ spawn myFileManager) , ("M-C-f", addName "launch file manager" $ spawn myFileManager)
, ("M-C-v", addName "launch windows VM" $ spawn myVBox >> appendWorkspace myVMWorkspace) , ("M-C-v", addName "launch windows VM" $ spawn myVBox >> appendWorkspace myVMWorkspace)
, ("M-C-g", addName "launch GIMP" $ spawn "gimp" >> appendWorkspace myGimpWorkspace) , ("M-C-g", addName "launch GIMP" $ spawn myGimp >> appendWorkspace myGimpWorkspace)
] ++ ] ++
mkNamedSubmap c "Multimedia" mkNamedSubmap c "Multimedia"