From 95c5aaf46dfb469d6cebeb80e67563aad2cf470e Mon Sep 17 00:00:00 2001 From: Josh Rahm Date: Thu, 6 Mar 2025 00:51:32 -0700 Subject: Implement a mouse-driven workspace switcher using dzen. When a key or button is pressed, RDE will display the set of current workspaces, prompting the user to click on one to switch to that workspace. --- src/Rahm/Desktop/Dzen/WorkspaceSelect.hs | 106 +++++++++++++++++++++++++++++++ src/Rahm/Desktop/Keys.hs | 10 +++ 2 files changed, 116 insertions(+) create mode 100644 src/Rahm/Desktop/Dzen/WorkspaceSelect.hs (limited to 'src/Rahm/Desktop') diff --git a/src/Rahm/Desktop/Dzen/WorkspaceSelect.hs b/src/Rahm/Desktop/Dzen/WorkspaceSelect.hs new file mode 100644 index 0000000..9d12e27 --- /dev/null +++ b/src/Rahm/Desktop/Dzen/WorkspaceSelect.hs @@ -0,0 +1,106 @@ +module Rahm.Desktop.Dzen.WorkspaceSelect where + +import Control.Arrow (Arrow (first, second)) +import Control.Concurrent (threadDelay) +import Control.Monad.Trans.Maybe (MaybeT (runMaybeT)) +import Data.Function (fix) +import Data.List (find, sortBy) +import Data.Ord (comparing) +import Data.Word +import qualified Graphics.X11 as X +import Rahm.Desktop.Common (gotoWorkspace) +import Rahm.Desktop.StackSet as W +import Rahm.Desktop.Submap (ButtonOrKeyEvent (ButtonPress), nextButton, nextButtonOrKeyEvent) +import System.IO +import Text.Printf (printf) +import XMonad +import XMonad.Util.Run (spawnPipe) + +dzenSize :: (Integral a) => a +dzenSize = 100 + +data ScreenType = Current | Visible | Hidden + +displayDzenSelection :: X () +displayDzenSelection = do + (W.StackSet cur vis other _floating) <- gets windowset + + let allWorkspaces :: [(W.Workspace WorkspaceId (Layout Window) Window, ScreenType)] + allWorkspaces = + sortBy (comparing (W.tag . fst)) $ + filter (not . null . W.integrate' . W.stack . fst) $ + ((W.workspace cur, Current) : map ((,Visible) . W.workspace) vis) ++ map (,Hidden) other + (SD rect) = W.screenDetail cur + dzenRects = calculatePositions rect allWorkspaces + rectsToWorkspaces = map (second (first W.tag)) dzenRects + + dzenPipes <- mapM (uncurry launchDzen) rectsToWorkspaces + + fix $ \retry -> do + evt <- runMaybeT nextButtonOrKeyEvent + + case evt of + Nothing -> return () -- timeout + Just (ButtonPress _ b) | b == button1 -> do + withDisplay $ \d -> do + (_, _, _, fi -> cx, fi -> cy, _, _, _) <- + io $ X.queryPointer d =<< rootWindow d 0 + case find (\(r, _) -> pointWithin cx cy r) rectsToWorkspaces of + Just (_, (ws, _)) -> gotoWorkspace ws + Nothing -> return () + _ -> retry + + io $ do + mapM_ hClose dzenPipes + + return () + where + fi :: (Integral a, Integral b) => a -> b + fi = fromIntegral + + toInt :: (Integral a) => a -> Int + toInt = fromIntegral + + -- Returns, starting x, dzen width + calculatePositions :: Rectangle -> [a] -> [(Rectangle, a)] + calculatePositions (Rectangle (toInt -> x) (toInt -> y) (toInt -> w) (toInt -> h)) as = + let nas = length as + cellSize = (w `div` nas) + y' = fi $ (h `div` 2) - (toInt cellSize `div` 2) + sz = min 200 (cellSize * 9 `div` 10) + in zipWith + (\x' a -> (embedRectangle (Rectangle (fi x') (fi y') (fi cellSize) (fi cellSize)) sz sz, a)) + [x, x + cellSize ..] + as + + embedRectangle :: Rectangle -> Int -> Int -> Rectangle + embedRectangle r@(Rectangle x y w h) w' h' | w' > fi w || h' > fi h = r + embedRectangle (Rectangle x y w h) w' h' = + Rectangle + (x + fi (w `div` 2) - fi (w' `div` 2)) + (y + fi (h `div` 2) - fi (h' `div` 2)) + (fi w') + (fi h') + + launchDzen :: Rectangle -> (String, ScreenType) -> X Handle + launchDzen (Rectangle x y w h) (wsTag, screenType) = do + pipe <- + spawnPipe + ( printf + "dzen2 -fn \"Monofur Nerd Font:size=15:style=bold\" -x %d -y %d -w %d -h %d -bg '%s' -fg '#000000'" + x + y + w + h + ( case screenType of + Current -> "#ff8888" + Visible -> "#8888ff" + Hidden -> "#888888" + ) + ) + + io $ do + hPutStrLn pipe wsTag + hFlush pipe + + return pipe diff --git a/src/Rahm/Desktop/Keys.hs b/src/Rahm/Desktop/Keys.hs index 61e483a..dc6628b 100644 --- a/src/Rahm/Desktop/Keys.hs +++ b/src/Rahm/Desktop/Keys.hs @@ -59,6 +59,7 @@ import Rahm.Desktop.Common ) import Rahm.Desktop.DMenu (runDMenu) import qualified Rahm.Desktop.Dragging as D +import Rahm.Desktop.Dzen.WorkspaceSelect (displayDzenSelection) import Rahm.Desktop.History ( historyBack, historyForward, @@ -1096,6 +1097,15 @@ bindings = do bind xK_l $ justMod increaseVolumeDoc + bind xK_o $ do + justMod $ + doc "Select a workspace using dzen" displayDzenSelection + + bind button10 $ do + noMod $ + doc "Select a workspace using dzen" $ do + noWindow displayDzenSelection + bind button14 $ do noMod $ doc "Additional Mouse Bindings" $ -- cgit