------------------------------------------------------------------------------- -- | -- Module : XMonad.Util.ClickableWorkspaces -- Description : Make workspace tags clickable in XMobar (for switching focus). -- Copyright : (c) Geoff deRosenroll <geoffderosenroll@gmail.com> -- License : BSD3-style (see LICENSE) -- -- Maintainer : Geoff deRosenroll <geoffderosenroll@gmail.com> -- Stability : unstable -- Portability : unportable -- -- Provides @clickablePP@, which when applied to the 'PP' pretty-printer used -- by "XMonad.Hooks.StatusBar" will make the workspace tags clickable in -- XMobar (for switching focus). -- ----------------------------------------------------------------------------- module XMonad.Util.ClickableWorkspaces ( -- * Usage -- $usage clickablePP, clickableWrap, ) where import XMonad.Prelude ((<&>), (>=>)) import XMonad import XMonad.Hooks.StatusBar.PP (xmobarAction, PP(..)) import XMonad.Util.WorkspaceCompare (getSortByIndex) import qualified XMonad.StackSet as W import Data.List (elemIndex) -- $usage -- If you're using the "XMonad.Hooks.StatusBar" interface, apply 'clickablePP' -- to the 'PP' passed to 'XMonad.Hooks.StatusBar.statusBarProp': -- -- > mySB <- statusBarProp "xmobar" (clickablePP xmobarPP) -- -- Or if you're using the old "XMonad.Hooks.DynamicLog" interface: -- -- > logHook = clickablePP xmobarPP { ... } >>= dynamicLogWithPP -- -- Requirements: -- -- * @xdotool@ on system (in path) -- * "XMonad.Hooks.EwmhDesktops" for @xdotool@ support (see Hackage docs for setup) -- * use of UnsafeStdinReader\/UnsafeXMonadLog in xmobarrc (rather than StdinReader\/XMonadLog) -- -- Note that UnsafeStdinReader is potentially dangerous if your workspace -- names are dynamically generated from untrusted input (like window titles). -- You may need to add @xmobarRaw@ to 'ppRename' before applying -- 'clickablePP' in such case. -- | Wrap string with an xmobar action that uses @xdotool@ to switch to -- workspace @i@. clickableWrap :: Int -> String -> String clickableWrap :: Int -> WorkspaceId -> WorkspaceId clickableWrap Int i = WorkspaceId -> WorkspaceId -> WorkspaceId -> WorkspaceId xmobarAction (WorkspaceId "xdotool set_desktop " WorkspaceId -> WorkspaceId -> WorkspaceId forall a. [a] -> [a] -> [a] ++ Int -> WorkspaceId forall a. Show a => a -> WorkspaceId show Int i) WorkspaceId "1" -- | 'XMonad.Util.WorkspaceCompare.getWsIndex' extended to handle workspaces -- not in the static 'workspaces' config, such as those created by -- "XMonad.Action.DynamicWorkspaces". -- -- Uses 'getSortByIndex', as that's what "XMonad.Hooks.EwmhDesktops" uses to -- export the information to tools like @xdotool@. (Note that EwmhDesktops can -- be configured with a custom sort function, and we don't handle that here -- yet.) getWsIndex :: X (WorkspaceId -> Maybe Int) getWsIndex :: X (WorkspaceId -> Maybe Int) getWsIndex = do wSort <- X WorkspaceSort getSortByIndex spaces <- gets (map W.tag . wSort . W.workspaces . windowset) return $ flip elemIndex spaces -- | Return a function that wraps workspace names in an xmobar action that -- switches to that workspace. -- -- This assumes that 'XMonad.Hooks.EwmhDesktops.ewmhDesktopsEventHook' -- isn't configured to change the workspace order. We might need to add an -- additional parameter if anyone needs that. getClickable :: X (String -> WindowSpace -> String) getClickable :: X (WorkspaceId -> WindowSpace -> WorkspaceId) getClickable = X (WorkspaceId -> Maybe Int) getWsIndex X (WorkspaceId -> Maybe Int) -> ((WorkspaceId -> Maybe Int) -> WorkspaceId -> WindowSpace -> WorkspaceId) -> X (WorkspaceId -> WindowSpace -> WorkspaceId) forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b <&> \WorkspaceId -> Maybe Int idx WorkspaceId s WindowSpace w -> (WorkspaceId -> WorkspaceId) -> (Int -> WorkspaceId -> WorkspaceId) -> Maybe Int -> WorkspaceId -> WorkspaceId forall b a. b -> (a -> b) -> Maybe a -> b maybe WorkspaceId -> WorkspaceId forall a. a -> a id Int -> WorkspaceId -> WorkspaceId clickableWrap (WorkspaceId -> Maybe Int idx (WindowSpace -> WorkspaceId forall i l a. Workspace i l a -> i W.tag WindowSpace w)) WorkspaceId s -- | Apply clickable wrapping to the given PP. clickablePP :: PP -> X PP clickablePP :: PP -> X PP clickablePP PP pp = X (WorkspaceId -> WindowSpace -> WorkspaceId) getClickable X (WorkspaceId -> WindowSpace -> WorkspaceId) -> ((WorkspaceId -> WindowSpace -> WorkspaceId) -> PP) -> X PP forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b <&> \WorkspaceId -> WindowSpace -> WorkspaceId ren -> PP pp{ ppRename = ppRename pp >=> ren }