81 Commits
v0.10 ... v0.11

Author SHA1 Message Date
Adam Vogt
6072d9c599 bump cabal-version to satsify hackage 2013-01-01 01:41:59 +00:00
Adam Vogt
277412af44 bump version to 0.11 2012-12-31 10:42:52 +00:00
Adam Vogt
0030802e46 Add more metadata to cabal file 2012-12-31 18:45:13 +00:00
Adam Vogt
f211874340 X.A.Workscreen make the whole module description show up for haddock 2012-12-31 02:46:00 +00:00
Adam Vogt
32548e056f Note that an alternative to XMonad.Actions.ShowText is X.U.Dzen 2012-12-31 02:30:42 +00:00
Dmitri Iouchtchenko
2c6f1c22b2 Add X.A.DynamicWorkspaces.renameWorkspaceByName. 2012-12-27 06:35:31 +00:00
Adam Vogt
b8a22c4dee Change type of X.A.ShowText.handleTimerEvent so example code typechecks. 2012-12-26 01:38:41 +00:00
Adam Vogt
42443e3df2 Describe arguments for X.A.ShowText.flashText 2012-12-26 01:37:25 +00:00
pastorelli.mario
9e0eb7f770 Add XMonad.Actions.ShowText 2012-12-25 20:26:35 +00:00
Adam Vogt
895c47fb4e Record polachok's fix for issue 507 2012-12-16 18:27:24 +00:00
c.lopez
cc98355700 Removes unused function spawnWithActions and redundant imports in XMonad.Actions.Launcher 2012-12-15 22:37:14 +00:00
Adam Vogt
205e7133ac A.Launcher markup identifiers for haddock links 2012-12-15 16:59:14 +00:00
Adam Vogt
9caedf2fff Address warnings from Debug modules
The warnings were related to ghc-7.6 removing Prelude.catch
(triggering warnings regarding the import hiding it), as well
as defaulting of some numeric types.
2012-12-15 16:55:25 +00:00
c.lopez
265df96ab8 Removes LocateMode and LocateRegexMode from XMonad.Actions.Launcher 2012-12-14 21:12:30 +00:00
allbery.b
8d1ad8b280 debug-hooks
Hooks to print diagnostic information to stderr (usually .xsession-errors)
to help debug complex issues involving the StackSet and received events.
2012-08-13 22:38:21 +00:00
Adam Vogt
de84dfef0d Remove trailing whitespace. 2012-11-09 01:41:56 +00:00
Adam Vogt
3fa51ed656 Use Control.Exception.catch explitly to avoid warnings
The base that comes with ghc-7.6.1 no longer includes Prelude.catch;
so these modules were changed so that there is no warning for

import Prelude hiding (catch)

At the same time these changes should be compatible with older GHCs,
since the catch being has never been the one in the Prelude.
2012-11-09 01:35:06 +00:00
Adam Vogt
a9911d2168 Add missing type signatures.
For whatever reason, some patches applied were missing these signatures.
While haddock has been able to include inferred signatures for a while,
including type signatures makes it easier to see if and when types have
been changed.
2012-11-09 01:27:52 +00:00
Adam Vogt
1716ffd9d0 Rename variables "state" to avoid warnings about shadowing
XMonad core re-exports Control.Monad.State, which includes
a function "state" if you happen to use mtl-2. Since there's
a chance xmonad still works with mtl-1 avoid imports like:

import XMonad hiding (state)
2012-11-09 01:23:16 +00:00
Adam Vogt
e776260133 Rename variable in L.Minimize to avoid shadowing.
This "state" is new with a newer mtl.
2012-11-09 00:34:10 +00:00
Adam Vogt
53c2e7833c Gut H.ICCCMFocus: issue 177 has been merged in core.
Keep the module for now: the LG3D bit might still be useful
and there's no need to break configs unnecessarily.
2012-11-08 22:57:16 +00:00
pastorelli.mario
fbb9eb36f9 ewmh-eventhook-custom
Add ewmhDesktopsEventHookCustom, a generalized version of ewmhDesktopsEventHook that takes a sort function as argument. This sort function should be the same used by the LogHook.
2012-08-16 15:30:32 +00:00
daedalusinfinity
4da5da430e Added smart spacing to the spacing module
Added smart spacing to the spacing module, which adds spacing to all windows,
except to windows on singleton workspaces.
2012-09-23 03:45:27 +00:00
c.lopez
0af63a4767 Improves haddock documentation 2012-08-26 09:17:16 +00:00
c.lopez
7245766c6d Improve comments, add an error throw that shouldn't happen 2012-08-26 08:54:26 +00:00
c.lopez
cd6feb81e2 fix a bug when ncompletions = nrows 2012-08-26 08:31:37 +00:00
c.lopez
8f9fa05c0f Fixes typos in Actions.Launcher haddock documentation 2012-08-11 11:25:02 +00:00
c.lopez
b5f9a61dbe Correctly get the autocompletion item when alwaysHighlight in XMonad.Prompt is True 2012-08-11 10:48:05 +00:00
c.lopez
96ab91fcfa Removes warnings, adds a browser value for LauncherConfig in haddock comments 2012-06-28 11:45:33 +00:00
c.lopez
3c74148a2f Changes on XPrompt:
* Adds mkPromptWithModes, creates a prompt given a list of modes (list of XPType).

    * Adds Setting `alwaysHighlight` to defaultXPConfig. When set to true, autocompletion always highlight the first result if it is not highlighted.
    
Adds module XMonad.Actions.Launcher. This module allows to combine and switch between instances of XPrompt. It includes a default set of modes which require the programs `hoogle`, `locate` and `calc` to be installed to work properly.
2012-06-28 10:17:49 +00:00
Daniel Wagner
9d34e848d9 accept more windows as docks 2012-08-23 12:41:53 +00:00
longpoke
a7c2c023fb strip newlines from dmenu's returns to be compatible with the newest version of dmenu 2012-07-23 21:28:07 +00:00
kedals0
814fda056b 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.
2012-07-06 09:33:08 +00:00
Daniel Wagner
2a6709ff5c refer to the new name 'handleEventHook' instead of the old name 'eventHook' in X.L.Fullscreen documentation 2012-06-18 18:10:03 +00:00
gopsychonauts
3ffc956b93 UrgencyHooks made available as Window -> X () functions
Adds an UrgencyHook instance for the type Window -> X (), allowing any such
functions to be used directly as UrgencyHooks. The Show and Read constraints
were removed from the UrgencyHook class in order to permit this; these
constraints were required only in a historical implementation of the module,
which used a layout modifier.

All existing configurations using UrgencyHooks should remain fully functional.
New configs may make use of this modification by declaring their UrgencyHook as
a simple Window -> X () function.
2012-05-04 06:23:39 +00:00
Brent Yorgey
e705eba1e0 updates to XMonad.Prompt re: word-oriented commands
+ change killWord and moveWord to have emacs-like behavior: first move
    past/kill consecutive whitespace, then move past/kill consecutive
    non-whitespace.

  + create variants killWord' and moveWord' which take a predicate
    specifying non-word characters.

  + create variants defaultXPKeymap' and emacsLikeXPKeymap' which take
    the same sort of predicate, which is applied to all keybindings with
    word-oriented commands.
2012-05-10 17:43:17 +00:00
Jesper Reenberg
2f2a217b85 Added isUnfocusedOnCurrentWS and fadeInactiveCurrentWSLogHook for better support of fading/opacity on multi monitor setups 2012-03-29 14:18:18 +00:00
Jesper Reenberg
6f996bb21f Fixed X.A.GridSelect to be consistent in the way it (now) sorts the shown
elements when modifying the searchString.

The implemented ordering sorts based on how "deep the needle is in the
haystack", meaning that searching for "st" in the elements "Install" and "Study"
will order them as "Study" and "Install". Previously there was no ordering and
when using GridSelect to select workspaces, the ordering was not consistent, as
the list of workspaces (if not modified manually) is ordered by last used. In
this case either "Study" or "Install" would come first depending on which
workspace was last visited.
2012-05-01 18:04:15 +00:00
Julia Jomantaite
3a740c4d5a Use getXMonadDir to get the default xmonad directory. 2012-05-01 12:14:27 +00:00
Adam Vogt
f09a61f5f5 Minor haddock formatting for X.L.OnHost and X.A.DynamicWorkspaceOrder 2012-04-28 19:45:52 +00:00
Adam Vogt
1a735f04e3 Remove trailing whitespace. 2012-04-28 19:40:48 +00:00
Carlos Lopez-Camey
d2739b1683 Add emacs-like keys to browse history in XMonad.Prompt 2012-04-21 11:07:37 +00:00
Carlos Lopez-Camey
9ecc76e087 Adds an emacs-like Keymap in XMonad.Prompt 2012-04-21 01:23:35 +00:00
jakob
7b21732ead add 'withNthWorkspace' to DynamicWorkspaceOrder.
Note this is very similar to the function of the same name exported by
DynamicWorkspaces.  Ultimately it would probably be cleaner to
generalize the one in DynamicWorkspaces to accept an arbitrary
workspace sort as a parameter; this is left as an exercise for future
hackers.
2012-04-07 18:46:40 +00:00
allbery.b
c691988bbf XMonad.Layout.OnHost allows host-specific modifications to a layout, which
is otherwise very difficult to do.  Similarly to X.L.PerWorkspace, it provides
onHost, onHosts, modHost, and modHosts layout modifiers.  It attempts to do
smart hostname comparison, such that short names will be matched with short
names and FQDNs with FQDNs.

This module currently requires that $HOST be set in the environment.
You can use System.Posix.Env.setEnv to do so in xmonad.hs if need be.
(Properly, this should be done via the network library, but I'm trying to
avoid adding that dependency.)  An alternative would be to shell out to
get the name, but that has considerable portability hurdles.
2012-03-20 03:09:12 +00:00
Adam Vogt
40d8c01894 Bump version to 0.10.1
Raising the X11 dependency while keeping the xmonad version the same leads to
problems where cabal install uses the dependency versions following hackage,
not what is installed.
2012-03-20 00:53:11 +00:00
Jens Petersen
328293a0a8 narrower BorderResize rectangles placed within border edges
Change the border resize rectangles to be narrower and only extend
  inside the window not outside.  Most window managers just seem to use
  the border decoration area for starting resizes which is often just 1 pixel
  wide but as a compromise the width is now 2 pixels (before it was 10!).
  The rectangles are now placed symmetrically within the border and window.
  This seems to work ok with PositionStoreFloat for the Bluetile config.
2012-03-14 06:47:03 +00:00
Ben Boeckel
434aec1038 add-dynamic-bars-module
This adds the X.H.DynamicBars module. It allows per-screen status bars to be
easily managed and dynamically handles the number of screens changing.
2012-03-16 00:22:04 +00:00
Daniel Wagner
69d2e0a873 bump X11 dependency so that noModMask is available 2012-03-16 00:03:02 +00:00
gwern0
9454dd5d7f Paste.hs: rm noModMask, shifted definition to X11 binding (see previous email) 2011-12-03 20:30:38 +00:00
Jens Petersen
60713064e7 GroupNavigation: fix import typo in usage 2012-03-12 10:33:49 +00:00
Jens Petersen
98b0e8e4c1 add sendToEmptyWorkspace to FindEmptyWorkspace
sendToEmptyWorkspace is like tagToEmptyWorkspace except
it does not change workspace after moving the window.
2012-03-12 10:23:31 +00:00
Jens Petersen
d2a076b1e7 xmonad-contrib.cabal: simplify xmonad dependency to >=0.10 && < 0.11
Unless there is a particular reason for listing the lower and upper bounds
separately then this seems simpler and cleaner.
2012-03-12 10:18:00 +00:00
crodjer
e2bb57bd63 ShowWName: Increase horizontal padding for flash
Currently the flash window width leaves a very small amount of padding. This
patch adds some extra horizontal width, governed by text width and length.
2012-03-05 16:45:17 +00:00
Ben Boeckel
d5e7d6217f persist-togglehook-options
Save the state of ToggleHook options over a restart.
2012-03-11 05:01:43 +00:00
Rohan Jain
4feb4fb058 ShowWName flash window background color
While calling paintAndWrite for flash window, the background color from config
should also be passed on as window background in addition to as text background
color. Otherwise the window color gets set to the default black which shows up
when text cannot span whole of the window.

This issue becomes visible when the font size is considerably large or even in
small size with truetype fonts.
2012-03-06 06:52:24 +00:00
crodjer
3f39d34994 ShowWName: Fix flash location by screen rectangle
In case of using this hook with multiple monitors, the Tag flash was not
following the screen's coordinates. This patch shifts the new window created for
flash according to the Rectangle defined by the screen.
2012-03-05 16:12:40 +00:00
crodjer
7789f18ce9 Fix typo in tabbed layout link for font utils docs 2012-02-29 07:00:22 +00:00
Steffen Schuldenzucker
807d356743 L.WorkspaceDir: cleanup redundant {definitions,imports} 2012-02-29 11:21:24 +00:00
Steffen Schuldenzucker
c012b3408d fix L.WorkspaceDir special char handling: remove "echo -n" processing 2012-02-27 12:20:04 +00:00
allbery.b
f6a050e5a3 Add BorderUrgencyHook to XMonad.Hooks.UrgencyHook
BorderUrgencyHook is a new UrgencyHook usable with withUrgencyHook or
withUrgencyHookC; it allows an urgent window to be given a different
border color.  This may not always work as intended, since UrgencyHook
likes to assume that a window being visible is sufficient to disable
urgency notification; but with suppressWhen = Never it may work well
enough.

There is a report that if a new window is created at the wrong time,
the wrong window may be marked urgent somehow.  I seem to once again
be revealing bugs in underlying packages, although a quick examination
of X.H.UrgencyHook doesn't seem to show any way for the wrong window
to be selected.
2012-02-25 08:26:16 +00:00
nicolas.dudebout
92e8f5ebef Adding use case for namedScratchpad. 2012-01-22 23:58:43 +00:00
gwern0
dd591587f6 Actions.WindowGo: typo fix - trim 's' per cub.uanic https://code.google.com/p/xmonad/issues/detail?id=491 2012-01-16 22:42:44 +00:00
gwern0
219b4dd8fb XMonad.Actions.PhysicalScreens: fix typo spotted by Chris Pick <haskell@chrispick.com> 2012-01-15 22:30:13 +00:00
Daniel Wagner
b944b1129c roll back previous incorrect fix 2012-01-11 21:41:33 +00:00
gwern0
08d432bde6 Extending: fix http://code.google.com/p/xmonad/issues/detail?id=490 2012-01-11 21:19:07 +00:00
Daniel Wagner
04d6cbc5f0 another documentation patch: XMonadContrib.UpdatePointer -> XMonad.Actions.UpdatePointer 2012-01-11 21:12:26 +00:00
Daniel Wagner
9cafb7c2af documentation patch, fixes issue 490 2012-01-11 21:08:32 +00:00
Adam Vogt
272c333f75 X.H.EwmhDesktops note that fullscreenEventHook is not included in ewmh
Just a documentation fix (nomeata's suggestion at issue 339).
2012-01-02 21:14:04 +00:00
Adam Vogt
aa96dd6e03 X.H.EwmhDesktops haddock formatting. 2012-01-02 21:12:03 +00:00
Norbert Zeh
59bfe97f63 X.A.Navigation2D
This is a new module to support directional navigation across multiple screens.
As such it is related to X.A.WindowNavigation and X.L.WindowNavigation, but it
is more general.  For a detailed discussion of the differences, see
http://www.cs.dal.ca/~nzeh/xmonad/Navigation2D.pdf.
2011-12-08 20:58:42 +00:00
Daniel Wagner
64efea4d0a documentation patch: mention PostfixOperators 2011-12-10 23:48:20 +00:00
Adam Vogt
a1a578010c P.Shell documentation and add missing unsafePrompt export
Haddock (version 2.9.2 at least) does not attach documentation to any of a b or
c when given:

    -- | documentation
    a,b,c :: X
2011-12-07 16:39:51 +00:00
gwern0
9209e96234 Paste: 3 more escaped characters from alistra 2011-11-29 16:03:35 +00:00
Daniel Wagner
c809ae6f5f unfuck X.U.Paste 2011-11-29 03:23:31 +00:00
gwern0
9b369949ff XMonad.Util.Paste: +alistra's patch for fixing his pasting of things like email address (@) 2011-11-28 21:56:48 +00:00
gwern0
9e69773d98 XMonad.Util.Paste: rm myself from maintainer field; I don't know how to fix any of it even if I wanted 2011-11-28 21:30:01 +00:00
gwern0
2f0ac73313 XMonad.Prompt.Shell: improve 'env' documentation to cover goodgrue's problem 2011-11-27 23:15:07 +00:00
Erik de Castro Lopo
95290ed278 Fix spelling 'prefered' -> 'preferred'. 2011-11-25 01:02:29 +00:00
Adam Vogt
a551d1367c Restore TrackFloating behavior to an earlier version.
Thanks for liskni_si for pressing the matter: without this change it is very
broken, with the patch it is still not perfect but still useful.
2011-11-20 04:55:38 +00:00
Adam Vogt
cb795c8c75 Explicitly list test files in .cabal
In the future, require Cabal >= 1.6 to be able to just write tests/*.hs
2011-11-18 23:25:11 +00:00
66 changed files with 3690 additions and 362 deletions

View File

@@ -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 ()

View File

@@ -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

View File

@@ -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

View File

@@ -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))

View File

@@ -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

View File

@@ -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
View 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)
-}

View 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)

View File

@@ -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)

View File

@@ -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
View 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)

View File

@@ -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 [[]])

View File

@@ -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"))
-}

View 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

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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.)

View 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
View 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

View File

@@ -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

View File

@@ -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) ++

View File

@@ -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

View File

@@ -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"

View File

@@ -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.

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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 ()

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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 (:)

View File

@@ -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
View 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

View File

@@ -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
--

View File

@@ -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)

View File

@@ -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

View File

@@ -26,7 +26,7 @@ module XMonad.Layout.Spiral (
) where
import Data.Ratio
import XMonad
import XMonad hiding ( Rotation )
import XMonad.StackSet ( integrate )
-- $usage

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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 []

View File

@@ -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)

View File

@@ -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

View File

@@ -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
View 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

View File

@@ -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)

View File

@@ -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-*-*-*-*-*-*-*"

View File

@@ -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

View File

@@ -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.

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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"

View File

@@ -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

View File

@@ -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

View File

@@ -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