{-# LANGUAGE DeriveDataTypeable, TypeSynonymInstances, MultiParamTypeClasses, ExistentialQuantification, FlexibleInstances, FlexibleContexts #-} module Main where import qualified Control.Arrow as A import Control.Monad import Control.Monad.Trans.Maybe import Data.Aeson import qualified Data.ByteString.Lazy as B import Data.List import qualified Data.Map as M import Data.Maybe import qualified Data.MultiMap as MM import Graphics.X11.ExtraTypes.XF86 import Network.HostName import System.Directory import System.FilePath.Posix import System.Taffybar.Hooks.PagerHints import Text.Printf import XMonad hiding ( (|||) ) import XMonad.Actions.CycleWS hiding (nextScreen) import qualified XMonad.Actions.DynamicWorkspaceOrder as DWO import XMonad.Actions.Minimize import XMonad.Actions.WindowBringer import XMonad.Actions.WindowGo import XMonad.Actions.WorkspaceNames import XMonad.Config () import XMonad.Hooks.EwmhDesktops import XMonad.Hooks.FadeInactive import XMonad.Hooks.ManageDocks import XMonad.Hooks.Minimize import XMonad.Layout.Accordion import XMonad.Layout.BoringWindows import XMonad.Layout.LayoutCombinators import XMonad.Layout.LayoutModifier import XMonad.Layout.LimitWindows import XMonad.Layout.MagicFocus import XMonad.Layout.Minimize import XMonad.Layout.MultiColumns import XMonad.Layout.MultiToggle import XMonad.Layout.MultiToggle.Instances import XMonad.Layout.NoBorders import qualified XMonad.Layout.Renamed as RN import XMonad.Layout.Spacing import qualified XMonad.StackSet as W import XMonad.Util.CustomKeys import qualified XMonad.Util.Dmenu as DM import qualified XMonad.Util.ExtensibleState as XS import XMonad.Util.Minimize import XMonad.Util.NamedScratchpad (NamedScratchpad(NS), nonFloating, namedScratchpadAction) import XMonad.Util.NamedWindows (getName) myGetWorkspaceNameFromTag getWSName tag = printf "%s: %s " tag (fromMaybe "(Empty)" (getWSName tag)) main = xmonad . docks $ def { modMask = mod4Mask , terminal = "urxvt" , manageHook = myManageHook <+> manageHook def , layoutHook = myLayoutHook , logHook = toggleFadeInactiveLogHook 0.9 +++ ewmhWorkspaceNamesLogHook' myGetWorkspaceNameFromTag +++ (myGetWorkspaceNameFromTag <$> getWorkspaceNames' >>= pagerHintsLogHookCustom) , handleEventHook = fullscreenEventHook +++ ewmhDesktopsEventHook +++ pagerHintsEventHook +++ followIfNoMagicFocus +++ minimizeEventHook , startupHook = myStartup +++ ewmhWorkspaceNamesLogHook , keys = customKeys (const []) addKeys } where x +++ y = mappend y x -- Utility functions (<..>) a b = (fmap . fmap) a b forkM :: Monad m => (i -> m a) -> (i -> m b) -> i -> m (a, b) forkM a b input = do resA <- a input resB <- b input return (resA, resB) tee :: Monad m => (i -> m a) -> (i -> m b) -> i -> m a tee = (fmap . fmap . fmap) (fmap fst) forkM (>>=/) :: Monad m => m a -> (a -> m b) -> m a (>>=/) a = (a >>=) . tee return findM :: (Monad m) => (a -> m (Maybe b)) -> [a] -> m (Maybe b) findM f = runMaybeT . msum . map (MaybeT . f) if' :: Bool -> a -> a -> a if' True x _ = x if' False _ y = y ifL :: a -> a -> Bool -> a ifL a b c = if' c a b toggleInMap' :: Ord k => Bool -> k -> M.Map k Bool -> M.Map k Bool toggleInMap' d k m = let existingValue = M.findWithDefault d k m in M.insert k (not existingValue) m toggleInMap :: Ord k => k -> M.Map k Bool -> M.Map k Bool toggleInMap = toggleInMap' True maybeRemap k = M.findWithDefault k k (<$.>) :: Functor f => (b -> c) -> (a -> f b) -> a -> f c (<$.>) l r = fmap l . r withFocusedR f = withWindowSet (f . W.peek) withFocusedD d f = maybe (return d) f <$> withWindowSet (return . W.peek) mapP = mapP' id mapP' f f' = map (f A.&&& f') -- Selectors isHangoutsTitle = isPrefixOf "Google Hangouts" chromeSelectorBase = className =? "Google-chrome" chromeSelector = chromeSelectorBase <&&> fmap (not . isHangoutsTitle) title spotifySelector = className =? "Spotify" emacsSelector = className =? "Emacs" transmissionSelector = fmap (isPrefixOf "Transmission") title hangoutsSelector = chromeSelectorBase <&&> fmap isHangoutsTitle title virtualClasses = [ (hangoutsSelector, "Hangouts") , (chromeSelector, "Chrome") , (transmissionSelector, "Transmission") ] -- Commands hangoutsCommand = "start_hangouts.sh" spotifyCommand = "spotify" chromeCommand = "google-chrome-stable" emacsCommand = "emacsclient -c" htopCommnad = "urxvt -e htop" transmissionCommand = "transmission-gtk" -- Startup hook tvScreenId :: ScreenId tvScreenId = 1 disableTVFading = setFading (Just tvScreenId) False hostNameToAction = M.fromList [ ("imalison-arch", disableTVFading) , ("imalison-uber-loaner", return ()) ] myStartup = do spawn "systemctl --user start wm.target" hostName <- io getHostName M.findWithDefault (return ()) hostName hostNameToAction -- Manage hook myManageHook = composeAll . concat $ [ [transmissionSelector --> doShift "5"] -- Hangouts being on a separate workspace freezes chrome -- , [ hangoutsSelector --> doShift "2"] ] -- Toggles unmodifyLayout (ModifiedLayout _ x') = x' selectLimit = DM.menuArgs "rofi" ["-dmenu", "-i"] ["2", "3", "4"] >>= (setLimit . read) data MyToggles = LIMIT | GAPS | MAGICFOCUS deriving (Read, Show, Eq, Typeable) instance Transformer MyToggles Window where transform LIMIT x k = k (limitSlice 2 x) unmodifyLayout transform GAPS x k = k (smartSpacing 5 x) unmodifyLayout transform MAGICFOCUS x k = k (magicFocus x) unmodifyLayout myToggles = [LIMIT, GAPS, MAGICFOCUS] otherToggles = [NBFULL, MIRROR] followIfNoMagicFocus = followOnlyIf $ maybe False not <$> isToggleActive MAGICFOCUS togglesMap = fmap M.fromList $ sequence $ map toggleTuple myToggles ++ map toggleTuple otherToggles where toggleTuple toggle = fmap (\str -> (str, Toggle toggle)) (toggleToStringWithState toggle) toggleStateToString s = case s of Just True -> "ON" Just False -> "OFF" Nothing -> "N/A" toggleToStringWithState :: (Transformer t Window, Show t) => t -> X String toggleToStringWithState toggle = printf "%s (%s)" (show toggle) . toggleStateToString <$> isToggleActive toggle selectToggle = togglesMap >>= DM.menuMapArgs "rofi" ["-dmenu", "-i"] >>= flip whenJust sendMessage toggleInState :: (Transformer t Window) => t -> Maybe Bool -> X Bool toggleInState t s = fmap (/= s) (isToggleActive t) setToggleActive' toggle active = toggleInState toggle (Just active) >>=/ flip when (sendMessage $ Toggle toggle) -- Ambiguous type reference without signature setToggleActive :: (Transformer t Window) => t -> Bool -> X () setToggleActive = (void .) . setToggleActive' deactivateFull = setToggleActive NBFULL False toggleOr toggle toState action = setToggleActive' toggle toState >>= ((`when` action) . not) deactivateFullOr = toggleOr NBFULL False deactivateFullAnd action = sequence_ [deactivateFull, action] andDeactivateFull action = sequence_ [action, deactivateFull] goFullscreen = sendMessage $ Toggle NBFULL -- Layout setup rename newName = RN.renamed [RN.Replace newName] layoutsStart layout = (layout, [Layout layout]) (|||!) (joined, layouts) newLayout = (joined ||| newLayout, layouts ++ [Layout newLayout]) layoutInfo = layoutsStart (rename "Columns" $ multiCol [1, 1] 2 0.01 (-0.5)) |||! rename "Large Main" (Tall 1 (3 / 100) (3 / 4)) |||! rename "2 Columns" (Tall 1 (3 / 100) (1 / 2)) |||! Accordion layoutList = snd layoutInfo layoutNames = [description layout | layout <- layoutList] selectLayout = DM.menuArgs "rofi" ["-dmenu", "-i"] layoutNames >>= (sendMessage . JumpToLayout) myLayoutHook = avoidStruts . minimize . boringAuto . mkToggle1 MIRROR . mkToggle1 LIMIT . mkToggle1 GAPS . mkToggle1 MAGICFOCUS . mkToggle1 NBFULL . workspaceNamesHook . smartBorders . noBorders $ fst layoutInfo -- WindowBringer myWindowBringerConfig = WindowBringerConfig { menuCommand = "rofi" , menuArgs = ["-dmenu", "-i"] , windowTitler = myDecorateName } classIfMatches window entry = if' <$> runQuery (fst entry) window <*> pure (Just $ snd entry) <*> pure Nothing getClassRaw w = fmap resClass $ withDisplay $ io . flip getClassHint w getVirtualClass = flip findM virtualClasses . classIfMatches getClass w = fromMaybe <$> getClassRaw w <*> getVirtualClass w myDecorateName ws w = do name <- show <$> getName w classTitle <- getClass w workspaceToName <- getWorkspaceNames return $ printf "%-20s%-40s %+30s" classTitle (take 40 name) "in " ++ workspaceToName (W.tag ws) -- This needs access to X in order to unminimize, which means that I can't be -- done with the existing window bringer interface myBringWindow WindowBringerConfig { menuCommand = cmd , menuArgs = args , windowTitler = titler } = windowMap' titler >>= DM.menuMapArgs cmd args >>= flip whenJust action where action window = sequence_ [ maximizeWindow window , windows $ W.focusWindow window . bringWindow window ] -- Dynamic Workspace Renaming windowClassFontAwesomeFile = fmap ( ".lib/resources/window_class_to_fontawesome.json") getHomeDirectory getClassRemap = fmap (fromMaybe M.empty . decode) $ windowClassFontAwesomeFile >>= B.readFile getClassRemapF = flip maybeRemap <$> getClassRemap getWSClassNames' w = mapM getClass $ W.integrate' $ W.stack w getWSClassNames w = io (fmap map getClassRemapF) <*> getWSClassNames' w currentWSName ws = fromMaybe "" <$> (getWorkspaceNames' <*> pure (W.tag ws)) desiredWSName = (intercalate "|" <$>) . getWSClassNames setWorkspaceNameToFocusedWindow workspace = do currentName <- currentWSName workspace newName <- desiredWSName workspace when (currentName /= newName) $ setWorkspaceName (W.tag workspace) newName setWorkspaceNames = gets windowset >>= mapM_ setWorkspaceNameToFocusedWindow . W.workspaces data WorkspaceNamesHook a = WorkspaceNamesHook deriving (Show, Read) instance LayoutModifier WorkspaceNamesHook Window where hook _ = setWorkspaceNames workspaceNamesHook = ModifiedLayout WorkspaceNamesHook -- Toggleable fade newtype ToggleFade a = ToggleFade { fadesMap :: M.Map a Bool } deriving (Typeable, Read, Show) instance (Typeable a, Read a, Show a, Ord a) => ExtensionClass (ToggleFade a) where initialValue = ToggleFade M.empty extensionType = PersistentExtension fadeEnabledFor query = M.findWithDefault True <$> query <*> liftX (fadesMap <$> XS.get) fadeEnabledForWindow = fadeEnabledFor ask fadeEnabledForWorkspace = fadeEnabledFor getWindowWorkspace fadeEnabledForScreen = fadeEnabledFor getWindowScreen getScreens = withWindowSet $ return . W.screens getWindowWorkspace' = W.findTag <$> ask <*> liftX (withWindowSet return) getWindowWorkspace = flip fromMaybe <$> getWindowWorkspace' <*> pure "1" getWorkspaceToScreen = M.fromList . mapP' (W.tag . W.workspace) W.screen <$> getScreens getWindowScreen = M.lookup <$> getWindowWorkspace <*> liftX getWorkspaceToScreen getCurrentScreen = join (withFocusedD Nothing (runQuery getWindowScreen)) fadeCondition = isUnfocused <&&> fadeEnabledForWindow <&&> fadeEnabledForWorkspace <&&> fadeEnabledForScreen toggleFadeInactiveLogHook = fadeOutLogHook . fadeIf fadeCondition toggleFadingForActiveWindow = withWindowSet $ maybe (return ()) toggleFading . W.peek toggleFadingForActiveWorkspace = withWindowSet $ \ws -> toggleFading $ W.currentTag ws toggleFadingForActiveScreen = getCurrentScreen >>= toggleFading toggleFading w = setFading' $ toggleInMap w setFading w f = setFading' $ M.insert w f setFading' f = fmap (ToggleFade . f . fadesMap) XS.get >>= XS.put -- Minimize not in class restoreFocus action = withFocused $ \orig -> action >> windows (W.focusWindow orig) getCurrentWS = W.stack . W.workspace . W.current withWorkspace f = withWindowSet $ \ws -> maybe (return ()) f (getCurrentWS ws) currentWS = withWindowSet $ return . getCurrentWS workspaceWindows = maybe [] W.integrate <$> currentWS minimizedWindows = withMinimized return getMinMaxWindows = partition <$> (flip elem <$> minimizedWindows) <*> workspaceWindows maximizedWindows = fmap snd getMinMaxWindows maximizedOtherClass = intersect <$> maximizedWindows <*> (currentWS >>= maybe (return []) windowsWithUnfocusedClass) getClassMatchesWindow w = (==) <$> getClass w getClassMatchesCurrent = join $ withFocusedD (`seq` False) getClassMatchesWindow minimizeOtherClassesInWorkspace = actOnWindowsInWorkspace minimizeWindow windowsWithUnfocusedClass maximizeSameClassesInWorkspace = actOnWindowsInWorkspace maybeUnminimize windowsWithFocusedClass -- Type annotation is needed to resolve ambiguity actOnWindowsInWorkspace :: (Window -> X ()) -> (W.Stack Window -> X [Window]) -> X () actOnWindowsInWorkspace windowAction getWindowsAction = restoreFocus $ withWorkspace (getWindowsAction >=> mapM_ windowAction) windowsWithUnfocusedClass ws = windowsWithOtherClasses (W.focus ws) ws windowsWithFocusedClass ws = windowsWithSameClass (W.focus ws) ws windowsWithOtherClasses = windowsMatchingClassPredicate (/=) windowsWithSameClass = windowsMatchingClassPredicate (==) windowsMatchingClassPredicate predicate window workspace = windowsSatisfyingPredicate workspace $ do windowClass <- getClass window return $ predicate windowClass windowsSatisfyingPredicate workspace getPredicate = do predicate <- getPredicate filterM (\w -> predicate <$> getClass w) (W.integrate workspace) getMatchingUnmatching = partition <$> ((. snd) <$> getClassMatchesCurrent) <*> getWindowClassPairs getWindowClassPairs = join $ mapM windowToClassPair <$> workspaceWindows windowToClassPair w = (,) w <$> getClass w windowIsMinimized w = do minimized <- XS.gets minimizedStack return $ w `elem` minimized maybeUnminimize w = windowIsMinimized w >>= flip when (maximizeWindow w) maybeUnminimizeFocused = withFocused maybeUnminimize maybeUnminimizeAfter = (>> maybeUnminimizeFocused) maybeUnminimizeClassAfter = (>> maximizeSameClassesInWorkspace) sameClassOnly action = action >> minimizeOtherClassesInWorkspace >> maximizeSameClassesInWorkspace restoreAllMinimized = void $ join $ mapM maximizeWindow <$> minimizedWindows restoreOrMinimizeOtherClasses = null <$> maximizedOtherClass >>= ifL restoreAllMinimized minimizeOtherClassesInWorkspace getClassPair w = flip (,) w <$> getClass w windowClassPairs = withWindowSet $ mapM getClassPair . W.allWindows classToWindowMap = MM.fromList <$> windowClassPairs allClasses = sort . MM.keys <$> classToWindowMap thisClass = withWindowSet $ sequence . (getClass <$.> W.peek) nextClass = do classes <- allClasses current <- thisClass let index = join $ elemIndex <$> current <*> pure classes return $ fmap (\i -> cycle classes !! (i + 1)) index classWindow c = do m <- classToWindowMap return $ join $ listToMaybe <$> (flip MM.lookup m <$> c) nextClassWindow = nextClass >>= classWindow focusNextClass' = join $ windows . maybe id greedyFocusWindow <$> nextClassWindow focusNextClass = sameClassOnly focusNextClass' selectClass = join $ DM.menuArgs "rofi" ["-dmenu", "-i"] <$> allClasses -- Window switching -- Use greedyView to switch to the correct workspace, and then focus on the -- appropriate window within that workspace. greedyFocusWindow w ws = W.focusWindow w $ W.greedyView (fromMaybe (W.currentTag ws) $ W.findTag w ws) ws shiftThenView i = W.greedyView i . W.shift i greedyBringWindow w = greedyFocusWindow w . bringWindow w shiftToEmptyAndView = doTo Next EmptyWS DWO.getSortByOrder (windows . shiftThenView) setFocusedScreen :: ScreenId -> WindowSet -> WindowSet setFocusedScreen to ws = maybe ws (`setFocusedScreen'` ws) $ find ((to ==) . W.screen) (W.visible ws) setFocusedScreen' to ws @ W.StackSet { W.current = prevCurr , W.visible = visible } = ws { W.current = to , W.visible = prevCurr:deleteBy screenEq to visible } where screenEq a b = W.screen a == W.screen b nextScreen ws @ W.StackSet { W.visible = visible } = case visible of next:_ -> setFocusedScreen (W.screen next) ws _ -> ws viewOtherScreen ws = W.greedyView ws . nextScreen shiftThenViewOtherScreen ws w = viewOtherScreen ws . W.shiftWin ws w shiftCurrentToWSOnOtherScreen ws s = fromMaybe s (flip (shiftThenViewOtherScreen ws) s <$> W.peek s) shiftToEmptyNextScreen = doTo Next EmptyWS DWO.getSortByOrder $ windows . shiftCurrentToWSOnOtherScreen swapFocusedWith w ws = W.modify' (swapFocusedWith' w) (W.delete' w ws) swapFocusedWith' w (W.Stack current ls rs) = W.Stack w ls (rs ++ [current]) swapMinimizeStateAfter action = withFocused $ \originalWindow -> do _ <- action restoreFocus $ do maybeUnminimizeFocused withFocused $ \newWindow -> when (newWindow /= originalWindow) $ minimizeWindow originalWindow -- Named Scratchpads scratchpads = [ NS "htop" htopCommnad (title =? "htop") nonFloating , NS "spotify" spotifyCommand spotifySelector nonFloating , NS "hangouts" hangoutsCommand hangoutsSelector nonFloating ] doScratchpad = deactivateFullAnd . namedScratchpadAction scratchpads -- Raise or spawn myRaiseNextMaybe = ((deactivateFullAnd . maybeUnminimizeClassAfter) .) . raiseNextMaybeCustomFocus greedyFocusWindow myBringNextMaybe = ((deactivateFullAnd . maybeUnminimizeAfter) .) . raiseNextMaybeCustomFocus greedyBringWindow bindBringAndRaise :: KeyMask -> KeySym -> X () -> Query Bool -> [((KeyMask, KeySym), X ())] bindBringAndRaise mask sym start query = [ ((mask, sym), doRaiseNext) , ((mask .|. controlMask, sym), myBringNextMaybe start query) , ((mask .|. shiftMask, sym), doRaiseNext >> minimizeOtherClassesInWorkspace) ] where doRaiseNext = myRaiseNextMaybe start query bindBringAndRaiseMany :: [(KeyMask, KeySym, X (), Query Bool)] -> [((KeyMask, KeySym), X())] bindBringAndRaiseMany = concatMap (\(a, b, c, d) -> bindBringAndRaise a b c d) -- Screen shift shiftToNextScreen = withWindowSet $ \ws -> case W.visible ws of W.Screen i _ _:_ -> windows $ W.view (W.tag i) . W.shift (W.tag i) _ -> return () -- Key bindings addKeys conf@XConfig {modMask = modm} = [ ((modm, xK_p), spawn "rofi -show drun") , ((modm .|. shiftMask, xK_p), spawn "rofi -show run") , ((modm, xK_g), andDeactivateFull . maybeUnminimizeAfter $ actionMenu myWindowBringerConfig greedyFocusWindow) , ((modm .|. shiftMask, xK_g), andDeactivateFull . sameClassOnly $ actionMenu myWindowBringerConfig greedyFocusWindow) , ((modm, xK_b), andDeactivateFull $ myBringWindow myWindowBringerConfig) , ((modm .|. shiftMask, xK_b), swapMinimizeStateAfter $ actionMenu myWindowBringerConfig swapFocusedWith) , ((modm .|. controlMask, xK_t), spawn "systemctl --user restart taffybar.service") , ((modm, xK_v), spawn "copyq paste") , ((modm, xK_s), swapNextScreen) , ((modm .|. controlMask, xK_space), goFullscreen) , ((modm, xK_slash), sendMessage $ Toggle MIRROR) , ((modm, xK_m), withFocused minimizeWindow) , ((modm .|. shiftMask, xK_m), deactivateFullOr $ withLastMinimized maximizeWindowAndFocus) , ((modm, xK_backslash), toggleWS) , ((modm, xK_space), deactivateFullOr $ sendMessage NextLayout) , ((modm, xK_z), shiftToNextScreen) , ((modm .|. shiftMask, xK_z), shiftToEmptyNextScreen) , ((modm, xK_x), windows $ W.shift "NSP") , ((modm .|. shiftMask, xK_h), shiftToEmptyAndView) -- These need to be rebound to support boringWindows , ((modm, xK_j), focusDown) , ((modm, xK_k), focusUp) , ((modm, xK_m), focusMaster) , ((modm, xK_Tab), focusNextClass) , ((modm .|. controlMask, xK_s), spawn "split_out.sh") -- Hyper bindings , ((mod3Mask, xK_1), toggleFadingForActiveWindow) , ((mod3Mask .|. shiftMask, xK_1), toggleFadingForActiveWorkspace) , ((mod3Mask .|. controlMask, xK_1), toggleFadingForActiveScreen) , ((mod3Mask, xK_e), moveTo Next EmptyWS) , ((mod3Mask, xK_v), spawn "copyq_rofi.sh") , ((mod3Mask, xK_p), spawn "rofi_password.sh") , ((mod3Mask, xK_h), spawn "screenshot.sh") , ((mod3Mask, xK_c), spawn "shell_command.sh") , ((mod3Mask .|. shiftMask, xK_l), spawn "dm-tool lock") , ((mod3Mask, xK_l), selectLayout) , ((mod3Mask, xK_k), spawn "rofi_kill_process.sh") , ((mod3Mask, xK_t), selectToggle) , ((mod3Mask, xK_r), spawn "rofi_restart_service.sh") -- ModAlt bindings , ((modalt, xK_w), spawn "rofi_wallpaper.sh") , ((modalt, xK_z), spawn "split_out_chrome_tab.sh") , ((modalt, xK_space), deactivateFullOr restoreOrMinimizeOtherClasses) , ((modalt, xK_Return), deactivateFullAnd restoreAllMinimized) , ((modalt, xK_4), selectLimit) -- ScratchPads , ((modalt, xK_m), doScratchpad "htop") , ((modalt .|. controlMask, xK_s), doScratchpad "spotify") , ((modalt .|. controlMask, xK_h), doScratchpad "hangouts") , ((modalt, xK_h), myRaiseNextMaybe (spawn hangoutsCommand) hangoutsSelector) , ((modalt, xK_s), myRaiseNextMaybe (spawn spotifyCommand) spotifySelector) -- playerctl , ((mod3Mask, xK_f), spawn "playerctl play-pause") , ((0, xF86XK_AudioPause), spawn "playerctl play-pause") , ((0, xF86XK_AudioPlay), spawn "playerctl play-pause") , ((mod3Mask, xK_d), spawn "playerctl next") , ((0, xF86XK_AudioNext), spawn "playerctl next") , ((mod3Mask, xK_a), spawn "playerctl previous") , ((0, xF86XK_AudioPrev), spawn "playerctl previous") -- volume control , ((0, xF86XK_AudioRaiseVolume), spawn "pulseaudio-ctl up") , ((0, xF86XK_AudioLowerVolume), spawn "pulseaudio-ctl down") , ((0, xF86XK_AudioMute), spawn "pulseaudio-ctl mute") , ((mod3Mask, xK_w), spawn "pulseaudio-ctl up") , ((mod3Mask, xK_s), spawn "pulseaudio-ctl down") ] ++ bindBringAndRaiseMany [ (modalt, xK_e, spawn emacsCommand, emacsSelector) , (modalt, xK_c, spawn chromeCommand, chromeSelector) -- , (modalt, xK_s, spawn spotifyCommand, spotifySelector) -- , (modalt, xK_h, spawn hangoutsCommand, hangoutsSelector) , (modalt, xK_t, spawn transmissionCommand, transmissionSelector) ] ++ -- Replace original moving stuff around + greedy view bindings [((additionalMask .|. modm, key), windows $ function workspace) | (workspace, key) <- zip (workspaces conf) [xK_1 .. xK_9] , (function, additionalMask) <- [ (W.greedyView, 0) , (W.shift, shiftMask) , (shiftThenView, controlMask)]] where modalt = modm .|. mod1Mask -- Local Variables: -- flycheck-ghc-args: ("-Wno-missing-signatures") -- haskell-indent-offset: 2 -- End: