mirror of
https://github.com/xmonad/xmonad-contrib.git
synced 2025-07-26 09:41:52 -07:00
Compare commits
81 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
6072d9c599 | ||
|
277412af44 | ||
|
0030802e46 | ||
|
f211874340 | ||
|
32548e056f | ||
|
2c6f1c22b2 | ||
|
b8a22c4dee | ||
|
42443e3df2 | ||
|
9e0eb7f770 | ||
|
895c47fb4e | ||
|
cc98355700 | ||
|
205e7133ac | ||
|
9caedf2fff | ||
|
265df96ab8 | ||
|
8d1ad8b280 | ||
|
de84dfef0d | ||
|
3fa51ed656 | ||
|
a9911d2168 | ||
|
1716ffd9d0 | ||
|
e776260133 | ||
|
53c2e7833c | ||
|
fbb9eb36f9 | ||
|
4da5da430e | ||
|
0af63a4767 | ||
|
7245766c6d | ||
|
cd6feb81e2 | ||
|
8f9fa05c0f | ||
|
b5f9a61dbe | ||
|
96ab91fcfa | ||
|
3c74148a2f | ||
|
9d34e848d9 | ||
|
a7c2c023fb | ||
|
814fda056b | ||
|
2a6709ff5c | ||
|
3ffc956b93 | ||
|
e705eba1e0 | ||
|
2f2a217b85 | ||
|
6f996bb21f | ||
|
3a740c4d5a | ||
|
f09a61f5f5 | ||
|
1a735f04e3 | ||
|
d2739b1683 | ||
|
9ecc76e087 | ||
|
7b21732ead | ||
|
c691988bbf | ||
|
40d8c01894 | ||
|
328293a0a8 | ||
|
434aec1038 | ||
|
69d2e0a873 | ||
|
9454dd5d7f | ||
|
60713064e7 | ||
|
98b0e8e4c1 | ||
|
d2a076b1e7 | ||
|
e2bb57bd63 | ||
|
d5e7d6217f | ||
|
4feb4fb058 | ||
|
3f39d34994 | ||
|
7789f18ce9 | ||
|
807d356743 | ||
|
c012b3408d | ||
|
f6a050e5a3 | ||
|
92e8f5ebef | ||
|
dd591587f6 | ||
|
219b4dd8fb | ||
|
b944b1129c | ||
|
08d432bde6 | ||
|
04d6cbc5f0 | ||
|
9cafb7c2af | ||
|
272c333f75 | ||
|
aa96dd6e03 | ||
|
59bfe97f63 | ||
|
64efea4d0a | ||
|
a1a578010c | ||
|
9209e96234 | ||
|
c809ae6f5f | ||
|
9b369949ff | ||
|
9e69773d98 | ||
|
2f0ac73313 | ||
|
95290ed278 | ||
|
a551d1367c | ||
|
cb795c8c75 |
@@ -28,6 +28,8 @@ module XMonad.Actions.DynamicWorkspaceOrder
|
||||
, moveToGreedy
|
||||
, shiftTo
|
||||
|
||||
, withNthWorkspace
|
||||
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
@@ -162,4 +164,15 @@ moveToGreedy dir t = doTo dir t getSortByOrder (windows . W.greedyView)
|
||||
-- | Shift the currently focused window to the next workspace of the
|
||||
-- given type in the given direction, using the dynamic workspace order.
|
||||
shiftTo :: Direction1D -> WSType -> X ()
|
||||
shiftTo dir t = doTo dir t getSortByOrder (windows . W.shift)
|
||||
shiftTo dir t = doTo dir t getSortByOrder (windows . W.shift)
|
||||
|
||||
-- | Do something with the nth workspace in the dynamic order. The
|
||||
-- callback is given the workspace's tag as well as the 'WindowSet'
|
||||
-- of the workspace itself.
|
||||
withNthWorkspace :: (String -> WindowSet -> WindowSet) -> Int -> X ()
|
||||
withNthWorkspace job wnum = do
|
||||
sort <- getSortByOrder
|
||||
ws <- gets (map W.tag . sort . W.workspaces . windowset)
|
||||
case drop wnum ws of
|
||||
(w:_) -> windows $ job w
|
||||
[] -> return ()
|
@@ -23,6 +23,7 @@ module XMonad.Actions.DynamicWorkspaces (
|
||||
addHiddenWorkspace,
|
||||
withWorkspace,
|
||||
selectWorkspace, renameWorkspace,
|
||||
renameWorkspaceByName,
|
||||
toNthWorkspace, withNthWorkspace
|
||||
) where
|
||||
|
||||
@@ -73,11 +74,13 @@ withWorkspace c job = do ws <- gets (workspaces . windowset)
|
||||
mkXPrompt (Wor "") c (mkCompl ts) job'
|
||||
|
||||
renameWorkspace :: XPConfig -> X ()
|
||||
renameWorkspace conf = workspacePrompt conf $ \w ->
|
||||
windows $ \s -> let sett wk = wk { tag = w }
|
||||
setscr scr = scr { workspace = sett $ workspace scr }
|
||||
sets q = q { current = setscr $ current q }
|
||||
in sets $ removeWorkspace' w s
|
||||
renameWorkspace conf = workspacePrompt conf renameWorkspaceByName
|
||||
|
||||
renameWorkspaceByName :: String -> X ()
|
||||
renameWorkspaceByName w = windows $ \s -> let sett wk = wk { tag = w }
|
||||
setscr scr = scr { workspace = sett $ workspace scr }
|
||||
sets q = q { current = setscr $ current q }
|
||||
in sets $ removeWorkspace' w s
|
||||
|
||||
toNthWorkspace :: (String -> X ()) -> Int -> X ()
|
||||
toNthWorkspace job wnum = do sort <- getSortByIndex
|
||||
|
@@ -15,7 +15,7 @@
|
||||
module XMonad.Actions.FindEmptyWorkspace (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
viewEmptyWorkspace, tagToEmptyWorkspace
|
||||
viewEmptyWorkspace, tagToEmptyWorkspace, sendToEmptyWorkspace
|
||||
) where
|
||||
|
||||
import Data.List
|
||||
@@ -65,3 +65,8 @@ viewEmptyWorkspace = withEmptyWorkspace (windows . view)
|
||||
-- all workspaces are in use.
|
||||
tagToEmptyWorkspace :: X ()
|
||||
tagToEmptyWorkspace = withEmptyWorkspace $ \w -> windows $ view w . shift w
|
||||
|
||||
-- | Send current window to an empty workspace. Do nothing if
|
||||
-- all workspaces are in use.
|
||||
sendToEmptyWorkspace :: X ()
|
||||
sendToEmptyWorkspace = withEmptyWorkspace $ \w -> windows $ shift w
|
||||
|
@@ -74,6 +74,7 @@ module XMonad.Actions.GridSelect (
|
||||
import Data.Maybe
|
||||
import Data.Bits
|
||||
import Data.Char
|
||||
import Data.Ord (comparing)
|
||||
import Control.Applicative
|
||||
import Control.Monad.State
|
||||
import Control.Arrow
|
||||
@@ -234,12 +235,39 @@ data TwoDState a = TwoDState { td_curpos :: TwoDPosition
|
||||
}
|
||||
|
||||
td_elementmap :: TwoDState a -> [(TwoDPosition,(String,a))]
|
||||
td_elementmap s =
|
||||
let positions = td_availSlots s
|
||||
elements = L.filter (((td_searchString s) `isSubstringOf`) . fst) (td_elements s)
|
||||
in zipWith (,) positions elements
|
||||
where sub `isSubstringOf` string = or [ (upper sub) `isPrefixOf` t | t <- tails (upper string) ]
|
||||
upper = map toUpper
|
||||
td_elementmap s = zipWith (,) positions sortedElements
|
||||
where
|
||||
TwoDState {td_availSlots = positions,
|
||||
td_searchString = searchString} = s
|
||||
-- Filter out any elements that don't contain the searchString (case insensitive)
|
||||
filteredElements = L.filter ((searchString `isInfixOfI`) . fst) (td_elements s)
|
||||
-- Sorts the elementmap
|
||||
sortedElements = orderElementmap searchString filteredElements
|
||||
-- Case Insensitive version of isInfixOf
|
||||
needle `isInfixOfI` haystack = (upper needle) `isInfixOf` (upper haystack)
|
||||
upper = map toUpper
|
||||
|
||||
|
||||
-- | We enforce an ordering such that we will always get the same result. If the
|
||||
-- elements position changes from call to call of gridselect, then the shown
|
||||
-- positions will also change when you search for the same string. This is
|
||||
-- especially the case when using gridselect for showing and switching between
|
||||
-- workspaces, as workspaces are usually shown in order of last visited. The
|
||||
-- chosen ordering is "how deep in the haystack the needle is" (number of
|
||||
-- characters from the beginning of the string and the needle).
|
||||
orderElementmap :: String -> [(String,a)] -> [(String,a)]
|
||||
orderElementmap searchString elements = if not $ null searchString then sortedElements else elements
|
||||
where
|
||||
upper = map toUpper
|
||||
-- Calculates a (score, element) tuple where the score is the depth of the (case insensitive) needle.
|
||||
calcScore element = ( length $ takeWhile (not . isPrefixOf (upper searchString)) (tails . upper . fst $ element)
|
||||
, element)
|
||||
-- Use the score and then the string as the parameters for comparing, making
|
||||
-- it consistent even when two strings that score the same, as it will then be
|
||||
-- sorted by the strings, making it consistent.
|
||||
compareScore = comparing (\(score, (str,_)) -> (score, str))
|
||||
sortedElements = map snd . sortBy compareScore $ map calcScore elements
|
||||
|
||||
|
||||
newtype TwoD a b = TwoD { unTwoD :: StateT (TwoDState a) X b }
|
||||
deriving (Monad,Functor,MonadState (TwoDState a))
|
||||
|
@@ -20,7 +20,7 @@
|
||||
--
|
||||
----------------------------------------------------------------------
|
||||
|
||||
module XMonad.Actions.GroupNavigation ( -- * Usage
|
||||
module XMonad.Actions.GroupNavigation ( -- * Usage
|
||||
-- $usage
|
||||
Direction (..)
|
||||
, nextMatch
|
||||
@@ -46,7 +46,7 @@ import qualified XMonad.Util.ExtensibleState as XS
|
||||
|
||||
Import the module into your @~\/.xmonad\/xmonad.hs@:
|
||||
|
||||
> import XMonad.Actions,GroupNavigation
|
||||
> import XMonad.Actions.GroupNavigation
|
||||
|
||||
To support cycling forward and backward through all xterm windows, add
|
||||
something like this to your keybindings:
|
||||
@@ -110,13 +110,13 @@ nextMatch dir qry = nextMatchOrDo dir qry (return ())
|
||||
-- | Focuses the next window that matches the given boolean query. If
|
||||
-- there is no such window, perform the given action instead.
|
||||
nextMatchOrDo :: Direction -> Query Bool -> X () -> X ()
|
||||
nextMatchOrDo dir qry act = orderedWindowList dir
|
||||
nextMatchOrDo dir qry act = orderedWindowList dir
|
||||
>>= focusNextMatchOrDo qry act
|
||||
|
||||
-- Produces the action to perform depending on whether there's a
|
||||
-- matching window
|
||||
focusNextMatchOrDo :: Query Bool -> X () -> Seq Window -> X ()
|
||||
focusNextMatchOrDo qry act = findM (runQuery qry)
|
||||
focusNextMatchOrDo qry act = findM (runQuery qry)
|
||||
>=> maybe act (windows . SS.focusWindow)
|
||||
|
||||
-- Returns the list of windows ordered by workspace as specified in
|
||||
@@ -126,7 +126,7 @@ orderedWindowList History = liftM (\(HistoryDB w ws) -> maybe ws (ws |>) w) XS.g
|
||||
orderedWindowList dir = withWindowSet $ \ss -> do
|
||||
wsids <- asks (Seq.fromList . workspaces . config)
|
||||
let wspcs = orderedWorkspaceList ss wsids
|
||||
wins = dirfun dir
|
||||
wins = dirfun dir
|
||||
$ Fold.foldl' (><) Seq.empty
|
||||
$ fmap (Seq.fromList . SS.integrate' . SS.stack) wspcs
|
||||
cur = SS.peek ss
|
||||
@@ -148,7 +148,7 @@ orderedWorkspaceList ss wsids = rotateTo isCurWS wspcs'
|
||||
--- History navigation, requires a layout modifier -------------------
|
||||
|
||||
-- The state extension that holds the history information
|
||||
data HistoryDB = HistoryDB (Maybe Window) -- currently focused window
|
||||
data HistoryDB = HistoryDB (Maybe Window) -- currently focused window
|
||||
(Seq Window) -- previously focused windows
|
||||
deriving (Read, Show, Typeable)
|
||||
|
||||
@@ -182,12 +182,12 @@ flt :: (a -> Bool) -> Seq a -> Seq a
|
||||
flt p = Fold.foldl (\xs x -> if p x then xs |> x else xs) Seq.empty
|
||||
|
||||
brkl :: (a -> Bool) -> Seq a -> (Seq a, Seq a)
|
||||
brkl p xs = flip Seq.splitAt xs
|
||||
$ snd
|
||||
brkl p xs = flip Seq.splitAt xs
|
||||
$ snd
|
||||
$ Fold.foldr (\x (i, j) -> if p x then (i-1, i-1) else (i-1, j)) (l, l) xs
|
||||
where
|
||||
l = Seq.length xs
|
||||
|
||||
|
||||
--- Some sequence helpers --------------------------------------------
|
||||
|
||||
-- Rotates the sequence by one position
|
||||
|
@@ -9,7 +9,7 @@
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Remap Keybinding on the fly, e.g having Dvorak char, but everything with Control/Shift
|
||||
-- Remap Keybinding on the fly, e.g having Dvorak char, but everything with Control/Shift
|
||||
-- is left us Layout
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
@@ -42,7 +42,7 @@ instance ExtensionClass KeymapTable where
|
||||
-- $usage
|
||||
-- Provides the possibility to remap parts of the keymap to generate different keys
|
||||
--
|
||||
-- * E.g You want to type Programmers Dvorak, but your keybindings should be the normal us layout
|
||||
-- * E.g You want to type Programmers Dvorak, but your keybindings should be the normal us layout
|
||||
-- after all
|
||||
--
|
||||
-- First, you must add all possible keybindings for all layout you want to use:
|
||||
|
123
XMonad/Actions/Launcher.hs
Normal file
123
XMonad/Actions/Launcher.hs
Normal file
@@ -0,0 +1,123 @@
|
||||
{- |
|
||||
Module : XMonad.Actions.Launcher
|
||||
Copyright : (C) 2012 Carlos López-Camey
|
||||
License : None; public domain
|
||||
|
||||
Maintainer : <c.lopez@kmels.net>
|
||||
Stability : unstable
|
||||
|
||||
A set of prompts for XMonad
|
||||
-}
|
||||
|
||||
module XMonad.Actions.Launcher(
|
||||
-- * Description and use
|
||||
-- $description
|
||||
defaultLauncherModes
|
||||
, ExtensionActions
|
||||
, LauncherConfig(..)
|
||||
, launcherPrompt
|
||||
) where
|
||||
|
||||
import Data.List (find, findIndex, isPrefixOf, tails)
|
||||
import qualified Data.Map as M
|
||||
import Data.Maybe (isJust)
|
||||
import XMonad hiding (config)
|
||||
import XMonad.Prompt
|
||||
import XMonad.Util.Run
|
||||
|
||||
{- $description
|
||||
This module exemplifies usage of `XMonad.Prompt.mkXPromptWithModes`. It includes two modes:
|
||||
|
||||
* Hoogle mode: Search for functions using hoogle, choosing a function leads you to documentation in Haddock.
|
||||
|
||||
* Calc: Uses the program calc to do calculations.
|
||||
|
||||
To test it, modify your local .xmonad:
|
||||
|
||||
> import XMonad.Prompt(defaultXPConfig)
|
||||
> import XMonad.Actions.Launcher
|
||||
|
||||
> ((modm .|. controlMask, xK_l), launcherPrompt defaultXPConfig $ defaultLauncherModes launcherConfig)
|
||||
|
||||
A LauncherConfig contains settings for the default modes, modify them accordingly.
|
||||
|
||||
> launcherConfig = LauncherConfig { pathToHoogle = "/home/YOU/.cabal/bin/hoogle" , browser = "firefox"}
|
||||
|
||||
Restart xmonad. Press Ctrl + Your_Modkey + L and the first prompt should pop up.
|
||||
|
||||
If you used 'defaultXPConfig', you can change mode with 'xK_grave'. If you are using your own 'XPConfig', define the value for 'changeModeKey'.
|
||||
-}
|
||||
|
||||
data HoogleMode = HMode FilePath String --path to hoogle and browser
|
||||
data CalculatorMode = CalcMode
|
||||
|
||||
data LauncherConfig = LauncherConfig {
|
||||
browser :: String
|
||||
, pathToHoogle :: String
|
||||
}
|
||||
|
||||
type ExtensionActions = M.Map String (String -> X())
|
||||
|
||||
-- | Uses the command `calc` to compute arithmetic expressions
|
||||
instance XPrompt CalculatorMode where
|
||||
showXPrompt CalcMode = "calc %s> "
|
||||
commandToComplete CalcMode = id --send the whole string to `calc`
|
||||
completionFunction CalcMode = \s -> if (length s == 0) then return [] else do
|
||||
fmap lines $ runProcessWithInput "calc" [s] ""
|
||||
modeAction CalcMode _ _ = return () -- do nothing; this might copy the result to the clipboard
|
||||
|
||||
-- | Uses the program `hoogle` to search for functions
|
||||
instance XPrompt HoogleMode where
|
||||
showXPrompt _ = "hoogle %s> "
|
||||
commandToComplete _ = id
|
||||
completionFunction (HMode pathToHoogleBin' _) = \s -> completionFunctionWith pathToHoogleBin' ["--count","8",s]
|
||||
-- This action calls hoogle again to find the URL corresponding to the autocompleted item
|
||||
modeAction (HMode pathToHoogleBin'' browser') query result = do
|
||||
completionsWithLink <- liftIO $ completionFunctionWith pathToHoogleBin'' ["--count","5","--link",query]
|
||||
let link = do
|
||||
s <- find (isJust . \complStr -> findSeqIndex complStr result) completionsWithLink
|
||||
i <- findSeqIndex s "http://"
|
||||
return $ drop i s
|
||||
case link of
|
||||
Just l -> spawn $ browser' ++ " " ++ l
|
||||
_ -> return ()
|
||||
where
|
||||
-- | Receives a sublist and a list. It returns the index where the sublist appears in the list.
|
||||
findSeqIndex :: (Eq a) => [a] -> [a] -> Maybe Int
|
||||
findSeqIndex xs xss = findIndex (isPrefixOf xss) $ tails xs
|
||||
|
||||
-- | Creates an autocompletion function for a programm given the program's name and a list of args to send to the command.
|
||||
completionFunctionWith :: String -> [String] -> IO [String]
|
||||
completionFunctionWith cmd args = do fmap lines $ runProcessWithInput cmd args ""
|
||||
|
||||
-- | Creates a prompt with the given modes
|
||||
launcherPrompt :: XPConfig -> [XPMode] -> X()
|
||||
launcherPrompt config modes = mkXPromptWithModes modes config
|
||||
|
||||
-- | Create a list of modes based on :
|
||||
-- a list of extensions mapped to actions
|
||||
-- the path to hoogle
|
||||
defaultLauncherModes :: LauncherConfig -> [XPMode]
|
||||
defaultLauncherModes cnf = let
|
||||
ph = pathToHoogle cnf
|
||||
in [ hoogleMode ph $ browser cnf
|
||||
, calcMode]
|
||||
|
||||
hoogleMode :: FilePath -> String -> XPMode
|
||||
hoogleMode pathToHoogleBin browser' = XPT $ HMode pathToHoogleBin browser'
|
||||
calcMode :: XPMode
|
||||
calcMode = XPT CalcMode
|
||||
|
||||
{-
|
||||
|
||||
-- ideas for XMonad.Prompt running on mode XPMultipleModes
|
||||
* Switch to mode by name of the prompt, 1. ':' at an empty(?) buffer, 2. autocomplete name in buffer should happen, 3. switch to mode with enter (cancel switch with C-g)
|
||||
|
||||
* Support for actions of type String -> X a
|
||||
|
||||
-- ideas for this module
|
||||
|
||||
* Hoogle mode: add a setting in the action to either go to documentation or to the source code (needs hoogle change?)
|
||||
|
||||
* Hoogle mode: add setting to query hoogle at haskell.org instead (with &mode=json)
|
||||
-}
|
778
XMonad/Actions/Navigation2D.hs
Normal file
778
XMonad/Actions/Navigation2D.hs
Normal file
@@ -0,0 +1,778 @@
|
||||
{-# LANGUAGE DeriveDataTypeable, MultiParamTypeClasses, PatternGuards, RankNTypes, TypeSynonymInstances #-}
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Layout.Navigation2D
|
||||
-- Copyright : (c) 2011 Norbert Zeh <nzeh@cs.dal.ca>
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : Norbert Zeh <nzeh@cs.dal.ca>
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Navigation2D is an xmonad extension that allows easy directional
|
||||
-- navigation of windows and screens (in a multi-monitor setup).
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Actions.Navigation2D ( -- * Usage
|
||||
-- $usage
|
||||
|
||||
-- * Finer points
|
||||
-- $finer_points
|
||||
|
||||
-- * Alternative directional navigation modules
|
||||
-- $alternatives
|
||||
|
||||
-- * Incompatibilities
|
||||
-- $incompatibilities
|
||||
|
||||
-- * Detailed technical discussion
|
||||
-- $technical
|
||||
|
||||
-- * Exported functions and types
|
||||
-- #Exports#
|
||||
|
||||
withNavigation2DConfig
|
||||
, Navigation2DConfig(..)
|
||||
, defaultNavigation2DConfig
|
||||
, Navigation2D
|
||||
, lineNavigation
|
||||
, centerNavigation
|
||||
, fullScreenRect
|
||||
, singleWindowRect
|
||||
, switchLayer
|
||||
, windowGo
|
||||
, windowSwap
|
||||
, windowToScreen
|
||||
, screenGo
|
||||
, screenSwap
|
||||
, Direction2D(..)
|
||||
) where
|
||||
|
||||
import Control.Applicative
|
||||
import qualified Data.List as L
|
||||
import qualified Data.Map as M
|
||||
import Data.Maybe
|
||||
import XMonad hiding (Screen)
|
||||
import qualified XMonad.StackSet as W
|
||||
import qualified XMonad.Util.ExtensibleState as XS
|
||||
import XMonad.Util.Types
|
||||
|
||||
-- $usage
|
||||
-- #Usage#
|
||||
-- Navigation2D provides directional navigation (go left, right, up, down) for
|
||||
-- windows and screens. It treats floating and tiled windows as two separate
|
||||
-- layers and provides mechanisms to navigate within each layer and to switch
|
||||
-- between layers. Navigation2D provides two different navigation strategies
|
||||
-- (see <#Technical_Discussion> for details): /Line navigation/ feels rather
|
||||
-- natural but may make it impossible to navigate to a given window from the
|
||||
-- current window, particularly in the floating layer. /Center navigation/
|
||||
-- feels less natural in certain situations but ensures that all windows can be
|
||||
-- reached without the need to involve the mouse. Navigation2D allows different
|
||||
-- navigation strategies to be used in the two layers and allows customization
|
||||
-- of the navigation strategy for the tiled layer based on the layout currently
|
||||
-- in effect.
|
||||
--
|
||||
-- You can use this module with (a subset of) the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Actions.Navigation2D
|
||||
--
|
||||
-- Then edit your keybindings:
|
||||
--
|
||||
-- > -- Switch between layers
|
||||
-- > , ((modm, xK_space), switchLayers)
|
||||
-- >
|
||||
-- > -- Directional navigation of windows
|
||||
-- > , ((modm, xK_Right), windowGo R False)
|
||||
-- > , ((modm, xK_Left ), windowGo L False)
|
||||
-- > , ((modm, xK_Up ), windowGo U False)
|
||||
-- > , ((modm, xK_Down ), windowGo D False)
|
||||
-- >
|
||||
-- > -- Swap adjacent windows
|
||||
-- > , ((modm .|. controlMask, xK_Right), windowSwap R False)
|
||||
-- > , ((modm .|. controlMask, xK_Left ), windowSwap L False)
|
||||
-- > , ((modm .|. controlMask, xK_Up ), windowSwap U False)
|
||||
-- > , ((modm .|. controlMask, xK_Down ), windowSwap D False)
|
||||
-- >
|
||||
-- > -- Directional navigation of screens
|
||||
-- > , ((modm, xK_r ), screenGo R False)
|
||||
-- > , ((modm, xK_l ), screenGo L False)
|
||||
-- > , ((modm, xK_u ), screenGo U False)
|
||||
-- > , ((modm, xK_d ), screenGo D False)
|
||||
-- >
|
||||
-- > -- Swap workspaces on adjacent screens
|
||||
-- > , ((modm .|. controlMask, xK_r ), screenSwap R False)
|
||||
-- > , ((modm .|. controlMask, xK_l ), screenSwap L False)
|
||||
-- > , ((modm .|. controlMask, xK_u ), screenSwap U False)
|
||||
-- > , ((modm .|. controlMask, xK_d ), screenSwap D False)
|
||||
-- >
|
||||
-- > -- Send window to adjacent screen
|
||||
-- > , ((modm .|. mod1Mask, xK_r ), windowToScreen R False)
|
||||
-- > , ((modm .|. mod1Mask, xK_l ), windowToScreen L False)
|
||||
-- > , ((modm .|. mod1Mask, xK_u ), windowToScreen U False)
|
||||
-- > , ((modm .|. mod1Mask, xK_d ), windowToScreen D False)
|
||||
--
|
||||
-- and add the configuration of the module to your main function:
|
||||
--
|
||||
-- > main = xmonad $ withNavigation2DConfig defaultNavigation2DConfig
|
||||
-- > $ defaultConfig
|
||||
--
|
||||
-- For detailed instruction on editing the key binding see:
|
||||
--
|
||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
||||
|
||||
-- $finer_points
|
||||
-- #Finer_Points#
|
||||
-- The above should get you started. Here are some finer points:
|
||||
--
|
||||
-- Navigation2D has the ability to wrap around at screen edges. For example, if
|
||||
-- you navigated to the rightmost window on the rightmost screen and you
|
||||
-- continued to go right, this would get you to the leftmost window on the
|
||||
-- leftmost screen. This feature may be useful for switching between screens
|
||||
-- that are far apart but may be confusing at least to novice users. Therefore,
|
||||
-- it is disabled in the above example (e.g., navigation beyond the rightmost
|
||||
-- window on the rightmost screen is not possible and trying to do so will
|
||||
-- simply not do anything.) If you want this feature, change all the 'False'
|
||||
-- values in the above example to 'True'. You could also decide you want
|
||||
-- wrapping only for a subset of the operations and no wrapping for others.
|
||||
--
|
||||
-- By default, all layouts use the 'defaultTiledNavigation' strategy specified
|
||||
-- in the 'Navigation2DConfig' (by default, line navigation is used). To
|
||||
-- override this behaviour for some layouts, add a pair (\"layout name\",
|
||||
-- navigation strategy) to the 'layoutNavigation' list in the
|
||||
-- 'Navigation2DConfig', where \"layout name\" is the string reported by the
|
||||
-- layout's description method (normally what is shown as the layout name in
|
||||
-- your status bar). For example, all navigation strategies normally allow only
|
||||
-- navigation between mapped windows. The first step to overcome this, for
|
||||
-- example, for the Full layout, is to switch to center navigation for the Full
|
||||
-- layout:
|
||||
--
|
||||
-- > myNavigation2DConfig = defaultNavigation2DConfig { layoutNavigation = [("Full", centerNavigation)] }
|
||||
-- >
|
||||
-- > main = xmonad $ withNavigation2DConfig myNavigation2DConfig
|
||||
-- > $ defaultConfig
|
||||
--
|
||||
-- The navigation between windows is based on their screen rectangles, which are
|
||||
-- available /and meaningful/ only for mapped windows. Thus, as already said,
|
||||
-- the default is to allow navigation only between mapped windows. However,
|
||||
-- there are layouts that do not keep all windows mapped. One example is the
|
||||
-- Full layout, which unmaps all windows except the one that has the focus,
|
||||
-- thereby preventing navigation to any other window in the layout. To make
|
||||
-- navigation to unmapped windows possible, unmapped windows need to be assigned
|
||||
-- rectangles to pretend they are mapped, and a natural way to do this for the
|
||||
-- Full layout is to pretend all windows occupy the full screen and are stacked
|
||||
-- on top of each other so that only the frontmost one is visible. This can be
|
||||
-- done as follows:
|
||||
--
|
||||
-- > myNavigation2DConfig = defaultNavigation2DConfig { layoutNavigation = [("Full", centerNavigation)]
|
||||
-- > , unmappedWindowRect = [("Full", singleWindowRect)]
|
||||
-- > }
|
||||
-- >
|
||||
-- > main = xmonad $ withNavigation2DConfig myNavigation2DConfig
|
||||
-- > $ defaultConfig
|
||||
--
|
||||
-- With this setup, Left/Up navigation behaves like standard
|
||||
-- 'XMonad.StackSet.focusUp' and Right/Down navigation behaves like
|
||||
-- 'XMonad.StackSet.focusDown', thus allowing navigation between windows in the
|
||||
-- layout.
|
||||
--
|
||||
-- In general, each entry in the 'unmappedWindowRect' association list is a pair
|
||||
-- (\"layout description\", function), where the function computes a rectangle
|
||||
-- for each unmapped window from the screen it is on and the window ID.
|
||||
-- Currently, Navigation2D provides only two functions of this type:
|
||||
-- 'singleWindowRect' and 'fullScreenRect'.
|
||||
--
|
||||
-- With per-layout navigation strategies, if different layouts are in effect on
|
||||
-- different screens in a multi-monitor setup, and different navigation
|
||||
-- strategies are defined for these active layouts, the most general of these
|
||||
-- navigation strategies is used across all screens (because Navigation2D does
|
||||
-- not distinguish between windows on different workspaces), where center
|
||||
-- navigation is more general than line navigation, as discussed formally under
|
||||
-- <#Technical_Discussion>.
|
||||
|
||||
-- $alternatives
|
||||
-- #Alternatives#
|
||||
--
|
||||
-- There exist two alternatives to Navigation2D:
|
||||
-- "XMonad.Actions.WindowNavigation" and "XMonad.Layout.WindowNavigation".
|
||||
-- X.L.WindowNavigation has the advantage of colouring windows to indicate the
|
||||
-- window that would receive the focus in each navigation direction, but it does
|
||||
-- not support navigation across multiple monitors, does not support directional
|
||||
-- navigation of floating windows, and has a very unintuitive definition of
|
||||
-- which window receives the focus next in each direction. X.A.WindowNavigation
|
||||
-- does support navigation across multiple monitors but does not provide window
|
||||
-- colouring while retaining the unintuitive navigational semantics of
|
||||
-- X.L.WindowNavigation. This makes it very difficult to predict which window
|
||||
-- receives the focus next. Neither X.A.WindowNavigation nor
|
||||
-- X.L.WindowNavigation supports directional navigation of screens.
|
||||
|
||||
-- $technical
|
||||
-- #Technical_Discussion#
|
||||
-- An in-depth discussion of the navigational strategies implemented in
|
||||
-- Navigation2D, including formal proofs of their properties, can be found
|
||||
-- at <http://www.cs.dal.ca/~nzeh/xmonad/Navigation2D.pdf>.
|
||||
|
||||
-- $incompatibilities
|
||||
-- #Incompatibilities#
|
||||
-- Currently Navigation2D is known not to play nicely with tabbed layouts, but
|
||||
-- it should work well with any other tiled layout. My hope is to address the
|
||||
-- incompatibility with tabbed layouts in a future version. The navigation to
|
||||
-- unmapped windows, for example in a Full layout, by assigning rectangles to
|
||||
-- unmapped windows is more a workaround than a clean solution. Figuring out
|
||||
-- how to deal with tabbed layouts may also lead to a more general and cleaner
|
||||
-- solution to query the layout for a window's rectangle that may make this
|
||||
-- workaround unnecessary. At that point, the 'unmappedWindowRect' field of the
|
||||
-- 'Navigation2DConfig' will disappear.
|
||||
|
||||
-- | A rectangle paired with an object
|
||||
type Rect a = (a, Rectangle)
|
||||
|
||||
-- | A shorthand for window-rectangle pairs. Reduces typing.
|
||||
type WinRect = Rect Window
|
||||
|
||||
-- | A shorthand for workspace-rectangle pairs. Reduces typing.
|
||||
type WSRect = Rect WorkspaceId
|
||||
|
||||
----------------------------------------------------------------------------------------------------
|
||||
----------------------------------------------------------------------------------------------------
|
||||
-- --
|
||||
-- PUBLIC INTERFACE --
|
||||
-- --
|
||||
----------------------------------------------------------------------------------------------------
|
||||
----------------------------------------------------------------------------------------------------
|
||||
|
||||
-- | Encapsulates the navigation strategy
|
||||
data Navigation2D = N Generality (forall a . Eq a => Direction2D -> Rect a -> [Rect a] -> Maybe a)
|
||||
|
||||
runNav :: forall a . Eq a => Navigation2D -> (Direction2D -> Rect a -> [Rect a] -> Maybe a)
|
||||
runNav (N _ nav) = nav
|
||||
|
||||
-- | Score that indicates how general a navigation strategy is
|
||||
type Generality = Int
|
||||
|
||||
instance Eq Navigation2D where
|
||||
(N x _) == (N y _) = x == y
|
||||
|
||||
instance Ord Navigation2D where
|
||||
(N x _) <= (N y _) = x <= y
|
||||
|
||||
-- | Line navigation. To illustrate this navigation strategy, consider
|
||||
-- navigating to the left from the current window. In this case, we draw a
|
||||
-- horizontal line through the center of the current window and consider all
|
||||
-- windows that intersect this horizontal line and whose right boundaries are to
|
||||
-- the left of the left boundary of the current window. From among these
|
||||
-- windows, we choose the one with the rightmost right boundary.
|
||||
lineNavigation :: Navigation2D
|
||||
lineNavigation = N 1 doLineNavigation
|
||||
|
||||
-- | Center navigation. Again, consider navigating to the left. Then we
|
||||
-- consider the cone bounded by the two rays shot at 45-degree angles in
|
||||
-- north-west and south-west direction from the center of the current window. A
|
||||
-- window is a candidate to receive the focus if its center lies in this cone.
|
||||
-- We choose the window whose center has minimum L1-distance from the current
|
||||
-- window center. The tie breaking strategy for windows with the same distance
|
||||
-- is a bit complicated (see <#Technical_Discussion>) but ensures that all
|
||||
-- windows can be reached and that windows with the same center are traversed in
|
||||
-- their order in the window stack, that is, in the order
|
||||
-- 'XMonad.StackSet.focusUp' and 'XMonad.StackSet.focusDown' would traverse
|
||||
-- them.
|
||||
centerNavigation :: Navigation2D
|
||||
centerNavigation = N 2 doCenterNavigation
|
||||
|
||||
-- | Stores the configuration of directional navigation
|
||||
data Navigation2DConfig = Navigation2DConfig
|
||||
{ defaultTiledNavigation :: Navigation2D -- ^ default navigation strategy for the tiled layer
|
||||
, floatNavigation :: Navigation2D -- ^ navigation strategy for the float layer
|
||||
, screenNavigation :: Navigation2D -- ^ strategy for navigation between screens
|
||||
, layoutNavigation :: [(String, Navigation2D)] -- ^ association list of customized navigation strategies
|
||||
-- for different layouts in the tiled layer. Each pair
|
||||
-- is of the form (\"layout description\", navigation
|
||||
-- strategy). If there is no pair in this list whose first
|
||||
-- component is the name of the current layout, the
|
||||
-- 'defaultTiledNavigation' strategy is used.
|
||||
, unmappedWindowRect :: [(String, Screen -> Window -> X (Maybe Rectangle))]
|
||||
-- ^ list associating functions to calculate rectangles
|
||||
-- for unmapped windows with layouts to which they are
|
||||
-- to be applied. Each pair in this list is of
|
||||
-- the form (\"layout description\", function), where the
|
||||
-- function calculates a rectangle for a given unmapped
|
||||
-- window from the screen it is on and its window ID.
|
||||
-- See <#Finer_Points> for how to use this.
|
||||
} deriving Typeable
|
||||
|
||||
-- | Shorthand for the tedious screen type
|
||||
type Screen = W.Screen WorkspaceId (Layout Window) Window ScreenId ScreenDetail
|
||||
|
||||
-- So we can store the configuration in extensible state
|
||||
instance ExtensionClass Navigation2DConfig where
|
||||
initialValue = defaultNavigation2DConfig
|
||||
|
||||
-- | Modifies the xmonad configuration to store the Navigation2D configuration
|
||||
withNavigation2DConfig :: Navigation2DConfig -> XConfig a -> XConfig a
|
||||
withNavigation2DConfig conf2d xconf = xconf { startupHook = startupHook xconf
|
||||
>> XS.put conf2d
|
||||
}
|
||||
|
||||
-- | Default navigation configuration. It uses line navigation for the tiled
|
||||
-- layer and for navigation between screens, and center navigation for the float
|
||||
-- layer. No custom navigation strategies or rectangles for unmapped windows are
|
||||
-- defined for individual layouts.
|
||||
defaultNavigation2DConfig :: Navigation2DConfig
|
||||
defaultNavigation2DConfig = Navigation2DConfig { defaultTiledNavigation = lineNavigation
|
||||
, floatNavigation = centerNavigation
|
||||
, screenNavigation = lineNavigation
|
||||
, layoutNavigation = []
|
||||
, unmappedWindowRect = []
|
||||
}
|
||||
|
||||
-- | Switches focus to the closest window in the other layer (floating if the
|
||||
-- current window is tiled, tiled if the current window is floating). Closest
|
||||
-- means that the L1-distance between the centers of the windows is minimized.
|
||||
switchLayer :: X ()
|
||||
switchLayer = actOnLayer otherLayer
|
||||
( \ _ cur wins -> windows
|
||||
$ doFocusClosestWindow cur wins
|
||||
)
|
||||
( \ _ cur wins -> windows
|
||||
$ doFocusClosestWindow cur wins
|
||||
)
|
||||
( \ _ _ _ -> return () )
|
||||
False
|
||||
|
||||
-- | Moves the focus to the next window in the given direction and in the same
|
||||
-- layer as the current window. The second argument indicates whether
|
||||
-- navigation should wrap around (e.g., from the left edge of the leftmost
|
||||
-- screen to the right edge of the rightmost screen).
|
||||
windowGo :: Direction2D -> Bool -> X ()
|
||||
windowGo dir wrap = actOnLayer thisLayer
|
||||
( \ conf cur wins -> windows
|
||||
$ doTiledNavigation conf dir W.focusWindow cur wins
|
||||
)
|
||||
( \ conf cur wins -> windows
|
||||
$ doFloatNavigation conf dir W.focusWindow cur wins
|
||||
)
|
||||
( \ conf cur wspcs -> windows
|
||||
$ doScreenNavigation conf dir W.view cur wspcs
|
||||
)
|
||||
wrap
|
||||
|
||||
-- | Swaps the current window with the next window in the given direction and in
|
||||
-- the same layer as the current window. (In the floating layer, all that
|
||||
-- changes for the two windows is their stacking order if they're on the same
|
||||
-- screen. If they're on different screens, each window is moved to the other
|
||||
-- window's screen but retains its position and size relative to the screen.)
|
||||
-- The second argument indicates wrapping (see 'windowGo').
|
||||
windowSwap :: Direction2D -> Bool -> X ()
|
||||
windowSwap dir wrap = actOnLayer thisLayer
|
||||
( \ conf cur wins -> windows
|
||||
$ doTiledNavigation conf dir swap cur wins
|
||||
)
|
||||
( \ conf cur wins -> windows
|
||||
$ doFloatNavigation conf dir swap cur wins
|
||||
)
|
||||
( \ _ _ _ -> return () )
|
||||
wrap
|
||||
|
||||
-- | Moves the current window to the next screen in the given direction. The
|
||||
-- second argument indicates wrapping (see 'windowGo').
|
||||
windowToScreen :: Direction2D -> Bool -> X ()
|
||||
windowToScreen dir wrap = actOnScreens ( \ conf cur wspcs -> windows
|
||||
$ doScreenNavigation conf dir W.shift cur wspcs
|
||||
)
|
||||
wrap
|
||||
|
||||
-- | Moves the focus to the next screen in the given direction. The second
|
||||
-- argument indicates wrapping (see 'windowGo').
|
||||
screenGo :: Direction2D -> Bool -> X ()
|
||||
screenGo dir wrap = actOnScreens ( \ conf cur wspcs -> windows
|
||||
$ doScreenNavigation conf dir W.view cur wspcs
|
||||
)
|
||||
wrap
|
||||
|
||||
-- | Swaps the workspace on the current screen with the workspace on the screen
|
||||
-- in the given direction. The second argument indicates wrapping (see
|
||||
-- 'windowGo').
|
||||
screenSwap :: Direction2D -> Bool -> X ()
|
||||
screenSwap dir wrap = actOnScreens ( \ conf cur wspcs -> windows
|
||||
$ doScreenNavigation conf dir W.greedyView cur wspcs
|
||||
)
|
||||
wrap
|
||||
|
||||
-- | Maps each window to a fullscreen rect. This may not be the same rectangle the
|
||||
-- window maps to under the Full layout or a similar layout if the layout
|
||||
-- respects statusbar struts. In such cases, it may be better to use
|
||||
-- 'singleWindowRect'.
|
||||
fullScreenRect :: Screen -> Window -> X (Maybe Rectangle)
|
||||
fullScreenRect scr _ = return (Just . screenRect . W.screenDetail $ scr)
|
||||
|
||||
-- | Maps each window to the rectangle it would receive if it was the only
|
||||
-- window in the layout. Useful, for example, for determining the default
|
||||
-- rectangle for unmapped windows in a Full layout that respects statusbar
|
||||
-- struts.
|
||||
singleWindowRect :: Screen -> Window -> X (Maybe Rectangle)
|
||||
singleWindowRect scr win = listToMaybe
|
||||
. map snd
|
||||
. fst
|
||||
<$> runLayout ((W.workspace scr) { W.stack = W.differentiate [win] })
|
||||
(screenRect . W.screenDetail $ scr)
|
||||
|
||||
----------------------------------------------------------------------------------------------------
|
||||
----------------------------------------------------------------------------------------------------
|
||||
-- --
|
||||
-- PRIVATE X ACTIONS --
|
||||
-- --
|
||||
----------------------------------------------------------------------------------------------------
|
||||
----------------------------------------------------------------------------------------------------
|
||||
|
||||
-- | Acts on the appropriate layer using the given action functions
|
||||
actOnLayer :: ([WinRect] -> [WinRect] -> [WinRect]) -- ^ Chooses which layer to operate on, relative
|
||||
-- to the current window (same or other layer)
|
||||
-> (Navigation2DConfig -> WinRect -> [WinRect] -> X ()) -- ^ The action for the tiled layer
|
||||
-> (Navigation2DConfig -> WinRect -> [WinRect] -> X ()) -- ^ The action for the float layer
|
||||
-> (Navigation2DConfig -> WSRect -> [WSRect] -> X ()) -- ^ The action if the current workspace is empty
|
||||
-> Bool -- ^ Should navigation wrap around screen edges?
|
||||
-> X ()
|
||||
actOnLayer choice tiledact floatact wsact wrap = withWindowSet $ \winset -> do
|
||||
conf <- XS.get
|
||||
(floating, tiled) <- navigableWindows conf wrap winset
|
||||
let cur = W.peek winset
|
||||
case cur of
|
||||
Nothing -> actOnScreens wsact wrap
|
||||
Just w | Just rect <- L.lookup w tiled -> tiledact conf (w, rect) (choice tiled floating)
|
||||
| Just rect <- L.lookup w floating -> floatact conf (w, rect) (choice floating tiled)
|
||||
| otherwise -> return ()
|
||||
|
||||
-- | Returns the list of windows on the currently visible workspaces
|
||||
navigableWindows :: Navigation2DConfig -> Bool -> WindowSet -> X ([WinRect], [WinRect])
|
||||
navigableWindows conf wrap winset = L.partition (\(win, _) -> M.member win (W.floating winset))
|
||||
. addWrapping winset wrap
|
||||
. catMaybes
|
||||
. concat
|
||||
<$>
|
||||
( mapM ( \scr -> mapM (maybeWinRect scr)
|
||||
$ W.integrate'
|
||||
$ W.stack
|
||||
$ W.workspace scr
|
||||
)
|
||||
. sortedScreens
|
||||
) winset
|
||||
where
|
||||
maybeWinRect scr win = do
|
||||
winrect <- windowRect win
|
||||
rect <- case winrect of
|
||||
Just _ -> return winrect
|
||||
Nothing -> maybe (return Nothing)
|
||||
(\f -> f scr win)
|
||||
(L.lookup (description . W.layout . W.workspace $ scr) (unmappedWindowRect conf))
|
||||
return ((,) win <$> rect)
|
||||
|
||||
-- | Returns the current rectangle of the given window, Nothing if the window isn't mapped
|
||||
windowRect :: Window -> X (Maybe Rectangle)
|
||||
windowRect win = withDisplay $ \dpy -> do
|
||||
mp <- isMapped win
|
||||
if mp then do (_, x, y, w, h, bw, _) <- io $ getGeometry dpy win
|
||||
return $ Just $ Rectangle x y (w + 2 * bw) (h + 2 * bw)
|
||||
`catchX` return Nothing
|
||||
else return Nothing
|
||||
|
||||
-- | Acts on the screens using the given action function
|
||||
actOnScreens :: (Navigation2DConfig -> WSRect -> [WSRect] -> X ())
|
||||
-> Bool -- ^ Should wrapping be used?
|
||||
-> X ()
|
||||
actOnScreens act wrap = withWindowSet $ \winset -> do
|
||||
conf <- XS.get
|
||||
let wsrects = visibleWorkspaces winset wrap
|
||||
cur = W.tag . W.workspace . W.current $ winset
|
||||
rect = fromJust $ L.lookup cur wsrects
|
||||
act conf (cur, rect) wsrects
|
||||
|
||||
-- | Determines whether a given window is mapped
|
||||
isMapped :: Window -> X Bool
|
||||
isMapped win = withDisplay
|
||||
$ \dpy -> io
|
||||
$ (waIsUnmapped /=)
|
||||
. wa_map_state
|
||||
<$> getWindowAttributes dpy win
|
||||
|
||||
----------------------------------------------------------------------------------------------------
|
||||
----------------------------------------------------------------------------------------------------
|
||||
-- --
|
||||
-- PRIVATE PURE FUNCTIONS --
|
||||
-- --
|
||||
----------------------------------------------------------------------------------------------------
|
||||
----------------------------------------------------------------------------------------------------
|
||||
|
||||
-- | Finds the window closest to the given window and focuses it. Ties are
|
||||
-- broken by choosing the first window in the window stack among the tied
|
||||
-- windows. (The stack order is the one produced by integrate'ing each visible
|
||||
-- workspace's window stack and concatenating these lists for all visible
|
||||
-- workspaces.)
|
||||
doFocusClosestWindow :: WinRect
|
||||
-> [WinRect]
|
||||
-> (WindowSet -> WindowSet)
|
||||
doFocusClosestWindow (cur, rect) winrects
|
||||
| null winctrs = id
|
||||
| otherwise = W.focusWindow . fst $ L.foldl1' closer winctrs
|
||||
where
|
||||
ctr = centerOf rect
|
||||
winctrs = filter ((cur /=) . fst)
|
||||
$ map (\(w, r) -> (w, centerOf r)) winrects
|
||||
closer wc1@(_, c1) wc2@(_, c2) | lDist ctr c1 > lDist ctr c2 = wc2
|
||||
| otherwise = wc1
|
||||
|
||||
-- | Implements navigation for the tiled layer
|
||||
doTiledNavigation :: Navigation2DConfig
|
||||
-> Direction2D
|
||||
-> (Window -> WindowSet -> WindowSet)
|
||||
-> WinRect
|
||||
-> [WinRect]
|
||||
-> (WindowSet -> WindowSet)
|
||||
doTiledNavigation conf dir act cur winrects winset
|
||||
| Just win <- runNav nav dir cur winrects = act win winset
|
||||
| otherwise = winset
|
||||
where
|
||||
layouts = map (description . W.layout . W.workspace)
|
||||
$ W.screens winset
|
||||
nav = maximum
|
||||
$ map ( fromMaybe (defaultTiledNavigation conf)
|
||||
. flip L.lookup (layoutNavigation conf)
|
||||
)
|
||||
$ layouts
|
||||
|
||||
-- | Implements navigation for the float layer
|
||||
doFloatNavigation :: Navigation2DConfig
|
||||
-> Direction2D
|
||||
-> (Window -> WindowSet -> WindowSet)
|
||||
-> WinRect
|
||||
-> [WinRect]
|
||||
-> (WindowSet -> WindowSet)
|
||||
doFloatNavigation conf dir act cur winrects
|
||||
| Just win <- runNav nav dir cur winrects = act win
|
||||
| otherwise = id
|
||||
where
|
||||
nav = floatNavigation conf
|
||||
|
||||
-- | Implements navigation between screens
|
||||
doScreenNavigation :: Navigation2DConfig
|
||||
-> Direction2D
|
||||
-> (WorkspaceId -> WindowSet -> WindowSet)
|
||||
-> WSRect
|
||||
-> [WSRect]
|
||||
-> (WindowSet -> WindowSet)
|
||||
doScreenNavigation conf dir act cur wsrects
|
||||
| Just ws <- runNav nav dir cur wsrects = act ws
|
||||
| otherwise = id
|
||||
where
|
||||
nav = screenNavigation conf
|
||||
|
||||
-- | Implements line navigation. For layouts without overlapping windows, there
|
||||
-- is no need to break ties between equidistant windows. When windows do
|
||||
-- overlap, even the best tie breaking rule cannot make line navigation feel
|
||||
-- natural. Thus, we fairly arbtitrarily break ties by preferring the window
|
||||
-- that comes first in the window stack. (The stack order is the one produced
|
||||
-- by integrate'ing each visible workspace's window stack and concatenating
|
||||
-- these lists for all visible workspaces.)
|
||||
doLineNavigation :: Eq a => Direction2D -> Rect a -> [Rect a] -> Maybe a
|
||||
doLineNavigation dir (cur, rect) winrects
|
||||
| null winrects' = Nothing
|
||||
| otherwise = Just . fst $ L.foldl1' closer winrects'
|
||||
where
|
||||
-- The current window's center
|
||||
ctr@(xc, yc) = centerOf rect
|
||||
|
||||
-- The list of windows that are candidates to receive focus.
|
||||
winrects' = filter dirFilter
|
||||
$ filter ((cur /=) . fst)
|
||||
$ winrects
|
||||
|
||||
-- Decides whether a given window matches the criteria to be a candidate to
|
||||
-- receive the focus.
|
||||
dirFilter (_, r) = (dir == L && leftOf r rect && intersectsY yc r)
|
||||
|| (dir == R && leftOf rect r && intersectsY yc r)
|
||||
|| (dir == U && above r rect && intersectsX xc r)
|
||||
|| (dir == D && above rect r && intersectsX xc r)
|
||||
|
||||
-- Decide whether r1 is left of/above r2.
|
||||
leftOf r1 r2 = rect_x r1 + fi (rect_width r1) <= rect_x r2
|
||||
above r1 r2 = rect_y r1 + fi (rect_height r1) <= rect_y r2
|
||||
|
||||
-- Check whether r's x-/y-range contains the given x-/y-coordinate.
|
||||
intersectsX x r = rect_x r <= x && rect_x r + fi (rect_width r) >= x
|
||||
intersectsY y r = rect_y r <= y && rect_y r + fi (rect_height r) >= y
|
||||
|
||||
-- Decides whether r1 is closer to the current window's center than r2
|
||||
closer wr1@(_, r1) wr2@(_, r2) | dist ctr r1 > dist ctr r2 = wr2
|
||||
| otherwise = wr1
|
||||
|
||||
-- Returns the distance of r from the point (x, y)
|
||||
dist (x, y) r | dir == L = x - rect_x r - fi (rect_width r)
|
||||
| dir == R = rect_x r - x
|
||||
| dir == U = y - rect_y r - fi (rect_height r)
|
||||
| otherwise = rect_y r - y
|
||||
|
||||
-- | Implements center navigation
|
||||
doCenterNavigation :: Eq a => Direction2D -> Rect a -> [Rect a] -> Maybe a
|
||||
doCenterNavigation dir (cur, rect) winrects
|
||||
| ((w, _):_) <- onCtr' = Just w
|
||||
| otherwise = closestOffCtr
|
||||
where
|
||||
-- The center of the current window
|
||||
(xc, yc) = centerOf rect
|
||||
|
||||
-- All the windows with their center points relative to the current
|
||||
-- center rotated so the right cone becomes the relevant cone.
|
||||
-- The windows are ordered in the order they should be preferred
|
||||
-- when they are otherwise tied.
|
||||
winctrs = map (\(w, r) -> (w, dirTransform . centerOf $ r))
|
||||
$ stackTransform
|
||||
$ winrects
|
||||
|
||||
-- Give preference to windows later in the stack for going left or up and to
|
||||
-- windows earlier in the stack for going right or down. (The stack order
|
||||
-- is the one produced by integrate'ing each visible workspace's window
|
||||
-- stack and concatenating these lists for all visible workspaces.)
|
||||
stackTransform | dir == L || dir == U = reverse
|
||||
| otherwise = id
|
||||
|
||||
-- Transform a point into a difference to the current window center and
|
||||
-- rotate it so that the relevant cone becomes the right cone.
|
||||
dirTransform (x, y) | dir == R = ( x - xc , y - yc )
|
||||
| dir == L = (-(x - xc), -(y - yc))
|
||||
| dir == D = ( y - yc , x - xc )
|
||||
| otherwise = (-(y - yc), -(x - xc))
|
||||
|
||||
-- Partition the points into points that coincide with the center
|
||||
-- and points that do not.
|
||||
(onCtr, offCtr) = L.partition (\(_, (x, y)) -> x == 0 && y == 0) winctrs
|
||||
|
||||
-- All the points that coincide with the current center and succeed it
|
||||
-- in the (appropriately ordered) window stack.
|
||||
onCtr' = L.tail $ L.dropWhile ((cur /=) . fst) onCtr
|
||||
-- tail should be safe here because cur should be in onCtr
|
||||
|
||||
-- All the points that do not coincide with the current center and which
|
||||
-- lie in the (rotated) right cone.
|
||||
offCtr' = L.filter (\(_, (x, y)) -> x > 0 && y < x && y >= -x) offCtr
|
||||
|
||||
-- The off-center point closest to the center and
|
||||
-- closest to the bottom ray of the cone. Nothing if no off-center
|
||||
-- point is in the cone
|
||||
closestOffCtr = if null offCtr' then Nothing
|
||||
else Just $ fst $ L.foldl1' closest offCtr'
|
||||
|
||||
closest wp@(_, p@(_, yp)) wq@(_, q@(_, yq))
|
||||
| lDist (0, 0) q < lDist (0, 0) p = wq -- q is closer than p
|
||||
| lDist (0, 0) p < lDist (0, 0) q = wp -- q is farther away than p
|
||||
| yq < yp = wq -- q is closer to the bottom ray than p
|
||||
| otherwise = wp -- q is farther away from the bottom ray than p
|
||||
-- or it has the same distance but comes later
|
||||
-- in the window stack
|
||||
|
||||
-- | Swaps the current window with the window given as argument
|
||||
swap :: Window -> WindowSet -> WindowSet
|
||||
swap win winset = W.focusWindow cur
|
||||
$ L.foldl' (flip W.focusWindow) newwinset newfocused
|
||||
where
|
||||
-- The current window
|
||||
cur = fromJust $ W.peek winset
|
||||
|
||||
-- All screens
|
||||
scrs = W.screens winset
|
||||
|
||||
-- All visible workspaces
|
||||
visws = map W.workspace scrs
|
||||
|
||||
-- The focused windows of the visible workspaces
|
||||
focused = mapMaybe (\ws -> W.focus <$> W.stack ws) visws
|
||||
|
||||
-- The window lists of the visible workspaces
|
||||
wins = map (W.integrate' . W.stack) visws
|
||||
|
||||
-- Update focused windows and window lists to reflect swap of windows.
|
||||
newfocused = map swapWins focused
|
||||
newwins = map (map swapWins) wins
|
||||
|
||||
-- Replaces the current window with the argument window and vice versa.
|
||||
swapWins x | x == cur = win
|
||||
| x == win = cur
|
||||
| otherwise = x
|
||||
|
||||
-- Reconstruct the workspaces' window stacks to reflect the swap.
|
||||
newvisws = zipWith (\ws wns -> ws { W.stack = W.differentiate wns }) visws newwins
|
||||
newscrs = zipWith (\scr ws -> scr { W.workspace = ws }) scrs newvisws
|
||||
newwinset = winset { W.current = head newscrs
|
||||
, W.visible = tail newscrs
|
||||
}
|
||||
|
||||
-- | Calculates the center of a rectangle
|
||||
centerOf :: Rectangle -> (Position, Position)
|
||||
centerOf r = (rect_x r + fi (rect_width r) `div` 2, rect_y r + fi (rect_height r) `div` 2)
|
||||
|
||||
-- | Shorthand for integer conversions
|
||||
fi :: (Integral a, Num b) => a -> b
|
||||
fi = fromIntegral
|
||||
|
||||
-- | Functions to choose the subset of windows to operate on
|
||||
thisLayer, otherLayer :: a -> a -> a
|
||||
thisLayer = curry fst
|
||||
otherLayer = curry snd
|
||||
|
||||
-- | Returns the list of visible workspaces and their screen rects
|
||||
visibleWorkspaces :: WindowSet -> Bool -> [WSRect]
|
||||
visibleWorkspaces winset wrap = addWrapping winset wrap
|
||||
$ map ( \scr -> ( W.tag . W.workspace $ scr
|
||||
, screenRect . W.screenDetail $ scr
|
||||
)
|
||||
)
|
||||
$ sortedScreens winset
|
||||
|
||||
-- | Creates five copies of each (window/workspace, rect) pair in the input: the
|
||||
-- original and four offset one desktop size (desktop = collection of all
|
||||
-- screens) to the left, to the right, up, and down. Wrap-around at desktop
|
||||
-- edges is implemented by navigating into these displaced copies.
|
||||
addWrapping :: WindowSet -- ^ The window set, used to get the desktop size
|
||||
-> Bool -- ^ Should wrapping be used? Do nothing if not.
|
||||
-> [Rect a] -- ^ Input set of (window/workspace, rect) pairs
|
||||
-> [Rect a]
|
||||
addWrapping _ False wrects = wrects
|
||||
addWrapping winset True wrects = [ (w, r { rect_x = rect_x r + fi x
|
||||
, rect_y = rect_y r + fi y
|
||||
}
|
||||
)
|
||||
| (w, r) <- wrects
|
||||
, (x, y) <- [(0, 0), (-xoff, 0), (xoff, 0), (0, -yoff), (0, yoff)]
|
||||
]
|
||||
where
|
||||
(xoff, yoff) = wrapOffsets winset
|
||||
|
||||
-- | Calculates the offsets for window/screen coordinates for the duplication
|
||||
-- of windows/workspaces that implements wrap-around.
|
||||
wrapOffsets :: WindowSet -> (Integer, Integer)
|
||||
wrapOffsets winset = (max_x - min_x, max_y - min_y)
|
||||
where
|
||||
min_x = fi $ minimum $ map rect_x rects
|
||||
min_y = fi $ minimum $ map rect_y rects
|
||||
max_x = fi $ maximum $ map (\r -> rect_x r + (fi $ rect_width r)) rects
|
||||
max_y = fi $ maximum $ map (\r -> rect_y r + (fi $ rect_height r)) rects
|
||||
rects = map snd $ visibleWorkspaces winset False
|
||||
|
||||
|
||||
-- | Returns the list of screens sorted primarily by their centers'
|
||||
-- x-coordinates and secondarily by their y-coordinates.
|
||||
sortedScreens :: WindowSet -> [Screen]
|
||||
sortedScreens winset = L.sortBy cmp
|
||||
$ W.screens winset
|
||||
where
|
||||
cmp s1 s2 | x1 < x2 = LT
|
||||
| x1 > x2 = GT
|
||||
| y1 < x2 = LT
|
||||
| y1 > y2 = GT
|
||||
| otherwise = EQ
|
||||
where
|
||||
(x1, y1) = centerOf (screenRect . W.screenDetail $ s1)
|
||||
(x2, y2) = centerOf (screenRect . W.screenDetail $ s2)
|
||||
|
||||
|
||||
-- | Calculates the L1-distance between two points.
|
||||
lDist :: (Position, Position) -> (Position, Position) -> Int
|
||||
lDist (x1, y1) (x2, y2) = abs (fi $ x1 - x2) + abs (fi $ y1 - y2)
|
@@ -41,7 +41,7 @@ and then left-to-right.
|
||||
|
||||
Example usage in your @~\/.xmonad\/xmonad.hs@ file:
|
||||
|
||||
> import XMonad.Actions.PhysicalSCreens
|
||||
> import XMonad.Actions.PhysicalScreens
|
||||
|
||||
> , ((modMask, xK_a), onPrevNeighbour W.view)
|
||||
> , ((modMask, xK_o), onNextNeighbour W.view)
|
||||
@@ -112,4 +112,3 @@ onNextNeighbour = neighbourWindows 1
|
||||
-- | Apply operation on a WindowSet with the WorkspaceId of the previous screen in the physical order as parameter.
|
||||
onPrevNeighbour :: (WorkspaceId -> WindowSet -> WindowSet) -> X ()
|
||||
onPrevNeighbour = neighbourWindows (-1)
|
||||
|
||||
|
@@ -110,7 +110,7 @@ plane ::
|
||||
(WorkspaceId -> WindowSet -> WindowSet) -> Lines -> Limits -> Direction ->
|
||||
X ()
|
||||
plane function numberLines_ limits direction = do
|
||||
state <- get
|
||||
st <- get
|
||||
xconf <- ask
|
||||
|
||||
numberLines <-
|
||||
@@ -205,7 +205,7 @@ plane function numberLines_ limits direction = do
|
||||
preColumns = div areas numberLines
|
||||
|
||||
mCurrentWS :: Maybe Int
|
||||
mCurrentWS = elemIndex (currentTag $ windowset state) areaNames
|
||||
mCurrentWS = elemIndex (currentTag $ windowset st) areaNames
|
||||
|
||||
areas :: Int
|
||||
areas = length areaNames
|
||||
|
116
XMonad/Actions/ShowText.hs
Normal file
116
XMonad/Actions/ShowText.hs
Normal file
@@ -0,0 +1,116 @@
|
||||
{-# LANGUAGE DeriveDataTypeable #-}
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.ShowText
|
||||
-- Copyright : (c) Mario Pastorelli (2012)
|
||||
-- License : BSD-style (see xmonad/LICENSE)
|
||||
--
|
||||
-- Maintainer : pastorelli.mario@gmail.com
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- ShowText displays text for sometime on the screen similar to "XMonad.Util.Dzen"
|
||||
-- which offers more features (currently)
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Actions.ShowText
|
||||
( -- * Usage
|
||||
-- $usage
|
||||
defaultSTConfig
|
||||
, handleTimerEvent
|
||||
, flashText
|
||||
, ShowTextConfig(..)
|
||||
) where
|
||||
|
||||
import Control.Monad (when)
|
||||
import Data.Map (Map,empty,insert,lookup)
|
||||
import Data.Monoid (mempty, All)
|
||||
import Prelude hiding (lookup)
|
||||
import XMonad
|
||||
import XMonad.StackSet (current,screen)
|
||||
import XMonad.Util.Font (Align(AlignCenter)
|
||||
, initXMF
|
||||
, releaseXMF
|
||||
, textExtentsXMF
|
||||
, textWidthXMF)
|
||||
import XMonad.Util.Timer (startTimer)
|
||||
import XMonad.Util.XUtils (createNewWindow
|
||||
, deleteWindow
|
||||
, fi
|
||||
, showWindow
|
||||
, paintAndWrite)
|
||||
import qualified XMonad.Util.ExtensibleState as ES
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Actions.ShowText
|
||||
--
|
||||
-- Then add the event hook handler:
|
||||
--
|
||||
-- > xmonad { handleEventHook = myHandleEventHooks <+> handleTimerEvent }
|
||||
--
|
||||
-- You can then use flashText in your keybindings:
|
||||
--
|
||||
-- > ((modMask, xK_Right), flashText defaultSTConfig 1 "->" >> nextWS)
|
||||
--
|
||||
|
||||
-- | ShowText contains the map with timers as keys and created windows as values
|
||||
newtype ShowText = ShowText (Map Atom Window)
|
||||
deriving (Read,Show,Typeable)
|
||||
|
||||
instance ExtensionClass ShowText where
|
||||
initialValue = ShowText empty
|
||||
|
||||
-- | Utility to modify a ShowText
|
||||
modShowText :: (Map Atom Window -> Map Atom Window) -> ShowText -> ShowText
|
||||
modShowText f (ShowText m) = ShowText $ f m
|
||||
|
||||
data ShowTextConfig =
|
||||
STC { st_font :: String -- ^ Font name
|
||||
, st_bg :: String -- ^ Background color
|
||||
, st_fg :: String -- ^ Foreground color
|
||||
}
|
||||
|
||||
defaultSTConfig :: ShowTextConfig
|
||||
defaultSTConfig =
|
||||
STC { st_font = "-misc-fixed-*-*-*-*-20-*-*-*-*-*-*-*"
|
||||
, st_bg = "black"
|
||||
, st_fg = "white"
|
||||
}
|
||||
|
||||
-- | Handles timer events that notify when a window should be removed
|
||||
handleTimerEvent :: Event -> X All
|
||||
handleTimerEvent (ClientMessageEvent _ _ _ dis _ mtyp d) = do
|
||||
(ShowText m) <- ES.get :: X ShowText
|
||||
a <- io $ internAtom dis "XMONAD_TIMER" False
|
||||
when (mtyp == a && length d >= 1)
|
||||
(whenJust (lookup (fromIntegral $ d !! 0) m) deleteWindow)
|
||||
mempty
|
||||
handleTimerEvent _ = mempty
|
||||
|
||||
-- | Shows a window in the center of the screen with the given text
|
||||
flashText :: ShowTextConfig
|
||||
-> Rational -- ^ number of seconds
|
||||
-> String -- ^ text to display
|
||||
-> X ()
|
||||
flashText c i s = do
|
||||
f <- initXMF (st_font c)
|
||||
d <- asks display
|
||||
sc <- gets $ fi . screen . current . windowset
|
||||
width <- textWidthXMF d f s
|
||||
(as,ds) <- textExtentsXMF f s
|
||||
let hight = as + ds
|
||||
ht = displayHeight d sc
|
||||
wh = displayWidth d sc
|
||||
y = (fi ht - hight + 2) `div` 2
|
||||
x = (fi wh - width + 2) `div` 2
|
||||
w <- createNewWindow (Rectangle (fi x) (fi y) (fi width) (fi hight))
|
||||
Nothing "" True
|
||||
showWindow w
|
||||
paintAndWrite w f (fi width) (fi hight) 0 (st_bg c) ""
|
||||
(st_fg c) (st_bg c) [AlignCenter] [s]
|
||||
releaseXMF f
|
||||
io $ sync d False
|
||||
t <- startTimer i
|
||||
ES.modify $ modShowText (insert (fromIntegral t) w)
|
@@ -26,10 +26,9 @@ module XMonad.Actions.TagWindows (
|
||||
TagPrompt,
|
||||
) where
|
||||
|
||||
import Prelude hiding (catch)
|
||||
import Data.List (nub,sortBy)
|
||||
import Control.Monad
|
||||
import Control.Exception
|
||||
import Control.Exception as E
|
||||
|
||||
import XMonad.StackSet hiding (filter)
|
||||
|
||||
@@ -82,7 +81,7 @@ setTag s w = withDisplay $ \d ->
|
||||
-- reads from the \"_XMONAD_TAGS\" window property
|
||||
getTags :: Window -> X [String]
|
||||
getTags w = withDisplay $ \d ->
|
||||
io $ catch (internAtom d "_XMONAD_TAGS" False >>=
|
||||
io $ E.catch (internAtom d "_XMONAD_TAGS" False >>=
|
||||
getTextProperty d w >>=
|
||||
wcTextPropertyToTextList d)
|
||||
(econst [[]])
|
||||
|
@@ -114,7 +114,7 @@ raise = raiseMaybe $ return ()
|
||||
Mutt which you just did for Firefox - but Mutt runs inside a terminal window?
|
||||
No problem: you search for a terminal window calling itself \"mutt\", and if
|
||||
there isn't you run a terminal with a command to run Mutt! Here's an example
|
||||
(borrowing 'runInTerm' from "XMonad.Utils.Run"):
|
||||
(borrowing 'runInTerm' from "XMonad.Util.Run"):
|
||||
|
||||
> , ((modm, xK_m), raiseMaybe (runInTerm "-title mutt" "mutt") (title =? "mutt"))
|
||||
-}
|
||||
|
109
XMonad/Actions/Workscreen.hs
Normal file
109
XMonad/Actions/Workscreen.hs
Normal file
@@ -0,0 +1,109 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.Workscreen
|
||||
-- Copyright : (c) 2012 kedals0
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : Dal <kedasl0@gmail.com>
|
||||
-- Stability : unstable
|
||||
-- Portability: unportable
|
||||
--
|
||||
-- A workscreen permits to display a set of workspaces on several
|
||||
-- screens. In xinerama mode, when a workscreen is viewed, workspaces
|
||||
-- associated to all screens are visible.
|
||||
--
|
||||
-- The first workspace of a workscreen is displayed on first screen,
|
||||
-- second on second screen, etc. Workspace position can be easily
|
||||
-- changed. If the current workscreen is called again, workspaces are
|
||||
-- shifted.
|
||||
--
|
||||
-- This also permits to see all workspaces of a workscreen even if just
|
||||
-- one screen is present, and to move windows from workspace to workscreen.
|
||||
-----------------------------------------------------------------------------
|
||||
{-# LANGUAGE DeriveDataTypeable #-}
|
||||
|
||||
module XMonad.Actions.Workscreen (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
configWorkscreen
|
||||
,viewWorkscreen
|
||||
,Workscreen(..)
|
||||
,shiftToWorkscreen
|
||||
,fromWorkspace
|
||||
,expandWorkspace
|
||||
) where
|
||||
|
||||
import XMonad hiding (workspaces)
|
||||
import qualified XMonad.StackSet as W
|
||||
import qualified XMonad.Util.ExtensibleState as XS
|
||||
import XMonad.Actions.OnScreen
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Actions.Workscreen
|
||||
-- > myWorkspaces = let myOldWorkspaces = ["adm","work","mail"]
|
||||
-- > in Workscreen.expandWorkspace 2 myOldWorkspaces
|
||||
-- > myStartupHook = do Workscreen.configWorkscreen (Workscreen.fromWorkspace 2 myWorkspaces)
|
||||
-- > return ()
|
||||
--
|
||||
-- Then, replace normal workspace view and shift keybinding:
|
||||
--
|
||||
-- > [((m .|. modm, k), f i)
|
||||
-- > | (i, k) <- zip [0..] [1..12]
|
||||
-- > , (f, m) <- [(Workscreen.viewWorkscreen, 0), (Workscreen.shiftToWorkscreen, shiftMask)]]
|
||||
--
|
||||
-- For detailed instructions on editing your key bindings, see
|
||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
||||
|
||||
|
||||
data Workscreen = Workscreen{workscreenId::Int,workspaces::[WorkspaceId]} deriving (Show,Typeable)
|
||||
type WorkscreenId=Int
|
||||
|
||||
data WorkscreenStorage = WorkscreenStorage WorkscreenId [Workscreen] deriving (Show,Typeable)
|
||||
instance ExtensionClass WorkscreenStorage where
|
||||
initialValue = WorkscreenStorage 0 []
|
||||
|
||||
-- | Helper to group workspaces. Multiply workspace by screens number.
|
||||
expandWorkspace :: Int -> [WorkspaceId] -> [WorkspaceId]
|
||||
expandWorkspace nscr ws = concat $ map expandId ws
|
||||
where expandId wsId = let t = wsId ++ "_"
|
||||
in map ((++) t . show ) [1..nscr]
|
||||
|
||||
-- | Create workscreen list from workspace list. Group workspaces to
|
||||
-- packets of screens number size.
|
||||
fromWorkspace :: Int -> [WorkspaceId] -> [Workscreen]
|
||||
fromWorkspace n ws = map (\(a,b) -> Workscreen a b) $ zip [0..] (fromWorkspace' n ws)
|
||||
fromWorkspace' :: Int -> [WorkspaceId] -> [[WorkspaceId]]
|
||||
fromWorkspace' _ [] = []
|
||||
fromWorkspace' n ws = take n ws : fromWorkspace' n (drop n ws)
|
||||
|
||||
-- | Initial configuration of workscreens
|
||||
configWorkscreen :: [Workscreen] -> X ()
|
||||
configWorkscreen wscrn = XS.put (WorkscreenStorage 0 wscrn)
|
||||
|
||||
-- | View workscreen of index @WorkscreenId@. If current workscreen is asked
|
||||
-- workscreen, workscreen's workspaces are shifted.
|
||||
viewWorkscreen :: WorkscreenId -> X ()
|
||||
viewWorkscreen wscrId = do (WorkscreenStorage c a) <- XS.get
|
||||
let wscr = if wscrId == c
|
||||
then Workscreen wscrId $ shiftWs (workspaces $ a !! wscrId)
|
||||
else a !! wscrId
|
||||
(x,_:ys) = splitAt wscrId a
|
||||
newWorkscreenStorage = WorkscreenStorage wscrId (x ++ [wscr] ++ ys)
|
||||
windows (viewWorkscreen' wscr)
|
||||
XS.put newWorkscreenStorage
|
||||
|
||||
viewWorkscreen' :: Workscreen -> WindowSet -> WindowSet
|
||||
viewWorkscreen' (Workscreen _ ws) = \s -> foldl wsToSc' s (zip [0..] ws)
|
||||
where wsToSc' s (scr,wsId) = greedyViewOnScreen scr wsId s
|
||||
|
||||
shiftWs :: [WorkspaceId] -> [WorkspaceId]
|
||||
shiftWs a = drop 1 a ++ take 1 a
|
||||
|
||||
-- | Shift a window on the first workspace of workscreen
|
||||
-- @WorkscreenId@.
|
||||
shiftToWorkscreen :: WorkscreenId -> X ()
|
||||
shiftToWorkscreen wscrId = do (WorkscreenStorage _ a) <- XS.get
|
||||
let ws = head . workspaces $ a !! wscrId
|
||||
windows $ W.shift ws
|
@@ -175,7 +175,7 @@ edit your key bindings.
|
||||
* "XMonad.Actions.FloatKeys":
|
||||
Move and resize floating windows.
|
||||
|
||||
* "XMonad.Layout.FloatSnap":
|
||||
* "XMonad.Actions.FloatSnap":
|
||||
Move and resize floating windows using other windows and the edge of the
|
||||
screen as guidelines.
|
||||
|
||||
@@ -257,7 +257,7 @@ edit your key bindings.
|
||||
* "XMonad.Actions.UpdateFocus":
|
||||
Updates the focus on mouse move in unfocused windows.
|
||||
|
||||
* "XMonadContrib.UpdatePointer":
|
||||
* "XMonad.Actions.UpdatePointer":
|
||||
Causes the pointer to follow whichever window focus changes to.
|
||||
|
||||
* "XMonad.Actions.Warp":
|
||||
|
1254
XMonad/Hooks/DebugEvents.hs
Normal file
1254
XMonad/Hooks/DebugEvents.hs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -45,7 +45,7 @@ import System.IO (hPutStrLn
|
||||
-- Logged key events look like:
|
||||
--
|
||||
-- @keycode 53 sym 120 (0x78, "x") mask 0x0 () clean 0x0 ()@
|
||||
--
|
||||
--
|
||||
-- The @mask@ and @clean@ indicate the modifiers pressed along with
|
||||
-- the key; @mask@ is raw, and @clean@ is what @xmonad@ sees after
|
||||
-- sanitizing it (removing @numberLockMask@, etc.)
|
||||
|
93
XMonad/Hooks/DebugStack.hs
Normal file
93
XMonad/Hooks/DebugStack.hs
Normal file
@@ -0,0 +1,93 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Hooks.DebugStack
|
||||
-- Copyright : (c) Brandon S Allbery KF8NH, 2012
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : allbery.b@gmail.com
|
||||
-- Stability : unstable
|
||||
-- Portability : not portable
|
||||
--
|
||||
-- Dump the state of the 'StackSet'. A @logHook@ and @handleEventHook@ are
|
||||
-- also provided.
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Hooks.DebugStack (debugStack
|
||||
,debugStackString
|
||||
,debugStackLogHook
|
||||
,debugStackEventHook
|
||||
) where
|
||||
|
||||
import XMonad.Core
|
||||
import qualified XMonad.StackSet as W
|
||||
|
||||
import XMonad.Util.DebugWindow
|
||||
|
||||
import Graphics.X11.Types (Window)
|
||||
import Graphics.X11.Xlib.Extras (Event)
|
||||
|
||||
import Control.Monad (foldM)
|
||||
import Data.Map (toList)
|
||||
import Data.Monoid (All(..))
|
||||
|
||||
-- | Print the state of the current window stack to @stderr@, which for most
|
||||
-- installations goes to @~/.xsession-errors@. "XMonad.Util.DebugWindow"
|
||||
-- is used to display the individual windows.
|
||||
debugStack :: X ()
|
||||
debugStack = debugStackString >>= trace
|
||||
|
||||
-- | The above packaged as a 'logHook'. (Currently this is identical.)
|
||||
debugStackLogHook :: X ()
|
||||
debugStackLogHook = debugStack
|
||||
|
||||
-- | The above packaged as a 'handleEventHook'. You almost certainly do not
|
||||
-- want to use this unconditionally, as it will cause massive amounts of
|
||||
-- output and possibly slow @xmonad@ down severely.
|
||||
|
||||
debugStackEventHook :: Event -> X All
|
||||
debugStackEventHook _ = debugStack >> return (All True)
|
||||
|
||||
-- | Dump the state of the current 'StackSet' as a multiline 'String'.
|
||||
-- @
|
||||
-- stack [ mm
|
||||
-- ,(*) ww
|
||||
-- , ww
|
||||
-- ]
|
||||
-- float { ww
|
||||
-- , ww
|
||||
-- }
|
||||
-- @
|
||||
--
|
||||
-- One thing I'm not sure of is where the zipper is when focus is on a
|
||||
-- floating window.
|
||||
debugStackString :: X String
|
||||
debugStackString = withWindowSet $ \ws -> do
|
||||
s <- emit "stack" ("[","]") (W.peek ws) $ W.index ws
|
||||
f <- emit "float" ("{","}") (W.peek ws) $ map fst $ toList $ W.floating ws
|
||||
return $ s ++ f
|
||||
where
|
||||
emit :: String -> (String,String) -> Maybe Window -> [Window] -> X String
|
||||
emit title (lb,rb) _ [] = return $ title ++ " " ++ lb ++ rb ++ "]\n"
|
||||
emit title (lb,rb) focused ws = do
|
||||
(_,_,_,_,ss) <- foldM emit' (title,lb,rb,focused,"") ws
|
||||
return $ ss ++
|
||||
replicate (length title + 1) ' ' ++
|
||||
rb ++
|
||||
"\n"
|
||||
|
||||
emit' :: (String,String,String,Maybe Window,String)
|
||||
-> Window
|
||||
-> X (String,String,String,Maybe Window,String)
|
||||
emit' (t,l,r,f,a) w = do
|
||||
w' <- emit'' f w
|
||||
return (replicate (length t) ' '
|
||||
,',' : replicate (length l - 1) ' '
|
||||
,r
|
||||
,f
|
||||
,a ++ t ++ " " ++ l ++ w' ++ "\n"
|
||||
)
|
||||
emit'' :: Maybe Window -> Window -> X String
|
||||
emit'' focus win =
|
||||
let fi f = if win == f then "(*) " else " "
|
||||
in (maybe " " fi focus ++) `fmap` debugWindow win
|
136
XMonad/Hooks/DynamicBars.hs
Normal file
136
XMonad/Hooks/DynamicBars.hs
Normal file
@@ -0,0 +1,136 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Hooks.DynamicBars
|
||||
-- Copyright : (c) Ben Boeckel 2012
|
||||
-- License : BSD-style (as xmonad)
|
||||
--
|
||||
-- Maintainer : mathstuf@gmail.com
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Manage per-screen status bars.
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Hooks.DynamicBars (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
DynamicStatusBar
|
||||
, DynamicStatusBarCleanup
|
||||
, dynStatusBarStartup
|
||||
, dynStatusBarEventHook
|
||||
, multiPP
|
||||
) where
|
||||
|
||||
import Prelude
|
||||
|
||||
import Control.Concurrent.MVar
|
||||
import Control.Monad
|
||||
import Control.Monad.Trans (lift)
|
||||
import Control.Monad.Writer (WriterT, execWriterT, tell)
|
||||
|
||||
import Data.Maybe
|
||||
import Data.Monoid
|
||||
import Data.Traversable (traverse)
|
||||
|
||||
import Graphics.X11.Xinerama
|
||||
import Graphics.X11.Xlib
|
||||
import Graphics.X11.Xlib.Extras
|
||||
import Graphics.X11.Xrandr
|
||||
|
||||
import System.IO
|
||||
import System.IO.Unsafe
|
||||
|
||||
import XMonad
|
||||
import qualified XMonad.StackSet as W
|
||||
import XMonad.Hooks.DynamicLog
|
||||
|
||||
-- $usage
|
||||
-- Provides a few helper functions to manage per-screen status bars while
|
||||
-- dynamically responding to screen changes. A startup action, event hook, and
|
||||
-- a way to separate PP styles based on the screen's focus are provided:
|
||||
--
|
||||
-- * The 'dynStatusBarStartup' hook which initializes the status bars.
|
||||
--
|
||||
-- * The 'dynStatusBarEventHook' hook which respawns status bars when the
|
||||
-- number of screens changes.
|
||||
--
|
||||
-- * The 'multiPP' function which allows for different output based on whether
|
||||
-- the screen for the status bar has focus.
|
||||
--
|
||||
-- The hooks take a 'DynamicStatusBar' function which is given the id of the
|
||||
-- screen to start up and returns the 'Handle' to the pipe to write to. The
|
||||
-- 'DynamicStatusBarCleanup' argument should tear down previous instances. It
|
||||
-- is called when the number of screens changes and on startup.
|
||||
--
|
||||
|
||||
data DynStatusBarInfo = DynStatusBarInfo
|
||||
{ dsbInfoScreens :: [ScreenId]
|
||||
, dsbInfoHandles :: [Handle]
|
||||
}
|
||||
|
||||
type DynamicStatusBar = ScreenId -> IO Handle
|
||||
type DynamicStatusBarCleanup = IO ()
|
||||
|
||||
-- Global state
|
||||
statusBarInfo :: MVar DynStatusBarInfo
|
||||
statusBarInfo = unsafePerformIO $ newMVar (DynStatusBarInfo [] [])
|
||||
|
||||
dynStatusBarStartup :: DynamicStatusBar -> DynamicStatusBarCleanup -> X ()
|
||||
dynStatusBarStartup sb cleanup = liftIO $ do
|
||||
dpy <- openDisplay ""
|
||||
xrrSelectInput dpy (defaultRootWindow dpy) rrScreenChangeNotifyMask
|
||||
closeDisplay dpy
|
||||
updateStatusBars sb cleanup
|
||||
|
||||
dynStatusBarEventHook :: DynamicStatusBar -> DynamicStatusBarCleanup -> Event -> X All
|
||||
dynStatusBarEventHook sb cleanup (RRScreenChangeNotifyEvent {}) = liftIO (updateStatusBars sb cleanup) >> return (All True)
|
||||
dynStatusBarEventHook _ _ _ = return (All True)
|
||||
|
||||
updateStatusBars :: DynamicStatusBar -> DynamicStatusBarCleanup -> IO ()
|
||||
updateStatusBars sb cleanup = liftIO $ do
|
||||
dsbInfo <- takeMVar statusBarInfo
|
||||
screens <- getScreens
|
||||
if (screens /= (dsbInfoScreens dsbInfo))
|
||||
then do
|
||||
mapM hClose (dsbInfoHandles dsbInfo)
|
||||
cleanup
|
||||
newHandles <- mapM sb screens
|
||||
putMVar statusBarInfo (DynStatusBarInfo screens newHandles)
|
||||
else putMVar statusBarInfo dsbInfo
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- The following code is from adamvo's xmonad.hs file.
|
||||
-- http://www.haskell.org/haskellwiki/Xmonad/Config_archive/adamvo%27s_xmonad.hs
|
||||
|
||||
multiPP :: PP -- ^ The PP to use if the screen is focused
|
||||
-> PP -- ^ The PP to use otherwise
|
||||
-> X ()
|
||||
multiPP focusPP unfocusPP = do
|
||||
dsbInfo <- liftIO $ readMVar statusBarInfo
|
||||
multiPP' dynamicLogString focusPP unfocusPP (dsbInfoHandles dsbInfo)
|
||||
|
||||
multiPP' :: (PP -> X String) -> PP -> PP -> [Handle] -> X ()
|
||||
multiPP' dynlStr focusPP unfocusPP handles = do
|
||||
st <- get
|
||||
let pickPP :: WorkspaceId -> WriterT (Last XState) X String
|
||||
pickPP ws = do
|
||||
let isFoc = (ws ==) . W.tag . W.workspace . W.current $ windowset st
|
||||
put st{ windowset = W.view ws $ windowset st }
|
||||
out <- lift $ dynlStr $ if isFoc then focusPP else unfocusPP
|
||||
when isFoc $ get >>= tell . Last . Just
|
||||
return out
|
||||
traverse put . getLast
|
||||
=<< execWriterT . (io . zipWithM_ hPutStrLn handles <=< mapM pickPP) . catMaybes
|
||||
=<< mapM screenWorkspace (zipWith const [0 .. ] handles)
|
||||
return ()
|
||||
|
||||
getScreens :: IO [ScreenId]
|
||||
getScreens = do
|
||||
screens <- do
|
||||
dpy <- openDisplay ""
|
||||
rects <- getScreenInfo dpy
|
||||
closeDisplay dpy
|
||||
return rects
|
||||
let ids = zip [0 .. ] screens
|
||||
return $ map fst ids
|
@@ -20,6 +20,7 @@ module XMonad.Hooks.EwmhDesktops (
|
||||
ewmhDesktopsLogHook,
|
||||
ewmhDesktopsLogHookCustom,
|
||||
ewmhDesktopsEventHook,
|
||||
ewmhDesktopsEventHookCustom,
|
||||
fullscreenEventHook
|
||||
) where
|
||||
|
||||
@@ -43,9 +44,10 @@ import XMonad.Util.WindowProperties (getProp32)
|
||||
-- > import XMonad
|
||||
-- > import XMonad.Hooks.EwmhDesktops
|
||||
-- >
|
||||
-- > main = xmonad $ ewmh defaultConfig
|
||||
-- > main = xmonad $ ewmh defaultConfig{ handleEventHook =
|
||||
-- > handleEventHook defaultConfig <+> fullscreenEventHook }
|
||||
--
|
||||
-- You may also be interested in 'avoidStruts' from XMonad.Hooks.ManageDocks.
|
||||
-- You may also be interested in 'avoidStruts' from "XMonad.Hooks.ManageDocks".
|
||||
|
||||
|
||||
-- | Add EWMH functionality to the given config. See above for an example.
|
||||
@@ -116,18 +118,23 @@ ewmhDesktopsLogHookCustom f = withWindowSet $ \s -> do
|
||||
-- * _NET_WM_DESKTOP (move windows to other desktops)
|
||||
--
|
||||
-- * _NET_ACTIVE_WINDOW (activate another window, changing workspace if needed)
|
||||
--
|
||||
ewmhDesktopsEventHook :: Event -> X All
|
||||
ewmhDesktopsEventHook e = handle e >> return (All True)
|
||||
ewmhDesktopsEventHook = ewmhDesktopsEventHookCustom id
|
||||
|
||||
handle :: Event -> X ()
|
||||
handle ClientMessageEvent {
|
||||
-- |
|
||||
-- Generalized version of ewmhDesktopsEventHook that allows an arbitrary
|
||||
-- user-specified function to transform the workspace list (post-sorting)
|
||||
ewmhDesktopsEventHookCustom :: ([WindowSpace] -> [WindowSpace]) -> Event -> X All
|
||||
ewmhDesktopsEventHookCustom f e = handle f e >> return (All True)
|
||||
|
||||
handle :: ([WindowSpace] -> [WindowSpace]) -> Event -> X ()
|
||||
handle f (ClientMessageEvent {
|
||||
ev_window = w,
|
||||
ev_message_type = mt,
|
||||
ev_data = d
|
||||
} = withWindowSet $ \s -> do
|
||||
}) = withWindowSet $ \s -> do
|
||||
sort' <- getSortByIndex
|
||||
let ws = sort' $ W.workspaces s
|
||||
let ws = f $ sort' $ W.workspaces s
|
||||
|
||||
a_cd <- getAtom "_NET_CURRENT_DESKTOP"
|
||||
a_d <- getAtom "_NET_WM_DESKTOP"
|
||||
@@ -154,17 +161,19 @@ handle ClientMessageEvent {
|
||||
-- The Message is unknown to us, but that is ok, not all are meant
|
||||
-- to be handled by the window manager
|
||||
return ()
|
||||
handle _ = return ()
|
||||
handle _ _ = return ()
|
||||
|
||||
-- |
|
||||
-- An event hook to handle applications that wish to fullscreen using the
|
||||
-- _NET_WM_STATE protocol. This includes users of the gtk_window_fullscreen()
|
||||
-- function, such as Totem, Evince and OpenOffice.org.
|
||||
--
|
||||
-- Note this is not included in 'ewmh'.
|
||||
fullscreenEventHook :: Event -> X All
|
||||
fullscreenEventHook (ClientMessageEvent _ _ _ dpy win typ (action:dats)) = do
|
||||
state <- getAtom "_NET_WM_STATE"
|
||||
wmstate <- getAtom "_NET_WM_STATE"
|
||||
fullsc <- getAtom "_NET_WM_STATE_FULLSCREEN"
|
||||
wstate <- fromMaybe [] `fmap` getProp32 state win
|
||||
wstate <- fromMaybe [] `fmap` getProp32 wmstate win
|
||||
|
||||
let isFull = fromIntegral fullsc `elem` wstate
|
||||
|
||||
@@ -173,9 +182,9 @@ fullscreenEventHook (ClientMessageEvent _ _ _ dpy win typ (action:dats)) = do
|
||||
add = 1
|
||||
toggle = 2
|
||||
ptype = 4 -- The atom property type for changeProperty
|
||||
chWstate f = io $ changeProperty32 dpy win state ptype propModeReplace (f wstate)
|
||||
chWstate f = io $ changeProperty32 dpy win wmstate ptype propModeReplace (f wstate)
|
||||
|
||||
when (typ == state && fi fullsc `elem` dats) $ do
|
||||
when (typ == wmstate && fi fullsc `elem` dats) $ do
|
||||
when (action == add || (action == toggle && not isFull)) $ do
|
||||
chWstate (fi fullsc:)
|
||||
windows $ W.float win $ W.RationalRect 0 0 1 1
|
||||
|
@@ -17,10 +17,12 @@ module XMonad.Hooks.FadeInactive (
|
||||
-- $usage
|
||||
setOpacity,
|
||||
isUnfocused,
|
||||
isUnfocusedOnCurrentWS,
|
||||
fadeIn,
|
||||
fadeOut,
|
||||
fadeIf,
|
||||
fadeInactiveLogHook,
|
||||
fadeInactiveCurrentWSLogHook,
|
||||
fadeOutLogHook
|
||||
) where
|
||||
|
||||
@@ -58,18 +60,18 @@ rationalToOpacity perc
|
||||
| perc < 0 || perc > 1 = round perc -- to maintain backwards-compatability
|
||||
| otherwise = round $ perc * 0xffffffff
|
||||
|
||||
-- | sets the opacity of a window
|
||||
-- | Sets the opacity of a window
|
||||
setOpacity :: Window -> Rational -> X ()
|
||||
setOpacity w t = withDisplay $ \dpy -> do
|
||||
a <- getAtom "_NET_WM_WINDOW_OPACITY"
|
||||
c <- getAtom "CARDINAL"
|
||||
io $ changeProperty32 dpy w a c propModeReplace [rationalToOpacity t]
|
||||
|
||||
-- | fades a window out by setting the opacity
|
||||
-- | Fades a window out by setting the opacity
|
||||
fadeOut :: Rational -> Window -> X ()
|
||||
fadeOut = flip setOpacity
|
||||
|
||||
-- | makes a window completely opaque
|
||||
-- | Makes a window completely opaque
|
||||
fadeIn :: Window -> X ()
|
||||
fadeIn = fadeOut 1
|
||||
|
||||
@@ -78,15 +80,34 @@ fadeIn = fadeOut 1
|
||||
fadeIf :: Query Bool -> Rational -> Query Rational
|
||||
fadeIf qry amt = qry >>= \b -> return $ if b then amt else 1
|
||||
|
||||
-- | sets the opacity of inactive windows to the specified amount
|
||||
-- | Sets the opacity of inactive windows to the specified amount
|
||||
fadeInactiveLogHook :: Rational -> X ()
|
||||
fadeInactiveLogHook = fadeOutLogHook . fadeIf isUnfocused
|
||||
|
||||
-- | returns True if the window doesn't have the focus.
|
||||
-- | Set the opacity of inactive windows, on the current workspace, to the
|
||||
-- specified amount. This is specifically usefull in a multi monitor setup. See
|
||||
-- 'isUnfocusedOnCurrentWS'.
|
||||
fadeInactiveCurrentWSLogHook :: Rational -> X ()
|
||||
fadeInactiveCurrentWSLogHook = fadeOutLogHook . fadeIf isUnfocusedOnCurrentWS
|
||||
|
||||
-- | Returns True if the window doesn't have the focus.
|
||||
isUnfocused :: Query Bool
|
||||
isUnfocused = ask >>= \w -> liftX . gets $ maybe True (w /=) . W.peek . windowset
|
||||
|
||||
-- | fades out every window by the amount returned by the query.
|
||||
-- | Returns True if the window doesn't have the focus, and the window is on the
|
||||
-- current workspace. This is specifically handy in a multi monitor setup
|
||||
-- (xinerama) where multiple workspaces are visible. Using this, non-focused
|
||||
-- workspaces are are not faded out making it easier to look and read the
|
||||
-- content on them.
|
||||
isUnfocusedOnCurrentWS :: Query Bool
|
||||
isUnfocusedOnCurrentWS = do
|
||||
w <- ask
|
||||
ws <- liftX $ gets windowset
|
||||
let thisWS = w `elem` W.index ws
|
||||
unfocused = maybe True (w /=) $ W.peek ws
|
||||
return $ thisWS && unfocused
|
||||
|
||||
-- | Fades out every window by the amount returned by the query.
|
||||
fadeOutLogHook :: Query Rational -> X ()
|
||||
fadeOutLogHook qry = withWindowSet $ \s -> do
|
||||
let visibleWins = (W.integrate' . W.stack . W.workspace . W.current $ s) ++
|
||||
|
@@ -105,7 +105,7 @@ import Graphics.X11.Xlib.Extras (Event(..))
|
||||
--
|
||||
-- "XMonad.Doc.Extending#Editing_the_event_hook"
|
||||
-- (which sadly doesnt exist at the time of writing...)
|
||||
--
|
||||
--
|
||||
-- /WARNING:/ This module is very good at triggering bugs in
|
||||
-- compositing managers. Symptoms range from windows not being
|
||||
-- repainted until the compositing manager is restarted or the
|
||||
|
@@ -18,8 +18,9 @@
|
||||
-- @
|
||||
-----------------------------------------------------------------------------
|
||||
module XMonad.Hooks.ICCCMFocus
|
||||
{-# DEPRECATED "XMonad.Hooks.ICCCMFocus: xmonad>0.10 core merged issue 177" #-}
|
||||
(
|
||||
atom_WM_TAKE_FOCUS
|
||||
atom_WM_TAKE_FOCUS
|
||||
, takeFocusX
|
||||
, takeTopFocus
|
||||
) where
|
||||
@@ -27,31 +28,15 @@ module XMonad.Hooks.ICCCMFocus
|
||||
import XMonad
|
||||
import XMonad.Hooks.SetWMName
|
||||
import qualified XMonad.StackSet as W
|
||||
import Control.Monad
|
||||
|
||||
atom_WM_TAKE_FOCUS ::
|
||||
X Atom
|
||||
atom_WM_TAKE_FOCUS =
|
||||
getAtom "WM_TAKE_FOCUS"
|
||||
|
||||
takeFocusX ::
|
||||
Window
|
||||
-> X ()
|
||||
takeFocusX w =
|
||||
withWindowSet . const $ do
|
||||
dpy <- asks display
|
||||
wmtakef <- atom_WM_TAKE_FOCUS
|
||||
wmprot <- atom_WM_PROTOCOLS
|
||||
protocols <- io $ getWMProtocols dpy w
|
||||
when (wmtakef `elem` protocols) $
|
||||
io . allocaXEvent $ \ev -> do
|
||||
setEventType ev clientMessage
|
||||
setClientMessageEvent ev w wmprot 32 wmtakef currentTime
|
||||
sendEvent dpy w False noEventMask ev
|
||||
takeFocusX _w = return ()
|
||||
|
||||
-- | The value to add to your log hook configuration.
|
||||
takeTopFocus ::
|
||||
X ()
|
||||
takeTopFocus =
|
||||
(withWindowSet $ maybe (setFocusX =<< asks theRoot) takeFocusX . W.peek) >> setWMName "LG3D"
|
||||
(withWindowSet $ maybe (setFocusX =<< asks theRoot) takeFocusX . W.peek) >> setWMName "LG3D"
|
||||
|
||||
|
@@ -111,8 +111,8 @@ checkDock = ask >>= \w -> liftX $ do
|
||||
desk <- getAtom "_NET_WM_WINDOW_TYPE_DESKTOP"
|
||||
mbr <- getProp32s "_NET_WM_WINDOW_TYPE" w
|
||||
case mbr of
|
||||
Just [r] -> return $ elem (fromIntegral r) [dock, desk]
|
||||
_ -> return False
|
||||
Just rs -> return $ any (`elem` [dock,desk]) (map fromIntegral rs)
|
||||
_ -> return False
|
||||
|
||||
-- | Whenever a new dock appears, refresh the layout immediately to avoid the
|
||||
-- new dock.
|
||||
|
@@ -26,8 +26,6 @@ module XMonad.Hooks.Script (
|
||||
--
|
||||
import XMonad
|
||||
|
||||
import System.Directory
|
||||
|
||||
-- $usage
|
||||
--
|
||||
-- This module allows you to run a centrally located script with the text
|
||||
@@ -47,7 +45,7 @@ import System.Directory
|
||||
|
||||
-- | Execute a named script hook
|
||||
execScriptHook :: MonadIO m => String -> m ()
|
||||
execScriptHook hook = io $ do
|
||||
home <- getHomeDirectory
|
||||
let script = home ++ "/.xmonad/hooks "
|
||||
execScriptHook hook = do
|
||||
xmonadDir <- getXMonadDir
|
||||
let script = xmonadDir ++ "/hooks "
|
||||
spawn (script ++ hook)
|
||||
|
@@ -63,10 +63,11 @@ _pp n f s st = (\b -> guard b >> Just (st s)) <$> _get n f
|
||||
|
||||
{- The current state is kept here -}
|
||||
|
||||
data HookState = HookState { hooks :: Map String (Bool, Bool) } deriving (Typeable)
|
||||
data HookState = HookState { hooks :: Map String (Bool, Bool) } deriving (Typeable, Read, Show)
|
||||
|
||||
instance ExtensionClass HookState where
|
||||
initialValue = HookState empty
|
||||
extensionType = PersistentExtension
|
||||
|
||||
modify' :: String -> ((Bool, Bool) -> (Bool, Bool)) -> X ()
|
||||
modify' n f = XS.modify (HookState . setter . hooks)
|
||||
|
@@ -59,6 +59,7 @@ module XMonad.Hooks.UrgencyHook (
|
||||
dzenUrgencyHook,
|
||||
DzenUrgencyHook(..),
|
||||
NoUrgencyHook(..),
|
||||
BorderUrgencyHook(..),
|
||||
FocusHook(..),
|
||||
minutes, seconds,
|
||||
-- * Stuff for developers:
|
||||
@@ -67,6 +68,7 @@ module XMonad.Hooks.UrgencyHook (
|
||||
SpawnUrgencyHook(..),
|
||||
UrgencyHook(urgencyHook),
|
||||
Interval,
|
||||
borderUrgencyHook, focusHook, spawnUrgencyHook, stdoutUrgencyHook
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
@@ -83,6 +85,7 @@ import Data.Bits (testBit)
|
||||
import Data.List (delete, (\\))
|
||||
import Data.Maybe (listToMaybe, maybeToList)
|
||||
import qualified Data.Set as S
|
||||
import System.IO (hPutStrLn, stderr)
|
||||
|
||||
-- $usage
|
||||
--
|
||||
@@ -388,9 +391,12 @@ suppressibleWindows Never = return []
|
||||
|
||||
-- | The class definition, and some pre-defined instances.
|
||||
|
||||
class (Read h, Show h) => UrgencyHook h where
|
||||
class UrgencyHook h where
|
||||
urgencyHook :: h -> Window -> X ()
|
||||
|
||||
instance UrgencyHook (Window -> X ()) where
|
||||
urgencyHook = id
|
||||
|
||||
data NoUrgencyHook = NoUrgencyHook deriving (Read, Show)
|
||||
|
||||
instance UrgencyHook NoUrgencyHook where
|
||||
@@ -418,11 +424,40 @@ instance UrgencyHook DzenUrgencyHook where
|
||||
|
||||
> withUrgencyHook FocusHook $ myconfig { ...
|
||||
-}
|
||||
focusHook :: Window -> X ()
|
||||
focusHook = urgencyHook FocusHook
|
||||
data FocusHook = FocusHook deriving (Read, Show)
|
||||
|
||||
instance UrgencyHook FocusHook where
|
||||
urgencyHook _ _ = focusUrgent
|
||||
|
||||
-- | A hook that sets the border color of an urgent window. The color
|
||||
-- will remain until the next time the window gains or loses focus, at
|
||||
-- which point the standard border color from the XConfig will be applied.
|
||||
-- You may want to use suppressWhen = Never with this:
|
||||
--
|
||||
-- > withUrgencyHookC BorderUrgencyHook { urgencyBorderColor = "#ff0000" } urgencyConfig { suppressWhen = Never } ...
|
||||
--
|
||||
-- (This should be @urgentBorderColor@ but that breaks "XMonad.Layout.Decoration".
|
||||
-- @borderColor@ breaks anyone using 'XPConfig' from "XMonad.Prompt". We need to
|
||||
-- think a bit more about namespacing issues, maybe.)
|
||||
|
||||
borderUrgencyHook :: String -> Window -> X ()
|
||||
borderUrgencyHook = urgencyHook . BorderUrgencyHook
|
||||
data BorderUrgencyHook = BorderUrgencyHook { urgencyBorderColor :: !String }
|
||||
deriving (Read, Show)
|
||||
|
||||
instance UrgencyHook BorderUrgencyHook where
|
||||
urgencyHook BorderUrgencyHook { urgencyBorderColor = cs } w =
|
||||
withDisplay $ \dpy -> io $ do
|
||||
c' <- initColor dpy cs
|
||||
case c' of
|
||||
Just c -> setWindowBorder dpy w c
|
||||
_ -> hPutStrLn stderr $ concat ["Warning: bad urgentBorderColor "
|
||||
,show cs
|
||||
," in BorderUrgencyHook"
|
||||
]
|
||||
|
||||
-- | Flashes when a window requests your attention and you can't see it.
|
||||
-- Defaults to a duration of five seconds, and no extra args to dzen.
|
||||
-- See 'DzenUrgencyHook'.
|
||||
@@ -432,12 +467,16 @@ dzenUrgencyHook = DzenUrgencyHook { duration = seconds 5, args = [] }
|
||||
-- | Spawn a commandline thing, appending the window id to the prefix string
|
||||
-- you provide. (Make sure to add a space if you need it.) Do your crazy
|
||||
-- xcompmgr thing.
|
||||
spawnUrgencyHook :: String -> Window -> X ()
|
||||
spawnUrgencyHook = urgencyHook . SpawnUrgencyHook
|
||||
newtype SpawnUrgencyHook = SpawnUrgencyHook String deriving (Read, Show)
|
||||
|
||||
instance UrgencyHook SpawnUrgencyHook where
|
||||
urgencyHook (SpawnUrgencyHook prefix) w = spawn $ prefix ++ show w
|
||||
|
||||
-- | For debugging purposes, really.
|
||||
stdoutUrgencyHook :: Window -> X ()
|
||||
stdoutUrgencyHook = urgencyHook StdoutUrgencyHook
|
||||
data StdoutUrgencyHook = StdoutUrgencyHook deriving (Read, Show)
|
||||
|
||||
instance UrgencyHook StdoutUrgencyHook where
|
||||
|
@@ -18,8 +18,7 @@ module XMonad.Hooks.XPropManage (
|
||||
xPropManageHook, XPropMatch, pmX, pmP
|
||||
) where
|
||||
|
||||
import Prelude hiding (catch)
|
||||
import Control.Exception
|
||||
import Control.Exception as E
|
||||
import Data.Char (chr)
|
||||
import Data.Monoid (mconcat, Endo(..))
|
||||
|
||||
@@ -76,7 +75,7 @@ xPropManageHook tms = mconcat $ map propToHook tms
|
||||
|
||||
getProp :: Display -> Window -> Atom -> X ([String])
|
||||
getProp d w p = do
|
||||
prop <- io $ catch (getTextProperty d w p >>= wcTextPropertyToTextList d) (\(_ :: IOException) -> return [[]])
|
||||
prop <- io $ E.catch (getTextProperty d w p >>= wcTextPropertyToTextList d) (\(_ :: IOException) -> return [[]])
|
||||
let filt q | q == wM_COMMAND = concat . map splitAtNull
|
||||
| otherwise = id
|
||||
return (filt p prop)
|
||||
|
@@ -59,10 +59,8 @@ type RectWithBorders = (Rectangle, [BorderInfo])
|
||||
|
||||
data BorderResize a = BR (M.Map Window RectWithBorders) deriving (Show, Read)
|
||||
|
||||
brBorderOffset :: Position
|
||||
brBorderOffset = 5
|
||||
brBorderSize :: Dimension
|
||||
brBorderSize = 10
|
||||
brBorderSize = 2
|
||||
|
||||
borderResize :: l a -> ModifiedLayout BorderResize l a
|
||||
borderResize = ModifiedLayout (BR M.empty)
|
||||
@@ -147,10 +145,10 @@ createBorderLookupTable wrsLastTime = concat $ map processSingleEntry $ M.toList
|
||||
|
||||
prepareBorders :: Rectangle -> [BorderBlueprint]
|
||||
prepareBorders (Rectangle x y wh ht) =
|
||||
[((Rectangle (x + fi wh - brBorderOffset) y brBorderSize ht), xC_right_side , RightSideBorder),
|
||||
((Rectangle (x - brBorderOffset) y brBorderSize ht) , xC_left_side , LeftSideBorder),
|
||||
((Rectangle x (y - brBorderOffset) wh brBorderSize) , xC_top_side , TopSideBorder),
|
||||
((Rectangle x (y + fi ht - brBorderOffset) wh brBorderSize), xC_bottom_side, BottomSideBorder)
|
||||
[((Rectangle (x + fi wh - fi brBorderSize) y brBorderSize ht), xC_right_side , RightSideBorder),
|
||||
((Rectangle x y brBorderSize ht) , xC_left_side , LeftSideBorder),
|
||||
((Rectangle x y wh brBorderSize) , xC_top_side , TopSideBorder),
|
||||
((Rectangle x (y + fi ht - fi brBorderSize) wh brBorderSize), xC_bottom_side, BottomSideBorder)
|
||||
]
|
||||
|
||||
handleResize :: [(Window, (BorderType, Window, Rectangle))] -> Event -> X ()
|
||||
|
@@ -53,7 +53,7 @@ import Control.Arrow (second)
|
||||
-- To use this module, add 'fullscreenEventHook' and 'fullscreenManageHook'
|
||||
-- to your config, i.e.
|
||||
--
|
||||
-- > xmonad defaultconfig { eventHook = fullscreenEventHook,
|
||||
-- > xmonad defaultconfig { handleEventHook = fullscreenEventHook,
|
||||
-- > manageHook = fullscreenManageHook,
|
||||
-- > layoutHook = myLayouts }
|
||||
--
|
||||
@@ -124,11 +124,11 @@ instance LayoutModifier FullscreenFloat Window where
|
||||
|
||||
-- Modify the floating member of the stack set directly; this is the hackish part.
|
||||
Just FullscreenChanged -> do
|
||||
state <- get
|
||||
let ws = windowset state
|
||||
st <- get
|
||||
let ws = windowset st
|
||||
flt = W.floating ws
|
||||
flt' = M.intersectionWith doFull fulls flt
|
||||
put state {windowset = ws {W.floating = M.union flt' flt}}
|
||||
put st {windowset = ws {W.floating = M.union flt' flt}}
|
||||
return $ Just $ FullscreenFloat frect $ M.filter snd fulls
|
||||
where doFull (_, True) _ = frect
|
||||
doFull (rect, False) _ = rect
|
||||
@@ -174,9 +174,9 @@ fullscreenFloatRect r = ModifiedLayout $ FullscreenFloat r M.empty
|
||||
-- | The event hook required for the layout modifiers to work
|
||||
fullscreenEventHook :: Event -> X All
|
||||
fullscreenEventHook (ClientMessageEvent _ _ _ dpy win typ (action:dats)) = do
|
||||
state <- getAtom "_NET_WM_STATE"
|
||||
wmstate <- getAtom "_NET_WM_STATE"
|
||||
fullsc <- getAtom "_NET_WM_STATE_FULLSCREEN"
|
||||
wstate <- fromMaybe [] `fmap` getProp32 state win
|
||||
wstate <- fromMaybe [] `fmap` getProp32 wmstate win
|
||||
let fi :: (Integral i, Num n) => i -> n
|
||||
fi = fromIntegral
|
||||
isFull = fi fullsc `elem` wstate
|
||||
@@ -184,8 +184,8 @@ fullscreenEventHook (ClientMessageEvent _ _ _ dpy win typ (action:dats)) = do
|
||||
add = 1
|
||||
toggle = 2
|
||||
ptype = 4
|
||||
chWState f = io $ changeProperty32 dpy win state ptype propModeReplace (f wstate)
|
||||
when (typ == state && fi fullsc `elem` dats) $ do
|
||||
chWState f = io $ changeProperty32 dpy win wmstate ptype propModeReplace (f wstate)
|
||||
when (typ == wmstate && fi fullsc `elem` dats) $ do
|
||||
when (action == add || (action == toggle && not isFull)) $ do
|
||||
chWState (fi fullsc:)
|
||||
broadcastMessage $ AddFullscreen win
|
||||
|
@@ -70,9 +70,9 @@ import Control.Monad (forM)
|
||||
-- group, and the layout with which the groups themselves will
|
||||
-- be arranged on the screen.
|
||||
--
|
||||
-- The "XMonad.Layout.Groups.Examples" and "XMonad.Layout.Groups.Wmii"
|
||||
-- modules contain examples of layouts that can be defined with this
|
||||
-- combinator. They're also the recommended starting point
|
||||
-- The "XMonad.Layout.Groups.Examples" and "XMonad.Layout.Groups.Wmii"
|
||||
-- modules contain examples of layouts that can be defined with this
|
||||
-- combinator. They're also the recommended starting point
|
||||
-- if you are a beginner and looking for something you can use easily.
|
||||
--
|
||||
-- One thing to note is that 'Groups'-based layout have their own
|
||||
@@ -81,7 +81,7 @@ import Control.Monad (forM)
|
||||
-- will have no visible effect, and those like 'XMonad.StackSet.focusUp'
|
||||
-- will focus the windows in an unpredictable order. For a better way of
|
||||
-- rearranging windows and moving focus in such a layout, see the
|
||||
-- example 'ModifySpec's (to be passed to the 'Modify' message) provided
|
||||
-- example 'ModifySpec's (to be passed to the 'Modify' message) provided
|
||||
-- by this module.
|
||||
--
|
||||
-- If you use both 'Groups'-based and other layouts, The "XMonad.Layout.Groups.Helpers"
|
||||
@@ -105,7 +105,7 @@ group l l2 = Groups l l2 startingGroups (U 1 0)
|
||||
data Uniq = U Integer Integer
|
||||
deriving (Eq, Show, Read)
|
||||
|
||||
-- | From a seed, generate an infinite list of keys and a new
|
||||
-- | From a seed, generate an infinite list of keys and a new
|
||||
-- seed. All keys generated with this method will be different
|
||||
-- provided you don't use 'gen' again with a key from the list.
|
||||
-- (if you need to do that, see 'split' instead)
|
||||
@@ -121,7 +121,7 @@ gen (U i1 i2) = (U (i1+1) i2, zipWith U (repeat i1) [i2..])
|
||||
|
||||
-- | Add a unique identity to a layout so we can
|
||||
-- follow it around.
|
||||
data WithID l a = ID { getID :: Uniq
|
||||
data WithID l a = ID { getID :: Uniq
|
||||
, unID :: (l a)}
|
||||
deriving (Show, Read)
|
||||
|
||||
@@ -133,15 +133,15 @@ instance Eq (WithID l a) where
|
||||
ID id1 _ == ID id2 _ = id1 == id2
|
||||
|
||||
instance LayoutClass l a => LayoutClass (WithID l) a where
|
||||
runLayout ws@W.Workspace { W.layout = ID id l } r
|
||||
= do (placements, ml') <- flip runLayout r
|
||||
runLayout ws@W.Workspace { W.layout = ID id l } r
|
||||
= do (placements, ml') <- flip runLayout r
|
||||
ws { W.layout = l}
|
||||
return (placements, ID id <$> ml')
|
||||
handleMessage (ID id l) sm = do ml' <- handleMessage l sm
|
||||
return $ ID id <$> ml'
|
||||
description (ID _ l) = description l
|
||||
|
||||
|
||||
|
||||
|
||||
-- * The 'Groups' layout
|
||||
|
||||
@@ -211,7 +211,7 @@ modifyGroups f g = let (seed', id:_) = gen (seed g)
|
||||
|
||||
-- | Adapt our groups to a new stack.
|
||||
-- This algorithm handles window additions and deletions correctly,
|
||||
-- ignores changes in window ordering, and tries to react to any
|
||||
-- ignores changes in window ordering, and tries to react to any
|
||||
-- other stack changes as gracefully as possible.
|
||||
readapt :: Eq a => Zipper a -> Groups l l2 a -> Groups l l2 a
|
||||
readapt z g = let mf = getFocusZ z
|
||||
@@ -233,7 +233,7 @@ removeDeleted :: Eq a => Zipper a -> Zipper a -> Zipper a
|
||||
removeDeleted z = filterZ_ (flip elemZ z)
|
||||
|
||||
-- | Identify the windows not already in a group.
|
||||
findNewWindows :: Eq a => [a] -> Zipper (Group l a)
|
||||
findNewWindows :: Eq a => [a] -> Zipper (Group l a)
|
||||
-> (Zipper (Group l a), [a])
|
||||
findNewWindows as gs = (gs, foldrZ_ removePresent as gs)
|
||||
where removePresent g as' = filter (not . flip elemZ (gZipper g)) as'
|
||||
@@ -279,10 +279,10 @@ instance (LayoutClass l Window, LayoutClass l2 (Group l Window))
|
||||
|
||||
let placements = concatMap fst results
|
||||
newL = justMakeNew l mpart' (map snd results ++ hidden')
|
||||
|
||||
|
||||
return $ (placements, newL)
|
||||
|
||||
handleMessage l@(Groups _ p _ _) sm | Just (ToEnclosing sm') <- fromMessage sm
|
||||
handleMessage l@(Groups _ p _ _) sm | Just (ToEnclosing sm') <- fromMessage sm
|
||||
= do mp' <- handleMessage p sm'
|
||||
return $ maybeMakeNew l mp' []
|
||||
|
||||
@@ -316,7 +316,7 @@ instance (LayoutClass l Window, LayoutClass l2 (Group l Window))
|
||||
step _ = return Nothing
|
||||
|
||||
|
||||
justMakeNew :: Groups l l2 a -> Maybe (l2 (Group l a)) -> [Maybe (WithID l a)]
|
||||
justMakeNew :: Groups l l2 a -> Maybe (l2 (Group l a)) -> [Maybe (WithID l a)]
|
||||
-> Maybe (Groups l l2 a)
|
||||
justMakeNew g mpart' ml's = Just g { partitioner = fromMaybe (partitioner g) mpart'
|
||||
, groups = combine (groups g) ml's }
|
||||
@@ -339,7 +339,7 @@ refocus g = case getFocusZ $ gZipper $ W.focus $ groups g
|
||||
|
||||
-- ** ModifySpec type
|
||||
|
||||
-- | Type of functions describing modifications to a 'Groups' layout. They
|
||||
-- | Type of functions describing modifications to a 'Groups' layout. They
|
||||
-- are transformations on 'Zipper's of groups.
|
||||
--
|
||||
-- Things you shouldn't do:
|
||||
@@ -358,7 +358,7 @@ refocus g = case getFocusZ $ gZipper $ W.focus $ groups g
|
||||
-- 'ModifySpec's as arguments, or returning them, you'll need to write a type
|
||||
-- signature and add @{-# LANGUAGE Rank2Types #-}@ at the beginning
|
||||
type ModifySpec = forall l. WithID l Window
|
||||
-> Zipper (Group l Window)
|
||||
-> Zipper (Group l Window)
|
||||
-> Zipper (Group l Window)
|
||||
|
||||
-- | Apply a ModifySpec.
|
||||
@@ -367,7 +367,7 @@ applySpec f g = let (seed', id:ids) = gen $ seed g
|
||||
g' = flip modifyGroups g $ f (ID id $ baseLayout g)
|
||||
>>> toTags
|
||||
>>> foldr reID ((ids, []), [])
|
||||
>>> snd
|
||||
>>> snd
|
||||
>>> fromTags
|
||||
in case groups g == groups g' of
|
||||
True -> Nothing
|
||||
@@ -448,7 +448,7 @@ _removeFocused (W.Stack f [] []) = (f, Nothing)
|
||||
|
||||
-- helper
|
||||
_moveToNewGroup :: WithID l Window -> W.Stack (Group l Window)
|
||||
-> (Group l Window -> Zipper (Group l Window)
|
||||
-> (Group l Window -> Zipper (Group l Window)
|
||||
-> Zipper (Group l Window))
|
||||
-> Zipper (Group l Window)
|
||||
_moveToNewGroup l0 s insertX | G l (Just f) <- W.focus s
|
||||
@@ -456,7 +456,7 @@ _moveToNewGroup l0 s insertX | G l (Just f) <- W.focus s
|
||||
s' = s { W.focus = G l f' }
|
||||
in insertX (G l0 $ singletonZ w) $ Just s'
|
||||
_moveToNewGroup _ s _ = Just s
|
||||
|
||||
|
||||
-- | Move the focused window to a new group before the current one.
|
||||
moveToNewGroupUp :: ModifySpec
|
||||
moveToNewGroupUp _ Nothing = Nothing
|
||||
|
@@ -67,12 +67,12 @@ import XMonad.Layout.Simplest
|
||||
|
||||
|
||||
-- $usage
|
||||
-- This module contains example 'G.Groups'-based layouts.
|
||||
-- This module contains example 'G.Groups'-based layouts.
|
||||
-- You can either import this module directly, or look at its source
|
||||
-- for ideas of how "XMonad.Layout.Groups" may be used.
|
||||
--
|
||||
-- You can use the contents of this module by adding
|
||||
--
|
||||
--
|
||||
-- > import XMonad.Layout.Groups.Examples
|
||||
--
|
||||
-- to the top of your @.\/.xmonad\/xmonad.hs@.
|
||||
@@ -80,10 +80,10 @@ import XMonad.Layout.Simplest
|
||||
-- For more information on using any of the layouts, jump directly
|
||||
-- to its \"Example\" section.
|
||||
--
|
||||
-- Whichever layout you choose to use, you will probably want to be
|
||||
-- Whichever layout you choose to use, you will probably want to be
|
||||
-- able to move focus and windows between groups in a consistent
|
||||
-- manner. For this, you should take a look at the functions from
|
||||
-- the "XMonad.Layout.Groups.Helpers" module, which are all
|
||||
-- the "XMonad.Layout.Groups.Helpers" module, which are all
|
||||
-- re-exported by this module.
|
||||
--
|
||||
-- For more information on how to extend your layour hook and key bindings, see
|
||||
@@ -99,7 +99,7 @@ data GroupEQ a = GroupEQ
|
||||
instance Eq a => EQF GroupEQ (G.Group l a) where
|
||||
eq _ (G.G l1 _) (G.G l2 _) = G.sameID l1 l2
|
||||
|
||||
zoomRowG :: (Eq a, Show a, Read a, Show (l a), Read (l a))
|
||||
zoomRowG :: (Eq a, Show a, Read a, Show (l a), Read (l a))
|
||||
=> ZoomRow GroupEQ (G.Group l a)
|
||||
zoomRowG = zoomRowWith GroupEQ
|
||||
|
||||
@@ -171,10 +171,10 @@ toggleWindowFull = sendMessage ZoomFullToggle
|
||||
|
||||
-- $example2
|
||||
-- A layout which arranges windows into tabbed groups, and the groups
|
||||
-- themselves according to XMonad's default algorithm
|
||||
-- themselves according to XMonad's default algorithm
|
||||
-- (@'Tall' ||| 'Mirror' 'Tall' ||| 'Full'@). As their names
|
||||
-- indicate, 'tallTabs' starts as 'Tall', 'mirrorTallTabs' starts
|
||||
-- as 'Mirror' 'Tall' and 'fullTabs' starts as 'Full', but in any
|
||||
-- indicate, 'tallTabs' starts as 'Tall', 'mirrorTallTabs' starts
|
||||
-- as 'Mirror' 'Tall' and 'fullTabs' starts as 'Full', but in any
|
||||
-- case you can freely switch between the three afterwards.
|
||||
--
|
||||
-- You can use any of these three layouts by including it in your layout hook.
|
||||
@@ -204,7 +204,7 @@ data TiledTabsConfig s = TTC { vNMaster :: Int
|
||||
defaultTiledTabsConfig :: TiledTabsConfig DefaultShrinker
|
||||
defaultTiledTabsConfig = TTC 1 0.5 (3/100) 1 0.5 (3/100) shrinkText defaultTheme
|
||||
|
||||
fullTabs c = _tab c $ G.group _tabs $ Full ||| _vert c ||| _horiz c
|
||||
fullTabs c = _tab c $ G.group _tabs $ Full ||| _vert c ||| _horiz c
|
||||
|
||||
tallTabs c = _tab c $ G.group _tabs $ _vert c ||| _horiz c ||| Full
|
||||
|
||||
|
@@ -69,7 +69,7 @@ import qualified Data.Map as M
|
||||
-- This module provides actions that try to send 'G.GroupsMessage's, and
|
||||
-- fall back to the classic way if the current layout doesn't hande them.
|
||||
-- They are in the section called \"Layout-generic actions\".
|
||||
--
|
||||
--
|
||||
-- The sections \"Groups-specific actions\" contains actions that don't make
|
||||
-- sense for non-'G.Groups'-based layouts. These are simply wrappers around
|
||||
-- the equivalent 'G.GroupsMessage's, but are included so you don't have to
|
||||
@@ -139,7 +139,7 @@ ifFloat x1 x2 = withFocused $ \w -> do floats <- getFloats
|
||||
|
||||
focusNonFloat :: X ()
|
||||
focusNonFloat = alt2 G.Refocus helper
|
||||
where helper = withFocused $ \w -> do
|
||||
where helper = withFocused $ \w -> do
|
||||
ws <- getWindows
|
||||
floats <- getFloats
|
||||
let (before, after) = span (/=w) ws
|
||||
@@ -170,7 +170,7 @@ focusNonFloatDown = alt2 (G.Modify G.focusDown) $ focusHelper not id
|
||||
|
||||
focusFloatUp :: X ()
|
||||
focusFloatUp = focusHelper id reverse
|
||||
|
||||
|
||||
focusFloatDown :: X ()
|
||||
focusFloatDown = focusHelper id id
|
||||
|
||||
|
@@ -17,7 +17,7 @@
|
||||
|
||||
module XMonad.Layout.Groups.Wmii ( -- * Usage
|
||||
-- $usage
|
||||
|
||||
|
||||
wmii
|
||||
, zoomGroupIn
|
||||
, zoomGroupOut
|
||||
@@ -48,9 +48,9 @@ import XMonad.Layout.Simplest
|
||||
|
||||
|
||||
-- $usage
|
||||
-- This module provides a layout inspired by the one used by the wmii
|
||||
-- This module provides a layout inspired by the one used by the wmii
|
||||
-- (<http://wmii.suckless.org>) window manager.
|
||||
-- Windows are arranged into groups in a horizontal row, and each group can lay out
|
||||
-- Windows are arranged into groups in a horizontal row, and each group can lay out
|
||||
-- its windows
|
||||
--
|
||||
-- * by maximizing the focused one
|
||||
@@ -59,16 +59,16 @@ import XMonad.Layout.Simplest
|
||||
--
|
||||
-- * by arranging them in a column.
|
||||
--
|
||||
-- As the groups are arranged in a 'ZoomRow', the relative width of each group can be
|
||||
-- increased or decreased at will. Groups can also be set to use the whole screen
|
||||
-- As the groups are arranged in a 'ZoomRow', the relative width of each group can be
|
||||
-- increased or decreased at will. Groups can also be set to use the whole screen
|
||||
-- whenever they have focus.
|
||||
--
|
||||
-- You can use the contents of this module by adding
|
||||
--
|
||||
--
|
||||
-- > import XMonad.Layout.Groups.Wmii
|
||||
--
|
||||
-- to the top of your @.\/.xmonad\/xmonad.hs@, and adding 'wmii'
|
||||
-- (with a 'Shrinker' and decoration 'Theme' as
|
||||
-- to the top of your @.\/.xmonad\/xmonad.hs@, and adding 'wmii'
|
||||
-- (with a 'Shrinker' and decoration 'Theme' as
|
||||
-- parameters) to your layout hook, for example:
|
||||
--
|
||||
-- > myLayout = wmii shrinkText defaultTheme
|
||||
@@ -92,10 +92,10 @@ import XMonad.Layout.Simplest
|
||||
wmii s t = G.group innerLayout zoomRowG
|
||||
where column = named "Column" $ Tall 0 (3/100) (1/2)
|
||||
tabs = named "Tabs" $ Simplest
|
||||
innerLayout = renamed [CutWordsLeft 3]
|
||||
innerLayout = renamed [CutWordsLeft 3]
|
||||
$ addTabs s t
|
||||
$ ignore NextLayout
|
||||
$ ignore (JumpToLayout "") $ unEscape
|
||||
$ ignore NextLayout
|
||||
$ ignore (JumpToLayout "") $ unEscape
|
||||
$ column ||| tabs ||| Full
|
||||
|
||||
-- | Increase the width of the focused group
|
||||
|
@@ -140,7 +140,7 @@ closeButton' = [[1,1,0,0,0,0,0,0,1,1],
|
||||
|
||||
|
||||
closeButton :: [[Bool]]
|
||||
closeButton = convertToBool closeButton'
|
||||
closeButton = convertToBool closeButton'
|
||||
|
||||
-- | A function intended to be plugged into the 'decorationCatchClicksHook' of a decoration.
|
||||
-- It will intercept clicks on the buttons of the decoration and invoke the associated action.
|
||||
|
@@ -40,7 +40,7 @@ import qualified XMonad.Layout.LayoutBuilder as B
|
||||
--
|
||||
-- compare "XMonad.Util.Invisible"
|
||||
|
||||
-- | Type class for predicates. This enables us to manage not only Windows,
|
||||
-- | Type class for predicates. This enables us to manage not only Windows,
|
||||
-- but any objects, for which instance Predicate is defined.
|
||||
--
|
||||
-- Another instance exists in XMonad.Util.WindowPropertiesRE in xmonad-extras
|
||||
|
@@ -84,12 +84,12 @@ setMinimizedState :: Window -> Int -> (CLong -> [CLong] -> [CLong]) -> X ()
|
||||
setMinimizedState win st f = do
|
||||
setWMState win st
|
||||
withDisplay $ \dpy -> do
|
||||
state <- getAtom "_NET_WM_STATE"
|
||||
wm_state <- getAtom "_NET_WM_STATE"
|
||||
mini <- getAtom "_NET_WM_STATE_HIDDEN"
|
||||
wstate <- fromMaybe [] `fmap` getProp32 state win
|
||||
wstate <- fromMaybe [] `fmap` getProp32 wm_state win
|
||||
let ptype = 4 -- The atom property type for changeProperty
|
||||
fi_mini = fromIntegral mini
|
||||
io $ changeProperty32 dpy win state ptype propModeReplace (f fi_mini wstate)
|
||||
io $ changeProperty32 dpy win wm_state ptype propModeReplace (f fi_mini wstate)
|
||||
|
||||
setMinimized :: Window -> X ()
|
||||
setMinimized win = setMinimizedState win iconicState (:)
|
||||
|
@@ -136,55 +136,55 @@ mouseResizableTileMirrored :: MouseResizableTile a
|
||||
mouseResizableTileMirrored = mouseResizableTile { isMirrored = True }
|
||||
|
||||
instance LayoutClass MouseResizableTile Window where
|
||||
doLayout state sr (W.Stack w l r) = do
|
||||
drg <- draggerGeometry $ draggerType state
|
||||
doLayout st sr (W.Stack w l r) = do
|
||||
drg <- draggerGeometry $ draggerType st
|
||||
let wins = reverse l ++ w : r
|
||||
num = length wins
|
||||
sr' = mirrorAdjust sr (mirrorRect sr)
|
||||
(rects, preparedDraggers) = tile (nmaster state) (masterFrac state)
|
||||
(leftFracs state ++ repeat (slaveFrac state))
|
||||
(rightFracs state ++ repeat (slaveFrac state)) sr' num drg
|
||||
(rects, preparedDraggers) = tile (nmaster st) (masterFrac st)
|
||||
(leftFracs st ++ repeat (slaveFrac st))
|
||||
(rightFracs st ++ repeat (slaveFrac st)) sr' num drg
|
||||
rects' = map (mirrorAdjust id mirrorRect . sanitizeRectangle sr') rects
|
||||
mapM_ deleteDragger $ draggers state
|
||||
mapM_ deleteDragger $ draggers st
|
||||
(draggerWrs, newDraggers) <- unzip <$> mapM
|
||||
(createDragger sr . adjustForMirror (isMirrored state))
|
||||
(createDragger sr . adjustForMirror (isMirrored st))
|
||||
preparedDraggers
|
||||
return (draggerWrs ++ zip wins rects', Just $ state { draggers = newDraggers,
|
||||
return (draggerWrs ++ zip wins rects', Just $ st { draggers = newDraggers,
|
||||
focusPos = length l,
|
||||
numWindows = length wins })
|
||||
where
|
||||
mirrorAdjust a b = if (isMirrored state)
|
||||
mirrorAdjust a b = if (isMirrored st)
|
||||
then b
|
||||
else a
|
||||
|
||||
handleMessage state m
|
||||
handleMessage st m
|
||||
| Just (IncMasterN d) <- fromMessage m =
|
||||
return $ Just $ state { nmaster = max 0 (nmaster state + d) }
|
||||
return $ Just $ st { nmaster = max 0 (nmaster st + d) }
|
||||
| Just Shrink <- fromMessage m =
|
||||
return $ Just $ state { masterFrac = max 0 (masterFrac state - fracIncrement state) }
|
||||
return $ Just $ st { masterFrac = max 0 (masterFrac st - fracIncrement st) }
|
||||
| Just Expand <- fromMessage m =
|
||||
return $ Just $ state { masterFrac = min 1 (masterFrac state + fracIncrement state) }
|
||||
return $ Just $ st { masterFrac = min 1 (masterFrac st + fracIncrement st) }
|
||||
| Just ShrinkSlave <- fromMessage m =
|
||||
return $ Just $ modifySlave state (- fracIncrement state)
|
||||
return $ Just $ modifySlave st (- fracIncrement st)
|
||||
| Just ExpandSlave <- fromMessage m =
|
||||
return $ Just $ modifySlave state (fracIncrement state)
|
||||
return $ Just $ modifySlave st (fracIncrement st)
|
||||
| Just (SetMasterFraction f) <- fromMessage m =
|
||||
return $ Just $ state { masterFrac = max 0 (min 1 f) }
|
||||
return $ Just $ st { masterFrac = max 0 (min 1 f) }
|
||||
| Just (SetLeftSlaveFraction pos f) <- fromMessage m =
|
||||
return $ Just $ state { leftFracs = replaceAtPos (slaveFrac state)
|
||||
(leftFracs state) pos (max 0 (min 1 f)) }
|
||||
return $ Just $ st { leftFracs = replaceAtPos (slaveFrac st)
|
||||
(leftFracs st) pos (max 0 (min 1 f)) }
|
||||
| Just (SetRightSlaveFraction pos f) <- fromMessage m =
|
||||
return $ Just $ state { rightFracs = replaceAtPos (slaveFrac state)
|
||||
(rightFracs state) pos (max 0 (min 1 f)) }
|
||||
return $ Just $ st { rightFracs = replaceAtPos (slaveFrac st)
|
||||
(rightFracs st) pos (max 0 (min 1 f)) }
|
||||
|
||||
| Just e <- fromMessage m :: Maybe Event = handleResize (draggers state) (isMirrored state) e >> return Nothing
|
||||
| Just Hide <- fromMessage m = releaseResources >> return (Just $ state { draggers = [] })
|
||||
| Just ReleaseResources <- fromMessage m = releaseResources >> return (Just $ state { draggers = [] })
|
||||
where releaseResources = mapM_ deleteDragger $ draggers state
|
||||
| Just e <- fromMessage m :: Maybe Event = handleResize (draggers st) (isMirrored st) e >> return Nothing
|
||||
| Just Hide <- fromMessage m = releaseResources >> return (Just $ st { draggers = [] })
|
||||
| Just ReleaseResources <- fromMessage m = releaseResources >> return (Just $ st { draggers = [] })
|
||||
where releaseResources = mapM_ deleteDragger $ draggers st
|
||||
handleMessage _ _ = return Nothing
|
||||
|
||||
description state = mirror "MouseResizableTile"
|
||||
where mirror = if isMirrored state then ("Mirror " ++) else id
|
||||
description st = mirror "MouseResizableTile"
|
||||
where mirror = if isMirrored st then ("Mirror " ++) else id
|
||||
|
||||
draggerGeometry :: DraggerType -> X DraggerGeometry
|
||||
draggerGeometry (FixedDragger g d) =
|
||||
@@ -203,28 +203,28 @@ adjustForMirror True (draggerRect, draggerCursor, draggerInfo) =
|
||||
else xC_sb_h_double_arrow
|
||||
|
||||
modifySlave :: MouseResizableTile a -> Rational -> MouseResizableTile a
|
||||
modifySlave state delta =
|
||||
let pos = focusPos state
|
||||
num = numWindows state
|
||||
nmaster' = nmaster state
|
||||
leftFracs' = leftFracs state
|
||||
rightFracs' = rightFracs state
|
||||
slFrac = slaveFrac state
|
||||
modifySlave st delta =
|
||||
let pos = focusPos st
|
||||
num = numWindows st
|
||||
nmaster' = nmaster st
|
||||
leftFracs' = leftFracs st
|
||||
rightFracs' = rightFracs st
|
||||
slFrac = slaveFrac st
|
||||
draggersLeft = nmaster' - 1
|
||||
draggersRight = (num - nmaster') - 1
|
||||
in if pos < nmaster'
|
||||
then if draggersLeft > 0
|
||||
then let draggerPos = min (draggersLeft - 1) pos
|
||||
oldFraction = (leftFracs' ++ repeat slFrac) !! draggerPos
|
||||
in state { leftFracs = replaceAtPos slFrac leftFracs' draggerPos
|
||||
in st { leftFracs = replaceAtPos slFrac leftFracs' draggerPos
|
||||
(max 0 (min 1 (oldFraction + delta))) }
|
||||
else state
|
||||
else st
|
||||
else if draggersRight > 0
|
||||
then let draggerPos = min (draggersRight - 1) (pos - nmaster')
|
||||
oldFraction = (rightFracs' ++ repeat slFrac) !! draggerPos
|
||||
in state { rightFracs = replaceAtPos slFrac rightFracs' draggerPos
|
||||
in st { rightFracs = replaceAtPos slFrac rightFracs' draggerPos
|
||||
(max 0 (min 1 (oldFraction + delta))) }
|
||||
else state
|
||||
else st
|
||||
|
||||
replaceAtPos :: (Num t, Eq t) => Rational -> [Rational] -> t -> Rational -> [Rational]
|
||||
replaceAtPos _ [] 0 x' = [x']
|
||||
|
155
XMonad/Layout/OnHost.hs
Normal file
155
XMonad/Layout/OnHost.hs
Normal file
@@ -0,0 +1,155 @@
|
||||
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Layout.OnHost
|
||||
-- Copyright : (c) Brandon S Allbery, Brent Yorgey
|
||||
-- License : BSD-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : <allbery.b@gmail.com>
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Configure layouts on a per-host basis: use layouts and apply
|
||||
-- layout modifiers selectively, depending on the host. Heavily based on
|
||||
-- "XMonad.Layout.PerWorkspace" by Brent Yorgey.
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Layout.OnHost (-- * Usage
|
||||
-- $usage
|
||||
OnHost
|
||||
,onHost
|
||||
,onHosts
|
||||
,modHost
|
||||
,modHosts
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import qualified XMonad.StackSet as W
|
||||
|
||||
import XMonad.Layout.LayoutModifier
|
||||
|
||||
import Data.Maybe (fromMaybe)
|
||||
import System.Posix.Env (getEnv)
|
||||
|
||||
-- $usage
|
||||
-- You can use this module by importing it into your ~\/.xmonad\/xmonad.hs file:
|
||||
--
|
||||
-- > import XMonad.Layout.OnHost
|
||||
--
|
||||
-- and modifying your 'layoutHook' as follows (for example):
|
||||
--
|
||||
-- > layoutHook = modHost "baz" m1 $ -- apply layout modifier m1 to all layouts on host "baz"
|
||||
-- > onHost "foo" l1 $ -- layout l1 will be used on host "foo".
|
||||
-- > onHosts ["bar","quux"] l2 $ -- layout l2 will be used on hosts "bar" and "quux".
|
||||
-- > l3 -- layout l3 will be used on all other hosts.
|
||||
--
|
||||
-- Note that @l1@, @l2@, and @l3@ can be arbitrarily complicated
|
||||
-- layouts, e.g. @(Full ||| smartBorders $ tabbed shrinkText
|
||||
-- defaultTConf ||| ...)@, and @m1@ can be any layout modifier, i.e. a
|
||||
-- function of type @(l a -> ModifiedLayout lm l a)@.
|
||||
--
|
||||
-- In another scenario, suppose you wanted to have layouts A, B, and C
|
||||
-- available on all hosts, except that on host foo you want
|
||||
-- layout D instead of C. You could do that as follows:
|
||||
--
|
||||
-- > layoutHook = A ||| B ||| onHost "foo" D C
|
||||
--
|
||||
-- Note that we rely on '$HOST' being set in the environment, as is true on most
|
||||
-- modern systems; if it's not, you may want to use a wrapper around xmonad or
|
||||
-- perhaps use 'System.Posix.Env.setEnv' (or 'putEnv') to set it in 'main'.
|
||||
-- This is to avoid dragging in the network package as an xmonad dependency.
|
||||
-- If '$HOST' is not defined, it will behave as if the host name never matches.
|
||||
--
|
||||
-- Also note that '$HOST' is usually a fully qualified domain name, not a short name.
|
||||
-- If you use a short name, this code will try to truncate $HOST to match; this may
|
||||
-- prove too magical, though, and may change in the future.
|
||||
|
||||
-- | Specify one layout to use on a particular host, and another
|
||||
-- to use on all others. The second layout can be another call to
|
||||
-- 'onHost', and so on.
|
||||
onHost :: (LayoutClass l1 a, LayoutClass l2 a)
|
||||
=> String -- ^ the name of the host to match
|
||||
-> (l1 a) -- ^ layout to use on the matched host
|
||||
-> (l2 a) -- ^ layout to use everywhere else
|
||||
-> OnHost l1 l2 a
|
||||
onHost host = onHosts [host]
|
||||
|
||||
-- | Specify one layout to use on a particular set of hosts, and
|
||||
-- another to use on all other hosts.
|
||||
onHosts :: (LayoutClass l1 a, LayoutClass l2 a)
|
||||
=> [String] -- ^ names of hosts to match
|
||||
-> (l1 a) -- ^ layout to use on matched hosts
|
||||
-> (l2 a) -- ^ layout to use everywhere else
|
||||
-> OnHost l1 l2 a
|
||||
onHosts hosts l1 l2 = OnHost hosts False l1 l2
|
||||
|
||||
-- | Specify a layout modifier to apply on a particular host; layouts
|
||||
-- on all other hosts will remain unmodified.
|
||||
modHost :: (LayoutClass l a)
|
||||
=> String -- ^ name of the host to match
|
||||
-> (l a -> ModifiedLayout lm l a) -- ^ the modifier to apply on the matching host
|
||||
-> l a -- ^ the base layout
|
||||
-> OnHost (ModifiedLayout lm l) l a
|
||||
modHost host = modHosts [host]
|
||||
|
||||
-- | Specify a layout modifier to apply on a particular set of
|
||||
-- hosts; layouts on all other hosts will remain
|
||||
-- unmodified.
|
||||
modHosts :: (LayoutClass l a)
|
||||
=> [String] -- ^ names of the hosts to match
|
||||
-> (l a -> ModifiedLayout lm l a) -- ^ the modifier to apply on the matching hosts
|
||||
-> l a -- ^ the base layout
|
||||
-> OnHost (ModifiedLayout lm l) l a
|
||||
modHosts hosts f l = OnHost hosts False (f l) l
|
||||
|
||||
-- | Structure for representing a host-specific layout along with
|
||||
-- a layout for all other hosts. We store the names of hosts
|
||||
-- to be matched, and the two layouts. We save the layout choice in
|
||||
-- the Bool, to be used to implement description.
|
||||
data OnHost l1 l2 a = OnHost [String]
|
||||
Bool
|
||||
(l1 a)
|
||||
(l2 a)
|
||||
deriving (Read, Show)
|
||||
|
||||
instance (LayoutClass l1 a, LayoutClass l2 a, Show a) => LayoutClass (OnHost l1 l2) a where
|
||||
runLayout (W.Workspace i p@(OnHost hosts _ lt lf) ms) r = do
|
||||
h <- io $ getEnv "HOST"
|
||||
if maybe False (`elemFQDN` hosts) h
|
||||
then do (wrs, mlt') <- runLayout (W.Workspace i lt ms) r
|
||||
return (wrs, Just $ mkNewOnHostT p mlt')
|
||||
else do (wrs, mlt') <- runLayout (W.Workspace i lf ms) r
|
||||
return (wrs, Just $ mkNewOnHostF p mlt')
|
||||
|
||||
handleMessage (OnHost hosts bool lt lf) m
|
||||
| bool = handleMessage lt m >>= maybe (return Nothing) (\nt -> return . Just $ OnHost hosts bool nt lf)
|
||||
| otherwise = handleMessage lf m >>= maybe (return Nothing) (\nf -> return . Just $ OnHost hosts bool lt nf)
|
||||
|
||||
description (OnHost _ True l1 _) = description l1
|
||||
description (OnHost _ _ _ l2) = description l2
|
||||
|
||||
-- | Construct new OnHost values with possibly modified layouts.
|
||||
mkNewOnHostT :: OnHost l1 l2 a -> Maybe (l1 a) -> OnHost l1 l2 a
|
||||
mkNewOnHostT (OnHost hosts _ lt lf) mlt' =
|
||||
(\lt' -> OnHost hosts True lt' lf) $ fromMaybe lt mlt'
|
||||
|
||||
mkNewOnHostF :: OnHost l1 l2 a -> Maybe (l2 a) -> OnHost l1 l2 a
|
||||
mkNewOnHostF (OnHost hosts _ lt lf) mlf' =
|
||||
(\lf' -> OnHost hosts False lt lf') $ fromMaybe lf mlf'
|
||||
|
||||
-- | 'Data.List.elem' except that if one side has a dot and the other doesn't, we truncate
|
||||
-- the one that does at the dot.
|
||||
elemFQDN :: String -> [String] -> Bool
|
||||
elemFQDN _ [] = False
|
||||
elemFQDN h0 (h:hs)
|
||||
| h0 `eqFQDN` h = True
|
||||
| otherwise = elemFQDN h0 hs
|
||||
|
||||
-- | String equality, possibly truncating one side at a dot.
|
||||
eqFQDN :: String -> String -> Bool
|
||||
eqFQDN a b
|
||||
| '.' `elem` a && '.' `elem` b = a == b
|
||||
| '.' `elem` a = takeWhile (/= '.') a == b
|
||||
| '.' `elem` b = a == takeWhile (/= '.') b
|
||||
| otherwise = a == b
|
@@ -24,7 +24,7 @@ import XMonad
|
||||
import XMonad.Layout.LayoutModifier
|
||||
|
||||
-- $usage
|
||||
-- You can use this module by adding
|
||||
-- You can use this module by adding
|
||||
--
|
||||
-- > import XMonad.Layout.Renamed
|
||||
--
|
||||
|
@@ -86,18 +86,18 @@ doShow (SWN True c Nothing ) r wrs = flashName c r wrs
|
||||
doShow (SWN False _ _ ) _ wrs = return (wrs, Nothing)
|
||||
|
||||
flashName :: SWNConfig -> Rectangle -> [(a, Rectangle)] -> X ([(a, Rectangle)], Maybe (ShowWName a))
|
||||
flashName c (Rectangle _ _ wh ht) wrs = do
|
||||
flashName c (Rectangle sx sy wh ht) wrs = do
|
||||
d <- asks display
|
||||
n <- withWindowSet (return . S.currentTag)
|
||||
f <- initXMF (swn_font c)
|
||||
width <- textWidthXMF d f n
|
||||
width <- fmap (\w -> w + w `div` length n) $ textWidthXMF d f n
|
||||
(as,ds) <- textExtentsXMF f n
|
||||
let hight = as + ds
|
||||
y = (fi ht - hight + 2) `div` 2
|
||||
x = (fi wh - width + 2) `div` 2
|
||||
y = fi sy + (fi ht - hight + 2) `div` 2
|
||||
x = fi sx + (fi wh - width + 2) `div` 2
|
||||
w <- createNewWindow (Rectangle (fi x) (fi y) (fi width) (fi hight)) Nothing "" True
|
||||
showWindow w
|
||||
paintAndWrite w f (fi width) (fi hight) 0 "" "" (swn_color c) (swn_bgcolor c) [AlignCenter] [n]
|
||||
paintAndWrite w f (fi width) (fi hight) 0 (swn_bgcolor c) "" (swn_color c) (swn_bgcolor c) [AlignCenter] [n]
|
||||
releaseXMF f
|
||||
io $ sync d False
|
||||
i <- startTimer (swn_fade c)
|
||||
|
@@ -18,6 +18,7 @@ module XMonad.Layout.Spacing (
|
||||
-- $usage
|
||||
|
||||
spacing, Spacing,
|
||||
smartSpacing, SmartSpacing,
|
||||
|
||||
) where
|
||||
|
||||
@@ -52,3 +53,17 @@ instance LayoutModifier Spacing a where
|
||||
|
||||
shrinkRect :: Int -> Rectangle -> Rectangle
|
||||
shrinkRect p (Rectangle x y w h) = Rectangle (x+fi p) (y+fi p) (w-2*fi p) (h-2*fi p)
|
||||
|
||||
-- | Surrounds all windows with blank space, except when the window is the only
|
||||
-- visible window on the current workspace.
|
||||
smartSpacing :: Int -> l a -> ModifiedLayout SmartSpacing l a
|
||||
smartSpacing p = ModifiedLayout (SmartSpacing p)
|
||||
|
||||
data SmartSpacing a = SmartSpacing Int deriving (Show, Read)
|
||||
|
||||
instance LayoutModifier SmartSpacing a where
|
||||
|
||||
pureModifier _ _ _ [x] = ([x], Nothing)
|
||||
pureModifier (SmartSpacing p) _ _ wrs = (map (second $ shrinkRect p) wrs, Nothing)
|
||||
|
||||
modifierDescription (SmartSpacing p) = "SmartSpacing " ++ show p
|
||||
|
@@ -26,7 +26,7 @@ module XMonad.Layout.Spiral (
|
||||
) where
|
||||
|
||||
import Data.Ratio
|
||||
import XMonad
|
||||
import XMonad hiding ( Rotation )
|
||||
import XMonad.StackSet ( integrate )
|
||||
|
||||
-- $usage
|
||||
|
@@ -45,7 +45,7 @@ data TrackFloating a = TrackFloating
|
||||
|
||||
|
||||
instance LayoutModifier TrackFloating Window where
|
||||
modifyLayoutWithUpdate os@(TrackFloating wasF mw) ws@(W.Workspace{ W.stack = ms }) r
|
||||
modifyLayoutWithUpdate os@(TrackFloating _wasF mw) ws@(W.Workspace{ W.stack = ms }) r
|
||||
= do
|
||||
winset <- gets windowset
|
||||
let xCur = fmap W.focus xStack
|
||||
@@ -57,7 +57,7 @@ instance LayoutModifier TrackFloating Window where
|
||||
newStack
|
||||
-- focus is floating, so use the remembered focus point
|
||||
| Just isF' <- isF,
|
||||
isF' || wasF,
|
||||
isF',
|
||||
Just w <- mw,
|
||||
Just s <- ms,
|
||||
Just ns <- find ((==) w . W.focus)
|
||||
|
@@ -105,7 +105,7 @@ configurableNavigation :: LayoutClass l a => WNConfig -> l a -> ModifiedLayout W
|
||||
configurableNavigation conf = ModifiedLayout (WindowNavigation conf (I Nothing))
|
||||
|
||||
instance LayoutModifier WindowNavigation Window where
|
||||
redoLayout (WindowNavigation conf (I state)) rscr (Just s) origwrs =
|
||||
redoLayout (WindowNavigation conf (I st)) rscr (Just s) origwrs =
|
||||
do XConf { normalBorder = nbc, focusedBorder = fbc, display = dpy } <- ask
|
||||
[uc,dc,lc,rc] <-
|
||||
case brightness conf of
|
||||
@@ -120,8 +120,8 @@ instance LayoutModifier WindowNavigation Window where
|
||||
let w = W.focus s
|
||||
r = case filter ((==w).fst) origwrs of ((_,x):_) -> x
|
||||
[] -> rscr
|
||||
pt = case state of Just (NS ptold _) | ptold `inrect` r -> ptold
|
||||
_ -> center r
|
||||
pt = case st of Just (NS ptold _) | ptold `inrect` r -> ptold
|
||||
_ -> center r
|
||||
existing_wins = W.integrate s
|
||||
wrs = filter ((`elem` existing_wins) . fst) $ filter ((/=r) . snd) $
|
||||
filter ((/=w) . fst) origwrs
|
||||
@@ -130,8 +130,8 @@ instance LayoutModifier WindowNavigation Window where
|
||||
wnavigablec = nub $ concatMap
|
||||
(\d -> map (\(win,_) -> (win,dirc d)) $
|
||||
take 1 $ navigable d pt wrs) [U,D,R,L]
|
||||
wothers = case state of Just (NS _ wo) -> map fst wo
|
||||
_ -> []
|
||||
wothers = case st of Just (NS _ wo) -> map fst wo
|
||||
_ -> []
|
||||
mapM_ (sc nbc) (wothers \\ map fst wnavigable)
|
||||
mapM_ (\(win,c) -> sc c win) wnavigablec
|
||||
return (origwrs, Just $ WindowNavigation conf $ I $ Just $ NS pt wnavigable)
|
||||
|
@@ -29,21 +29,15 @@ module XMonad.Layout.WorkspaceDir (
|
||||
WorkspaceDir,
|
||||
) where
|
||||
|
||||
import Prelude hiding (catch)
|
||||
import Control.Exception
|
||||
import System.Directory ( setCurrentDirectory, getCurrentDirectory )
|
||||
import Control.Monad ( when )
|
||||
|
||||
import XMonad hiding ( focus )
|
||||
import XMonad.Util.Run ( runProcessWithInput )
|
||||
import XMonad.Prompt ( XPConfig )
|
||||
import XMonad.Prompt.Directory ( directoryPrompt )
|
||||
import XMonad.Layout.LayoutModifier
|
||||
import XMonad.StackSet ( tag, currentTag )
|
||||
|
||||
econst :: Monad m => a -> IOException -> m a
|
||||
econst = const . return
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
@@ -90,8 +84,7 @@ cleanDir :: String -> X String
|
||||
cleanDir x = scd x >> io getCurrentDirectory
|
||||
|
||||
scd :: String -> X ()
|
||||
scd x = do x' <- io (runProcessWithInput "bash" [] ("echo -n " ++ x) `catch` econst x)
|
||||
catchIO $ setCurrentDirectory x'
|
||||
scd x = catchIO $ setCurrentDirectory x
|
||||
|
||||
changeDir :: XPConfig -> X ()
|
||||
changeDir c = directoryPrompt c "Set working directory: " (sendMessage . Chdir)
|
||||
|
@@ -42,7 +42,7 @@ import XMonad.Layout.Decoration (fi)
|
||||
|
||||
import Data.Maybe (fromMaybe)
|
||||
import Control.Arrow (second)
|
||||
|
||||
|
||||
-- $usage
|
||||
-- This module provides a layout which places all windows in a single
|
||||
-- row; the size occupied by each individual window can be increased
|
||||
@@ -80,9 +80,9 @@ zoomRow :: (Eq a, Show a, Read a) => ZoomRow ClassEQ a
|
||||
zoomRow = ZC ClassEQ emptyZ
|
||||
|
||||
-- $noneq
|
||||
-- Haskell's 'Eq' class is usually concerned with structural equality, whereas
|
||||
-- Haskell's 'Eq' class is usually concerned with structural equality, whereas
|
||||
-- what this layout really wants is for its elements to have a unique identity,
|
||||
-- even across changes. There are cases (such as, importantly, 'Window's) where
|
||||
-- even across changes. There are cases (such as, importantly, 'Window's) where
|
||||
-- the 'Eq' instance for a type actually does that, but if you want to lay
|
||||
-- out something more exotic than windows and your 'Eq' means something else,
|
||||
-- you can use the following.
|
||||
@@ -92,7 +92,7 @@ zoomRow = ZC ClassEQ emptyZ
|
||||
-- sure that the layout never has to handle two \"equal\" elements
|
||||
-- at the same time (it won't do any huge damage, but might behave
|
||||
-- a bit strangely).
|
||||
zoomRowWith :: (EQF f a, Show (f a), Read (f a), Show a, Read a)
|
||||
zoomRowWith :: (EQF f a, Show (f a), Read (f a), Show a, Read a)
|
||||
=> f a -> ZoomRow f a
|
||||
zoomRowWith f = ZC f emptyZ
|
||||
|
||||
@@ -185,7 +185,7 @@ zoomReset = ZoomTo 1
|
||||
|
||||
-- * LayoutClass instance
|
||||
|
||||
instance (EQF f a, Show a, Read a, Show (f a), Read (f a))
|
||||
instance (EQF f a, Show a, Read a, Show (f a), Read (f a))
|
||||
=> LayoutClass (ZoomRow f) a where
|
||||
description (ZC _ Nothing) = "ZoomRow"
|
||||
description (ZC _ (Just s)) = "ZoomRow" ++ if full $ W.focus s
|
||||
@@ -197,7 +197,7 @@ instance (EQF f a, Show a, Read a, Show (f a), Read (f a))
|
||||
|
||||
doLayout (ZC f zelts) r@(Rectangle _ _ w _) s
|
||||
= let elts = W.integrate' zelts
|
||||
zelts' = mapZ_ (\a -> fromMaybe (E a 1 False)
|
||||
zelts' = mapZ_ (\a -> fromMaybe (E a 1 False)
|
||||
$ lookupBy (eq f) a elts) $ Just s
|
||||
elts' = W.integrate' zelts'
|
||||
|
||||
@@ -251,7 +251,7 @@ instance (EQF f a, Show a, Read a, Show (f a), Read (f a))
|
||||
= case fromMessage sm of
|
||||
Just (Zoom r') -> Just $ ZC f $ setFocus zelts $ E a (r*r') b
|
||||
Just (ZoomTo r') -> Just $ ZC f $ setFocus zelts $ E a r' b
|
||||
Just ZoomFullToggle -> pureMessage (ZC f zelts)
|
||||
Just ZoomFullToggle -> pureMessage (ZC f zelts)
|
||||
$ SomeMessage $ ZoomFull $ not b
|
||||
_ -> Nothing
|
||||
|
||||
|
442
XMonad/Prompt.hs
442
XMonad/Prompt.hs
@@ -18,20 +18,23 @@ module XMonad.Prompt
|
||||
-- $usage
|
||||
mkXPrompt
|
||||
, mkXPromptWithReturn
|
||||
, mkXPromptWithModes
|
||||
, amberXPConfig
|
||||
, defaultXPConfig
|
||||
, greenXPConfig
|
||||
, XPMode
|
||||
, XPType (..)
|
||||
, XPPosition (..)
|
||||
, XPConfig (..)
|
||||
, XPrompt (..)
|
||||
, XP
|
||||
, defaultXPKeymap
|
||||
, defaultXPKeymap, defaultXPKeymap'
|
||||
, emacsLikeXPKeymap, emacsLikeXPKeymap'
|
||||
, quit
|
||||
, killBefore, killAfter, startOfLine, endOfLine
|
||||
, pasteString, moveCursor
|
||||
, setInput, getInput
|
||||
, moveWord, killWord, deleteString
|
||||
, moveWord, moveWord', killWord, killWord', deleteString
|
||||
, moveHistory, setSuccess, setDone
|
||||
, Direction1D(..)
|
||||
, ComplFunction
|
||||
@@ -65,31 +68,29 @@ module XMonad.Prompt
|
||||
, XPState
|
||||
) where
|
||||
|
||||
import Prelude hiding (catch)
|
||||
import XMonad hiding (cleanMask, config)
|
||||
import qualified XMonad as X (numberlockMask)
|
||||
import qualified XMonad.StackSet as W
|
||||
import XMonad.Util.Font
|
||||
import XMonad.Util.Types
|
||||
import XMonad.Util.XSelection (getSelection)
|
||||
|
||||
import XMonad hiding (config, cleanMask)
|
||||
import qualified XMonad as X (numberlockMask)
|
||||
import qualified XMonad.StackSet as W
|
||||
import XMonad.Util.Font
|
||||
import XMonad.Util.Types
|
||||
import XMonad.Util.XSelection (getSelection)
|
||||
|
||||
import Codec.Binary.UTF8.String (decodeString)
|
||||
import Control.Applicative ((<$>))
|
||||
import Control.Arrow ((&&&),first)
|
||||
import Control.Concurrent (threadDelay)
|
||||
import Control.Exception.Extensible hiding (handle)
|
||||
import Control.Monad.State
|
||||
import Data.Bits
|
||||
import Data.Char (isSpace)
|
||||
import Data.IORef
|
||||
import Data.List
|
||||
import Data.Maybe (fromMaybe)
|
||||
import Data.Set (fromList, toList)
|
||||
import System.Directory (getAppUserDataDirectory)
|
||||
import System.IO
|
||||
import System.Posix.Files
|
||||
import qualified Data.Map as M
|
||||
import Codec.Binary.UTF8.String (decodeString,isUTF8Encoded)
|
||||
import Control.Applicative ((<$>))
|
||||
import Control.Arrow (first, (&&&), (***))
|
||||
import Control.Concurrent (threadDelay)
|
||||
import Control.Exception.Extensible as E hiding (handle)
|
||||
import Control.Monad.State
|
||||
import Data.Bits
|
||||
import Data.Char (isSpace)
|
||||
import Data.IORef
|
||||
import Data.List
|
||||
import qualified Data.Map as M
|
||||
import Data.Maybe (fromMaybe)
|
||||
import Data.Set (fromList, toList)
|
||||
import System.Directory (getAppUserDataDirectory)
|
||||
import System.IO
|
||||
import System.Posix.Files
|
||||
|
||||
-- $usage
|
||||
-- For usage examples see "XMonad.Prompt.Shell",
|
||||
@@ -108,11 +109,12 @@ data XPState =
|
||||
, screen :: !Rectangle
|
||||
, complWin :: Maybe Window
|
||||
, complWinDim :: Maybe ComplWindowDim
|
||||
, completionFunction :: String -> IO [String]
|
||||
, complIndex :: !(Int,Int)
|
||||
, showComplWin :: Bool
|
||||
, operationMode :: XPOperationMode
|
||||
, highlightedCompl :: Maybe String
|
||||
, gcon :: !GC
|
||||
, fontS :: !XMonadFont
|
||||
, xptype :: !XPType
|
||||
, commandHistory :: W.Stack String
|
||||
, offset :: !Int
|
||||
, config :: XPConfig
|
||||
@@ -130,6 +132,7 @@ data XPConfig =
|
||||
, borderColor :: String -- ^ Border color
|
||||
, promptBorderWidth :: !Dimension -- ^ Border width
|
||||
, position :: XPPosition -- ^ Position: 'Top' or 'Bottom'
|
||||
, alwaysHighlight :: !Bool -- ^ Always highlight an item, overriden to True with multiple modes. This implies having *one* column of autocompletions only.
|
||||
, height :: !Dimension -- ^ Window height
|
||||
, historySize :: !Int -- ^ The number of history entries to be saved
|
||||
, historyFilter :: [String] -> [String]
|
||||
@@ -138,6 +141,7 @@ data XPConfig =
|
||||
, promptKeymap :: M.Map (KeyMask,KeySym) (XP ())
|
||||
-- ^ Mapping from key combinations to actions
|
||||
, completionKey :: KeySym -- ^ Key that should trigger completion
|
||||
, changeModeKey :: KeySym -- ^ Key to change mode (when the prompt has multiple modes)
|
||||
, defaultText :: String -- ^ The text by default in the prompt line
|
||||
, autoComplete :: Maybe Int -- ^ Just x: if only one completion remains, auto-select it,
|
||||
, showCompletionOnTab :: Bool -- ^ Only show list of completions when Tab was pressed
|
||||
@@ -148,6 +152,9 @@ data XPConfig =
|
||||
}
|
||||
|
||||
data XPType = forall p . XPrompt p => XPT p
|
||||
type ComplFunction = String -> IO [String]
|
||||
type XPMode = XPType
|
||||
data XPOperationMode = XPSingleMode ComplFunction XPType | XPMultipleModes (W.Stack XPType)
|
||||
|
||||
instance Show XPType where
|
||||
show (XPT p) = showXPrompt p
|
||||
@@ -157,6 +164,8 @@ instance XPrompt XPType where
|
||||
nextCompletion (XPT t) = nextCompletion t
|
||||
commandToComplete (XPT t) = commandToComplete t
|
||||
completionToCommand (XPT t) = completionToCommand t
|
||||
completionFunction (XPT t) = completionFunction t
|
||||
modeAction (XPT t) = modeAction t
|
||||
|
||||
-- | The class prompt types must be an instance of. In order to
|
||||
-- create a prompt you need to create a data type, without parameters,
|
||||
@@ -178,11 +187,13 @@ class XPrompt t where
|
||||
-- printed in the command line when tab is pressed, given the
|
||||
-- string presently in the command line and the list of
|
||||
-- completion.
|
||||
-- This function is not used when in multiple modes (because alwaysHighlight in XPConfig is True)
|
||||
nextCompletion :: t -> String -> [String] -> String
|
||||
nextCompletion = getNextOfLastWord
|
||||
|
||||
-- | This method is used to generate the string to be passed to
|
||||
-- the completion function.
|
||||
-- This function is not used when in multiple modes (because alwaysHighlight in XPConfig is True)
|
||||
commandToComplete :: t -> String -> String
|
||||
commandToComplete _ = getLastWord
|
||||
|
||||
@@ -196,6 +207,22 @@ class XPrompt t where
|
||||
completionToCommand :: t -> String -> String
|
||||
completionToCommand _ c = c
|
||||
|
||||
-- | When the prompt has multiple modes, this is the function
|
||||
-- used to generate the autocompletion list.
|
||||
-- The argument passed to this function is given by `commandToComplete`
|
||||
-- The default implementation shows an error message.
|
||||
completionFunction :: t -> ComplFunction
|
||||
completionFunction t = \_ -> return ["Completions for " ++ (showXPrompt t) ++ " could not be loaded"]
|
||||
|
||||
-- | When the prompt has multiple modes (created with mkXPromptWithModes), this function is called
|
||||
-- when the user picks an item from the autocompletion list.
|
||||
-- The first argument is the prompt (or mode) on which the item was picked
|
||||
-- The first string argument is the autocompleted item's text.
|
||||
-- The second string argument is the query made by the user (written in the prompt's buffer).
|
||||
-- See XMonad/Actions/Launcher.hs for a usage example.
|
||||
modeAction :: t -> String -> String -> X ()
|
||||
modeAction _ _ _ = return ()
|
||||
|
||||
data XPPosition = Top
|
||||
| Bottom
|
||||
deriving (Show,Read)
|
||||
@@ -212,6 +239,7 @@ defaultXPConfig =
|
||||
, promptBorderWidth = 1
|
||||
, promptKeymap = defaultXPKeymap
|
||||
, completionKey = xK_Tab
|
||||
, changeModeKey = xK_grave
|
||||
, position = Bottom
|
||||
, height = 18
|
||||
, historySize = 256
|
||||
@@ -220,29 +248,29 @@ defaultXPConfig =
|
||||
, autoComplete = Nothing
|
||||
, showCompletionOnTab = False
|
||||
, searchPredicate = isPrefixOf
|
||||
, alwaysHighlight = False
|
||||
}
|
||||
greenXPConfig = defaultXPConfig { fgColor = "green", bgColor = "black", promptBorderWidth = 0 }
|
||||
amberXPConfig = defaultXPConfig { fgColor = "#ca8f2d", bgColor = "black", fgHLight = "#eaaf4c" }
|
||||
|
||||
type ComplFunction = String -> IO [String]
|
||||
|
||||
initState :: XPrompt p => Display -> Window -> Window -> Rectangle -> ComplFunction
|
||||
-> GC -> XMonadFont -> p -> [String] -> XPConfig -> KeyMask -> XPState
|
||||
initState d rw w s compl gc fonts pt h c nm =
|
||||
initState :: Display -> Window -> Window -> Rectangle -> XPOperationMode
|
||||
-> GC -> XMonadFont -> [String] -> XPConfig -> KeyMask -> XPState
|
||||
initState d rw w s opMode gc fonts h c nm =
|
||||
XPS { dpy = d
|
||||
, rootw = rw
|
||||
, win = w
|
||||
, screen = s
|
||||
, complWin = Nothing
|
||||
, complWinDim = Nothing
|
||||
, completionFunction = compl
|
||||
, showComplWin = not (showCompletionOnTab c)
|
||||
, operationMode = opMode
|
||||
, highlightedCompl = Nothing
|
||||
, gcon = gc
|
||||
, fontS = fonts
|
||||
, xptype = XPT pt
|
||||
, commandHistory = W.Stack { W.focus = defaultText c
|
||||
, W.up = []
|
||||
, W.down = h }
|
||||
, complIndex = (0,0) --(column index, row index), used when `alwaysHighlight` in XPConfig is True
|
||||
, offset = length (defaultText c)
|
||||
, config = c
|
||||
, successful = False
|
||||
@@ -250,6 +278,36 @@ initState d rw w s compl gc fonts pt h c nm =
|
||||
, numlockMask = nm
|
||||
}
|
||||
|
||||
-- Returns the current XPType
|
||||
currentXPMode :: XPState -> XPType
|
||||
currentXPMode st = case operationMode st of
|
||||
XPMultipleModes modes -> W.focus modes
|
||||
XPSingleMode _ xptype -> xptype
|
||||
|
||||
-- When in multiple modes, this function sets the next mode
|
||||
-- in the list of modes as active
|
||||
setNextMode :: XPState -> XPState
|
||||
setNextMode st = case operationMode st of
|
||||
XPMultipleModes modes -> case W.down modes of
|
||||
[] -> st -- there is no next mode, return same state
|
||||
(m:ms) -> let
|
||||
currentMode = W.focus modes
|
||||
in st { operationMode = XPMultipleModes W.Stack { W.up = [], W.focus = m, W.down = ms ++ [currentMode]}} --set next and move previous current mode to the of the stack
|
||||
_ -> st --nothing to do, the prompt's operation has only one mode
|
||||
|
||||
-- Returns the highlighted item
|
||||
highlightedItem :: XPState -> [String] -> Maybe String
|
||||
highlightedItem st' completions = case complWinDim st' of
|
||||
Nothing -> Nothing -- when there isn't any compl win, we can't say how many cols,rows there are
|
||||
Just winDim ->
|
||||
let
|
||||
(_,_,_,_,xx,yy) = winDim
|
||||
complMatrix = splitInSubListsAt (length yy) (take (length xx * length yy) completions)
|
||||
(col_index,row_index) = (complIndex st')
|
||||
in case completions of
|
||||
[] -> Nothing
|
||||
_ -> Just $ complMatrix !! col_index !! row_index
|
||||
|
||||
-- this would be much easier with functional references
|
||||
command :: XPState -> String
|
||||
command = W.focus . commandHistory
|
||||
@@ -257,6 +315,9 @@ command = W.focus . commandHistory
|
||||
setCommand :: String -> XPState -> XPState
|
||||
setCommand xs s = s { commandHistory = (commandHistory s) { W.focus = xs }}
|
||||
|
||||
setHighlightedCompl :: Maybe String -> XPState -> XPState
|
||||
setHighlightedCompl hc st = st { highlightedCompl = hc}
|
||||
|
||||
-- | Sets the input string to the given value.
|
||||
setInput :: String -> XP ()
|
||||
setInput = modify . setCommand
|
||||
@@ -284,23 +345,30 @@ mkXPromptWithReturn t conf compl action = do
|
||||
fs <- initXMF (font conf)
|
||||
numlock <- gets $ X.numberlockMask
|
||||
let hs = fromMaybe [] $ M.lookup (showXPrompt t) hist
|
||||
st = initState d rw w s compl gc fs (XPT t) hs conf numlock
|
||||
om = (XPSingleMode compl (XPT t)) --operation mode
|
||||
st = initState d rw w s om gc fs hs conf numlock
|
||||
st' <- io $ execStateT runXP st
|
||||
|
||||
releaseXMF fs
|
||||
io $ freeGC d gc
|
||||
if successful st'
|
||||
then do
|
||||
let prune = take (historySize conf)
|
||||
io $ writeHistory $ M.insertWith
|
||||
(\xs ys -> prune . historyFilter conf $ xs ++ ys)
|
||||
(showXPrompt t)
|
||||
(prune $ historyFilter conf [command st'])
|
||||
hist
|
||||
if successful st' then do
|
||||
let
|
||||
prune = take (historySize conf)
|
||||
|
||||
io $ writeHistory $ M.insertWith
|
||||
(\xs ys -> prune . historyFilter conf $ xs ++ ys)
|
||||
(showXPrompt t)
|
||||
(prune $ historyFilter conf [command st'])
|
||||
hist
|
||||
-- we need to apply historyFilter before as well, since
|
||||
-- otherwise the filter would not be applied if
|
||||
-- there is no history
|
||||
Just <$> action (command st')
|
||||
--When alwaysHighlight is True, autocompletion is handled with indexes.
|
||||
--When it is false, it is handled depending on the prompt buffer's value
|
||||
let selectedCompletion = case alwaysHighlight (config st') of
|
||||
False -> command st'
|
||||
True -> fromMaybe "" $ highlightedCompl st'
|
||||
Just <$> action selectedCompletion
|
||||
else return Nothing
|
||||
|
||||
-- | Creates a prompt given:
|
||||
@@ -317,6 +385,60 @@ mkXPromptWithReturn t conf compl action = do
|
||||
mkXPrompt :: XPrompt p => p -> XPConfig -> ComplFunction -> (String -> X ()) -> X ()
|
||||
mkXPrompt t conf compl action = mkXPromptWithReturn t conf compl action >> return ()
|
||||
|
||||
-- | Creates a prompt with multiple modes given:
|
||||
--
|
||||
-- * A non-empty list of modes
|
||||
-- * A prompt configuration
|
||||
--
|
||||
-- The created prompt allows to switch between modes with `changeModeKey` in `conf`. The modes are
|
||||
-- instances of XPrompt. See XMonad.Actions.Launcher for more details
|
||||
--
|
||||
-- The argument supplied to the action to execute is always the current highlighted item,
|
||||
-- that means that this prompt overrides the value `alwaysHighlight` for its configuration to True.
|
||||
mkXPromptWithModes :: [XPType] -> XPConfig -> X ()
|
||||
mkXPromptWithModes modes conf = do
|
||||
XConf { display = d, theRoot = rw } <- ask
|
||||
s <- gets $ screenRect . W.screenDetail . W.current . windowset
|
||||
hist <- io readHistory
|
||||
w <- io $ createWin d rw conf s
|
||||
io $ selectInput d w $ exposureMask .|. keyPressMask
|
||||
gc <- io $ createGC d w
|
||||
io $ setGraphicsExposures d gc False
|
||||
fs <- initXMF (font conf)
|
||||
numlock <- gets $ X.numberlockMask
|
||||
let
|
||||
defaultMode = head modes
|
||||
hs = fromMaybe [] $ M.lookup (showXPrompt defaultMode) hist
|
||||
modeStack = W.Stack{ W.focus = defaultMode --current mode
|
||||
, W.up = []
|
||||
, W.down = tail modes --other modes
|
||||
}
|
||||
st = initState d rw w s (XPMultipleModes modeStack) gc fs hs conf { alwaysHighlight = True} numlock
|
||||
st' <- io $ execStateT runXP st
|
||||
|
||||
releaseXMF fs
|
||||
io $ freeGC d gc
|
||||
|
||||
if successful st' then do
|
||||
let
|
||||
prune = take (historySize conf)
|
||||
|
||||
-- insert into history the buffers value
|
||||
io $ writeHistory $ M.insertWith
|
||||
(\xs ys -> prune . historyFilter conf $ xs ++ ys)
|
||||
(showXPrompt defaultMode)
|
||||
(prune $ historyFilter conf [command st'])
|
||||
hist
|
||||
|
||||
case operationMode st' of
|
||||
XPMultipleModes ms -> let
|
||||
action = modeAction $ W.focus ms
|
||||
in action (command st') $ (fromMaybe "" $ highlightedCompl st')
|
||||
_ -> error "The impossible occurred: This prompt runs with multiple modes but they could not be found." --we are creating a prompt with multiple modes, so its operationMode should have been constructed with XPMultipleMode
|
||||
else
|
||||
return ()
|
||||
|
||||
|
||||
runXP :: XP ()
|
||||
runXP = do
|
||||
(d,w) <- gets (dpy &&& win)
|
||||
@@ -358,11 +480,16 @@ cleanMask msk = do
|
||||
handle :: KeyStroke -> Event -> XP ()
|
||||
handle ks@(sym,_) e@(KeyEvent {ev_event_type = t, ev_state = m}) = do
|
||||
complKey <- gets $ completionKey . config
|
||||
chgModeKey <- gets $ changeModeKey . config
|
||||
c <- getCompletions
|
||||
when (length c > 1) $ modify (\s -> s { showComplWin = True })
|
||||
if complKey == sym
|
||||
then completionHandle c ks e
|
||||
else when (t == keyPress) $ keyPressHandle m ks
|
||||
else if (sym == chgModeKey) then
|
||||
do
|
||||
modify setNextMode
|
||||
updateWindows
|
||||
else when (t == keyPress) $ keyPressHandle m ks
|
||||
handle _ (ExposeEvent {ev_window = w}) = do
|
||||
st <- get
|
||||
when (win st == w) updateWindows
|
||||
@@ -372,15 +499,20 @@ handle _ _ = return ()
|
||||
completionHandle :: [String] -> KeyStroke -> Event -> XP ()
|
||||
completionHandle c ks@(sym,_) (KeyEvent { ev_event_type = t, ev_state = m }) = do
|
||||
complKey <- gets $ completionKey . config
|
||||
alwaysHlight <- gets $ alwaysHighlight . config
|
||||
case () of
|
||||
() | t == keyPress && sym == complKey ->
|
||||
do
|
||||
st <- get
|
||||
let updateState l =
|
||||
let new_command = nextCompletion (xptype st) (command st) l
|
||||
in modify $ \s -> setCommand new_command $ s { offset = length new_command }
|
||||
updateWins l = redrawWindows l >>
|
||||
eventLoop (completionHandle l)
|
||||
let updateState l = case alwaysHlight of
|
||||
-- modify the buffer's value
|
||||
False -> let newCommand = nextCompletion (currentXPMode st) (command st) l
|
||||
in modify $ \s -> setCommand newCommand $ s { offset = length newCommand, highlightedCompl = Just newCommand}
|
||||
--TODO: Scroll or paginate results
|
||||
True -> let complIndex' = nextComplIndex st (length l)
|
||||
highlightedCompl' = highlightedItem st { complIndex = complIndex'} c
|
||||
in modify $ \s -> setHighlightedCompl highlightedCompl' $ s { complIndex = complIndex' }
|
||||
updateWins l = redrawWindows l >> eventLoop (completionHandle l)
|
||||
case c of
|
||||
[] -> updateWindows >> eventLoop handle
|
||||
[x] -> updateState [x] >> getCompletions >>= updateWins
|
||||
@@ -390,6 +522,23 @@ completionHandle c ks@(sym,_) (KeyEvent { ev_event_type = t, ev_state = m }) = d
|
||||
-- some other event: go back to main loop
|
||||
completionHandle _ k e = handle k e
|
||||
|
||||
--Receives an state of the prompt, the size of the autocompletion list and returns the column,row
|
||||
--which should be highlighted next
|
||||
nextComplIndex :: XPState -> Int -> (Int,Int)
|
||||
nextComplIndex st nitems = case complWinDim st of
|
||||
Nothing -> (0,0) --no window dims (just destroyed or not created)
|
||||
Just (_,_,_,_,_,yy) -> let
|
||||
(ncols,nrows) = (nitems `div` length yy + if (nitems `mod` length yy > 0) then 1 else 0, length yy)
|
||||
(currentcol,currentrow) = complIndex st
|
||||
in if (currentcol + 1 >= ncols) then --hlight is in the last column
|
||||
if (currentrow + 1 < nrows ) then --hlight is still not at the last row
|
||||
(currentcol, currentrow + 1)
|
||||
else
|
||||
(0,0)
|
||||
else if(currentrow + 1 < nrows) then --hlight not at the last row
|
||||
(currentcol, currentrow + 1)
|
||||
else
|
||||
(currentcol + 1, 0)
|
||||
|
||||
tryAutoComplete :: XP Bool
|
||||
tryAutoComplete = do
|
||||
@@ -402,7 +551,7 @@ tryAutoComplete = do
|
||||
Nothing -> return False
|
||||
where runCompleted cmd delay = do
|
||||
st <- get
|
||||
let new_command = nextCompletion (xptype st) (command st) [cmd]
|
||||
let new_command = nextCompletion (currentXPMode st) (command st) [cmd]
|
||||
modify $ setCommand "autocompleting..."
|
||||
updateWindows
|
||||
io $ threadDelay delay
|
||||
@@ -411,19 +560,31 @@ tryAutoComplete = do
|
||||
|
||||
-- KeyPresses
|
||||
|
||||
-- | Default key bindings for prompts. Click on the \"Source\" link
|
||||
-- to the right to see the complete list. See also 'defaultXPKeymap''.
|
||||
defaultXPKeymap :: M.Map (KeyMask,KeySym) (XP ())
|
||||
defaultXPKeymap = M.fromList $
|
||||
defaultXPKeymap = defaultXPKeymap' isSpace
|
||||
|
||||
-- | A variant of 'defaultXPKeymap' which lets you specify a custom
|
||||
-- predicate for identifying non-word characters, which affects all
|
||||
-- the word-oriented commands (move\/kill word). The default is
|
||||
-- 'isSpace'. For example, by default a path like @foo\/bar\/baz@
|
||||
-- would be considered as a single word. You could use a predicate
|
||||
-- like @(\\c -> isSpace c || c == \'\/\')@ to move through or
|
||||
-- delete components of the path one at a time.
|
||||
defaultXPKeymap' :: (Char -> Bool) -> M.Map (KeyMask,KeySym) (XP ())
|
||||
defaultXPKeymap' p = M.fromList $
|
||||
map (first $ (,) controlMask) -- control + <key>
|
||||
[ (xK_u, killBefore)
|
||||
, (xK_k, killAfter)
|
||||
, (xK_a, startOfLine)
|
||||
, (xK_e, endOfLine)
|
||||
, (xK_y, pasteString)
|
||||
, (xK_Right, moveWord Next)
|
||||
, (xK_Left, moveWord Prev)
|
||||
, (xK_Delete, killWord Next)
|
||||
, (xK_BackSpace, killWord Prev)
|
||||
, (xK_w, killWord Prev)
|
||||
, (xK_Right, moveWord' p Next)
|
||||
, (xK_Left, moveWord' p Prev)
|
||||
, (xK_Delete, killWord' p Next)
|
||||
, (xK_BackSpace, killWord' p Prev)
|
||||
, (xK_w, killWord' p Prev)
|
||||
, (xK_g, quit)
|
||||
, (xK_bracketleft, quit)
|
||||
] ++
|
||||
@@ -441,6 +602,57 @@ defaultXPKeymap = M.fromList $
|
||||
, (xK_Escape, quit)
|
||||
]
|
||||
|
||||
-- | A keymap with many emacs-like key bindings. Click on the
|
||||
-- \"Source\" link to the right to see the complete list.
|
||||
-- See also 'emacsLikeXPKeymap''.
|
||||
emacsLikeXPKeymap :: M.Map (KeyMask,KeySym) (XP ())
|
||||
emacsLikeXPKeymap = emacsLikeXPKeymap' isSpace
|
||||
|
||||
-- | A variant of 'emacsLikeXPKeymap' which lets you specify a custom
|
||||
-- predicate for identifying non-word characters, which affects all
|
||||
-- the word-oriented commands (move\/kill word). The default is
|
||||
-- 'isSpace'. For example, by default a path like @foo\/bar\/baz@
|
||||
-- would be considered as a single word. You could use a predicate
|
||||
-- like @(\\c -> isSpace c || c == \'\/\')@ to move through or
|
||||
-- delete components of the path one at a time.
|
||||
emacsLikeXPKeymap' :: (Char -> Bool) -> M.Map (KeyMask,KeySym) (XP ())
|
||||
emacsLikeXPKeymap' p = M.fromList $
|
||||
map (first $ (,) controlMask) -- control + <key>
|
||||
[ (xK_z, killBefore) --kill line backwards
|
||||
, (xK_k, killAfter) -- kill line fowards
|
||||
, (xK_a, startOfLine) --move to the beginning of the line
|
||||
, (xK_e, endOfLine) -- move to the end of the line
|
||||
, (xK_d, deleteString Next) -- delete a character foward
|
||||
, (xK_b, moveCursor Prev) -- move cursor forward
|
||||
, (xK_f, moveCursor Next) -- move cursor backward
|
||||
, (xK_BackSpace, killWord' p Prev) -- kill the previous word
|
||||
, (xK_y, pasteString)
|
||||
, (xK_g, quit)
|
||||
, (xK_bracketleft, quit)
|
||||
] ++
|
||||
map (first $ (,) mod1Mask) -- meta key + <key>
|
||||
[ (xK_BackSpace, killWord' p Prev)
|
||||
, (xK_f, moveWord' p Next) -- move a word forward
|
||||
, (xK_b, moveWord' p Prev) -- move a word backward
|
||||
, (xK_d, killWord' p Next) -- kill the next word
|
||||
, (xK_n, moveHistory W.focusUp')
|
||||
, (xK_p, moveHistory W.focusDown')
|
||||
]
|
||||
++
|
||||
map (first $ (,) 0) -- <key>
|
||||
[ (xK_Return, setSuccess True >> setDone True)
|
||||
, (xK_KP_Enter, setSuccess True >> setDone True)
|
||||
, (xK_BackSpace, deleteString Prev)
|
||||
, (xK_Delete, deleteString Next)
|
||||
, (xK_Left, moveCursor Prev)
|
||||
, (xK_Right, moveCursor Next)
|
||||
, (xK_Home, startOfLine)
|
||||
, (xK_End, endOfLine)
|
||||
, (xK_Down, moveHistory W.focusUp')
|
||||
, (xK_Up, moveHistory W.focusDown')
|
||||
, (xK_Escape, quit)
|
||||
]
|
||||
|
||||
keyPressHandle :: KeyMask -> KeyStroke -> XP ()
|
||||
keyPressHandle m (ks,str) = do
|
||||
km <- gets (promptKeymap . config)
|
||||
@@ -450,8 +662,12 @@ keyPressHandle m (ks,str) = do
|
||||
Nothing -> case str of
|
||||
"" -> eventLoop handle
|
||||
_ -> when (kmask .&. controlMask == 0) $ do
|
||||
insertString (decodeString str)
|
||||
let str' = if isUTF8Encoded str
|
||||
then decodeString str
|
||||
else str
|
||||
insertString str'
|
||||
updateWindows
|
||||
updateHighlightedCompl
|
||||
completed <- tryAutoComplete
|
||||
when completed $ setSuccess True >> setDone True
|
||||
|
||||
@@ -477,16 +693,26 @@ killAfter :: XP ()
|
||||
killAfter =
|
||||
modify $ \s -> setCommand (take (offset s) (command s)) s
|
||||
|
||||
-- | Kill the next\/previous word
|
||||
-- | Kill the next\/previous word, using 'isSpace' as the default
|
||||
-- predicate for non-word characters. See 'killWord''.
|
||||
killWord :: Direction1D -> XP ()
|
||||
killWord d = do
|
||||
killWord = killWord' isSpace
|
||||
|
||||
-- | Kill the next\/previous word, given a predicate to identify
|
||||
-- non-word characters. First delete any consecutive non-word
|
||||
-- characters; then delete consecutive word characters, stopping
|
||||
-- just before the next non-word character.
|
||||
--
|
||||
-- For example, by default (using 'killWord') a path like
|
||||
-- @foo\/bar\/baz@ would be deleted in its entirety. Instead you can
|
||||
-- use something like @killWord' (\\c -> isSpace c || c == \'\/\')@ to
|
||||
-- delete the path one component at a time.
|
||||
killWord' :: (Char -> Bool) -> Direction1D -> XP ()
|
||||
killWord' p d = do
|
||||
o <- gets offset
|
||||
c <- gets command
|
||||
let (f,ss) = splitAt o c
|
||||
delNextWord w =
|
||||
case w of
|
||||
' ':x -> x
|
||||
word -> snd . break isSpace $ word
|
||||
delNextWord = snd . break p . dropWhile p
|
||||
delPrevWord = reverse . delNextWord . reverse
|
||||
(ncom,noff) =
|
||||
case d of
|
||||
@@ -508,10 +734,18 @@ startOfLine =
|
||||
flushString :: XP ()
|
||||
flushString = modify $ \s -> setCommand "" $ s { offset = 0}
|
||||
|
||||
--reset index if config has `alwaysHighlight`. The inserted char could imply fewer autocompletions.
|
||||
--If the current index was column 2, row 1 and now there are only 4 autocompletion rows with 1 column, what should we highlight? Set it to the first and start navigation again
|
||||
resetComplIndex :: XPState -> XPState
|
||||
resetComplIndex st = if (alwaysHighlight $ config st) then st { complIndex = (0,0) } else st
|
||||
|
||||
-- | Insert a character at the cursor position
|
||||
insertString :: String -> XP ()
|
||||
insertString str =
|
||||
modify $ \s -> setCommand (c (command s) (offset s)) $ s { offset = o (offset s)}
|
||||
modify $ \s -> let
|
||||
cmd = (c (command s) (offset s))
|
||||
st = resetComplIndex $ s { offset = o (offset s)}
|
||||
in setCommand cmd st
|
||||
where o oo = oo + length str
|
||||
c oc oo | oo >= length oc = oc ++ str
|
||||
| otherwise = f ++ str ++ ss
|
||||
@@ -539,19 +773,25 @@ moveCursor d =
|
||||
modify $ \s -> s { offset = o (offset s) (command s)}
|
||||
where o oo c = if d == Prev then max 0 (oo - 1) else min (length c) (oo + 1)
|
||||
|
||||
-- | move the cursor one word
|
||||
-- | Move the cursor one word, using 'isSpace' as the default
|
||||
-- predicate for non-word characters. See 'moveWord''.
|
||||
moveWord :: Direction1D -> XP ()
|
||||
moveWord d = do
|
||||
moveWord = moveWord' isSpace
|
||||
|
||||
-- | Move the cursor one word, given a predicate to identify non-word
|
||||
-- characters. First move past any consecutive non-word characters;
|
||||
-- then move to just before the next non-word character.
|
||||
moveWord' :: (Char -> Bool) -> Direction1D -> XP ()
|
||||
moveWord' p d = do
|
||||
c <- gets command
|
||||
o <- gets offset
|
||||
let (f,ss) = splitAt o c
|
||||
lenToS = length . fst . break isSpace
|
||||
ln p s = case p s of
|
||||
' ':x -> 1 + lenToS x
|
||||
x -> lenToS x
|
||||
len = uncurry (+)
|
||||
. (length *** (length . fst . break p))
|
||||
. break (not . p)
|
||||
newoff = case d of
|
||||
Prev -> o - ln reverse f
|
||||
Next -> o + ln id ss
|
||||
Prev -> o - len (reverse f)
|
||||
Next -> o + len ss
|
||||
modify $ \s -> s { offset = newoff }
|
||||
|
||||
moveHistory :: (W.Stack String -> W.Stack String) -> XP ()
|
||||
@@ -559,6 +799,13 @@ moveHistory f = modify $ \s -> let ch = f $ commandHistory s
|
||||
in s { commandHistory = ch
|
||||
, offset = length $ W.focus ch }
|
||||
|
||||
updateHighlightedCompl :: XP ()
|
||||
updateHighlightedCompl = do
|
||||
st <- get
|
||||
cs <- getCompletions
|
||||
alwaysHighlight' <- gets $ alwaysHighlight . config
|
||||
when (alwaysHighlight') $ modify $ \s -> s {highlightedCompl = highlightedItem st cs}
|
||||
|
||||
-- X Stuff
|
||||
|
||||
updateWindows :: XP ()
|
||||
@@ -611,7 +858,7 @@ printPrompt :: Drawable -> XP ()
|
||||
printPrompt drw = do
|
||||
st <- get
|
||||
let (gc,(c,(d,fs))) = (gcon &&& config &&& dpy &&& fontS) st
|
||||
(prt,(com,off)) = (show . xptype &&& command &&& offset) st
|
||||
(prt,(com,off)) = (show . currentXPMode &&& command &&& offset) st
|
||||
str = prt ++ com
|
||||
-- break the string in 3 parts: till the cursor, the cursor and the rest
|
||||
(f,p,ss) = if off >= length com
|
||||
@@ -633,13 +880,18 @@ printPrompt drw = do
|
||||
-- reverse the colors and print the rest of the string
|
||||
draw (fgColor c) (bgColor c) (x + fromIntegral (fsl + psl)) y ss
|
||||
|
||||
-- Completions
|
||||
-- get the current completion function depending on the active mode
|
||||
getCompletionFunction :: XPState -> ComplFunction
|
||||
getCompletionFunction st = case operationMode st of
|
||||
XPSingleMode compl _ -> compl
|
||||
XPMultipleModes modes -> completionFunction $ W.focus modes
|
||||
|
||||
-- Completions
|
||||
getCompletions :: XP [String]
|
||||
getCompletions = do
|
||||
s <- get
|
||||
io $ completionFunction s (commandToComplete (xptype s) (command s))
|
||||
`catch` \(SomeException _) -> return []
|
||||
io $ getCompletionFunction s (commandToComplete (currentXPMode s) (command s))
|
||||
`E.catch` \(SomeException _) -> return []
|
||||
|
||||
setComplWin :: Window -> ComplWindowDim -> XP ()
|
||||
setComplWin w wi =
|
||||
@@ -713,7 +965,9 @@ drawComplWin w compl = do
|
||||
(defaultDepthOfScreen scr)
|
||||
io $ fillDrawable d p gc border bgcolor (fi bw) wh ht
|
||||
let ac = splitInSubListsAt (length yy) (take (length xx * length yy) compl)
|
||||
|
||||
printComplList d p gc (fgColor c) (bgColor c) xx yy ac
|
||||
--lift $ spawn $ "xmessage " ++ " ac: " ++ show ac ++ " xx: " ++ show xx ++ " length xx: " ++ show (length xx) ++ " yy: " ++ show (length yy)
|
||||
io $ copyArea d p w gc 0 0 wh ht 0 0
|
||||
io $ freePixmap d p
|
||||
|
||||
@@ -734,16 +988,34 @@ redrawComplWin compl = do
|
||||
Nothing -> recreate
|
||||
else destroyComplWin
|
||||
|
||||
-- Finds the column and row indexes in which a string appears.
|
||||
-- if the string is not in the matrix, the indexes default to (0,0)
|
||||
findComplIndex :: String -> [[String]] -> (Int,Int)
|
||||
findComplIndex x xss = let
|
||||
colIndex = fromMaybe 0 $ findIndex (\cols -> x `elem` cols) xss
|
||||
rowIndex = fromMaybe 0 $ elemIndex x $ (!!) xss colIndex
|
||||
in (colIndex,rowIndex)
|
||||
|
||||
printComplList :: Display -> Drawable -> GC -> String -> String
|
||||
-> [Position] -> [Position] -> [[String]] -> XP ()
|
||||
printComplList d drw gc fc bc xs ys sss =
|
||||
zipWithM_ (\x ss ->
|
||||
zipWithM_ (\y s -> do
|
||||
zipWithM_ (\y item -> do
|
||||
st <- get
|
||||
let (f,b) = if completionToCommand (xptype st) s == commandToComplete (xptype st) (command st)
|
||||
then (fgHLight $ config st,bgHLight $ config st)
|
||||
else (fc,bc)
|
||||
printStringXMF d drw (fontS st) gc f b x y s)
|
||||
alwaysHlight <- gets $ alwaysHighlight . config
|
||||
let (f,b) = case alwaysHlight of
|
||||
True -> -- default to the first item, the one in (0,0)
|
||||
let
|
||||
(colIndex,rowIndex) = findComplIndex item sss
|
||||
in -- assign some colors
|
||||
if ((complIndex st) == (colIndex,rowIndex)) then (fgHLight $ config st,bgHLight $ config st)
|
||||
else (fc,bc)
|
||||
False ->
|
||||
-- compare item with buffer's value
|
||||
if completionToCommand (currentXPMode st) item == commandToComplete (currentXPMode st) (command st)
|
||||
then (fgHLight $ config st,bgHLight $ config st)
|
||||
else (fc,bc)
|
||||
printStringXMF d drw (fontS st) gc f b x y item)
|
||||
ys ss) xs sss
|
||||
|
||||
-- History
|
||||
@@ -757,7 +1029,7 @@ getHistoryFile :: IO FilePath
|
||||
getHistoryFile = fmap (++ "/history") $ getAppUserDataDirectory "xmonad"
|
||||
|
||||
readHistory :: IO History
|
||||
readHistory = readHist `catch` \(SomeException _) -> return emptyHistory
|
||||
readHistory = readHist `E.catch` \(SomeException _) -> return emptyHistory
|
||||
where
|
||||
readHist = do
|
||||
path <- getHistoryFile
|
||||
@@ -768,7 +1040,7 @@ writeHistory :: History -> IO ()
|
||||
writeHistory hist = do
|
||||
path <- getHistoryFile
|
||||
let filtered = M.filter (not . null) hist
|
||||
writeFile path (show filtered) `catch` \(SomeException e) ->
|
||||
writeFile path (show filtered) `E.catch` \(SomeException e) ->
|
||||
hPutStrLn stderr ("error writing history: "++show e)
|
||||
setFileMode path mode
|
||||
where mode = ownerReadMode .|. ownerWriteMode
|
||||
|
@@ -24,8 +24,7 @@ module XMonad.Prompt.DirExec
|
||||
, DirExec
|
||||
) where
|
||||
|
||||
import Prelude hiding (catch)
|
||||
import Control.Exception
|
||||
import Control.Exception as E
|
||||
import System.Directory
|
||||
import Control.Monad
|
||||
import Data.List
|
||||
@@ -104,4 +103,4 @@ getDirectoryExecutables path =
|
||||
liftM2 (&&)
|
||||
(doesFileExist x')
|
||||
(liftM executable (getPermissions x'))))
|
||||
`catch` econst []
|
||||
`E.catch` econst []
|
||||
|
@@ -26,8 +26,7 @@ import XMonad.Prompt.Shell
|
||||
import XMonad.Actions.WindowGo (runOrRaise)
|
||||
import XMonad.Util.Run (runProcessWithInput)
|
||||
|
||||
import Prelude hiding (catch)
|
||||
import Control.Exception
|
||||
import Control.Exception as E
|
||||
import Control.Monad (liftM, liftM2)
|
||||
import System.Directory (doesDirectoryExist, doesFileExist, executable, getPermissions)
|
||||
|
||||
@@ -71,7 +70,7 @@ isApp "thunderbird" = className =? "Thunderbird-bin" <||> className =? "Thunderb
|
||||
isApp x = liftM2 (==) pid $ pidof x
|
||||
|
||||
pidof :: String -> Query Int
|
||||
pidof x = io $ (runProcessWithInput "pidof" [x] [] >>= readIO) `catch` econst 0
|
||||
pidof x = io $ (runProcessWithInput "pidof" [x] [] >>= readIO) `E.catch` econst 0
|
||||
|
||||
pid :: Query Int
|
||||
pid = ask >>= (\w -> liftX $ withDisplay $ \d -> getPID d w)
|
||||
|
@@ -15,27 +15,31 @@ module XMonad.Prompt.Shell
|
||||
-- $usage
|
||||
Shell (..)
|
||||
, shellPrompt
|
||||
-- ** Variations on shellPrompt
|
||||
-- $spawns
|
||||
, prompt
|
||||
, safePrompt
|
||||
, unsafePrompt
|
||||
|
||||
-- * Utility functions
|
||||
, getCommands
|
||||
, getBrowser
|
||||
, getEditor
|
||||
, getShellCompl
|
||||
, split
|
||||
, prompt
|
||||
, safePrompt
|
||||
) where
|
||||
|
||||
import Codec.Binary.UTF8.String (encodeString)
|
||||
import Control.Exception
|
||||
import Control.Monad (forM)
|
||||
import Data.List (isPrefixOf)
|
||||
import Prelude hiding (catch)
|
||||
import System.Directory (doesDirectoryExist, getDirectoryContents)
|
||||
import System.Environment (getEnv)
|
||||
import System.Posix.Files (getFileStatus, isDirectory)
|
||||
import Codec.Binary.UTF8.String (encodeString)
|
||||
import Control.Exception as E
|
||||
import Control.Monad (forM)
|
||||
import Data.List (isPrefixOf)
|
||||
import System.Directory (doesDirectoryExist, getDirectoryContents)
|
||||
import System.Environment (getEnv)
|
||||
import System.Posix.Files (getFileStatus, isDirectory)
|
||||
|
||||
import XMonad.Util.Run
|
||||
import XMonad hiding (config)
|
||||
import XMonad.Prompt
|
||||
import XMonad hiding (config)
|
||||
import XMonad.Prompt
|
||||
import XMonad.Util.Run
|
||||
|
||||
econst :: Monad m => a -> IOException -> m a
|
||||
econst = const . return
|
||||
@@ -64,7 +68,9 @@ shellPrompt c = do
|
||||
cmds <- io getCommands
|
||||
mkXPrompt Shell c (getShellCompl cmds) spawn
|
||||
|
||||
{- | See safe and unsafeSpawn. prompt is an alias for safePrompt;
|
||||
{- $spawns
|
||||
See safe and unsafeSpawn in "XMonad.Util.Run".
|
||||
prompt is an alias for safePrompt;
|
||||
safePrompt and unsafePrompt work on the same principles, but will use
|
||||
XPrompt to interactively query the user for input; the appearance is
|
||||
set by passing an XPConfig as the second argument. The first argument
|
||||
@@ -78,6 +84,7 @@ shellPrompt c = do
|
||||
wants URLs, and unsafePrompt for the XTerm example because this allows
|
||||
you to easily start a terminal executing an arbitrary command, like
|
||||
'top'. -}
|
||||
|
||||
prompt, unsafePrompt, safePrompt :: FilePath -> XPConfig -> X ()
|
||||
prompt = unsafePrompt
|
||||
safePrompt c config = mkXPrompt Shell config (getShellCompl [c]) run
|
||||
@@ -103,7 +110,7 @@ commandCompletionFunction cmds str | '/' `elem` str = []
|
||||
|
||||
getCommands :: IO [String]
|
||||
getCommands = do
|
||||
p <- getEnv "PATH" `catch` econst []
|
||||
p <- getEnv "PATH" `E.catch` econst []
|
||||
let ds = filter (/= "") $ split ':' p
|
||||
es <- forM ds $ \d -> do
|
||||
exists <- doesDirectoryExist d
|
||||
@@ -130,9 +137,11 @@ escape (x:xs)
|
||||
isSpecialChar :: Char -> Bool
|
||||
isSpecialChar = flip elem " &\\@\"'#?$*()[]{};"
|
||||
|
||||
-- | Ask the shell environment for
|
||||
-- | Ask the shell environment for the value of a variable in XMonad's environment, with a default value.
|
||||
-- In order to /set/ an environment variable (eg. combine with a prompt so you can modify @$HTTP_PROXY@ dynamically),
|
||||
-- you need to use 'System.Posix.putEnv'.
|
||||
env :: String -> String -> IO String
|
||||
env variable fallthrough = getEnv variable `catch` econst fallthrough
|
||||
env variable fallthrough = getEnv variable `E.catch` econst fallthrough
|
||||
|
||||
{- | Ask the shell what browser the user likes. If the user hasn't defined any
|
||||
$BROWSER, defaults to returning \"firefox\", since that seems to be the most
|
||||
|
@@ -19,15 +19,13 @@ module XMonad.Prompt.Ssh
|
||||
Ssh,
|
||||
) where
|
||||
|
||||
import Prelude hiding (catch)
|
||||
|
||||
import XMonad
|
||||
import XMonad.Util.Run
|
||||
import XMonad.Prompt
|
||||
|
||||
import System.Directory
|
||||
import System.Environment
|
||||
import Control.Exception
|
||||
import Control.Exception as E
|
||||
|
||||
import Control.Monad
|
||||
import Data.Maybe
|
||||
@@ -78,7 +76,7 @@ sshComplListLocal = do
|
||||
|
||||
sshComplListGlobal :: IO [String]
|
||||
sshComplListGlobal = do
|
||||
env <- getEnv "SSH_KNOWN_HOSTS" `catch` econst "/nonexistent"
|
||||
env <- getEnv "SSH_KNOWN_HOSTS" `E.catch` econst "/nonexistent"
|
||||
fs <- mapM fileExists [ env
|
||||
, "/usr/local/etc/ssh/ssh_known_hosts"
|
||||
, "/usr/local/etc/ssh_known_hosts"
|
||||
|
140
XMonad/Util/DebugWindow.hs
Normal file
140
XMonad/Util/DebugWindow.hs
Normal file
@@ -0,0 +1,140 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Util.DebugWindow
|
||||
-- Copyright : (c) Brandon S Allbery KF8NH, 2012
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : allbery.b@gmail.com
|
||||
-- Stability : unstable
|
||||
-- Portability : not portable
|
||||
--
|
||||
-- Module to dump window information for diagnostic/debugging purposes. See
|
||||
-- "XMonad.Hooks.DebugEvents" and "XMonad.Hooks.DebugStack" for practical uses.
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Util.DebugWindow (debugWindow) where
|
||||
|
||||
import Prelude
|
||||
|
||||
import XMonad
|
||||
|
||||
import Codec.Binary.UTF8.String (decodeString)
|
||||
import Control.Exception.Extensible as E
|
||||
import Control.Monad (when)
|
||||
import Data.List (unfoldr
|
||||
,intercalate
|
||||
)
|
||||
import Foreign
|
||||
import Foreign.C.String
|
||||
import Numeric (showHex)
|
||||
import System.Exit
|
||||
|
||||
-- | Output a window by ID in hex, decimal, its ICCCM resource name and class,
|
||||
-- and its title if available. Also indicate override_redirect with an
|
||||
-- exclamation mark, and wrap in brackets if it is unmapped or withdrawn.
|
||||
debugWindow :: Window -> X String
|
||||
debugWindow 0 = return "None"
|
||||
debugWindow w = do
|
||||
let wx = pad 8 '0' $ showHex w ""
|
||||
w' <- withDisplay $ \d -> io (safeGetWindowAttributes d w)
|
||||
case w' of
|
||||
Nothing ->
|
||||
return $ "(deleted window " ++ wx ++ ")"
|
||||
Just (WindowAttributes x y wid ht bw m o) -> do
|
||||
c' <- withDisplay $ \d ->
|
||||
io (getWindowProperty8 d wM_CLASS w)
|
||||
let c = case c' of
|
||||
Nothing -> ""
|
||||
Just c'' -> intercalate "/" $
|
||||
flip unfoldr (map (toEnum . fromEnum) c'') $
|
||||
\s -> if null s
|
||||
then Nothing
|
||||
else let (w'',s'') = break (== '\NUL') s
|
||||
s' = if null s''
|
||||
then s''
|
||||
else tail s''
|
||||
in Just (w'',s')
|
||||
t <- catchX' (wrap `fmap` getEWMHTitle "VISIBLE" w) $
|
||||
catchX' (wrap `fmap` getEWMHTitle "" w) $
|
||||
catchX' (wrap `fmap` getICCCMTitle w) $
|
||||
return ""
|
||||
let (lb,rb) = case () of
|
||||
() | m == waIsViewable -> ("","")
|
||||
| otherwise -> ("[","]")
|
||||
o' = if o then "!" else ""
|
||||
return $ concat [lb
|
||||
,o'
|
||||
,"window "
|
||||
,wx
|
||||
,t
|
||||
," ("
|
||||
,show wid
|
||||
,',':show ht
|
||||
,')':if bw == 0 then "" else '+':show bw
|
||||
,"@("
|
||||
,show x
|
||||
,',':show y
|
||||
,')':if null c then "" else ' ':c
|
||||
,rb
|
||||
]
|
||||
|
||||
getEWMHTitle :: String -> Window -> X String
|
||||
getEWMHTitle sub w = do
|
||||
a <- getAtom $ "_NET_WM_" ++ (if null sub then "" else '_':sub) ++ "_NAME"
|
||||
(Just t) <- withDisplay $ \d -> io $ getWindowProperty32 d a w
|
||||
return $ map (toEnum . fromEnum) t
|
||||
|
||||
getICCCMTitle :: Window -> X String
|
||||
getICCCMTitle w = do
|
||||
t@(TextProperty t' _ 8 _) <- withDisplay $ \d -> io $ getTextProperty d w wM_NAME
|
||||
[s] <- catchX' (tryUTF8 t) $
|
||||
catchX' (tryCompound t) $
|
||||
io ((:[]) `fmap` peekCString t')
|
||||
return s
|
||||
|
||||
tryUTF8 :: TextProperty -> X [String]
|
||||
tryUTF8 (TextProperty s enc _ _) = do
|
||||
uTF8_STRING <- getAtom "UTF8_STRING"
|
||||
when (enc == uTF8_STRING) $ error "String is not UTF8_STRING"
|
||||
(map decodeString . splitNul) `fmap` io (peekCString s)
|
||||
|
||||
tryCompound :: TextProperty -> X [String]
|
||||
tryCompound t@(TextProperty _ enc _ _) = do
|
||||
cOMPOUND_TEXT <- getAtom "COMPOUND_TEXT"
|
||||
when (enc == cOMPOUND_TEXT) $ error "String is not COMPOUND_TEXT"
|
||||
withDisplay $ \d -> io $ wcTextPropertyToTextList d t
|
||||
|
||||
splitNul :: String -> [String]
|
||||
splitNul "" = []
|
||||
splitNul s = let (s',ss') = break (== '\NUL') s in s' : splitNul ss'
|
||||
|
||||
pad :: Int -> Char -> String -> String
|
||||
pad w c s = replicate (w - length s) c ++ s
|
||||
|
||||
-- modified 'catchX' without the print to 'stderr'
|
||||
catchX' :: X a -> X a -> X a
|
||||
catchX' job errcase = do
|
||||
st <- get
|
||||
c <- ask
|
||||
(a, s') <- io $ runX c st job `E.catch` \e -> case fromException e of
|
||||
Just x -> throw e `const` (x `asTypeOf` ExitSuccess)
|
||||
_ -> runX c st errcase
|
||||
put s'
|
||||
return a
|
||||
|
||||
wrap :: String -> String
|
||||
wrap s = ' ' : '"' : wrap' s ++ "\""
|
||||
where
|
||||
wrap' (s':ss) | s' == '"' = '\\' : s' : wrap' ss
|
||||
| s' == '\\' = '\\' : s' : wrap' ss
|
||||
| otherwise = s' : wrap' ss
|
||||
wrap' "" = ""
|
||||
|
||||
-- Graphics.X11.Extras.getWindowAttributes is bugggggggy
|
||||
safeGetWindowAttributes :: Display -> Window -> IO (Maybe WindowAttributes)
|
||||
safeGetWindowAttributes d w = alloca $ \p -> do
|
||||
s <- xGetWindowAttributes d w p
|
||||
case s of
|
||||
0 -> return Nothing
|
||||
_ -> Just `fmap` peek p
|
@@ -55,7 +55,7 @@ menu menuCmd opts = menuArgs menuCmd [] opts
|
||||
|
||||
-- | Like 'menu' but also takes a list of command line arguments.
|
||||
menuArgs :: String -> [String] -> [String] -> X String
|
||||
menuArgs menuCmd args opts = runProcessWithInput menuCmd args (unlines opts)
|
||||
menuArgs menuCmd args opts = fmap (filter (/='\n')) $ runProcessWithInput menuCmd args (unlines opts)
|
||||
|
||||
-- | Like 'dmenuMap' but also takes the command to run.
|
||||
menuMap :: String -> M.Map String a -> X (Maybe a)
|
||||
|
@@ -32,11 +32,10 @@ module XMonad.Util.Font
|
||||
, fi
|
||||
) where
|
||||
|
||||
import Prelude hiding (catch)
|
||||
import XMonad
|
||||
import Foreign
|
||||
import Control.Applicative
|
||||
import Control.Exception
|
||||
import Control.Exception as E
|
||||
import Data.Maybe
|
||||
|
||||
#ifdef XFT
|
||||
@@ -53,7 +52,7 @@ data XMonadFont = Core FontStruct
|
||||
#endif
|
||||
|
||||
-- $usage
|
||||
-- See "Xmonad.Layout.Tabbed" or "XMonad.Prompt" for usage examples
|
||||
-- See "XMonad.Layout.Tabbed" or "XMonad.Prompt" for usage examples
|
||||
|
||||
-- | Get the Pixel value for a named color: if an invalid name is
|
||||
-- given the black pixel will be returned.
|
||||
@@ -70,7 +69,7 @@ econst = const
|
||||
initCoreFont :: String -> X FontStruct
|
||||
initCoreFont s = do
|
||||
d <- asks display
|
||||
io $ catch (getIt d) (fallBack d)
|
||||
io $ E.catch (getIt d) (fallBack d)
|
||||
where getIt d = loadQueryFont d s
|
||||
fallBack d = econst $ loadQueryFont d "-misc-fixed-*-*-*-*-10-*-*-*-*-*-*-*"
|
||||
|
||||
@@ -82,7 +81,7 @@ releaseCoreFont fs = do
|
||||
initUtf8Font :: String -> X FontSet
|
||||
initUtf8Font s = do
|
||||
d <- asks display
|
||||
(_,_,fs) <- io $ catch (getIt d) (fallBack d)
|
||||
(_,_,fs) <- io $ E.catch (getIt d) (fallBack d)
|
||||
return fs
|
||||
where getIt d = createFontSet d s
|
||||
fallBack d = econst $ createFontSet d "-misc-fixed-*-*-*-*-10-*-*-*-*-*-*-*"
|
||||
|
@@ -29,7 +29,7 @@ data Placement = OffsetLeft Int Int -- ^ An exact amount of pixels from the up
|
||||
| CenterLeft Int -- ^ Centered in the y-axis, an amount of pixels from the left
|
||||
| CenterRight Int -- ^ Centered in the y-axis, an amount of pixels from the right
|
||||
deriving (Show, Read)
|
||||
|
||||
|
||||
-- $usage
|
||||
-- This module uses matrices of boolean values as images. When drawing them,
|
||||
-- a True value tells that we want the fore color, and a False value that we
|
||||
|
@@ -52,9 +52,8 @@ import XMonad.Hooks.DynamicLog
|
||||
import XMonad.Util.Font (Align (..))
|
||||
import XMonad.Util.NamedWindows (getName)
|
||||
|
||||
import Prelude hiding (catch)
|
||||
import Control.Applicative ((<$>))
|
||||
import Control.Exception
|
||||
import Control.Exception as E
|
||||
import Data.List (isPrefixOf, isSuffixOf)
|
||||
import Data.Maybe (fromMaybe)
|
||||
import Data.Traversable (traverse)
|
||||
@@ -143,7 +142,7 @@ loadAvg = logCmd "/usr/bin/uptime | sed 's/.*: //; s/,//g'"
|
||||
-- | Create a 'Logger' from an arbitrary shell command.
|
||||
logCmd :: String -> Logger
|
||||
logCmd c = io $ do (_, out, _, _) <- runInteractiveCommand c
|
||||
fmap Just (hGetLine out) `catch` econst Nothing
|
||||
fmap Just (hGetLine out) `E.catch` econst Nothing
|
||||
-- no need to waitForProcess, we ignore SIGCHLD
|
||||
|
||||
-- | Get a count of filtered files in a directory.
|
||||
|
@@ -24,12 +24,14 @@ module XMonad.Util.NamedScratchpad (
|
||||
namedScratchpadAction,
|
||||
allNamedScratchpadAction,
|
||||
namedScratchpadManageHook,
|
||||
namedScratchpadFilterOutWorkspace
|
||||
namedScratchpadFilterOutWorkspace,
|
||||
namedScratchpadFilterOutWorkspacePP
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import XMonad.Hooks.ManageHelpers (doRectFloat)
|
||||
import XMonad.Actions.DynamicWorkspaces (addHiddenWorkspace)
|
||||
import XMonad.Hooks.DynamicLog (PP, ppSort)
|
||||
|
||||
import Control.Monad (filterM)
|
||||
import Data.Maybe (listToMaybe)
|
||||
@@ -160,4 +162,20 @@ namedScratchpadManageHook = composeAll . fmap (\c -> query c --> hook c)
|
||||
namedScratchpadFilterOutWorkspace :: [WindowSpace] -> [WindowSpace]
|
||||
namedScratchpadFilterOutWorkspace = filter (\(W.Workspace tag _ _) -> tag /= scratchpadWorkspaceTag)
|
||||
|
||||
-- | Transforms a pretty-printer into one not displaying the NSP workspace.
|
||||
--
|
||||
-- A simple use could be:
|
||||
--
|
||||
-- > logHook = dynamicLogWithPP . namedScratchpadFilterOutWorkspace $ defaultPP
|
||||
--
|
||||
-- Here is another example, when using "XMonad.Layout.IndependentScreens".
|
||||
-- If you have handles @hLeft@ and @hRight@ for bars on the left and right screens, respectively, and @pp@ is a pretty-printer function that takes a handle, you could write
|
||||
--
|
||||
-- > logHook = let log screen handle = dynamicLogWithPP . namedScratchpadFilterOutWorkspacePP . marshallPP screen . pp $ handle
|
||||
-- > in log 0 hLeft >> log 1 hRight
|
||||
namedScratchpadFilterOutWorkspacePP :: PP -> PP
|
||||
namedScratchpadFilterOutWorkspacePP pp = pp {
|
||||
ppSort = fmap (. namedScratchpadFilterOutWorkspace) (ppSort pp)
|
||||
}
|
||||
|
||||
-- vim:ts=4:shiftwidth=4:softtabstop=4:expandtab:foldlevel=20:
|
||||
|
@@ -22,9 +22,8 @@ module XMonad.Util.NamedWindows (
|
||||
unName
|
||||
) where
|
||||
|
||||
import Prelude hiding ( catch )
|
||||
import Control.Applicative ( (<$>) )
|
||||
import Control.Exception.Extensible ( bracket, catch, SomeException(..) )
|
||||
import Control.Exception.Extensible as E
|
||||
import Data.Maybe ( fromMaybe, listToMaybe )
|
||||
|
||||
import qualified XMonad.StackSet as W ( peek )
|
||||
@@ -50,11 +49,11 @@ getName w = withDisplay $ \d -> do
|
||||
let getIt = bracket getProp (xFree . tp_value) (fmap (`NW` w) . copy)
|
||||
|
||||
getProp = (internAtom d "_NET_WM_NAME" False >>= getTextProperty d w)
|
||||
`catch` \(SomeException _) -> getTextProperty d w wM_NAME
|
||||
`E.catch` \(SomeException _) -> getTextProperty d w wM_NAME
|
||||
|
||||
copy prop = fromMaybe "" . listToMaybe <$> wcTextPropertyToTextList d prop
|
||||
|
||||
io $ getIt `catch` \(SomeException _) -> ((`NW` w) . resName) `fmap` getClassHint d w
|
||||
io $ getIt `E.catch` \(SomeException _) -> ((`NW` w) . resName) `fmap` getClassHint d w
|
||||
|
||||
unName :: NamedWindow -> Window
|
||||
unName (NW _ w) = w
|
||||
|
@@ -3,7 +3,7 @@ Module : XMonad.Util.Paste
|
||||
Copyright : (C) 2008 Jérémy Bobbio, gwern
|
||||
License : BSD3
|
||||
|
||||
Maintainer : gwern <gwern0@gmail.com>
|
||||
Maintainer : none
|
||||
Stability : unstable
|
||||
Portability : unportable
|
||||
|
||||
@@ -54,9 +54,9 @@ pasteSelection :: X ()
|
||||
pasteSelection = getSelection >>= pasteString
|
||||
|
||||
-- | Send a string to the window which is currently focused. This function correctly
|
||||
-- handles capitalization.
|
||||
-- handles capitalization. Warning: in dealing with capitalized characters, this assumes a QWERTY layout.
|
||||
pasteString :: String -> X ()
|
||||
pasteString = mapM_ (\x -> if isUpper x then pasteChar shiftMask x else pasteChar noModMask x)
|
||||
pasteString = mapM_ (\x -> if isUpper x || x `elem` "~!@#$%^&*()_+{}|:\"<>?" then pasteChar shiftMask x else pasteChar noModMask x)
|
||||
|
||||
{- | Send a character to the current window. This is more low-level.
|
||||
Remember that you must handle the case of capitalization appropriately.
|
||||
@@ -89,9 +89,3 @@ sendKeyWindow mods key w = withDisplay $ \d -> do
|
||||
sendEvent d w True keyPressMask ev
|
||||
setEventType ev keyRelease
|
||||
sendEvent d w True keyReleaseMask ev
|
||||
|
||||
-- | A null 'KeyMask'. Used when you don't want a character or string shifted, control'd, or what.
|
||||
--
|
||||
-- TODO: This really should be a function in the X11 binding. When noModMask shows up there, remove.
|
||||
noModMask :: KeyMask
|
||||
noModMask = 0
|
||||
|
@@ -86,6 +86,15 @@ runProcessWithInputAndWait cmd args input timeout = io $ do
|
||||
-- Use like:
|
||||
--
|
||||
-- > (5.5 `seconds`)
|
||||
--
|
||||
-- In GHC 7 and later, you must either enable the PostfixOperators extension
|
||||
-- (by adding
|
||||
--
|
||||
-- > {-# LANGUAGE PostfixOperators #-}
|
||||
--
|
||||
-- to the top of your file) or use seconds in prefix form:
|
||||
--
|
||||
-- > 5.5 seconds
|
||||
seconds :: Rational -> Int
|
||||
seconds = fromEnum . (* 1000000)
|
||||
|
||||
|
@@ -145,7 +145,7 @@ swapUpZ (Just s) = Just s { W.up = reverse (W.down s), W.down = [] }
|
||||
swapDownZ :: Zipper a -> Zipper a
|
||||
swapDownZ Nothing = Nothing
|
||||
swapDownZ (Just s) | d:down <- W.down s = Just s { W.down = down, W.up = d:W.up s }
|
||||
swapDownZ (Just s) = Just s { W.up = [], W.down = reverse (W.up s) }
|
||||
swapDownZ (Just s) = Just s { W.up = [], W.down = reverse (W.up s) }
|
||||
|
||||
-- | Swap the focused element with the first one
|
||||
swapMasterZ :: Zipper a -> Zipper a
|
||||
@@ -197,7 +197,7 @@ sortByZ f = fromTags . sortBy (adapt f) . toTags
|
||||
where adapt g e1 e2 = g (fromE e1) (fromE e2)
|
||||
|
||||
-- ** Maps
|
||||
|
||||
|
||||
-- | Map a function over a stack. The boolean argument indcates whether
|
||||
-- the current element is the focused one
|
||||
mapZ :: (Bool -> a -> b) -> Zipper a -> Zipper b
|
||||
|
@@ -131,12 +131,12 @@ smallClean =
|
||||
}
|
||||
}
|
||||
|
||||
-- | Don's prefered colors - from DynamicLog...;)
|
||||
-- | Don's preferred colors - from DynamicLog...;)
|
||||
donaldTheme :: ThemeInfo
|
||||
donaldTheme =
|
||||
newTheme { themeName = "donaldTheme"
|
||||
, themeAuthor = "Andrea Rossato"
|
||||
, themeDescription = "Don's prefered colors - from DynamicLog...;)"
|
||||
, themeDescription = "Don's preferred colors - from DynamicLog...;)"
|
||||
, theme = defaultTheme { activeColor = "#2b4f98"
|
||||
, inactiveColor = "#cccccc"
|
||||
, activeBorderColor = "#2b4f98"
|
||||
|
@@ -82,7 +82,7 @@ promptSelection = unsafePromptSelection
|
||||
safePromptSelection app = join $ io $ liftM (safeSpawn app . return) getSelection
|
||||
unsafePromptSelection app = join $ io $ liftM unsafeSpawn $ fmap (\x -> app ++ " " ++ x) getSelection
|
||||
|
||||
{- | A wrapper around 'promptSelection' and its safe variant. They take two parameters, the
|
||||
{- | A wrapper around 'promptSelection' and its safe variant. They take two parameters, the
|
||||
first is a function that transforms strings, and the second is the application to run.
|
||||
The transformer essentially transforms the selection in X.
|
||||
One example is to wrap code, such as a command line action copied out of the browser
|
||||
|
@@ -38,7 +38,7 @@ import XMonad.Util.Image
|
||||
import Control.Monad
|
||||
|
||||
-- $usage
|
||||
-- See "XMonad.Layout.Tabbed" or "XMonad.Layout.DragPane" or
|
||||
-- See "XMonad.Layout.Tabbed" or "XMonad.Layout.DragPane" or
|
||||
-- "XMonad.Layout.Decoration" for usage examples
|
||||
|
||||
-- | Compute the weighted average the colors of two given Pixel values.
|
||||
@@ -163,7 +163,7 @@ paintTextAndIcons w fs wh ht bw bc borc ffc fbc als strs i_als icons = do
|
||||
-- drawn inside it.
|
||||
-- Not exported.
|
||||
paintWindow' :: Window -> Rectangle -> Dimension -> String -> String
|
||||
-> Maybe (XMonadFont,String,String,[(String, (Position, Position))])
|
||||
-> Maybe (XMonadFont,String,String,[(String, (Position, Position))])
|
||||
-> Maybe (String, String, [((Position, Position), [[Bool]])]) -> X ()
|
||||
paintWindow' win (Rectangle _ _ wh ht) bw color b_color strStuff iconStuff = do
|
||||
d <- asks display
|
||||
|
@@ -1,5 +1,5 @@
|
||||
name: xmonad-contrib
|
||||
version: 0.10
|
||||
version: 0.11
|
||||
homepage: http://xmonad.org/
|
||||
synopsis: Third party extensions for xmonad
|
||||
description:
|
||||
@@ -24,10 +24,25 @@ maintainer: spencerjanssen@gmail.com
|
||||
extra-source-files: README scripts/generate-configs scripts/run-xmonad.sh
|
||||
scripts/window-properties.sh
|
||||
scripts/xinitrc scripts/xmonad-acpi.c
|
||||
scripts/xmonad-clock.c tests/test_SwapWorkspaces.hs
|
||||
tests/test_XPrompt.hs
|
||||
cabal-version: >= 1.2.1
|
||||
scripts/xmonad-clock.c
|
||||
tests/genMain.hs
|
||||
tests/ManageDocks.hs
|
||||
tests/Selective.hs
|
||||
tests/SwapWorkspaces.hs
|
||||
tests/XPrompt.hs
|
||||
cabal-version: >= 1.6
|
||||
build-type: Simple
|
||||
bug-reports: http://code.google.com/p/xmonad/issues/list
|
||||
|
||||
tested-with: GHC==7.6.1,
|
||||
GHC==7.4.1,
|
||||
GHC==7.2.1,
|
||||
GHC==6.12.3,
|
||||
GHC==6.10.4
|
||||
source-repository head
|
||||
type: darcs
|
||||
location: http://code.haskell.org/XMonadContrib
|
||||
|
||||
|
||||
flag small_base
|
||||
description: Choose the new smaller, split-up base package.
|
||||
@@ -57,7 +72,7 @@ library
|
||||
extensions: ForeignFunctionInterface
|
||||
cpp-options: -DXFT
|
||||
|
||||
build-depends: mtl >= 1 && < 3, unix, X11>=1.5.0.0 && < 1.6, xmonad>=0.10, xmonad<0.11, utf8-string
|
||||
build-depends: mtl >= 1 && < 3, unix, X11>=1.6 && < 1.7, xmonad>=0.11 && < 0.12, utf8-string
|
||||
|
||||
if true
|
||||
ghc-options: -fwarn-tabs -Wall
|
||||
@@ -98,9 +113,11 @@ library
|
||||
XMonad.Actions.FocusNth
|
||||
XMonad.Actions.GridSelect
|
||||
XMonad.Actions.GroupNavigation
|
||||
XMonad.Actions.Launcher
|
||||
XMonad.Actions.MessageFeedback
|
||||
XMonad.Actions.MouseGestures
|
||||
XMonad.Actions.MouseResize
|
||||
XMonad.Actions.Navigation2D
|
||||
XMonad.Actions.NoBorders
|
||||
XMonad.Actions.OnScreen
|
||||
XMonad.Actions.PerWorkspaceKeys
|
||||
@@ -111,6 +128,7 @@ library
|
||||
XMonad.Actions.KeyRemap
|
||||
XMonad.Actions.RotSlaves
|
||||
XMonad.Actions.Search
|
||||
XMonad.Actions.ShowText
|
||||
XMonad.Actions.SimpleDate
|
||||
XMonad.Actions.SinkAll
|
||||
XMonad.Actions.SpawnOn
|
||||
@@ -128,6 +146,7 @@ library
|
||||
XMonad.Actions.WithAll
|
||||
XMonad.Actions.WorkspaceCursors
|
||||
XMonad.Actions.WorkspaceNames
|
||||
XMonad.Actions.Workscreen
|
||||
XMonad.Config.Arossato
|
||||
XMonad.Config.Azerty
|
||||
XMonad.Config.Bluetile
|
||||
@@ -138,9 +157,12 @@ library
|
||||
XMonad.Config.Sjanssen
|
||||
XMonad.Config.Xfce
|
||||
XMonad.Hooks.CurrentWorkspaceOnTop
|
||||
XMonad.Hooks.DebugEvents
|
||||
XMonad.Hooks.DebugKeyEvents
|
||||
XMonad.Hooks.DynamicBars
|
||||
XMonad.Hooks.DynamicHooks
|
||||
XMonad.Hooks.DynamicLog
|
||||
XMonad.Hooks.DebugStack
|
||||
XMonad.Hooks.EwmhDesktops
|
||||
XMonad.Hooks.FadeInactive
|
||||
XMonad.Hooks.FadeWindows
|
||||
@@ -217,6 +239,7 @@ library
|
||||
XMonad.Layout.Named
|
||||
XMonad.Layout.NoBorders
|
||||
XMonad.Layout.NoFrillsDecoration
|
||||
XMonad.Layout.OnHost
|
||||
XMonad.Layout.OneBig
|
||||
XMonad.Layout.PerWorkspace
|
||||
XMonad.Layout.PositionStoreFloat
|
||||
@@ -264,6 +287,7 @@ library
|
||||
XMonad.Prompt.XMonad
|
||||
XMonad.Util.Cursor
|
||||
XMonad.Util.CustomKeys
|
||||
XMonad.Util.DebugWindow
|
||||
XMonad.Util.Dmenu
|
||||
XMonad.Util.Dzen
|
||||
XMonad.Util.ExtensibleState
|
||||
|
Reference in New Issue
Block a user