222 Commits

Author SHA1 Message Date
Adam Vogt
d4c7c51616 Export types to improve haddock links. 2011-11-18 19:06:42 +00:00
nzeh
c4f3e94377 Better control over GridVariants geometry
Added new messages the layout understands to allow changing the grid aspect
ratio and setting the fraction of the master to a given value rather than
changing it relative to the current value.
2011-09-07 13:33:04 +00:00
Norbert Zeh
2e91cde115 Support for scratchpad applications with multiple windows
I recently found that I use xpad to add sticky notes to my desktop.  I wanted
to be able to show/hide these in the same fashion as regular scratchpads.  This
patch adds a function that allows to do this while reusing most of the existing
NamedScratchpad code.
2011-04-06 14:02:13 +00:00
Norbert Zeh
e09cfba7dd Additional messages for SplitGrid layout
This patch introduces two new message SetMasterRows and SetMasterCols for the
X.GridVariants.SplitGrid layout, which set the number of rows/columns in the
master grid to the given value.  This is useful when setting the number of rows
and/or columns non-incrementally using an interface such as GridSelect.
2009-12-15 19:21:42 +00:00
Adam Vogt
001b38c7ab Be consistent with core utf8-string usage.
Now that spawn assumes executeFile takes a String containing utf8 codepoints
(and takes an actual String as input) adjust Prompt.Shell to avoid double
encoding. U.Run functions are updated to be consistent with spawn.
2011-11-18 18:47:45 +00:00
Adam Vogt
067ccb950e Export types to reduce haddock warnings. 2010-10-23 19:57:55 +00:00
Daniel Wagner
0226b8cb4f documentation patch: note the drawbacks of X.U.Dmenu 2011-11-15 02:27:26 +00:00
Daniel Wagner
68d49ad3aa get ready for GHC 7.4: Num a no longer implies (Eq a, Show a) 2011-11-15 02:26:50 +00:00
Adam Vogt
d3ef59256b Correct completions of utf8-named file in X.P.Shell 2011-11-11 21:56:55 +00:00
Wirt Wolff
1fb2696710 Expose X.L.Groups.Helpers and Groups.Wmii in xmonad-contrib.cabal
They provide many useful exports and are linked from X.L.Groups so promote
them from other-modules or missing status.
2011-11-04 05:37:03 +00:00
Audun Skaugen
71bb40156a Small bugfix to XMonad.Layout.Fullscreen
Fixed a small bug in the layout modifers where 
windows entering fullscreen were not refreshed.

Also fixed some funny whitespace characters.
2011-10-23 10:29:40 +00:00
Daniel Wagner
189f489e03 documentation patch: add a bit more context to the code snippets in X.L.IndependentScreens 2011-10-11 20:46:19 +00:00
Adam Vogt
c00dd7b51b U.EZConfig allow removing more than one mouse binding. 2011-09-23 12:39:07 +00:00
Norbert Zeh
41d23c1749 Remove X.A.GroupNavigation.currentWindow
This function does the same as X.StackSet.peek and all its uses have been
replaced with X.StackSet.peek.
2011-09-20 08:39:22 +00:00
Adam Vogt
7f70beaf4f Fix typo in NoBorders example code. 2011-08-14 19:53:14 +00:00
Daniel Schoepe
f27e89a2ff Add XF86TouchpadToggle to the list of multimedia keys in X.U.EZConfig 2011-09-17 15:14:19 +00:00
Daniel Wagner
61e991afaa documentation patch to XMonad.Doc.Extending 2011-09-16 20:28:45 +00:00
Brent Yorgey
9e5a712929 fix warnings in X.U.EZConfig 2011-09-08 13:32:46 +00:00
Wirt Wolff
f4fc9fe503 X.A.CycleWS Refactor and add toggleWS' that excludes listed tags 2011-09-07 23:27:30 +00:00
Wirt Wolff
23c2896c6f X.A.FlexibleManipulate: Set expected fixities for Pnt math operators
Restores broken mouseWindow discrete linear and resize to 0.9.1 behavior
2011-09-04 22:12:47 +00:00
Daniel Wagner
2443a962a0 GHC 7 compat
* true error: more modules export foldl/foldl'/foldr, so explicitly use the Data.Foldable one
* -Werror error: transition from Control.OldException to Control.Exception, assuming everything was IOException
2011-07-31 17:08:50 +00:00
Adam Vogt
1364a00c84 Correct H.DynamicLog.dynamicLogXinerama comment. Wuzzeb's patch at issue 466.
Slight changes to the patch to 1. work with haddock, and 2. remove ppOutput
which distracts from the formatting and is covered elsewhere.
2011-07-14 23:17:41 +00:00
Ben Boeckel
a9d1ce1efc ungrab-keyboard-before-action
If an action that requires the keyboard to be grabbed (e.g., launching dmenu),
it is a race when submapping the action as to whether the action will have
access to the keyboard or not. To fix this, the keyboard should be ungrabbed
before executing the action.
2011-05-15 21:03:12 +00:00
Ben Boeckel
9f65044be5 add-movenext-moveprev-bindings
Adds default bindings to GridSelect for the moveNext and movePrev motions.
2011-05-15 19:33:26 +00:00
Tomas Janousek
82dff7f91d X.L.LayoutHints: refresh only if hints are not satisfied 2011-06-15 15:03:33 +00:00
Adam Vogt
758094e4c7 L.Spacing use imported fi 2011-06-12 19:23:39 +00:00
Adam Vogt
4ea488d906 Use a phantom type instead of undefined in L.LayoutBuilderP
This better expresses the idea that the argument to alwaysTrue is just there to
select an instance. Another option could be to do use a fundep, which seems to
be compatible with the two instances so far.

class Predicate p w | p -> w
2011-06-09 05:18:58 +00:00
Adam Vogt
6ec8898a54 Add more L.LayoutBuilderP documentation 2011-06-09 05:09:22 +00:00
Adam Vogt
2ba8788b8e Correct L.LayoutBuilderP module name in haddock. 2011-06-09 04:39:40 +00:00
Ilya Portnov
b2f260e9ea Cleanup in X.L.LayoutBuilderP.
Remove unused datatype declaration and export usefull typeclass.
2011-05-14 13:22:32 +00:00
Adam Vogt
334344b804 Extend script for generating the code which runs tests
Now the number of runs each can be set, and the failures and successes are
summarized in the same way as the core Properties.hs. There is some duplicated
code which could be avoided by modifying Properties.hs.
2011-06-09 04:07:22 +00:00
Adam Vogt
dea9cdea5e Move tests from ManageDocks to tests/
The change to use a newtype for RectC is kind of ugly, but this way instances
are less likely to conflict in the tests.
2011-06-09 04:02:20 +00:00
Adam Vogt
ff41d7dc68 Export X.A.CycleWS.screenBy (issue 439) 2011-06-07 00:20:53 +00:00
Tomas Janousek
d752141be1 X.H.FloatNext: export X.H.ToggleHook.runLogHook
Otherwise the user has to import XMonad.Hooks.ToggleHook as well, which he
didn't have to in earlier versions.
2011-05-28 19:17:00 +00:00
Adam Vogt
cc162bba44 Documentation fix (issue 445)
Daniel's change which broke -Wall (adding an import for haddock only) was
somehow removed. Instead we can just modify the sample code to add the import.
2011-05-27 03:35:21 +00:00
Adam Vogt
c438c17e4d X.A.AppendFile documentation fix.
Forgotten > means haddock complained (and generated incorrect output).
More controversially I reworded a sentence and use do notation.
2011-05-27 03:28:54 +00:00
Ben Boeckel
4377e75bcc add-willhook-function
Adds a function that hooks into whether the hook will be triggered on the next
request.
2011-05-15 19:17:18 +00:00
Ben Boeckel
f4dd8973b1 use-map-in-toggle-hook
Use "Data.Map String (Bool, Bool)" instead of "[(String, (Bool, Bool))]" in
X.H.ToggleHook.
2011-05-15 19:14:18 +00:00
Ilya Portnov
af9e1863eb Extend GridSelect navigation
Add moveNext and movePrev, which move selection to next/previous item.
2011-05-15 15:42:46 +00:00
Ilya Portnov
fa6ce67fba Generalize X.L.AutoMaster modifier
Enable it to work not only with Windows, but with any (Eq) type.
2011-05-14 13:25:49 +00:00
Mats Rauhala
a4da8cd41b Aesthetics on Flexiblemanipulate
Based on Adam Vogts recommendation on the mailing list. I had to give explicit
type signatures to get rid of warnings, but nearly verbatim to his version.
2011-05-06 09:44:31 +00:00
Ilya Portnov
d9c9e0c10e Add new layout combinator: LayoutBuilderP.
LayoutBuilderP is similar to LayoutBuilder (and is based on it), but LayoutBuilderP places windows matching given X.U.WindowProperties.Property (or any other predicate) into one rectangle, instead of fixed number of windows.
2011-05-11 15:40:10 +00:00
Mats Rauhala
2ab79a7c35 Compile with ghc7 2011-05-04 19:24:55 +00:00
Mats Rauhala
8056bb5c2c Action search autocomplete based on whole line
The previous version autocompleted based on words, but when searching from web
sites, it makes more sense to autocomplete based on the entire search.
2011-05-04 21:52:01 +00:00
Daniel Wagner
29cad0672e documentation: tell where to find a few auxiliary functions that might be useful in conjunction with X.A.DynamicWorkspaces 2011-04-15 22:48:46 +00:00
Brandon S Allbery KF8NH
08c4c09fc5 Typo in window-properties.sh
Somewhere between my creating the original version of this script and
adding it to the tree, a backslash got lost.  It appears to have been
lost in the version I put on the wiki, so I suspect a copy-paste
problem at that point.
2011-04-13 05:30:02 +00:00
Brandon S Allbery KF8NH
85d6b79ab9 XMonad.Hooks.FadeWindows: A generalized window fading hook 2011-02-26 00:24:36 +00:00
Brandon S Allbery KF8NH
4bcf636259 Script to simplify getting ManageHook information from a window 2011-02-24 02:49:37 +00:00
Brandon S Allbery KF8NH
864732dbdc XMonad/Hooks/DebugKeyEvents - debug helper to see what keys xmonad sees 2011-02-24 02:36:13 +00:00
Brandon S Allbery KF8NH
521ef9e01d Prevent non-default XUtils windows from being composited 2011-02-24 00:32:24 +00:00
gwern0
8fb4c1e734 XMonad.Hooks.FloatNext: issue #406, make FloatNext use ToggleHook 2011-04-12 01:52:17 +00:00
gwern0
9b4e43e20f issue #406: ben boeckel <mathstuf@gmail.com> +XMonad.Hooks.ToggleHook 2011-04-12 01:51:27 +00:00
Adam Vogt
786613198b Fix xinerama workspace swapping with A.CopyWindow.killAllOtherCopies
Spotted by arlinius in #xmonad, and this only shows up for xinerama setups.
Using an algorithm that scales better with number of workspaces would probably
be shorter too (visiting them in turn, rather than doing random access), but
probably not worth the effort.
2011-03-01 03:37:36 +00:00
gwern0
1844c80978 XMonad.Util.Run: resolve issue #441
See <http://code.google.com/p/xmonad/issues/detail?id=441>

> I have run into programs that fail when run by safeSpawn but succeed with spawn.
> I tracked it down in one (python) and it seems to be due to uninstallSignalHandlers.
> When run by safeSpawn, the program reports errors from wait.

dylan did not supply a patch and his version doesn't match the declared type signature;
since I don't want to break every `safeSpawn` user, I tossed a `>> return ()` in to make
the type right, although I'm troubled at removing the exception functions.
2011-04-11 16:37:40 +00:00
gwern0
7c4358d2d6 AppendFile: additional example of usage 2011-01-26 20:10:18 +00:00
Adam Vogt
ed12889c2c Fix A.Gridselect image links (thanks dobblego) 2011-01-19 23:01:13 +00:00
Adam Vogt
9d3e169fb0 Bump version to 0.10 to help keep the correct contrib/core versions together. 2011-01-15 18:05:53 +00:00
Adam Vogt
0377a9e335 H.ICCCMFocus had atom_WM_TAKE_FOCUS incorrectly removed
It is possible that this atom should be defined in the X11 library, but fix the
build of contrib for now. In any case, this would have to wait for a change and
release of the X11 binding.

rolling back:

Wed Jan  5 22:38:39 EST 2011  Adam Vogt <vogt.adam@gmail.com>
  * Remove accidental atom_WM_TAKE_FOCUS from H.ICCCMFocus
  
  The XMonad module exports this already

    M ./XMonad/Hooks/ICCCMFocus.hs -7 +1
2011-01-06 19:20:52 +00:00
Adam Vogt
fe9fb9c62d Remove accidental atom_WM_TAKE_FOCUS from H.ICCCMFocus
The XMonad module exports this already
2011-01-06 03:38:39 +00:00
haskell
37fc674790 Java swing application focus patch 2011-01-05 03:25:35 +00:00
Brent Yorgey
bbd9761130 fix X.L.Gaps documentation, thanks to Carl Mueller for the report 2010-12-23 01:07:44 +00:00
Adam Vogt
a976d33038 Fix A.OnScreen example code typo 2010-12-12 16:18:50 +00:00
Brent Yorgey
1c50b1aa9a fix up funny unicode whitespace in Fullscreen 2010-12-12 14:22:41 +00:00
Audun Skaugen
02a3b820c9 Add X.L.Fullscreen 2010-11-16 22:16:11 +00:00
quesel
3813d625b6 Close the display correctly after counting the number of screens
This patch adds support for calling countScreens in arbitrary places. Prior to
this patch one would end up with an open display for each call of the
countScreens function with would eventually mess up X. This patch ensures that
the display that is no longer needed is closed after the operation and thus
using the function without side effects.
2010-11-16 08:14:49 +00:00
Adam Vogt
67db59bf73 Compatibility with mtl-1 and mtl-2 2010-11-15 23:26:54 +00:00
Adam Vogt
52d3aa1500 Rename state in A.Gridselect to avoid name shadowing (mtl-2) 2010-11-15 23:22:22 +00:00
Clemens Fruhwirth
cbb20fb3a8 Substring search support for X.A.GridSelect. As keymaps get more complicated to support different styles, the gs_navigate element is fundamentially changed. 2010-11-02 21:12:13 +00:00
Clemens Fruhwirth
e544e09cbb Make substring search case insensitive 2010-10-16 21:29:04 +00:00
Clemens Fruhwirth
3a886e0844 Introduce grayoutAllElements in X.A.GridSelect 2010-10-16 21:25:59 +00:00
Clemens Fruhwirth
27fc66bb2c Add substring filter to td_elementmap 2010-10-16 18:36:44 +00:00
Clemens Fruhwirth
3a35fe3f3d Refactor for ad-hoc element and position matching turning td_elementmap into a function using the new td_availSlot and td_elements fields 2010-10-16 18:35:54 +00:00
Clemens Fruhwirth
06bb702240 Remove nub from diamondLayer in X.A.GridSelect 2010-10-16 18:31:32 +00:00
Clemens Fruhwirth
3cee73fe02 Convert access of td_elementmap from field styled to function call styled in X.A.GridSelect 2010-10-16 16:47:57 +00:00
Clemens Fruhwirth
ffe08858ab Make use of field names when constructing TwoDState in X.A.GridSelect 2010-10-16 16:41:51 +00:00
Adam Vogt
96d2982c60 Pointfree and -XRank2Types don't mix in X.L.Groups.Helpers
It used to work with ghc-6.12 (and earlier?), but ghc-7RC2 type inference
doesn't work with . instead of it's definition.
2010-11-13 02:28:39 +00:00
Adam Vogt
f9d2a6bd7f Restrict dependencies, since mtl-2 is incompatible
A couple removed constructors need to be replaced by the lowercase versions
(ex. State = StateT Identity now). But it isn't clear that mtl-1 should be
dropped.
2010-11-13 02:22:04 +00:00
Adam Vogt
b1ff22411d X.L.TrackFloating docs and help nested layouts
Now TrackFloating remembers focus for the given layout when the other window is
also tiled, but not fed to the given layout: this helps with X.L.IM, among
others.
2010-10-30 17:56:15 +00:00
Norbert Zeh
a73a61302c X.L.Maximize: Make layout forget maximized window when it is closed
The X.L.Maximize layout modifier does not track whether the window it stores as
maximized does still exist.  The X server reuses window IDs.  As a result, I
was able to reproduce the following behaviour (e.g., by opening and closing
xpdf windows): Create a window, maximize it, close it without restoring it to
its normal state.  Open a new window with the same window ID (e.g., an xpdf
window after just closing an xpdf window).  The new window will open maximized,
which is not what one would expect.  This patch addresses this problem,
removing the ID of the maximized window from the layout when the maximized
window is closed.
2010-10-29 22:15:51 +00:00
Adam Vogt
81d338952d Fix bug in L.TrackFloating
Addresses the comment that:

If the focus goes from the floating layer to tiling by deleting a floating
window, it's again the master window that gets focus, not the remembered
window.
2010-10-30 00:06:20 +00:00
Daniel Schoepe
d7005d529a Add X.L.Groups.Helpers to other-modules
Not listing aforementioned module can cause build failures in libaries
that depend on xmonad-contrib.
2010-10-24 19:18:50 +00:00
mathstuf
88380dc1da windowbringer-menu-choice
Add functions to allow users to use a menu other than dmenu and pass arguments
to the menu.
2010-09-05 01:35:22 +00:00
Adam Vogt
25889f2d0c Add X.L.TrackFloating for tiled-floating focus issues (#4) 2010-10-16 16:55:36 +00:00
Daniel Wagner
eec8dc9dcb minor documentation fixes 2010-10-07 01:19:57 +00:00
Daniel Schoepe
3a405285b0 Minor documentation fixes in X.U.ExtensibleState 2010-10-04 12:05:09 +00:00
Adam Vogt
8fa66c829d Clarify the note on -XRank2Types in L.Groups 2010-10-02 02:08:41 +00:00
quentin.moser
44a1889345 Mention X.L.Groups.ModifySpec's rank-2 type in the doc 2010-01-17 11:56:01 +00:00
moserq
4339b7ac00 Orphan my modules 2010-10-01 10:43:00 +00:00
moserq
021245b5fa Split X.L.Groups.Examples
X.L.G.Examples : rowOfColumns and tiled tabs layouts
X.L.G.Helpers : helper actions
X.L.G.Wmii : wmii layout
2010-10-01 10:41:42 +00:00
moserq
ea10cbbbd8 X.L.G.Examples: improve the tabs of tiledTabs 2010-01-20 10:32:40 +00:00
moserq
0b4c57d769 X.L.G.Examples: improve the tabs of wmiiLike 2010-01-20 10:17:46 +00:00
quentin.moser
e3af1c3dfc X.L.Groups: Always keep one group, even if empty. 2010-01-18 02:15:26 +00:00
quentin.moser
8e298ca8b8 Do not duplicate layouts in X.L.Groups
I liked the idea, but it completey messes up Decoration layouts.
2010-01-17 11:47:08 +00:00
Adam Vogt
85973b0550 Add missing module re-export (issue 366) 2010-09-30 00:20:46 +00:00
Tomas Janousek
f8be680472 X.H.ManageDocks: event hook to refresh on new docks 2010-07-06 18:58:34 +00:00
quesel
5fb228cfac This patch adds support for multiple master windows to X.L.Master 2010-05-18 06:05:57 +00:00
Tomas Janousek
adbb52d4f2 X.L.LayoutHints: event hook to refresh on hints change 2010-07-06 18:59:25 +00:00
Adam Vogt
2e30d259b8 Remove last excess definition of `fi' (fromIntegral) 2010-09-13 23:38:50 +00:00
Adam Vogt
d5a5522187 Explain fields added for "XMonad.Layout.ImageButtonDecoration" 2010-09-13 23:27:20 +00:00
Adam Vogt
6458e60812 Adjust X.C.Desktop documentation content.
Correct errors regarding a description of `mappend' for X

Use <+> more often since that's `consistent', and there is no difference since
it's the same as >> when all arguments have the same type (which they do...
otherwise people aren't just combining valid values for that field of the
config).
2010-08-03 14:11:17 +00:00
Jan Vornberger
82147d137c Minimize: Replaced calls to 'sendMessage' (BW.focusDown) and 'windows' with alternative methods
Calling these functions during message handling results in the loss of layout state.
This fixes a number of bugs related to the combination of X.L.Minimize with a decoration.
2010-07-27 22:48:41 +00:00
Jan Vornberger
980a22434b CurrentWorkspaceOnTop: proper reimplementation of XMonad.Operation
Fixes bugs in combination with stateful layouts and floating windows
2010-07-27 19:41:54 +00:00
Justin Bogner
fd99373c39 A hook to handle minimize/restore window manager hints.
XMonad.Hooks.Minimize handles both minimize and restore
messages. Handling restore messages was already done in
RestoreMinimized, which this module intends to replace.
2010-06-16 05:11:24 +00:00
gwern0
bac3e0d658 WindowGo: bulk up 'runOrRaise' doc to point to 'raiseMaybe' for shell scripting 2010-07-12 04:56:32 +00:00
gwern0
9f6bb1a26e WindowGo: fmt & sp 2010-07-12 04:29:15 +00:00
Adam Vogt
129af43738 Note that Simplest works well with BoringWindows 2010-06-22 03:08:50 +00:00
gwern0
3f8e0109cc XMonad.Util.Run: improve linking and rearrange docs 2010-06-20 17:52:15 +00:00
gwern0
eed47b3f81 XMonad.Util.Run: correct broken example 2010-06-20 17:51:58 +00:00
gwern0
7dd1384884 XMonad.Util.Run: fix unicode char 2010-06-20 17:51:40 +00:00
gwern0
f23f8e0bf7 XSelection.hs: update docs w/r/t unicode
see http://code.google.com/p/xmonad/issues/detail?id=348
2010-06-15 00:09:02 +00:00
Khudyakov Alexey
e708caf2ac encode string of bytes not list of chars 2010-06-13 11:33:41 +00:00
gwern0
c6178cacd2 GroupNavigation.hs: clean up imports 2010-06-08 20:38:32 +00:00
gwern0
6472683476 remove decodeInput/encodeOutput
see http://code.google.com/p/xmonad/issues/detail?id=348
they are just synonyms for 2 utf8-string functions, and don't really help
2010-06-14 23:23:00 +00:00
gwern0
955dd48153 Developing: be good to mention hlint in a hacking guide 2010-05-06 16:05:35 +00:00
Norbert Zeh
9300140701 Fix bug in history maintenance of X.A.GroupNavigation
When the focused window was closed without a new window receiving focus, the
closed window was not removed from the history database, making for example
"nextMatch History (return True)" misbehave.  This patch fixes this.
2010-06-04 08:14:31 +00:00
Jan Vornberger
af24766a83 PositionStoreHook: take decoration into account 2010-06-02 22:30:15 +00:00
Jan Vornberger
12cc1dc53e PositionStoreHook: take docks into account 2010-06-02 21:50:48 +00:00
Nicolas Pouillard
316e26fd0c TopicSpace: +reverseLastFocusedTopics 2010-05-20 07:28:44 +00:00
Nicolas Pouillard
c1a3a1c19d TopicSpace: improve the lastFocusedTopic handling
Now the list of last topics is internally kept but
only visually truncated.
2009-12-20 21:28:13 +00:00
Norbert Zeh
142eac2eb3 X.A.GroupNavigation with containers < 0.3.0.0 compatibility
This patch replaces the use of Seq.filter and Seq.breakl with two
functions flt and brkl that do the same.  This is necessary to
keep compatibility with containers < 0.3.0.0 because Seq.filter and
Seq.breakl were introduced only in containers 0.3.0.0.
2010-05-14 22:21:53 +00:00
Norbert Zeh
d01bb24022 New module XMonad.Actions.GroupNavigation
This module adds two related facilities.  The first one allows cycling through
the windows in a window group.  A group is defined as the set of windows for
which a given Boolean Query returns True.  The second one keeps track of the
history of focused windows and allows returning to the most recently focused
window in a given window group before the currently focused window.
2010-05-10 08:14:12 +00:00
Daniel Schoepe
2ee34742ca Add a way to update the modifier in X.L.LayoutModifier
This patch adds the possibility to update the state of a layout modifier when
modifying the underlying layout before it is run(i.e. using modifyLayout). 
The modified state is also passed to the subsequent call of redoLayout, whose 
return takes precedence if both functions return modified states of the layout 
modifier.
2009-08-22 21:39:58 +00:00
Adam Vogt
b21208dad7 Remove trailing whitespace in A.KeyRemap 2010-05-03 15:32:58 +00:00
stettberger
99e5a4393f Adding XMonad.Actions.KeyRemap for mapping single keys
With KeyRemap it is possible to emit different keys to client windows, when 
pressing some key. For example having dvorak layout for typing, but us for 
keybindings.
2010-05-02 15:23:22 +00:00
Adam Vogt
ad5277a189 Move Util.Font to .hs, and enable -XCPP
As the CPP pass was the only feature being used in Font.hsc (no other FFI)
it's better to avoid hsc2hs, if only to make the purpose of the module
clearer from the filename.
2010-04-29 14:07:44 +00:00
Adam Vogt
d310cf5f69 A.Search: Remove unnecessary `do' 2010-04-29 13:47:49 +00:00
Khudyakov Alexey
f7d8eb3fdd Fix escaping of URI 2010-04-23 20:47:07 +00:00
Adam Vogt
ceb24fb8b4 Prompt: handle case of historySize=0 better. 2010-04-21 18:30:06 +00:00
Adam Vogt
36bcb743d6 Rearrange tests. See test/genMain.hs for instructions. 2010-04-19 01:49:46 +00:00
Adam Vogt
c3c06a4567 Use CPP to add to exports for Selective tests (L.LimitWindows) 2010-04-19 01:43:44 +00:00
Adam Vogt
78f13d2acd Use imported `fi' alias for fromIntegral more often.
Also moves `fi' into U.Image to avoid cyclic imports,
though XUtils sill exports that definition.
2010-04-16 21:29:39 +00:00
Adam Vogt
d511ffd01a Note that mouseResizableTileMirrored may be removed. 2010-04-16 16:11:18 +00:00
Adam Vogt
4950c69dbd Structure L.MouseResizableTile documentation. 2010-04-16 16:06:41 +00:00
Tomas Janousek
7129622eb9 X.L.MouseResizableTile: make everything configurable 2010-04-15 21:46:09 +00:00
Tomas Janousek
1e847cb65a X.L.MouseResizableTile: configurable gaps (dragger size and position)
(with the option of putting the draggers over window borders with no gaps at
all)
2010-04-15 21:38:13 +00:00
Adam Vogt
2853dc65c8 Remove unnecessary imports. 2010-04-16 16:02:39 +00:00
gwern0
ddd9674b14 update module imports 2010-04-14 21:19:47 +00:00
Adam Vogt
9372aac28e tests/test_XPrompt can build now. 2010-04-14 20:46:12 +00:00
Adam Vogt
2477f81f73 prettier haddock markup for L.NoBorders 2010-04-05 18:40:20 +00:00
Jan Vornberger
c37cbbadf5 ImageButtonDecoration: new image for menu button 2010-04-02 17:49:10 +00:00
trupill
e125c84616 image_buttons
* Added a XMonad.Util.Image module to manipulate simple images
  and show them into an X drawable
* Added the possibility of using image buttons instead of plain
  text buttons into the title bar
* Added a XMonad.Layout.ImageButtonDecoration as an example of
  how to use the image buttons
2010-03-31 09:38:08 +00:00
Jan Vornberger
4e8285dcbe WindowMenu: own colorizer that works better with Bluetile's new theme 2010-04-02 18:41:19 +00:00
Anders Engstrom
96f3456b96 X.L.Named deprecate and implement using X.L.Renamed
nameTail behaves slightly different if there are whitespace before the first word or the name contains tabs or other such whitespace. But I expect few users are affected since the only usecase where nameTail is actually needed is to remove automatically added prefixes. These prefixes will be removed as they should by the new implementation.
2010-04-01 21:24:03 +00:00
Anders Engstrom
5b045e458d X.L.Minimize remove redundant imports 2010-04-01 20:44:00 +00:00
Adam Vogt
5e274b254e Correct module header. 2010-03-30 18:10:29 +00:00
trupill
b6c5550334 minimize_ewmh 2010-03-30 18:36:16 +00:00
Adam Vogt
086c8c209c Use more monoid instances to clean up U.WorkspaceCompare 2010-02-22 15:17:10 +00:00
Adam Vogt
6215d71600 Note that Groups has redundancies and the interface may change.
Refer to:
http://www.haskell.org/pipermail/xmonad/2010-January/009585.html
2010-03-30 17:59:45 +00:00
Tomas Janousek
f093c11a27 X.H.UrgencyHook: performance fix
cleanupUrgents would update the Map in extensible state 2-times the number of
visible windows, resulting in excessive memory usage and garbage collection.
This seems to make it behave correctly.
2010-03-30 14:13:41 +00:00
quentin.moser
97e68c1bc8 Update my e-mail address 2010-01-17 01:11:09 +00:00
quentin.moser
dd1a8ff05d New module: X.L.Groups.Examples
Utility functions and examples using X.L.Groups.
2010-01-17 01:02:36 +00:00
quentin.moser
10e1e1d4c1 New module: X.L.Groups
The mother of all layout combinators.
2010-01-17 00:53:01 +00:00
quentin.moser
38f1a07042 New module: X.L.ZoomRow
Row layout with individually resizable elements.
2010-01-17 00:39:39 +00:00
quentin.moser
317afc33af New module: X.L.Renamed 2010-01-17 00:26:12 +00:00
quentin.moser
6aeca44187 New module: X.U.Stack
Utility functions for working with Maybe Stacks, including:
  - useful conversions to and from lists
  - insertUp/Down, swapUp/Down, focusUp/Down, etc
  - maps, filters and folds
2010-01-17 00:21:04 +00:00
Daniel Wagner
8705542d1d bugfix: removeKeys should remove all keys in the provided list 2010-03-27 19:25:41 +00:00
Jurgen Doser
208d920b6b fixed argument order to isPrefixOf in a couple of places in X.A.Search
In some places, ((!>), prefixAware, and one place in the documentation),
isPrefixOf was used with wrong argument order.  In particular, this made
combining search engines not work as advertised, for example, the predefined
search engine "multi".
2010-03-16 12:20:10 +00:00
Brent Yorgey
5485ba57ac X.P.Ssh: add entries from .ssh/config to ssh prompt completion 2009-12-29 17:13:46 +00:00
Tomas Janousek
21526d1532 X.H.DynamicLog: let the user of xmonadPropLog choose property name 2010-03-19 21:46:31 +00:00
gwern0
520b9ccf6e Replace.hs: rm trailing whitespace 2010-03-14 21:01:09 +00:00
gwern0
e28cd8cd6e Workspace.hs: rm trailing whitespace 2010-03-14 21:01:01 +00:00
gwern0
98a320cbb5 Layout.hs: rm trailing whitespace 2010-03-14 21:00:54 +00:00
gwern0
3b258409db Directory.hs: rm trailing whitespace 2010-03-14 21:00:47 +00:00
gwern0
cf0c3194de MessageControl: rm trailing whitespace 2010-03-14 21:00:38 +00:00
gwern0
796b775d5c LimitWindows.hs: rm trailing whitespace 2010-03-14 21:00:30 +00:00
gwern0
0d3293ce52 LayoutCombinators.hs: rm trailing whitespace 2010-03-14 21:00:21 +00:00
gwern0
ce5f81fe16 DecorationAddons.hs: rm trailing whitespace 2010-03-14 21:00:12 +00:00
gwern0
b267617eee Column.hs: rm whitespace 2010-03-14 21:00:01 +00:00
gwern0
c3796a9cb1 DynamicWorkspaces.hs: rm whitespace 2010-03-14 20:59:51 +00:00
Max Rabkin
ee38a0328b Fix bugs with nested drawers in X.L.Drawer
There were two bugs:
1. The layout modifier assumed the rect's x was zero.
2. The layout modifier assumed that the stackset's focus actually had focus.
2010-03-10 17:01:59 +00:00
Adam Vogt
8cc604c4ad Correct L.Drawer haddock markup and re-export required module. 2010-03-08 22:52:58 +00:00
Max Rabkin
8f58fb4c2f Added X.L.Drawer
X.L.Drawer provides a layout modifier for retracting windows which roll down
(like the Quake console) when they gain focus.
2010-03-08 21:27:52 +00:00
Anders Engstrom
0c9619e5cd X.U.WorkspaceCompare xinerama compare with physical order
Like the old xinerama workspace comparison, but order by physical location just like X.A.PhysicalScreens. Useful if using xinerama sort for statusbar together with physicalscreens.
2010-03-08 11:54:02 +00:00
Anders Engstrom
5e6c03c2ca X.U.Dmenu helpers to run dmenu with arguments 2010-03-08 11:50:22 +00:00
Anders Engstrom
abebe3085c X.L.LayoutScreens split current screen
This patch will allow the user to split the currently focused screen instead of all screens together. This is usefull for multiscreen users who have functioning xinerama, but wish to split one of the screens.
2010-03-08 11:43:18 +00:00
Anders Engstrom
649bb08374 X.A.PhysicalScreens cleaning and allow cycling
Remove redundant import to supress warning, did some refactoring to use xmonad internal things to find screens instead of using X11-stuff. Also added ability to cycle between screens in physical order.
2010-03-08 11:37:04 +00:00
Adam Vogt
16fce733c0 Use imported 'fi' in H.ScreenCorners 2010-02-22 15:06:33 +00:00
Nils Schweinsberg
c8cd7df334 X.H.ScreenCorners typos 2010-02-22 11:51:42 +00:00
Nils Schweinsberg
c057c24f70 X.H.ScreenCorners rewritten to use InputOnly windows instead of waiting for MotionEvents on the root window 2010-02-22 11:24:59 +00:00
Nils Schweinsberg
4a138012ba [patch] X.H.ScreenCorners: move the mouse cursor to avoid loops 2010-02-21 23:15:50 +00:00
Daniel Schoepe
aa34798b99 Prevent possible pattern match failure in X.A.UpdateFocus 2010-02-21 23:47:35 +00:00
Nils Schweinsberg
9fdd63bd8b New extension: XMonad.Hooks.ScreenCorners 2010-02-21 23:02:59 +00:00
daniel
bd47cc5d3e documentation for marshallPP 2010-02-15 00:07:31 +00:00
Daniel Wagner
e44bab10e7 DynamicLog support for IndependentScreens 2010-01-04 05:42:51 +00:00
Daniel Wagner
0909472d54 minor style changes 2009-12-28 17:30:16 +00:00
gwern0
38228517eb XMonad.Prompt: remove white border from greenXPConfig 2010-02-11 16:36:41 +00:00
Daniel Schoepe
de9a2e8adb Fixed reversed history searching direction in X.P.history(Up|Down)Matching 2010-02-08 16:29:01 +00:00
Adam Vogt
52a2eba7e6 Compatibility for rename of XMonad.numlockMask 2010-01-24 20:19:55 +00:00
Adam Vogt
aa8290b60d Use extensible-exceptions to allow base-3 or base-4 2010-01-24 20:33:24 +00:00
Brent Yorgey
b435a6a519 suppress some warnings under ghc 6.12.1 and clean up redundant imports to get rid of some others. 2010-01-12 17:25:07 +00:00
Daniel Schoepe
96792aa4ab Corrected documentation in X.Prompt 2010-02-01 20:45:22 +00:00
Daniel Schoepe
59667f39ab Use Stack instead of list in X.Prompt.history*Matching 2010-02-01 20:28:39 +00:00
Jan Vornberger
9b76a85c74 BluetileConfig: Fullscreen tweaks and border color change 2010-01-31 23:33:47 +00:00
Wirt Wolff
57c00ea498 A.CycleWindows replace partial rotUp and rotDown with safer versions
Rather than throw exceptions, handle null and singleton lists, i.e.
f [] gives [] and f [x] gives [x].
2010-01-23 23:19:12 +00:00
Adam Vogt
a9f2b82337 Use <+> instead of explicit M.union to merge keybindings in X.C.* 2010-01-24 20:21:36 +00:00
Adam Vogt
0b4d34fa7e Fix incorrect import suggestion in L.Tabbed (issue 362) 2010-01-21 18:25:01 +00:00
Adam Vogt
685cc6931f Swap window ordering in L.Accordion (closes Issue 358). Thanks rsaarelm.
This change keeps windows in the same ordering when focus is changed.
2010-01-21 15:43:44 +00:00
Jens Petersen
7b0fd3ba3a use restart to restart xmonad (no longer bluetile) 2010-01-16 10:59:35 +00:00
Tomas Janousek
0ce76fd152 X.L.Decoration: avoid flicker by not showing decowins without rectangles
These would be hidden by updateDecos immediately after being shown. This
caused flicker with simpleTabbed and only one window on a workspace.
2010-01-16 11:20:54 +00:00
Daniel Schoepe
c0d5c4a278 Add a way to cycle only through matching history entries in X.Prompt
This patch adds a way go up through X.Prompt's history using
only those entries that start with the current input, similar
to zsh's `history-search-backward'.
2010-01-13 23:30:36 +00:00
Adam Vogt
882ddc25f4 Style changes in L.Minimize 2010-01-04 14:44:48 +00:00
konstantin.sobolev
6c452e066e minimize_floating
Adds floating windows support to X.L.Minimize
2009-12-30 07:01:05 +00:00
Adam Vogt
6fc1530fe9 Use more imported cursor constants. 2009-12-30 22:09:27 +00:00
Brent Yorgey
1eb50b2028 import new contrib module, X.A.DynamicWorkspaceOrder 2009-12-30 19:23:50 +00:00
Brent Yorgey
f25c348669 X.A.CycleWS: export generalized 'doTo' function for performing an action on a workspace relative to the current one 2009-12-30 19:19:53 +00:00
Brent Yorgey
2c4e5f5d53 new contrib module, X.A.DynamicWorkspaceGroups, for managing groups of workspaces on multi-head setups 2009-12-29 16:57:02 +00:00
Brent Yorgey
d384a98ccb new contrib module from Tomas Janousek, X.A.WorkspaceNames 2009-12-29 16:39:15 +00:00
Tim Horton
4e2e0ef0ba X.P.Shell, filter empty string from PATH
doesDirectoryExist returns True if given an empty string using ghc <= 6.10.4.
This causes getDirectoryContents to raise an exception and X.P.Shell does not
render. This is only an issue if you have an empty string in your PATH.

Using ghc == 6.12.1, doesDirectoryExist returns False given an empty string, so
this should not be an issue in the future.
2009-12-24 03:32:17 +00:00
Brent Yorgey
997fdef24b small tweak to battery logger 2009-12-27 08:56:41 +00:00
Adam Vogt
2f0e880ccd Use imported xC_bottom_right_corner in A.MouseResize 2009-12-27 23:37:05 +00:00
Tomas Janousek
311994f9ef X.A.MouseResize: assign an appropriate cursor for the resizing inpuwin 2009-12-27 21:21:40 +00:00
Spencer Janssen
12c791d02f Fix the createSession bug in spawnPipe
Both the new XMonad.Core.xfork function and spawnPipe call createSession, calling
this function twice results in an error.
2009-12-27 00:35:01 +00:00
Jan Vornberger
adb7144a98 Let the core know about MouseResizableTile's draggers, so they are stacked correctly 2009-12-23 14:54:28 +00:00
Spencer Janssen
e8c0f39fd5 Update all uses of forkProcess to xfork 2009-12-23 06:45:58 +00:00
Jan Vornberger
98fe292e9f Make X.L.Minimize explicitly mark minimized windows as boring 2009-12-22 21:45:29 +00:00
intrigeri
d32efe75e4 Actions/Search: added openstreetmap 2009-12-22 11:45:45 +00:00
Mike Lundy
efbcf16cee Add a search predicate option to XMonad.Prompt 2009-12-21 02:54:08 +00:00
Adam Vogt
05ed62a455 In D.Extending note how <+> can be used with keybindings. 2009-12-20 19:07:39 +00:00
Tomas Janousek
16181ce6e7 Fix MultiToggle crashes with decorated layouts
The problem was that certain layouts keep their "world" state in their value,
which was thrown away and forgotten after ReleaseResources during toggle.

In particular, decorated layouts store some X11 handles in them and
allocate/deallocate it as appropriate. If any modification to their state is
ignored, they may try to deallocate already deallocated memory, which results
in a crash somewhere inside Xlib.

This patch makes Transformers reversible so that nothing is ever ignored. As a
side effect, layout transformers now do receive messages and messages for the
base layout do not need the undo/reapply cycle -- we just pass messages to the
current transformed layout and unapply the transformer when needed.
(This, however, doesn't mean that the base layout is not asked to release
resources on a transformer change -- we still need the transformer to release
its resources and there's no way to do this without asking the base layout as
well.)
2009-12-20 00:47:33 +00:00
Adam Vogt
d616e92dba Golf / style change in U.ExtensibleState 2009-12-08 01:05:06 +00:00
Adam Vogt
5ec429ee6f Style changes in EwmhDesktops 2009-12-19 00:38:24 +00:00
audunskaugen
75775178fd Add support for fullscreen through the _NET_WM_STATE protocol
This patch adds support for applications using the
gtk_window_fullscreen function, and other applications using
_NET_WM_STATE for the same purpose.
2009-12-14 13:51:19 +00:00
167 changed files with 6379 additions and 1064 deletions

View File

@@ -26,7 +26,6 @@ module XMonad.Actions.CopyWindow (
import XMonad
import Control.Arrow ((&&&))
import Control.Monad
import qualified Data.List as L
import XMonad.Actions.WindowGo
@@ -146,7 +145,9 @@ killAllOtherCopies = do ss <- gets windowset
delFromAllButCurrent w ss = foldr ($) ss $
map (delWinFromWorkspace w . W.tag) $
W.hidden ss ++ map W.workspace (W.visible ss)
delWinFromWorkspace w wid = W.modify Nothing (W.filter (/= w)) . W.view wid
delWinFromWorkspace w wid = viewing wid $ W.modify Nothing (W.filter (/= w))
viewing wis f ss = W.view (W.currentTag ss) $ f $ W.view wis ss
-- | A list of hidden workspaces containing a copy of the focused window.
wsContainingCopies :: X [WorkspaceId]

View File

@@ -46,6 +46,7 @@ module XMonad.Actions.CycleWS (
-- * Toggling the previous workspace
-- $toggling
, toggleWS
, toggleWS'
, toggleOrView
-- * Moving between screens (xinerama)
@@ -65,6 +66,7 @@ module XMonad.Actions.CycleWS (
, shiftTo
, moveTo
, doTo
-- * The mother-combinator
@@ -72,6 +74,8 @@ module XMonad.Actions.CycleWS (
, toggleOrDoSkip
, skipTags
, screenBy
) where
import Control.Monad ( unless )
@@ -147,9 +151,16 @@ shiftToPrev = shiftBy (-1)
-- | Toggle to the workspace displayed previously.
toggleWS :: X ()
toggleWS = do
hs <- gets (hidden . windowset)
unless (null hs) (windows . view . tag $ head hs)
toggleWS = toggleWS' []
-- | Toggle to the previous workspace while excluding some workspaces.
--
-- > -- Ignore the scratchpad workspace while toggling:
-- > ("M-b", toggleWS' ["NSP"])
toggleWS' :: [WorkspaceId] -> X ()
toggleWS' skips = do
hs' <- cleanHiddens skips
unless (null hs') (windows . view . tag $ head hs')
-- | 'XMonad.StackSet.greedyView' a workspace, or if already there, view
-- the previously displayed workspace ala weechat. Change @greedyView@ to
@@ -159,9 +170,8 @@ toggleWS = do
toggleOrView :: WorkspaceId -> X ()
toggleOrView = toggleOrDoSkip [] greedyView
-- | Allows ignoring listed workspace tags (such as scratchpad's \"NSP\") while
-- finding the previously displayed workspace, or choice of different actions,
-- like view, shift, etc. For example:
-- | Allows ignoring listed workspace tags (such as scratchpad's \"NSP\"), and
-- running other actions such as view, shift, etc. For example:
--
-- > import qualified XMonad.StackSet as W
-- > import XMonad.Actions.CycleWS
@@ -174,9 +184,9 @@ toggleOrView = toggleOrDoSkip [] greedyView
toggleOrDoSkip :: [WorkspaceId] -> (WorkspaceId -> WindowSet -> WindowSet)
-> WorkspaceId -> X ()
toggleOrDoSkip skips f toWS = do
ws <- gets windowset
let hs' = hidden ws `skipTags` skips
if toWS == (tag . workspace $ current ws)
hs' <- cleanHiddens skips
cur <- gets (currentTag . windowset)
if toWS == cur
then unless (null hs') (windows . f . tag $ head hs')
else windows (f toWS)
@@ -185,6 +195,9 @@ toggleOrDoSkip skips f toWS = do
skipTags :: (Eq i) => [Workspace i l a] -> [i] -> [Workspace i l a]
skipTags wss ids = filter ((`notElem` ids) . tag) wss
cleanHiddens :: [WorkspaceId] -> X [WindowSpace]
cleanHiddens skips = gets $ (flip skipTags) skips . hidden . windowset
switchWorkspace :: Int -> X ()
switchWorkspace d = wsBy d >>= windows . greedyView
@@ -244,12 +257,17 @@ wsTypeToPred (WSIs p) = p
-- | View the next workspace in the given direction that satisfies
-- the given condition.
moveTo :: Direction1D -> WSType -> X ()
moveTo dir t = findWorkspace getSortByIndex dir t 1 >>= windows . greedyView
moveTo dir t = doTo dir t getSortByIndex (windows . greedyView)
-- | Move the currently focused window to the next workspace in the
-- given direction that satisfies the given condition.
shiftTo :: Direction1D -> WSType -> X ()
shiftTo dir t = findWorkspace getSortByIndex dir t 1 >>= windows . shift
shiftTo dir t = doTo dir t getSortByIndex (windows . shift)
-- | Using the given sort, find the next workspace in the given
-- direction of the given type, and perform the given action on it.
doTo :: Direction1D -> WSType -> X WorkspaceSort -> (WorkspaceId -> X ()) -> X ()
doTo dir t srt act = findWorkspace srt dir t 1 >>= act
-- | Given a function @s@ to sort workspaces, a direction @dir@, a
-- predicate @p@ on workspaces, and an integer @n@, find the tag of
@@ -307,6 +325,17 @@ switchScreen d = do s <- screenBy d
Nothing -> return ()
Just ws -> windows (view ws)
{- | Get the 'ScreenId' /d/ places over. Example usage is a variation of the
the default screen keybindings:
> -- mod-{w,e}, Switch to previous/next Xinerama screen
> -- mod-shift-{w,e}, Move client to previous/next Xinerama screen
> --
> [((m .|. modm, key), sc >>= screenWorkspace >>= flip whenJust (windows . f))
> | (key, sc) <- zip [xK_w, xK_e] [(screenBy (-1)),(screenBy 1)]
> , (f, m) <- [(W.view, 0), (W.shift, shiftMask)]]
-}
screenBy :: Int -> X (ScreenId)
screenBy d = do ws <- gets windowset
--let ss = sortBy screen (screens ws)

View File

@@ -226,8 +226,10 @@ rotUnfocused' f (W.Stack t ls rs) = W.Stack t (reverse revls') rs' -- otherwis
(revls',rs') = splitAt (length ls) (f $ master:revls ++ rs)
-- $generic
-- Generic list rotations
-- Generic list rotations such that @rotUp [1..4]@ is equivalent to
-- @[2,3,4,1]@ and @rotDown [1..4]@ to @[4,1,2,3]@. They both are
-- @id@ for null or singleton lists.
rotUp :: [a] -> [a]
rotUp l = tail l ++ [head l]
rotUp l = drop 1 l ++ take 1 l
rotDown :: [a] -> [a]
rotDown l = last l : init l
rotDown = reverse . rotUp . reverse

View File

@@ -0,0 +1,139 @@
{-# LANGUAGE DeriveDataTypeable #-}
-----------------------------------------------------------------------------
-- |
-- Module : XMonad.Actions.DynamicWorkspaceGroups
-- Copyright : (c) Brent Yorgey 2009
-- License : BSD-style (see LICENSE)
--
-- Maintainer : <byorgey@gmail.com>
-- Stability : experimental
-- Portability : unportable
--
-- Dynamically manage \"workspace groups\", sets of workspaces being
-- used together for some common task or purpose, to allow switching
-- between workspace groups in a single action. Note that this only
-- makes sense for multi-head setups.
--
-----------------------------------------------------------------------------
module XMonad.Actions.DynamicWorkspaceGroups
( -- * Usage
-- $usage
WSGroupId
, addWSGroup
, addCurrentWSGroup
, forgetWSGroup
, viewWSGroup
, promptWSGroupView
, promptWSGroupAdd
, promptWSGroupForget
, WSGPrompt
) where
import Data.List (find)
import Control.Arrow ((&&&))
import qualified Data.Map as M
import XMonad
import qualified XMonad.StackSet as W
import XMonad.Prompt
import qualified XMonad.Util.ExtensibleState as XS
-- $usage
-- You can use this module by importing it into your ~\/.xmonad\/xmonad.hs file:
--
-- > import XMonad.Actions.DynamicWorkspaceGroups
--
-- Then add keybindings like the following (this example uses
-- "XMonad.Util.EZConfig"-style keybindings, but this is not necessary):
--
-- > , ("M-y n", promptWSGroupAdd myXPConfig "Name this group: ")
-- > , ("M-y g", promptWSGroupView myXPConfig "Go to group: ")
-- > , ("M-y d", promptWSGroupForget myXPConfig "Forget group: ")
--
type WSGroup = [(ScreenId,WorkspaceId)]
type WSGroupId = String
data WSGroupStorage = WSG { unWSG :: M.Map WSGroupId WSGroup }
deriving (Typeable, Read, Show)
withWSG :: (M.Map WSGroupId WSGroup -> M.Map WSGroupId WSGroup) -> WSGroupStorage -> WSGroupStorage
withWSG f = WSG . f . unWSG
instance ExtensionClass WSGroupStorage where
initialValue = WSG $ M.empty
extensionType = PersistentExtension
-- | Add a new workspace group with the given name.
addWSGroup :: WSGroupId -> [WorkspaceId] -> X ()
addWSGroup name wids = withWindowSet $ \w -> do
let wss = map ((W.tag . W.workspace) &&& W.screen) $ W.screens w
wmap = mapM (strength . (flip lookup wss &&& id)) wids
case wmap of
Just ps -> XS.modify . withWSG . M.insert name $ ps
Nothing -> return ()
where strength (ma, b) = ma >>= \a -> return (a,b)
-- | Give a name to the current workspace group.
addCurrentWSGroup :: WSGroupId -> X ()
addCurrentWSGroup name = withWindowSet $ \w ->
addWSGroup name $ map (W.tag . W.workspace) (W.current w : W.visible w)
-- | Delete the named workspace group from the list of workspace
-- groups. Note that this has no effect on the workspaces involved;
-- it simply forgets the given name.
forgetWSGroup :: WSGroupId -> X ()
forgetWSGroup = XS.modify . withWSG . M.delete
-- | View the workspace group with the given name.
viewWSGroup :: WSGroupId -> X ()
viewWSGroup name = do
WSG m <- XS.get
case M.lookup name m of
Just grp -> mapM_ (uncurry viewWS) grp
Nothing -> return ()
-- | View the given workspace on the given screen.
viewWS :: ScreenId -> WorkspaceId -> X ()
viewWS sid wid = do
mw <- findScreenWS sid
case mw of
Just w -> do
windows $ W.view w
windows $ W.greedyView wid
Nothing -> return ()
-- | Find the workspace which is currently on the given screen.
findScreenWS :: ScreenId -> X (Maybe WorkspaceId)
findScreenWS sid = withWindowSet $
return . fmap (W.tag . W.workspace) . find ((==sid) . W.screen) . W.screens
data WSGPrompt = WSGPrompt String
instance XPrompt WSGPrompt where
showXPrompt (WSGPrompt s) = s
-- | Prompt for a workspace group to view.
promptWSGroupView :: XPConfig -> String -> X ()
promptWSGroupView xp s = do
gs <- fmap (M.keys . unWSG) XS.get
mkXPrompt (WSGPrompt s) xp (mkComplFunFromList' gs) viewWSGroup
-- | Prompt for a name for the current workspace group.
promptWSGroupAdd :: XPConfig -> String -> X ()
promptWSGroupAdd xp s =
mkXPrompt (WSGPrompt s) xp (const $ return []) addCurrentWSGroup
-- | Prompt for a workspace group to forget.
promptWSGroupForget :: XPConfig -> String -> X ()
promptWSGroupForget xp s = do
gs <- fmap (M.keys . unWSG) XS.get
mkXPrompt (WSGPrompt s) xp (mkComplFunFromList' gs) forgetWSGroup

View File

@@ -0,0 +1,165 @@
{-# LANGUAGE DeriveDataTypeable #-}
-----------------------------------------------------------------------------
-- |
-- Module : XMonad.Actions.DynamicWorkspaceOrder
-- Copyright : (c) Brent Yorgey 2009
-- License : BSD-style (see LICENSE)
--
-- Maintainer : <byorgey@gmail.com>
-- Stability : experimental
-- Portability : unportable
--
-- Remember a dynamically updateable ordering on workspaces, together
-- with tools for using this ordering with "XMonad.Actions.CycleWS"
-- and "XMonad.Hooks.DynamicLog".
--
-----------------------------------------------------------------------------
module XMonad.Actions.DynamicWorkspaceOrder
( -- * Usage
-- $usage
getWsCompareByOrder
, getSortByOrder
, swapWith
, moveTo
, moveToGreedy
, shiftTo
) where
import XMonad
import qualified XMonad.StackSet as W
import qualified XMonad.Util.ExtensibleState as XS
import XMonad.Util.WorkspaceCompare (WorkspaceCompare, WorkspaceSort, mkWsSort)
import XMonad.Actions.CycleWS (findWorkspace, WSType(..), Direction1D(..), doTo)
import qualified Data.Map as M
import qualified Data.Set as S
import Data.Maybe (fromJust, fromMaybe)
import Data.Ord (comparing)
-- $usage
-- You can use this module by importing it into your ~\/.xmonad\/xmonad.hs file:
--
-- > import qualified XMonad.Actions.DynamicWorkspaceOrder as DO
--
-- Then add keybindings to swap the order of workspaces (these
-- examples use "XMonad.Util.EZConfig" emacs-style keybindings):
--
-- > , ("M-C-<R>", DO.swapWith Next NonEmptyWS)
-- > , ("M-C-<L>", DO.swapWith Prev NonEmptyWS)
--
-- See "XMonad.Actions.CycleWS" for information on the possible
-- arguments to 'swapWith'.
--
-- However, by itself this will do nothing; 'swapWith' does not change
-- the actual workspaces in any way. It simply keeps track of an
-- auxiliary ordering on workspaces. Anything which cares about the
-- order of workspaces must be updated to use the auxiliary ordering.
--
-- To change the order in which workspaces are displayed by
-- "XMonad.Hooks.DynamicLog", use 'getSortByOrder' in your
-- 'XMonad.Hooks.DynamicLog.ppSort' field, for example:
--
-- > ... dynamicLogWithPP $ byorgeyPP {
-- > ...
-- > , ppSort = DO.getSortByOrder
-- > ...
-- > }
--
-- To use workspace cycling commands like those from
-- "XMonad.Actions.CycleWS", use the versions of 'moveTo',
-- 'moveToGreedy', and 'shiftTo' exported by this module. For example:
--
-- > , ("M-S-<R>", DO.shiftTo Next HiddenNonEmptyWS)
-- > , ("M-S-<L>", DO.shiftTo Prev HiddenNonEmptyWS)
-- > , ("M-<R>", DO.moveTo Next HiddenNonEmptyWS)
-- > , ("M-<L>", DO.moveTo Prev HiddenNonEmptyWS)
--
-- For slight variations on these, use the source for examples and
-- tweak as desired.
-- | Extensible state storage for the workspace order.
data WSOrderStorage = WSO { unWSO :: Maybe (M.Map WorkspaceId Int) }
deriving (Typeable, Read, Show)
instance ExtensionClass WSOrderStorage where
initialValue = WSO Nothing
extensionType = PersistentExtension
-- | Lift a Map function to a function on WSOrderStorage.
withWSO :: (M.Map WorkspaceId Int -> M.Map WorkspaceId Int)
-> (WSOrderStorage -> WSOrderStorage)
withWSO f = WSO . fmap f . unWSO
-- | Update the ordering storage: initialize if it doesn't yet exist;
-- add newly created workspaces at the end as necessary.
updateOrder :: X ()
updateOrder = do
WSO mm <- XS.get
case mm of
Nothing -> do
-- initialize using ordering of workspaces from the user's config
ws <- asks (workspaces . config)
XS.put . WSO . Just . M.fromList $ zip ws [0..]
Just m -> do
-- check for new workspaces and add them at the end
curWs <- gets (S.fromList . map W.tag . W.workspaces . windowset)
let mappedWs = M.keysSet m
newWs = curWs `S.difference` mappedWs
nextIndex = 1 + maximum (-1 : M.elems m)
newWsIxs = zip (S.toAscList newWs) [nextIndex..]
XS.modify . withWSO . M.union . M.fromList $ newWsIxs
-- | A comparison function which orders workspaces according to the
-- stored dynamic ordering.
getWsCompareByOrder :: X WorkspaceCompare
getWsCompareByOrder = do
updateOrder
-- after the call to updateOrder we are guaranteed that the dynamic
-- workspace order is initialized and contains all existing
-- workspaces.
WSO (Just m) <- XS.get
return $ comparing (fromMaybe 1000 . flip M.lookup m)
-- | Sort workspaces according to the stored dynamic ordering.
getSortByOrder :: X WorkspaceSort
getSortByOrder = mkWsSort getWsCompareByOrder
-- | Swap the current workspace with another workspace in the stored
-- dynamic order.
swapWith :: Direction1D -> WSType -> X ()
swapWith dir which = findWorkspace getSortByOrder dir which 1 >>= swapWithCurrent
-- | Swap the given workspace with the current one.
swapWithCurrent :: WorkspaceId -> X ()
swapWithCurrent w = do
cur <- gets (W.currentTag . windowset)
swapOrder w cur
-- | Swap the two given workspaces in the dynamic order.
swapOrder :: WorkspaceId -> WorkspaceId -> X ()
swapOrder w1 w2 = do
io $ print (w1,w2)
WSO (Just m) <- XS.get
let [i1,i2] = map (fromJust . flip M.lookup m) [w1,w2]
XS.modify (withWSO (M.insert w1 i2 . M.insert w2 i1))
windows id -- force a status bar update
-- | View the next workspace of the given type in the given direction,
-- where \"next\" is determined using the dynamic workspace order.
moveTo :: Direction1D -> WSType -> X ()
moveTo dir t = doTo dir t getSortByOrder (windows . W.view)
-- | Same as 'moveTo', but using 'greedyView' instead of 'view'.
moveToGreedy :: Direction1D -> WSType -> X ()
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)

View File

@@ -28,8 +28,8 @@ module XMonad.Actions.DynamicWorkspaces (
import XMonad hiding (workspaces)
import XMonad.StackSet hiding (filter, modify, delete)
import XMonad.Prompt.Workspace
import XMonad.Prompt ( XPConfig, mkXPrompt, XPrompt(..) )
import XMonad.Prompt.Workspace ( Wor(Wor), workspacePrompt )
import XMonad.Prompt ( XPConfig, mkXPrompt )
import XMonad.Util.WorkspaceCompare ( getSortByIndex )
import Data.List (find)
import Data.Maybe (isNothing)
@@ -39,6 +39,7 @@ import Control.Monad (when)
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@ file:
--
-- > import XMonad.Actions.DynamicWorkspaces
-- > import XMonad.Actions.CopyWindow(copy)
--
-- Then add keybindings like the following:
--
@@ -56,14 +57,10 @@ import Control.Monad (when)
-- > zip (zip (repeat (modm .|. shiftMask)) [xK_1..xK_9]) (map (withNthWorkspace W.shift) [0..])
--
-- For detailed instructions on editing your key bindings, see
-- "XMonad.Doc.Extending#Editing_key_bindings".
-- "XMonad.Doc.Extending#Editing_key_bindings". See also the documentation for
-- "XMonad.Actions.CopyWindow", 'windows', 'shift', and 'defaultXPConfig'.
data Wor = Wor String
instance XPrompt Wor where
showXPrompt (Wor x) = x
mkCompl :: [String] -> String -> IO [String]
mkCompl l s = return $ filter (\x -> take (length s) x == s) l
@@ -153,7 +150,7 @@ removeEmptyWorkspaceAfter :: X () -> X ()
removeEmptyWorkspaceAfter = removeEmptyWorkspaceAfterExcept []
-- | Like 'removeEmptyWorkspaceAfter' but use a list of sticky workspaces,
-- whose entries will never be removed.
-- whose entries will never be removed.
removeEmptyWorkspaceAfterExcept :: [String] -> X () -> X ()
removeEmptyWorkspaceAfterExcept sticky f = do
before <- gets (currentTag . windowset)

View File

@@ -23,6 +23,8 @@ module XMonad.Actions.FlexibleManipulate (
) where
import XMonad
import qualified Prelude as P
import Prelude (($), (.), fst, snd, uncurry, const, id, Ord(..), Monad(..), fromIntegral, Double, Integer, map, round, otherwise)
-- $usage
-- First, add this import to your @~\/.xmonad\/xmonad.hs@:
@@ -84,7 +86,7 @@ mouseWindow f w = whenX (isClient w) $ withDisplay $ \d -> do
let uv = (pointer - wpos) / wsize
fc = mapP f uv
mul = mapP (\x -> 2 - 2 * abs(x - 0.5)) fc --Fudge factors: interpolation between 1 when on edge, 2 in middle
mul = mapP (\x -> 2 P.- 2 P.* P.abs(x P.- 0.5)) fc --Fudge factors: interpolation between 1 when on edge, 2 in middle
atl = ((1, 1) - fc) * mul
abr = fc * mul
mouseDrag (\ex ey -> io $ do
@@ -121,14 +123,13 @@ zipP f (ax,ay) (bx,by) = (f ax bx, f ay by)
minP :: Ord a => (a,a) -> (a,a) -> (a,a)
minP = zipP min
instance Num Pnt where
(+) = zipP (+)
(-) = zipP (-)
(*) = zipP (*)
abs = mapP abs
signum = mapP signum
fromInteger = const undefined
infixl 6 +, -
infixl 7 *, /
(+), (-), (*) :: (P.Num a) => (a,a) -> (a,a) -> (a,a)
(+) = zipP (P.+)
(-) = zipP (P.-)
(*) = zipP (P.*)
(/) :: (P.Fractional a) => (a,a) -> (a,a) -> (a,a)
(/) = zipP (P./)
instance Fractional Pnt where
fromRational = const undefined
recip = mapP recip

View File

@@ -20,6 +20,7 @@ module XMonad.Actions.FlexibleResize (
) where
import XMonad
import XMonad.Util.XUtils (fi)
import Foreign.C.Types
-- $usage
@@ -76,6 +77,3 @@ mouseResizeEdgeWindow edge w = whenX (isClient w) $ withDisplay $ \d -> do
Just True -> (0, (fi k + fi p -).fi, (fi k + fi p -).fi)
Nothing -> (k `div` 2, const p, const $ fi k)
Just False -> (k, const p, subtract (fi p) . fi)
fi :: (Num b, Integral a) => a -> b
fi = fromIntegral

View File

@@ -17,7 +17,9 @@ module XMonad.Actions.FloatKeys (
keysMoveWindow,
keysMoveWindowTo,
keysResizeWindow,
keysAbsResizeWindow) where
keysAbsResizeWindow,
P, G,
) where
import XMonad

View File

@@ -28,7 +28,6 @@ module XMonad.Actions.GridSelect (
-- * Configuration
GSConfig(..),
defaultGSConfig,
NavigateMap,
TwoDPosition,
buildDefaultGSConfig,
@@ -46,13 +45,35 @@ module XMonad.Actions.GridSelect (
HasColorizer(defaultColorizer),
fromClassName,
stringColorizer,
colorRangeFromClassName
colorRangeFromClassName,
-- * Navigation Mode assembly
TwoD,
makeXEventhandler,
shadowWithKeymap,
-- * Built-in Navigation Mode
defaultNavigation,
substringSearch,
navNSearch,
-- * Navigation Components
setPos,
move,
moveNext, movePrev,
select,
cancel,
transformSearchString,
-- * Screenshots
-- $screenshots
-- * Types
TwoDState,
) where
import Data.Maybe
import Data.Bits
import Data.Char
import Control.Applicative
import Control.Monad.State
import Control.Arrow
@@ -118,45 +139,48 @@ import Data.Word (Word8)
-- $keybindings
--
-- Adding more keybindings for gridselect to listen to is similar:
-- You can build you own navigation mode and submodes by combining the
-- exported action ingredients and assembling them using 'makeXEventhandler' and 'shadowWithKeymap'.
--
-- At the top of your config:
-- > myNavigation :: TwoD a (Maybe a)
-- > myNavigation = makeXEventhandler $ shadowWithKeymap navKeyMap navDefaultHandler
-- > where navKeyMap = M.fromList [
-- > ((0,xK_Escape), cancel)
-- > ,((0,xK_Return), select)
-- > ,((0,xK_slash) , substringSearch myNavigation)
-- > ,((0,xK_Left) , move (-1,0) >> myNavigation)
-- > ,((0,xK_h) , move (-1,0) >> myNavigation)
-- > ,((0,xK_Right) , move (1,0) >> myNavigation)
-- > ,((0,xK_l) , move (1,0) >> myNavigation)
-- > ,((0,xK_Down) , move (0,1) >> myNavigation)
-- > ,((0,xK_j) , move (0,1) >> myNavigation)
-- > ,((0,xK_Up) , move (0,-1) >> myNavigation)
-- > ,((0,xK_y) , move (-1,-1) >> myNavigation)
-- > ,((0,xK_i) , move (1,-1) >> myNavigation)
-- > ,((0,xK_n) , move (-1,1) >> myNavigation)
-- > ,((0,xK_m) , move (1,-1) >> myNavigation)
-- > ,((0,xK_space) , setPos (0,0) >> myNavigation)
-- > ]
-- > -- The navigation handler ignores unknown key symbols
-- > navDefaultHandler = const myNavigation
--
-- > {-# LANGAUGE NoMonomorphismRestriction #-}
-- > import XMonad
-- > import qualified Data.Map as M
--
-- Then define @gsconfig3@ which may be used in exactly the same manner as @gsconfig1@:
-- You can then define @gsconfig3@ which may be used in exactly the same manner as @gsconfig1@:
--
-- > gsconfig3 = defaultGSConfig
-- > { gs_cellheight = 30
-- > , gs_cellwidth = 100
-- > , gs_navigate = M.unions
-- > [reset
-- > ,nethackKeys
-- > ,gs_navigate -- get the default navigation bindings
-- > $ defaultGSConfig `asTypeOf` gsconfig3 -- needed to fix an ambiguous type variable
-- > ]
-- > , gs_navigate = myNavigation
-- > }
-- > where addPair (a,b) (x,y) = (a+x,b+y)
-- > nethackKeys = M.map addPair $ M.fromList
-- > [((0,xK_y),(-1,-1))
-- > ,((0,xK_i),(1,-1))
-- > ,((0,xK_n),(-1,1))
-- > ,((0,xK_m),(1,1))
-- > ]
-- > -- jump back to the center with the spacebar, regardless of the current position.
-- > reset = M.singleton (0,xK_space) (const (0,0))
-- $screenshots
--
-- Selecting a workspace:
--
-- <<http://haskell.org/sitewiki/images/a/a9/Xmonad-gridselect-workspace.png>>
-- <<http://haskell.org/wikiupload/a/a9/Xmonad-gridselect-workspace.png>>
--
-- Selecting a window by title:
--
-- <<http://haskell.org/sitewiki/images/3/35/Xmonad-gridselect-window-aavogt.png>>
-- <<http://haskell.org/wikiupload/3/35/Xmonad-gridselect-window-aavogt.png>>
data GSConfig a = GSConfig {
gs_cellheight :: Integer,
@@ -164,7 +188,7 @@ data GSConfig a = GSConfig {
gs_cellpadding :: Integer,
gs_colorizer :: a -> Bool -> X (String, String),
gs_font :: String,
gs_navigate :: NavigateMap,
gs_navigate :: TwoD a (Maybe a),
gs_originFractX :: Double,
gs_originFractY :: Double
}
@@ -194,21 +218,29 @@ instance HasColorizer a where
defaultGSConfig :: HasColorizer a => GSConfig a
defaultGSConfig = buildDefaultGSConfig defaultColorizer
type NavigateMap = M.Map (KeyMask,KeySym) (TwoDPosition -> TwoDPosition)
type TwoDPosition = (Integer, Integer)
type TwoDElementMap a = [(TwoDPosition,(String,a))]
data TwoDState a = TwoDState { td_curpos :: TwoDPosition
, td_elementmap :: TwoDElementMap a
, td_availSlots :: [TwoDPosition]
, td_elements :: [(String,a)]
, td_gsconfig :: GSConfig a
, td_font :: XMonadFont
, td_paneX :: Integer
, td_paneY :: Integer
, td_drawingWin :: Window
, td_searchString :: String
}
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
newtype TwoD a b = TwoD { unTwoD :: StateT (TwoDState a) X b }
deriving (Monad,Functor,MonadState (TwoDState a))
@@ -222,14 +254,16 @@ liftX = TwoD . lift
evalTwoD :: TwoD a1 a -> TwoDState a1 -> X a
evalTwoD m s = flip evalStateT s $ unTwoD m
diamondLayer :: (Enum b', Num b') => b' -> [(b', b')]
-- FIXME remove nub
diamondLayer n = let ul = [ (x,n-x) | x <- [0..n] ]
in nub $ ul ++ (map (negate *** id) ul) ++
(map (negate *** negate) ul) ++
(map (id *** negate) ul)
diamondLayer :: (Enum a, Num a, Eq a) => a -> [(a, a)]
diamondLayer 0 = [(0,0)]
diamondLayer n =
-- tr = top right
-- r = ur ++ 90 degree clock-wise rotation of ur
let tr = [ (x,n-x) | x <- [0..n-1] ]
r = tr ++ (map (\(x,y) -> (y,-x)) tr)
in r ++ (map (negate *** negate) r)
diamond :: (Enum a, Num a) => [(a, a)]
diamond :: (Enum a, Num a, Eq a) => [(a, a)]
diamond = concatMap diamondLayer [0..]
diamondRestrict :: Integer -> Integer -> Integer -> Integer -> [(Integer, Integer)]
@@ -266,11 +300,23 @@ drawWinBox win font (fg,bg) ch cw text x y cp =
updateAllElements :: TwoD a ()
updateAllElements =
do
TwoDState { td_elementmap = els } <- get
updateElements els
s <- get
updateElements (td_elementmap s)
grayoutAllElements :: TwoD a ()
grayoutAllElements =
do
s <- get
updateElementsWithColorizer grayOnly (td_elementmap s)
where grayOnly _ _ = return ("#808080", "#808080")
updateElements :: TwoDElementMap a -> TwoD a ()
updateElements elementmap = do
s <- get
updateElementsWithColorizer (gs_colorizer (td_gsconfig s)) elementmap
updateElementsWithColorizer :: (a -> Bool -> X (String, String)) -> TwoDElementMap a -> TwoD a ()
updateElementsWithColorizer colorizer elementmap = do
TwoDState { td_curpos = curpos,
td_drawingWin = win,
td_gsconfig = gsconfig,
@@ -282,7 +328,7 @@ updateElements elementmap = do
paneX' = div (paneX-cellwidth) 2
paneY' = div (paneY-cellheight) 2
updateElement (pos@(x,y),(text, element)) = liftX $ do
colors <- gs_colorizer gsconfig element (pos == curpos)
colors <- colorizer element (pos == curpos)
drawWinBox win font
colors
cellheight
@@ -293,52 +339,180 @@ updateElements elementmap = do
(gs_cellpadding gsconfig)
mapM_ updateElement elementmap
eventLoop :: TwoD a (Maybe a)
eventLoop = do
(keysym,string,event) <- liftX $ withDisplay $ \d -> liftIO $ allocaXEvent $ \e -> do
maskEvent d (exposureMask .|. keyPressMask .|. buttonReleaseMask) e
ev <- getEvent e
(ks,s) <- if ev_event_type ev == keyPress
then lookupString $ asKeyEvent e
else return (Nothing, "")
return (ks,s,ev)
handle (fromMaybe xK_VoidSymbol keysym,string) event
handle :: (KeySym, t) -> Event -> TwoD a (Maybe a)
handle (ks,_) (KeyEvent {ev_event_type = t, ev_state = m })
| t == keyPress && ks == xK_Escape = return Nothing
| t == keyPress && ks == xK_Return = do
(TwoDState { td_curpos = pos, td_elementmap = elmap }) <- get
return $ fmap (snd . snd) $ findInElementMap pos elmap
| t == keyPress = do
m' <- liftX (cleanMask m)
keymap <- gets (gs_navigate . td_gsconfig)
maybe eventLoop diffAndRefresh . M.lookup (m',ks) $ keymap
where diffAndRefresh diff = do
state <- get
let elmap = td_elementmap state
oldPos = td_curpos state
newPos = diff oldPos
newSelectedEl = findInElementMap newPos elmap
when (isJust newSelectedEl) $ do
put state { td_curpos = newPos }
updateElements (catMaybes [(findInElementMap oldPos elmap), newSelectedEl])
eventLoop
handle _ (ButtonEvent { ev_event_type = t, ev_x = x, ev_y = y })
stdHandle :: Event -> TwoD a (Maybe a) -> TwoD a (Maybe a)
stdHandle (ButtonEvent { ev_event_type = t, ev_x = x, ev_y = y }) contEventloop
| t == buttonRelease = do
(TwoDState { td_elementmap = elmap, td_paneX = px, td_paneY = py,
td_gsconfig = (GSConfig ch cw _ _ _ _ _ _) }) <- get
s @ TwoDState { td_paneX = px, td_paneY = py,
td_gsconfig = (GSConfig ch cw _ _ _ _ _ _) } <- get
let gridX = (fi x - (px - cw) `div` 2) `div` cw
gridY = (fi y - (py - ch) `div` 2) `div` ch
case lookup (gridX,gridY) elmap of
case lookup (gridX,gridY) (td_elementmap s) of
Just (_,el) -> return (Just el)
Nothing -> eventLoop
| otherwise = eventLoop
Nothing -> contEventloop
| otherwise = contEventloop
handle _ (ExposeEvent { }) = updateAllElements >> eventLoop
stdHandle (ExposeEvent { }) contEventloop = updateAllElements >> contEventloop
stdHandle _ contEventloop = contEventloop
-- | Embeds a key handler into the X event handler that dispatches key
-- events to the key handler, while non-key event go to the standard
-- handler.
makeXEventhandler :: ((KeySym, String, KeyMask) -> TwoD a (Maybe a)) -> TwoD a (Maybe a)
makeXEventhandler keyhandler = fix $ \me -> join $ liftX $ withDisplay $ \d -> liftIO $ allocaXEvent $ \e -> do
maskEvent d (exposureMask .|. keyPressMask .|. buttonReleaseMask) e
ev <- getEvent e
if ev_event_type ev == keyPress
then do
(ks,s) <- lookupString $ asKeyEvent e
return $ do
mask <- liftX $ cleanMask (ev_state ev)
keyhandler (fromMaybe xK_VoidSymbol ks, s, mask)
else
return $ stdHandle ev me
-- | When the map contains (KeySym,KeyMask) tuple for the given event,
-- the associated action in the map associated shadows the default key
-- handler
shadowWithKeymap :: M.Map (KeyMask, KeySym) a -> ((KeySym, String, KeyMask) -> a) -> (KeySym, String, KeyMask) -> a
shadowWithKeymap keymap dflt keyEvent@(ks,_,m') = fromMaybe (dflt keyEvent) (M.lookup (m',ks) keymap)
-- Helper functions to use for key handler functions
-- | Closes gridselect returning the element under the cursor
select :: TwoD a (Maybe a)
select = do
s <- get
return $ fmap (snd . snd) $ findInElementMap (td_curpos s) (td_elementmap s)
-- | Closes gridselect returning no element.
cancel :: TwoD a (Maybe a)
cancel = return Nothing
-- | Sets the absolute position of the cursor.
setPos :: (Integer, Integer) -> TwoD a ()
setPos newPos = do
s <- get
let elmap = td_elementmap s
newSelectedEl = findInElementMap newPos (td_elementmap s)
oldPos = td_curpos s
when (isJust newSelectedEl && newPos /= oldPos) $ do
put s { td_curpos = newPos }
updateElements (catMaybes [(findInElementMap oldPos elmap), newSelectedEl])
-- | Moves the cursor by the offsets specified
move :: (Integer, Integer) -> TwoD a ()
move (dx,dy) = do
s <- get
let (x,y) = td_curpos s
newPos = (x+dx,y+dy)
setPos newPos
moveNext :: TwoD a ()
moveNext = do
position <- gets td_curpos
elems <- gets td_elementmap
let n = length elems
m = case findIndex (\p -> fst p == position) elems of
Nothing -> Nothing
Just k | k == n-1 -> Just 0
| otherwise -> Just (k+1)
whenJust m $ \i ->
setPos (fst $ elems !! i)
movePrev :: TwoD a ()
movePrev = do
position <- gets td_curpos
elems <- gets td_elementmap
let n = length elems
m = case findIndex (\p -> fst p == position) elems of
Nothing -> Nothing
Just 0 -> Just (n-1)
Just k -> Just (k-1)
whenJust m $ \i ->
setPos (fst $ elems !! i)
-- | Apply a transformation function the current search string
transformSearchString :: (String -> String) -> TwoD a ()
transformSearchString f = do
s <- get
let oldSearchString = td_searchString s
newSearchString = f oldSearchString
when (newSearchString /= oldSearchString) $ do
-- FIXME: grayoutAllElements + updateAllElements paint some fields twice causing flickering
-- we would need a much smarter update strategy to fix that
when (length newSearchString > length oldSearchString) grayoutAllElements
-- FIXME curpos might end up outside new bounds
put s { td_searchString = newSearchString }
updateAllElements
-- | By default gridselect used the defaultNavigation action, which
-- binds left,right,up,down and vi-style h,l,j,k navigation. Return
-- quits gridselect, returning the selected element, while Escape
-- cancels the selection. Slash enters the substring search mode. In
-- substring search mode, every string-associated keystroke is
-- added to a search string, which narrows down the object
-- selection. Substring search mode comes back to regular navigation
-- via Return, while Escape cancels the search. If you want that
-- navigation style, add 'defaultNavigation' as 'gs_navigate' to your
-- 'GSConfig' object. This is done by 'buildDefaultGSConfig' automatically.
defaultNavigation :: TwoD a (Maybe a)
defaultNavigation = makeXEventhandler $ shadowWithKeymap navKeyMap navDefaultHandler
where navKeyMap = M.fromList [
((0,xK_Escape) , cancel)
,((0,xK_Return) , select)
,((0,xK_slash) , substringSearch defaultNavigation)
,((0,xK_Left) , move (-1,0) >> defaultNavigation)
,((0,xK_h) , move (-1,0) >> defaultNavigation)
,((0,xK_Right) , move (1,0) >> defaultNavigation)
,((0,xK_l) , move (1,0) >> defaultNavigation)
,((0,xK_Down) , move (0,1) >> defaultNavigation)
,((0,xK_j) , move (0,1) >> defaultNavigation)
,((0,xK_Up) , move (0,-1) >> defaultNavigation)
,((0,xK_k) , move (0,-1) >> defaultNavigation)
,((0,xK_Tab) , moveNext >> defaultNavigation)
,((0,xK_n) , moveNext >> defaultNavigation)
,((shiftMask,xK_Tab), movePrev >> defaultNavigation)
,((0,xK_p) , movePrev >> defaultNavigation)
]
-- The navigation handler ignores unknown key symbols, therefore we const
navDefaultHandler = const defaultNavigation
-- | This navigation style combines navigation and search into one mode at the cost of losing vi style
-- navigation. With this style, there is no substring search submode,
-- but every typed character is added to the substring search.
navNSearch :: TwoD a (Maybe a)
navNSearch = makeXEventhandler $ shadowWithKeymap navNSearchKeyMap navNSearchDefaultHandler
where navNSearchKeyMap = M.fromList [
((0,xK_Escape) , cancel)
,((0,xK_Return) , select)
,((0,xK_Left) , move (-1,0) >> navNSearch)
,((0,xK_Right) , move (1,0) >> navNSearch)
,((0,xK_Down) , move (0,1) >> navNSearch)
,((0,xK_Up) , move (0,-1) >> navNSearch)
,((0,xK_Tab) , moveNext >> navNSearch)
,((shiftMask,xK_Tab), movePrev >> navNSearch)
,((0,xK_BackSpace), transformSearchString (\s -> if (s == "") then "" else init s) >> navNSearch)
]
-- The navigation handler ignores unknown key symbols, therefore we const
navNSearchDefaultHandler (_,s,_) = do
transformSearchString (++ s)
navNSearch
-- | Navigation submode used for substring search. It returns to the
-- first argument navigation style when the user hits Return.
substringSearch :: TwoD a (Maybe a) -> TwoD a (Maybe a)
substringSearch returnNavigation = fix $ \me ->
let searchKeyMap = M.fromList [
((0,xK_Escape) , transformSearchString (const "") >> returnNavigation)
,((0,xK_Return) , returnNavigation)
,((0,xK_BackSpace), transformSearchString (\s -> if (s == "") then "" else init s) >> me)
]
searchDefaultHandler (_,s,_) = do
transformSearchString (++ s)
me
in makeXEventhandler $ shadowWithKeymap searchKeyMap searchDefaultHandler
handle _ _ = eventLoop
-- FIXME probably move that into Utils?
-- Conversion scheme as in http://en.wikipedia.org/wiki/HSV_color_space
@@ -417,7 +591,7 @@ stringToRatio s = let gen = mkStdGen $ sum $ map fromEnum s
-- select an element with cursors keys. The selected element is returned.
gridselect :: GSConfig a -> [(String,a)] -> X (Maybe a)
gridselect _ [] = return Nothing
gridselect gsconfig elmap =
gridselect gsconfig elements =
withDisplay $ \dpy -> do
rootw <- asks theRoot
s <- gets $ screenRect . W.screenDetail . W.current . windowset
@@ -437,16 +611,16 @@ gridselect gsconfig elmap =
originPosX = floor $ ((gs_originFractX gsconfig) - (1/2)) * 2 * fromIntegral restrictX
originPosY = floor $ ((gs_originFractY gsconfig) - (1/2)) * 2 * fromIntegral restrictY
coords = diamondRestrict restrictX restrictY originPosX originPosY
elmap' = zip coords elmap
evalTwoD (updateAllElements >> eventLoop)
(TwoDState (head coords)
elmap'
gsconfig
font
screenWidth
screenHeight
win)
evalTwoD (updateAllElements >> (gs_navigate gsconfig)) TwoDState { td_curpos = (head coords),
td_availSlots = coords,
td_elements = elements,
td_gsconfig = gsconfig,
td_font = font,
td_paneX = screenWidth,
td_paneY = screenHeight,
td_drawingWin = win,
td_searchString = "" }
else
return Nothing
liftIO $ do
@@ -483,19 +657,7 @@ decorateName' w = do
-- | Builds a default gs config from a colorizer function.
buildDefaultGSConfig :: (a -> Bool -> X (String,String)) -> GSConfig a
buildDefaultGSConfig col = GSConfig 50 130 10 col "xft:Sans-8" defaultGSNav (1/2) (1/2)
defaultGSNav :: NavigateMap
defaultGSNav = M.map (\(x,y) (a,b) -> (x+a,y+b)) $ M.fromList
[((0,xK_Left) ,(-1,0))
,((0,xK_h) ,(-1,0))
,((0,xK_Right),(1,0))
,((0,xK_l) ,(1,0))
,((0,xK_Down) ,(0,1))
,((0,xK_j) ,(0,1))
,((0,xK_Up) ,(0,-1))
,((0,xK_k) ,(0,-1))
]
buildDefaultGSConfig col = GSConfig 50 130 10 col "xft:Sans-8" defaultNavigation (1/2) (1/2)
borderColor :: String
borderColor = "white"
@@ -533,4 +695,4 @@ gridselectWorkspace :: GSConfig WorkspaceId ->
(WorkspaceId -> WindowSet -> WindowSet) -> X ()
gridselectWorkspace conf viewFunc = withWindowSet $ \ws -> do
let wss = map W.tag $ W.hidden ws ++ map W.workspace (W.current ws : W.visible ws)
gridselect conf (zip wss wss) >>= flip whenJust (windows . viewFunc)
gridselect conf (zip wss wss) >>= flip whenJust (windows . viewFunc)

View File

@@ -0,0 +1,218 @@
{-# LANGUAGE DeriveDataTypeable #-}
----------------------------------------------------------------------
-- |
-- Module : XMonad.Actions.GroupNavigation
-- Copyright : (c) nzeh@cs.dal.ca
-- License : BSD3-style (see LICENSE)
--
-- Maintainer : nzeh@cs.dal.ca
-- Stability : unstable
-- Portability : unportable
--
-- Provides methods for cycling through groups of windows across
-- workspaces, ignoring windows that do not belong to this group. A
-- group consists of all windows matching a user-provided boolean
-- query.
--
-- Also provides a method for jumping back to the most recently used
-- window in any given group.
--
----------------------------------------------------------------------
module XMonad.Actions.GroupNavigation ( -- * Usage
-- $usage
Direction (..)
, nextMatch
, nextMatchOrDo
, nextMatchWithThis
, historyHook
) where
import Control.Monad.Reader
import Data.Foldable as Fold
import Data.Map as Map
import Data.Sequence as Seq
import Data.Set as Set
import Graphics.X11.Types
import Prelude hiding (concatMap, drop, elem, filter, null, reverse)
import XMonad.Core
import XMonad.ManageHook
import XMonad.Operations (windows, withFocused)
import qualified XMonad.StackSet as SS
import qualified XMonad.Util.ExtensibleState as XS
{- $usage
Import the module into your @~\/.xmonad\/xmonad.hs@:
> import XMonad.Actions,GroupNavigation
To support cycling forward and backward through all xterm windows, add
something like this to your keybindings:
> , ((modm , xK_t), nextMatch Forward (className =? "XTerm"))
> , ((modm .|. shiftMask, xK_t), nextMatch Backward (className =? "XTerm"))
These key combinations do nothing if there is no xterm window open.
If you rather want to open a new xterm window if there is no open
xterm window, use 'nextMatchOrDo' instead:
> , ((modm , xK_t), nextMatchOrDo Forward (className =? "XTerm") (spawn "xterm"))
> , ((modm .|. shiftMask, xK_t), nextMatchOrDo Backward (className =? "XTerm") (spawn "xterm"))
You can use 'nextMatchWithThis' with an arbitrary query to cycle
through all windows for which this query returns the same value as the
current window. For example, to cycle through all windows in the same
window class as the current window use:
> , ((modm , xK_f), nextMatchWithThis Forward className)
> , ((modm , xK_b), nextMatchWithThis Backward className)
Finally, you can define keybindings to jump to the most recent window
matching a certain Boolean query. To do this, you need to add
'historyHook' to your logHook:
> main = xmonad $ defaultConfig { logHook = historyHook }
Then the following keybindings, for example, allow you to return to
the most recent xterm or emacs window or to simply to the most recent
window:
> , ((modm .|. controlMask, xK_e), nextMatch History (className =? "Emacs"))
> , ((modm .|. controlMask, xK_t), nextMatch History (className =? "XTerm"))
> , ((modm , xK_BackSpace), nextMatch History (return True))
Again, you can use 'nextMatchOrDo' instead of 'nextMatch' if you want
to execute an action if no window matching the query exists. -}
--- Basic cyclic navigation based on queries -------------------------
-- | The direction in which to look for the next match
data Direction = Forward -- ^ Forward from current window or workspace
| Backward -- ^ Backward from current window or workspace
| History -- ^ Backward in history
-- | Focuses the next window for which the given query produces the
-- same result as the currently focused window. Does nothing if there
-- is no focused window (i.e., the current workspace is empty).
nextMatchWithThis :: Eq a => Direction -> Query a -> X ()
nextMatchWithThis dir qry = withFocused $ \win -> do
prop <- runQuery qry win
nextMatch dir (qry =? prop)
-- | Focuses the next window that matches the given boolean query.
-- Does nothing if there is no such window. This is the same as
-- 'nextMatchOrDo' with alternate action @return ()@.
nextMatch :: Direction -> Query Bool -> X ()
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
>>= 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)
>=> maybe act (windows . SS.focusWindow)
-- Returns the list of windows ordered by workspace as specified in
-- ~/.xmonad/xmonad.hs
orderedWindowList :: Direction -> X (Seq Window)
orderedWindowList History = liftM (\(HistoryDB w ws) -> maybe ws (ws |>) w) XS.get
orderedWindowList dir = withWindowSet $ \ss -> do
wsids <- asks (Seq.fromList . workspaces . config)
let wspcs = orderedWorkspaceList ss wsids
wins = dirfun dir
$ Fold.foldl' (><) Seq.empty
$ fmap (Seq.fromList . SS.integrate' . SS.stack) wspcs
cur = SS.peek ss
return $ maybe wins (rotfun wins) cur
where
dirfun Backward = Seq.reverse
dirfun _ = id
rotfun wins x = rotate $ rotateTo (== x) wins
-- Returns the ordered workspace list as specified in ~/.xmonad/xmonad.hs
orderedWorkspaceList :: WindowSet -> Seq String -> Seq WindowSpace
orderedWorkspaceList ss wsids = rotateTo isCurWS wspcs'
where
wspcs = SS.workspaces ss
wspcsMap = Fold.foldl' (\m ws -> Map.insert (SS.tag ws) ws m) Map.empty wspcs
wspcs' = fmap (\wsid -> wspcsMap ! wsid) wsids
isCurWS ws = SS.tag ws == SS.tag (SS.workspace $ SS.current ss)
--- History navigation, requires a layout modifier -------------------
-- The state extension that holds the history information
data HistoryDB = HistoryDB (Maybe Window) -- currently focused window
(Seq Window) -- previously focused windows
deriving (Read, Show, Typeable)
instance ExtensionClass HistoryDB where
initialValue = HistoryDB Nothing Seq.empty
extensionType = PersistentExtension
-- | Action that needs to be executed as a logHook to maintain the
-- focus history of all windows as the WindowSet changes.
historyHook :: X ()
historyHook = XS.get >>= updateHistory >>= XS.put
-- Updates the history in response to a WindowSet change
updateHistory :: HistoryDB -> X HistoryDB
updateHistory (HistoryDB oldcur oldhist) = withWindowSet $ \ss -> do
let newcur = SS.peek ss
wins = Set.fromList $ SS.allWindows ss
newhist = flt (flip Set.member wins) (ins oldcur oldhist)
return $ HistoryDB newcur (del newcur newhist)
where
ins x xs = maybe xs (<| xs) x
del x xs = maybe xs (\x' -> flt (/= x') xs) x
--- Two replacements for Seq.filter and Seq.breakl available only in
--- containers-0.3.0.0, which only ships with ghc 6.12. Once we
--- decide to no longer support ghc < 6.12, these should be replaced
--- with Seq.filter and Seq.breakl.
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
$ 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
rotate :: Seq a -> Seq a
rotate xs = rotate' (viewl xs)
where
rotate' EmptyL = Seq.empty
rotate' (x' :< xs') = xs' |> x'
-- Rotates the sequence until an element matching the given condition
-- is at the beginning of the sequence.
rotateTo :: (a -> Bool) -> Seq a -> Seq a
rotateTo cond xs = let (lxs, rxs) = brkl cond xs in rxs >< lxs
--- A monadic find ---------------------------------------------------
-- Applies the given action to every sequence element in turn until
-- the first element is found for which the action returns true. The
-- remaining elements in the sequence are ignored.
findM :: Monad m => (a -> m Bool) -> Seq a -> m (Maybe a)
findM cond xs = findM' cond (viewl xs)
where
findM' _ EmptyL = return Nothing
findM' qry (x' :< xs') = do
isMatch <- qry x'
if isMatch
then return (Just x')
else findM qry xs'

156
XMonad/Actions/KeyRemap.hs Normal file
View File

@@ -0,0 +1,156 @@
{-# LANGUAGE DeriveDataTypeable #-}
-----------------------------------------------------------------------------
-- |
-- Module : XMonad.Actions.KeyRemap
-- Copyright : (c) Christian Dietrich
-- License : BSD-style (as xmonad)
--
-- Maintainer : stettberger@dokucde.de
-- Stability : unstable
-- Portability : unportable
--
-- Remap Keybinding on the fly, e.g having Dvorak char, but everything with Control/Shift
-- is left us Layout
--
-----------------------------------------------------------------------------
module XMonad.Actions.KeyRemap (
-- * Usage
-- $usage
setKeyRemap,
buildKeyRemapBindings,
setDefaultKeyRemap,
KeymapTable (KeymapTable),
emptyKeyRemap,
dvorakProgrammerKeyRemap
) where
import XMonad
import XMonad.Util.Paste
import Data.List
import qualified XMonad.Util.ExtensibleState as XS
import Control.Monad
data KeymapTable = KeymapTable [((KeyMask, KeySym), (KeyMask, KeySym))] deriving (Typeable, Show)
instance ExtensionClass KeymapTable where
initialValue = KeymapTable []
-- $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
-- after all
--
-- First, you must add all possible keybindings for all layout you want to use:
--
-- > keys = myKeys ++ buildKeyRemapBindings [dvorakProgrammerKeyRemap,emptyKeyRemap]
--
-- Then you must add setDefaultKeyRemap to your startup hook (e.g. you want to set the
-- empty keyremap (no remapping is done) as default after startup):
--
-- > myStartupHook :: X()
-- > myStartupHook = do
-- > setWMName "LG3D"
-- > setDefaultKeyRemap emptyKeyRemap [dvorakProgrammerKeyRemap, emptyKeyRemap]
--
-- Then you add keybindings for changing keyboard layouts;
--
-- > , ((0 , xK_F1 ), setKeyRemap emptyKeyRemap)
-- > , ((0 , xK_F2 ), setKeyRemap dvorakProgrammerKeyRemap)
--
-- When defining your own keymappings, please be aware of:
--
-- * If you want to emulate a key that is shifted on us you must emulate that keypress:
--
-- > KeymapTable [((0, xK_a), (shiftMask, xK_5))] -- would bind 'a' to '%'
-- > KeymapTable [((shiftMask, xK_a), (0, xK_5))] -- would bind 'A' to '5'
--
-- * the dvorakProgrammerKeyRemap uses the original us layout as lookuptable to generate
-- the KeymapTable
--
-- * KeySym and (ord Char) are incompatible, therefore the magic numbers in dvorakProgrammerKeyRemap
-- are nessesary
doKeyRemap :: KeyMask -> KeySym -> X()
doKeyRemap mask sym = do
table <- XS.get
let (insertMask, insertSym) = extractKeyMapping table mask sym
sendKey insertMask insertSym
-- | Using this in the keybindings to set the actual Key Translation table
setKeyRemap :: KeymapTable -> X()
setKeyRemap table = do
let KeymapTable newtable = table
KeymapTable oldtable <- XS.get
XConf { display = dpy, theRoot = rootw } <- ask
let grab kc m = io $ grabKey dpy kc m rootw True grabModeAsync grabModeAsync
let ungrab kc m = io $ ungrabKey dpy kc m rootw
forM_ oldtable $ \((mask, sym), _) -> do
kc <- io $ keysymToKeycode dpy sym
-- "If the specified KeySym is not defined for any KeyCode,
-- XKeysymToKeycode() returns zero."
when (kc /= 0) $ ungrab kc mask
forM_ newtable $ \((mask, sym), _) -> do
kc <- io $ keysymToKeycode dpy sym
-- "If the specified KeySym is not defined for any KeyCode,
-- XKeysymToKeycode() returns zero."
when (kc /= 0) $ grab kc mask
XS.put table
-- | Adding this to your startupHook, to select your default Key Translation table.
-- You also must give it all the KeymapTables you are willing to use
setDefaultKeyRemap :: KeymapTable -> [KeymapTable] -> X()
setDefaultKeyRemap dflt keyremaps = do
XS.put (KeymapTable mappings)
setKeyRemap dflt
where
mappings = nub (keyremaps >>= \(KeymapTable table) -> table)
extractKeyMapping :: KeymapTable -> KeyMask -> KeySym -> (KeyMask, KeySym)
extractKeyMapping (KeymapTable table) mask sym =
insertKey filtered
where filtered = filter (\((m, s),_) -> m == mask && s == sym) table
insertKey [] = (mask, sym)
insertKey ((_, to):_) = to
-- | Append the output of this function to your keybindings with ++
buildKeyRemapBindings :: [KeymapTable] -> [((KeyMask, KeySym), X ())]
buildKeyRemapBindings keyremaps =
[((mask, sym), doKeyRemap mask sym) | (mask, sym) <- bindings]
where mappings = concat (map (\(KeymapTable table) -> table) keyremaps)
bindings = nub (map (\binding -> fst binding) mappings)
-- Here come the Keymappings
-- | The empty KeymapTable, does no translation
emptyKeyRemap :: KeymapTable
emptyKeyRemap = KeymapTable []
-- | The dvorak Programmers keymap, translates from us keybindings to dvorak programmers
dvorakProgrammerKeyRemap :: KeymapTable
dvorakProgrammerKeyRemap =
KeymapTable [((charToMask maskFrom, from), (charToMask maskTo, to)) |
(maskFrom, from, maskTo, to) <- (zip4 layoutUsShift layoutUsKey layoutDvorakShift layoutDvorakKey)]
where
layoutUs = map (fromIntegral . fromEnum) "`1234567890-=qwertyuiop[]\\asdfghjkl;'zxcvbnm,./~!@#$%^&*()_+QWERTYUIOP{}|ASDFGHJKL:\"ZXCVBNM<>?" :: [KeySym]
layoutUsKey = map (fromIntegral . fromEnum) "`1234567890-=qwertyuiop[]\\asdfghjkl;'zxcvbnm,./`1234567890-=qwertyuiop[]\\asdfghjkl;'zxcvbnm,./" :: [KeySym]
layoutUsShift = "0000000000000000000000000000000000000000000000011111111111111111111111111111111111111111111111"
layoutDvorak = map (fromIntegral . fromEnum) "$&[{}(=*)+]!#;,.pyfgcrl/@\\aoeuidhtns-'qjkxbmwvz~%7531902468`:<>PYFGCRL?^|AOEUIDHTNS_\"QJKXBMWVZ" :: [KeySym]
layoutDvorakShift = map getShift layoutDvorak
layoutDvorakKey = map getKey layoutDvorak
getKey char = let Just index = elemIndex char layoutUs
in layoutUsKey !! index
getShift char = let Just index = elemIndex char layoutUs
in layoutUsShift !! index
charToMask char = if [char] == "0" then 0 else shiftMask

View File

@@ -1,10 +1,10 @@
-----------------------------------------------------------------------------
-- |
-- Module : XMonad.Actions.MessageFeedback
-- Copyright : (c) Quentin Moser <quentin.moser@unifr.ch>
-- Copyright : (c) Quentin Moser <moserq@gmail.com>
-- License : BSD3
--
-- Maintainer : None
-- Maintainer : orphaned
-- Stability : unstable
-- Portability : unportable
--

View File

@@ -23,12 +23,8 @@ module XMonad.Actions.MouseResize
, MouseResize (..)
) where
import Control.Monad
import Data.Maybe
import XMonad
import XMonad.Layout.Decoration
import XMonad.Layout.LayoutModifier
import XMonad.Layout.WindowArranger
import XMonad.Util.XUtils
@@ -114,6 +110,11 @@ createInputWindow ((w,r),mr) = do
Just tr -> withDisplay $ \d -> do
tw <- mkInputWindow d tr
io $ selectInput d tw (exposureMask .|. buttonPressMask)
cursor <- io $ createFontCursor d xC_bottom_right_corner
io $ defineCursor d tw cursor
io $ freeCursor d cursor
showWindow tw
return ((w,r), Just tw)
Nothing -> return ((w,r), Nothing)

View File

@@ -26,7 +26,6 @@ module XMonad.Actions.OnScreen (
) where
import XMonad
import XMonad.Core
import XMonad.StackSet hiding (new)
import Control.Monad (guard)
@@ -181,7 +180,7 @@ toggleOrView' f i st = fromMaybe (f i st) $ do
--
-- A more basic version inside the default keybindings would be:
--
-- > , ((modm .|. controlMask, xK_1) windows (viewOnScreen 0 "1"))
-- > , ((modm .|. controlMask, xK_1), windows (viewOnScreen 0 "1"))
--
-- where 0 is the first screen and \"1\" the workspace with the tag \"1\".
--

View File

@@ -19,15 +19,14 @@ module XMonad.Actions.PhysicalScreens (
, getScreen
, viewScreen
, sendToScreen
, onNextNeighbour
, onPrevNeighbour
) where
import XMonad
import qualified XMonad.StackSet as W
import qualified Graphics.X11.Xlib as X
import Graphics.X11.Xinerama
import Data.List (sortBy)
import Data.List (sortBy,findIndex)
import Data.Function (on)
{- $usage
@@ -44,6 +43,11 @@ Example usage in your @~\/.xmonad\/xmonad.hs@ file:
> import XMonad.Actions.PhysicalSCreens
> , ((modMask, xK_a), onPrevNeighbour W.view)
> , ((modMask, xK_o), onNextNeighbour W.view)
> , ((modMask .|. shiftMask, xK_a), onPrevNeighbour W.shift)
> , ((modMask .|. shiftMask, xK_o), onNextNeighbour W.shift)
> --
> -- mod-{w,e,r}, Switch to physical/Xinerama screens 1, 2, or 3
> -- mod-shift-{w,e,r}, Move client to screen 1, 2, or 3
@@ -61,12 +65,12 @@ newtype PhysicalScreen = P Int deriving (Eq,Ord,Show,Read,Enum,Num,Integral,Real
-- | Translate a physical screen index to a "ScreenId"
getScreen :: PhysicalScreen -> X (Maybe ScreenId)
getScreen (P i) = withDisplay $ \dpy -> do
screens <- io $ getScreenInfo dpy
if i >= length screens
then return Nothing
else let ss = sortBy (cmpScreen `on` fst) $ zip screens [0..]
in return $ Just $ snd $ ss !! i
getScreen (P i) = do w <- gets windowset
let screens = W.current w : W.visible w
if i<0 || i >= length screens
then return Nothing
else let ss = sortBy (cmpScreen `on` (screenRect . W.screenDetail)) screens
in return $ Just $ W.screen $ ss !! i
-- | Switch to a given physical screen
viewScreen :: PhysicalScreen -> X ()
@@ -85,4 +89,27 @@ sendToScreen p = do i <- getScreen p
-- | Compare two screens by their top-left corners, ordering
-- | top-to-bottom and then left-to-right.
cmpScreen :: Rectangle -> Rectangle -> Ordering
cmpScreen (Rectangle x1 y1 _ _) (Rectangle x2 y2 _ _) = compare (y1,x1) (y2,x2)
cmpScreen (Rectangle x1 y1 _ _) (Rectangle x2 y2 _ _) = compare (y1,x1) (y2,x2)
-- | Get ScreenId for neighbours of the current screen based on position offset.
getNeighbour :: Int -> X ScreenId
getNeighbour d = do w <- gets windowset
let ss = map W.screen $ sortBy (cmpScreen `on` (screenRect . W.screenDetail)) $ W.current w : W.visible w
curPos = maybe 0 id $ findIndex (== W.screen (W.current w)) ss
pos = (curPos + d) `mod` length ss
return $ ss !! pos
neighbourWindows :: Int -> (WorkspaceId -> WindowSet -> WindowSet) -> X ()
neighbourWindows d f = do s <- getNeighbour d
w <- screenWorkspace s
whenJust w $ windows . f
-- | Apply operation on a WindowSet with the WorkspaceId of the next screen in the physical order as parameter.
onNextNeighbour :: (WorkspaceId -> WindowSet -> WindowSet) -> X ()
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

@@ -44,26 +44,32 @@ module XMonad.Actions.Search ( -- * Usage
lucky,
maps,
mathworld,
openstreetmap,
scholar,
thesaurus,
wayback,
wikipedia,
wiktionary,
youtube,
multi
multi,
-- * Use case: searching with a submap
-- $tip
-- * Types
Browser, Site, Query, Name, Search
) where
import Data.Char (chr, ord, isAlpha, isMark, isDigit)
import Codec.Binary.UTF8.String (encode)
import Data.Char (isAlphaNum, isAscii)
import Data.List (isPrefixOf)
import Numeric (showIntAtBase)
import Text.Printf
import XMonad (X(), MonadIO, liftIO)
import XMonad.Prompt (XPrompt(showXPrompt), mkXPrompt, XPConfig(), historyCompletionP)
import XMonad.Prompt (XPrompt(showXPrompt, nextCompletion, commandToComplete), mkXPrompt, XPConfig(), historyCompletionP, getNextCompletion)
import XMonad.Prompt.Shell (getBrowser)
import XMonad.Util.Run (safeSpawn)
import XMonad.Util.XSelection (getSelection)
{- $usage
This module is intended to allow easy access to databases on the
@@ -120,6 +126,8 @@ import XMonad.Util.XSelection (getSelection)
* 'mathworld' -- Wolfram MathWorld search.
* 'openstreetmap' -- OpenStreetMap free wiki world map.
* 'scholar' -- Google scholar academic search.
* 'thesaurus' -- thesaurus.reference.com search.
@@ -194,31 +202,18 @@ Happy searching! -}
data Search = Search Name
instance XPrompt Search where
showXPrompt (Search name)= "Search [" ++ name ++ "]: "
nextCompletion _ = getNextCompletion
commandToComplete _ c = c
-- | Escape the search string so search engines understand it.
-- Note that everything is escaped; we could be smarter and use 'isAllowedInURI'
-- but then that'd be hard enough to copy-and-paste we'd need to depend on @network@.
-- | Escape the search string so search engines understand it. Only
-- digits and ASCII letters are not encoded. All non ASCII characters
-- which are encoded as UTF8
escape :: String -> String
escape = escapeURIString (\c -> isAlpha c || isDigit c || isMark c)
where -- Copied from Network.URI.
escapeURIString ::
(Char -> Bool) -- a predicate which returns 'False' if should escape
-> String -- the string to process
-> String -- the resulting URI string
escapeURIString = concatMap . escapeURIChar
escapeURIChar :: (Char->Bool) -> Char -> String
escapeURIChar p c
| p c = [c]
| otherwise = '%' : myShowHex (ord c) ""
where
myShowHex :: Int -> ShowS
myShowHex n r = case showIntAtBase 16 toChrHex n r of
[] -> "00"
[ch] -> ['0',ch]
cs -> cs
toChrHex d
| d < 10 = chr (ord '0' + fromIntegral d)
| otherwise = chr (ord 'A' + fromIntegral (d - 10))
escape = concatMap escapeURIChar
escapeURIChar :: Char -> String
escapeURIChar c | isAscii c && isAlphaNum c = [c]
| otherwise = concatMap (printf "%%%02X") $ encode [c]
type Browser = FilePath
type Query = String
@@ -258,8 +253,8 @@ searchEngine name site = searchEngineF name (\s -> site ++ (escape s))
inside of a URL instead of in the end) you can use the alternative 'searchEngineF' function.
> searchFunc :: String -> String
> searchFunc s | s `isPrefixOf` "wiki:" = "http://en.wikipedia.org/wiki/" ++ (escape $ tail $ snd $ break (==':') s)
> | s `isPrefixOf` "http://" = s
> searchFunc s | "wiki:" `isPrefixOf` s = "http://en.wikipedia.org/wiki/" ++ (escape $ tail $ snd $ break (==':') s)
> | "http://" `isPrefixOf` s = s
> | otherwise = (use google) s
> myNewEngine = searchEngineF "mymulti" searchFunc
@@ -276,33 +271,34 @@ searchEngineF = SearchEngine
-- The engines.
amazon, alpha, codesearch, deb, debbts, debpts, dictionary, google, hackage, hoogle,
images, imdb, isohunt, lucky, maps, mathworld, scholar, thesaurus, wayback, wikipedia, wiktionary,
images, imdb, isohunt, lucky, maps, mathworld, openstreetmap, scholar, thesaurus, wayback, wikipedia, wiktionary,
youtube :: SearchEngine
amazon = searchEngine "amazon" "http://www.amazon.com/exec/obidos/external-search?index=all&keyword="
alpha = searchEngine "alpha" "http://www.wolframalpha.com/input/?i="
codesearch = searchEngine "codesearch" "http://www.google.com/codesearch?q="
deb = searchEngine "deb" "http://packages.debian.org/"
debbts = searchEngine "debbts" "http://bugs.debian.org/"
debpts = searchEngine "debpts" "http://packages.qa.debian.org/"
dictionary = searchEngine "dict" "http://dictionary.reference.com/browse/"
google = searchEngine "google" "http://www.google.com/search?num=100&q="
hackage = searchEngine "hackage" "http://hackage.haskell.org/package/"
hoogle = searchEngine "hoogle" "http://www.haskell.org/hoogle/?q="
images = searchEngine "images" "http://images.google.fr/images?q="
imdb = searchEngine "imdb" "http://www.imdb.com/find?s=all&q="
isohunt = searchEngine "isohunt" "http://isohunt.com/torrents/?ihq="
lucky = searchEngine "lucky" "http://www.google.com/search?btnI&q="
maps = searchEngine "maps" "http://maps.google.com/maps?q="
mathworld = searchEngine "mathworld" "http://mathworld.wolfram.com/search/?query="
scholar = searchEngine "scholar" "http://scholar.google.com/scholar?q="
thesaurus = searchEngine "thesaurus" "http://thesaurus.reference.com/search?q="
wikipedia = searchEngine "wiki" "http://en.wikipedia.org/wiki/Special:Search?go=Go&search="
wiktionary = searchEngine "wikt" "http://en.wiktionary.org/wiki/Special:Search?go=Go&search="
youtube = searchEngine "youtube" "http://www.youtube.com/results?search_type=search_videos&search_query="
wayback = searchEngineF "wayback" ("http://web.archive.org/web/*/"++)
amazon = searchEngine "amazon" "http://www.amazon.com/exec/obidos/external-search?index=all&keyword="
alpha = searchEngine "alpha" "http://www.wolframalpha.com/input/?i="
codesearch = searchEngine "codesearch" "http://www.google.com/codesearch?q="
deb = searchEngine "deb" "http://packages.debian.org/"
debbts = searchEngine "debbts" "http://bugs.debian.org/"
debpts = searchEngine "debpts" "http://packages.qa.debian.org/"
dictionary = searchEngine "dict" "http://dictionary.reference.com/browse/"
google = searchEngine "google" "http://www.google.com/search?num=100&q="
hackage = searchEngine "hackage" "http://hackage.haskell.org/package/"
hoogle = searchEngine "hoogle" "http://www.haskell.org/hoogle/?q="
images = searchEngine "images" "http://images.google.fr/images?q="
imdb = searchEngine "imdb" "http://www.imdb.com/find?s=all&q="
isohunt = searchEngine "isohunt" "http://isohunt.com/torrents/?ihq="
lucky = searchEngine "lucky" "http://www.google.com/search?btnI&q="
maps = searchEngine "maps" "http://maps.google.com/maps?q="
mathworld = searchEngine "mathworld" "http://mathworld.wolfram.com/search/?query="
openstreetmap = searchEngine "openstreetmap" "http://gazetteer.openstreetmap.org/namefinder/?find="
scholar = searchEngine "scholar" "http://scholar.google.com/scholar?q="
thesaurus = searchEngine "thesaurus" "http://thesaurus.reference.com/search?q="
wikipedia = searchEngine "wiki" "http://en.wikipedia.org/wiki/Special:Search?go=Go&search="
wiktionary = searchEngine "wikt" "http://en.wiktionary.org/wiki/Special:Search?go=Go&search="
youtube = searchEngine "youtube" "http://www.youtube.com/results?search_type=search_videos&search_query="
wayback = searchEngineF "wayback" ("http://web.archive.org/web/*/"++)
multi :: SearchEngine
multi = namedEngine "multi" $ foldr1 (!>) [amazon, alpha, codesearch, deb, debbts, debpts, dictionary, google, hackage, hoogle, images, imdb, isohunt, lucky, maps, mathworld, scholar, thesaurus, wayback, wikipedia, wiktionary, (prefixAware google)]
multi = namedEngine "multi" $ foldr1 (!>) [amazon, alpha, codesearch, deb, debbts, debpts, dictionary, google, hackage, hoogle, images, imdb, isohunt, lucky, maps, mathworld, openstreetmap, scholar, thesaurus, wayback, wikipedia, wiktionary, (prefixAware google)]
{- | This function wraps up a search engine and creates a new one, which works
like the argument, but goes directly to a URL if one is given rather than
@@ -331,14 +327,14 @@ removeColonPrefix s = if ':' `elem` s then drop 1 $ dropWhile (':' /=) s else s
\"mathworld:integral\" will search mathworld, and everything else will fall back to
google. The use of intelligent will make sure that URLs are opened directly. -}
(!>) :: SearchEngine -> SearchEngine -> SearchEngine
(SearchEngine name1 site1) !> (SearchEngine name2 site2) = searchEngineF (name1 ++ "/" ++ name2) (\s -> if s `isPrefixOf` (name1++":") then site1 (removeColonPrefix s) else site2 s)
(SearchEngine name1 site1) !> (SearchEngine name2 site2) = searchEngineF (name1 ++ "/" ++ name2) (\s -> if (name1++":") `isPrefixOf` s then site1 (removeColonPrefix s) else site2 s)
{- | Makes a search engine prefix-aware. Especially useful together with '!>'.
It will automatically remove the prefix from a query so that you don\'t end
up searching for google:xmonad if google is your fallback engine and you
explicitly add the prefix. -}
prefixAware :: SearchEngine -> SearchEngine
prefixAware (SearchEngine name site) = SearchEngine name (\s -> if s `isPrefixOf` (name++":") then site $ removeColonPrefix s else site s)
prefixAware (SearchEngine name site) = SearchEngine name (\s -> if (name++":") `isPrefixOf` s then site $ removeColonPrefix s else site s)
{- | Changes search engine's name -}
namedEngine :: Name -> SearchEngine -> SearchEngine

View File

@@ -76,6 +76,7 @@ submapDefault def keys = do
else return (m, keysym)
-- Remove num lock mask and Xkb group state bits
m' <- cleanMask $ m .&. ((1 `shiftL` 12) - 1)
maybe def id (M.lookup (m', s) keys)
io $ ungrabKeyboard d currentTime
maybe def id (M.lookup (m', s) keys)

View File

@@ -25,7 +25,6 @@ module XMonad.Actions.SwapWorkspaces (
import XMonad (windows, X())
import XMonad.StackSet
import XMonad.Actions.CycleWS
import XMonad.Util.Types
import XMonad.Util.WorkspaceCompare

View File

@@ -22,17 +22,23 @@ module XMonad.Actions.TagWindows (
focusDownTagged, focusDownTaggedGlobal,
shiftHere, shiftToScreen,
tagPrompt,
tagDelPrompt
tagDelPrompt,
TagPrompt,
) where
import Data.List (nub,concat,sortBy)
import Prelude hiding (catch)
import Data.List (nub,sortBy)
import Control.Monad
import Control.Exception
import XMonad.StackSet hiding (filter)
import XMonad.Prompt
import XMonad hiding (workspaces)
econst :: Monad m => a -> IOException -> m a
econst = const . return
-- $usage
--
-- To use window tags, import this module into your @~\/.xmonad\/xmonad.hs@:
@@ -79,7 +85,7 @@ getTags w = withDisplay $ \d ->
io $ catch (internAtom d "_XMONAD_TAGS" False >>=
getTextProperty d w >>=
wcTextPropertyToTextList d)
(\_ -> return [[]])
(econst [[]])
>>= return . words . unwords
-- | check a window for the given tag

View File

@@ -25,6 +25,7 @@ module XMonad.Actions.TopicSpace
, defaultTopicConfig
, getLastFocusedTopics
, setLastFocusedTopic
, reverseLastFocusedTopics
, pprWindowSet
, topicActionWithPrompt
, topicAction
@@ -41,13 +42,12 @@ where
import XMonad
import Data.List
import Data.Maybe (fromMaybe, isNothing, listToMaybe)
import Data.Maybe (fromMaybe, isNothing, listToMaybe, fromJust)
import Data.Ord
import qualified Data.Map as M
import Control.Monad ((=<<),liftM2,when,unless,replicateM_)
import Control.Monad (liftM2,when,unless,replicateM_)
import System.IO
import XMonad.Operations
import qualified XMonad.StackSet as W
import XMonad.Prompt
@@ -226,11 +226,17 @@ getLastFocusedTopics = XS.gets getPrevTopics
-- | Given a 'TopicConfig', the last focused topic, and a predicate that will
-- select topics that one want to keep, this function will set the property
-- of last focused topics.
setLastFocusedTopic :: TopicConfig -> Topic -> (Topic -> Bool) -> X ()
setLastFocusedTopic tg w predicate =
setLastFocusedTopic :: Topic -> (Topic -> Bool) -> X ()
setLastFocusedTopic w predicate =
XS.modify $ PrevTopics
. take (maxTopicHistory tg) . nub . (w:) . filter predicate
. seqList . nub . (w:) . filter predicate
. getPrevTopics
where seqList xs = length xs `seq` xs
-- | Reverse the list of "last focused topics"
reverseLastFocusedTopics :: X ()
reverseLastFocusedTopics =
XS.modify $ PrevTopics . reverse . getPrevTopics
-- | This function is a variant of 'DL.pprWindowSet' which takes a topic configuration
-- and a pretty-printing record 'PP'. It will show the list of topics sorted historically
@@ -241,13 +247,13 @@ pprWindowSet tg pp = do
urgents <- readUrgents
let empty_workspaces = map W.tag $ filter (isNothing . W.stack) $ W.workspaces winset
maxDepth = maxTopicHistory tg
setLastFocusedTopic tg (W.tag . W.workspace . W.current $ winset)
(`notElem` empty_workspaces)
setLastFocusedTopic (W.tag . W.workspace . W.current $ winset)
(`notElem` empty_workspaces)
lastWs <- getLastFocusedTopics
let depth topic = elemIndex topic lastWs
add_depth proj topic = proj pp $ maybe topic (((topic++":")++) . show) $ depth topic
let depth topic = fromJust $ elemIndex topic (lastWs ++ [topic])
add_depth proj topic = proj pp . (((topic++":")++) . show) . depth $ topic
pp' = pp { ppHidden = add_depth ppHidden, ppVisible = add_depth ppVisible }
sortWindows = take (maxDepth - 1) . sortBy (comparing $ fromMaybe maxDepth . depth . W.tag)
sortWindows = take maxDepth . sortBy (comparing $ depth . W.tag)
return $ DL.pprWindowSet sortWindows urgents pp' winset
-- | Given a prompt configuration and a topic configuration, triggers the action associated with
@@ -271,7 +277,7 @@ switchTopic tg topic = do
when (null wins) $ topicAction tg topic
-- | Switch to the Nth last focused topic or failback to the 'defaultTopic'.
switchNthLastFocused ::TopicConfig -> Int -> X ()
switchNthLastFocused :: TopicConfig -> Int -> X ()
switchNthLastFocused tg depth = do
lastWs <- getLastFocusedTopics
switchTopic tg $ (lastWs ++ repeat (defaultTopic tg)) !! depth

View File

@@ -21,7 +21,6 @@ module XMonad.Actions.UpdateFocus (
import XMonad
import qualified XMonad.StackSet as W
import Graphics.X11.Xlib.Extras
import Control.Monad (when)
import Data.Monoid
@@ -45,10 +44,10 @@ focusOnMouseMove (MotionEvent { ev_x = x, ev_y = y, ev_window = root }) = do
-- check only every 15 px to avoid excessive calls to translateCoordinates
when (x `mod` 15 == 0 || y `mod` 15 == 0) $ do
dpy <- asks display
Just foc <- withWindowSet $ return . W.peek
foc <- withWindowSet $ return . W.peek
-- get the window under the pointer:
(_,_,_,w) <- io $ translateCoordinates dpy root root (fromIntegral x) (fromIntegral y)
when (foc /= w) $ focus w
when (foc /= Just w) $ focus w
return (All True)
focusOnMouseMove _ = return (All True)

View File

@@ -24,6 +24,7 @@ module XMonad.Actions.UpdatePointer
where
import XMonad
import XMonad.Util.XUtils (fi)
import Control.Monad
import XMonad.StackSet (member, peek, screenDetail, current)
import Data.Maybe
@@ -102,6 +103,3 @@ moveWithin now lower upper =
else if now > upper
then upper
else now
fi :: (Num b, Integral a) => a -> b
fi = fromIntegral

View File

@@ -22,7 +22,6 @@ module XMonad.Actions.Warp (
warpToWindow
) where
import Data.Ratio
import Data.List
import XMonad
import XMonad.StackSet as W

View File

@@ -17,7 +17,9 @@
module XMonad.Actions.WindowBringer (
-- * Usage
-- $usage
gotoMenu, gotoMenu', bringMenu, windowMap,
gotoMenu, gotoMenu', gotoMenuArgs, gotoMenuArgs',
bringMenu, bringMenu', bringMenuArgs, bringMenuArgs',
windowMap,
bringWindow
) where
@@ -27,7 +29,7 @@ import qualified Data.Map as M
import qualified XMonad.StackSet as W
import XMonad
import qualified XMonad as X
import XMonad.Util.Dmenu (menuMap)
import XMonad.Util.Dmenu (menuMapArgs)
import XMonad.Util.NamedWindows (getName)
-- $usage
@@ -44,19 +46,54 @@ import XMonad.Util.NamedWindows (getName)
-- For detailed instructions on editing your key bindings, see
-- "XMonad.Doc.Extending#Editing_key_bindings".
-- | Default menu command
defaultCmd :: String
defaultCmd = "dmenu"
-- | Pops open a dmenu with window titles. Choose one, and you will be
-- taken to the corresponding workspace.
gotoMenu :: X ()
gotoMenu = actionMenu W.focusWindow
gotoMenu = gotoMenuArgs []
-- | Pops open a dmenu with window titles. Choose one, and you will be
-- taken to the corresponding workspace. This version takes a list of
-- arguments to pass to dmenu.
gotoMenuArgs :: [String] -> X ()
gotoMenuArgs menuArgs = gotoMenuArgs' defaultCmd menuArgs
-- | Pops open an application with window titles given over stdin. Choose one,
-- and you will be taken to the corresponding workspace.
gotoMenu' :: String -> X ()
gotoMenu' menuCmd = actionMenu' menuCmd W.focusWindow
gotoMenu' menuCmd = gotoMenuArgs' menuCmd []
-- | Pops open an application with window titles given over stdin. Choose one,
-- and you will be taken to the corresponding workspace. This version takes a
-- list of arguments to pass to dmenu.
gotoMenuArgs' :: String -> [String] -> X ()
gotoMenuArgs' menuCmd menuArgs = actionMenu menuCmd menuArgs W.focusWindow
-- | Pops open a dmenu with window titles. Choose one, and it will be
-- dragged, kicking and screaming, into your current workspace.
bringMenu :: X ()
bringMenu = actionMenu bringWindow
bringMenu = bringMenuArgs []
-- | Pops open a dmenu with window titles. Choose one, and it will be
-- dragged, kicking and screaming, into your current workspace. This version
-- takes a list of arguments to pass to dmenu.
bringMenuArgs :: [String] -> X ()
bringMenuArgs menuArgs = bringMenuArgs' defaultCmd menuArgs
-- | Pops open an application with window titles given over stdin. Choose one,
-- and it will be dragged, kicking and screaming, into your current
-- workspace.
bringMenu' :: String -> X ()
bringMenu' menuCmd = bringMenuArgs' menuCmd []
-- | Pops open an application with window titles given over stdin. Choose one,
-- and it will be dragged, kicking and screaming, into your current
-- workspace. This version allows arguments to the chooser to be specified.
bringMenuArgs' :: String -> [String] -> X ()
bringMenuArgs' menuCmd menuArgs = actionMenu menuCmd menuArgs bringWindow
-- | Brings the specified window into the current workspace.
bringWindow :: Window -> X.WindowSet -> X.WindowSet
@@ -64,14 +101,11 @@ bringWindow w ws = W.shiftWin (W.currentTag ws) w ws
-- | Calls dmenuMap to grab the appropriate Window, and hands it off to action
-- if found.
actionMenu :: (Window -> X.WindowSet -> X.WindowSet) -> X()
actionMenu action = actionMenu' "dmenu" action
actionMenu' :: String -> (Window -> X.WindowSet -> X.WindowSet) -> X()
actionMenu' menuCmd action = windowMap >>= menuMapFunction >>= flip X.whenJust (windows . action)
actionMenu :: String -> [String] -> (Window -> X.WindowSet -> X.WindowSet) -> X ()
actionMenu menuCmd menuArgs action = windowMap >>= menuMapFunction >>= flip X.whenJust (windows . action)
where
menuMapFunction :: M.Map String a -> X (Maybe a)
menuMapFunction selectionMap = menuMap menuCmd selectionMap
menuMapFunction selectionMap = menuMapArgs menuCmd menuArgs selectionMap
-- | A map from window names to Windows.
windowMap :: X (M.Map String Window)

View File

@@ -80,8 +80,11 @@ ifWindows qry f el = withWindowSet $ \wins -> do
ifWindow :: Query Bool -> ManageHook -> X () -> X ()
ifWindow qry mh = ifWindows qry (windows . appEndo <=< runQuery mh . head)
-- | 'action' is an executable to be run via 'safeSpawnProg' (of "XMonad.Util.Run") if the Window cannot be found.
-- Presumably this executable is the same one that you were looking for.
{- | 'action' is an executable to be run via 'safeSpawnProg' (of "XMonad.Util.Run") if the Window cannot be found.
Presumably this executable is the same one that you were looking for.
Note that this does not go through the shell. If you wish to run an arbitrary IO action
(such as 'spawn', which will run its String argument through the shell), then you will want to use
'raiseMaybe' directly. -}
runOrRaise :: String -> Query Bool -> X ()
runOrRaise = raiseMaybe . safeSpawnProg
@@ -160,11 +163,11 @@ raiseEditor = raiseVar getEditor
{- | If the window is found the window is focused and the third argument is called
otherwise, the first argument is called
See 'raiseMaster' for an example. -}
raiseAndDo :: X () -> Query Bool -> (Window -> X ())-> X ()
raiseAndDo :: X () -> Query Bool -> (Window -> X ()) -> X ()
raiseAndDo f qry after = ifWindow qry (afterRaise `mappend` raiseHook) f
where afterRaise = ask >>= (>> idHook) . liftX . after
{- | If a window matching the second arugment is found, the window is focused and the third argument is called;
{- | If a window matching the second argument is found, the window is focused and the third argument is called;
otherwise, the first argument is called. -}
runOrRaiseAndDo :: String -> Query Bool -> (Window -> X ()) -> X ()
runOrRaiseAndDo = raiseAndDo . safeSpawnProg

View File

@@ -41,6 +41,14 @@ import XMonad.Util.XUtils (fi)
--
-- > , ((modm, xK_o ), windowMenu)
colorizer :: a -> Bool -> X (String, String)
colorizer _ isFg = do
fBC <- asks (focusedBorderColor . config)
nBC <- asks (normalBorderColor . config)
return $ if isFg
then (fBC, nBC)
else (nBC, fBC)
windowMenu :: X ()
windowMenu = withFocused $ \w -> do
tags <- asks (workspaces . config)
@@ -48,13 +56,13 @@ windowMenu = withFocused $ \w -> do
Rectangle sx sy swh sht <- gets $ screenRect . W.screenDetail . W.current . windowset
let originFractX = (fi x - fi sx + fi wh / 2) / fi swh
originFractY = (fi y - fi sy + fi ht / 2) / fi sht
gsConfig = defaultGSConfig
gsConfig = (buildDefaultGSConfig colorizer)
{ gs_originFractX = originFractX
, gs_originFractY = originFractY }
actions = [ ("Cancel menu", return ())
, ("Close" , kill)
, ("Maximize" , sendMessage $ maximizeRestore w)
, ("Minimize" , sendMessage $ MinimizeWin w)
, ("Minimize" , minimizeWindow w)
] ++
[ ("Move to " ++ tag, windows $ W.shift tag)
| tag <- tags ]

View File

@@ -36,7 +36,7 @@ module XMonad.Actions.WindowNavigation (
withWindowNavigationKeys,
WNAction(..),
go, swap,
Direction2D(..)
Direction2D(..), WNState,
) where
import XMonad
@@ -52,7 +52,6 @@ import qualified Data.Map as M
import Data.Maybe (catMaybes, fromMaybe, listToMaybe)
import Data.Ord (comparing)
import qualified Data.Set as S
import Graphics.X11.Xlib
-- $usage
--

View File

@@ -18,8 +18,6 @@ module XMonad.Actions.WithAll (
import Data.Foldable hiding (foldr)
import XMonad
import XMonad.Core
import XMonad.Operations
import XMonad.StackSet
-- $usage

View File

@@ -32,10 +32,13 @@ module XMonad.Actions.WorkspaceCursors
-- * Functions to pass to 'modifyLayer'
,focusNth'
,noWrapUp,noWrapDown
,noWrapUp,noWrapDown,
-- * Todo
-- $todo
-- * Types
Cursors,
) where
import qualified XMonad.StackSet as W

View File

@@ -0,0 +1,152 @@
-----------------------------------------------------------------------------
-- |
-- Module : XMonad.Actions.WorkspaceNames
-- Copyright : (c) Tomas Janousek <tomi@nomi.cz>
-- License : BSD3-style (see LICENSE)
--
-- Maintainer : Tomas Janousek <tomi@nomi.cz>
-- Stability : experimental
-- Portability : unportable
--
-- Provides bindings to rename workspaces, show these names in DynamicLog and
-- swap workspaces along with their names. These names survive restart.
-- Together with "XMonad.Layout.WorkspaceDir" this provides for a fully
-- dynamic topic space workflow.
--
-----------------------------------------------------------------------------
{-# LANGUAGE DeriveDataTypeable #-}
module XMonad.Actions.WorkspaceNames (
-- * Usage
-- $usage
-- * Workspace naming
renameWorkspace,
workspaceNamesPP,
getWorkspaceNames,
setWorkspaceName,
setCurrentWorkspaceName,
-- * Workspace swapping
swapTo,
swapTo',
swapWithCurrent,
) where
import XMonad
import qualified XMonad.StackSet as W
import qualified XMonad.Util.ExtensibleState as XS
import XMonad.Actions.CycleWS (findWorkspace, WSType(..), Direction1D(..))
import qualified XMonad.Actions.SwapWorkspaces as Swap
import XMonad.Hooks.DynamicLog (PP(..))
import XMonad.Prompt (mkXPrompt, XPConfig)
import XMonad.Prompt.Workspace (Wor(Wor))
import XMonad.Util.WorkspaceCompare (getSortByIndex)
import qualified Data.Map as M
import Data.Maybe (fromMaybe)
-- $usage
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@ file:
--
-- > import XMonad.Actions.WorkspaceNames
--
-- Then add keybindings like the following:
--
-- > , ((modm .|. shiftMask, xK_r ), renameWorkspace defaultXPConfig)
--
-- and apply workspaceNamesPP to your DynamicLog pretty-printer:
--
-- > myLogHook =
-- > workspaceNamesPP xmobarPP >>= dynamicLogString >>= xmonadPropLog
--
-- We also provide a modification of "XMonad.Actions.SwapWorkspaces"\'s
-- functionality, which may be used this way:
--
-- > , ((modMask .|. shiftMask, xK_Left ), swapTo Prev)
-- > , ((modMask .|. shiftMask, xK_Right ), swapTo Next)
--
-- > [((modm .|. controlMask, k), swapWithCurrent i)
-- > | (i, k) <- zip workspaces [xK_1 ..]]
--
-- For detailed instructions on editing your key bindings, see
-- "XMonad.Doc.Extending#Editing_key_bindings".
-- | Workspace names container.
newtype WorkspaceNames = WorkspaceNames (M.Map WorkspaceId String)
deriving (Typeable, Read, Show)
instance ExtensionClass WorkspaceNames where
initialValue = WorkspaceNames M.empty
extensionType = PersistentExtension
-- | Returns a function that maps workspace tag @\"t\"@ to @\"t:name\"@ for
-- workspaces with a name, and to @\"t\"@ otherwise.
getWorkspaceNames :: X (WorkspaceId -> String)
getWorkspaceNames = do
WorkspaceNames m <- XS.get
return $ \wks -> case M.lookup wks m of
Nothing -> wks
Just s -> wks ++ ":" ++ s
-- | Sets the name of a workspace. Empty string makes the workspace unnamed
-- again.
setWorkspaceName :: WorkspaceId -> String -> X ()
setWorkspaceName w name = do
WorkspaceNames m <- XS.get
XS.put $ WorkspaceNames $ if null name then M.delete w m else M.insert w name m
refresh
-- | Sets the name of the current workspace. See 'setWorkspaceName'.
setCurrentWorkspaceName :: String -> X ()
setCurrentWorkspaceName name = do
current <- gets (W.currentTag . windowset)
setWorkspaceName current name
-- | Prompt for a new name for the current workspace and set it.
renameWorkspace :: XPConfig -> X ()
renameWorkspace conf = do
mkXPrompt pr conf (const (return [])) setCurrentWorkspaceName
where pr = Wor "Workspace name: "
-- | Modify "XMonad.Hooks.DynamicLog"\'s pretty-printing format to show
-- workspace names as well.
workspaceNamesPP :: PP -> X PP
workspaceNamesPP pp = do
names <- getWorkspaceNames
return $
pp {
ppCurrent = ppCurrent pp . names,
ppVisible = ppVisible pp . names,
ppHidden = ppHidden pp . names,
ppHiddenNoWindows = ppHiddenNoWindows pp . names,
ppUrgent = ppUrgent pp . names
}
-- | See 'XMonad.Actions.SwapWorkspaces.swapTo'. This is the same with names.
swapTo :: Direction1D -> X ()
swapTo dir = swapTo' dir AnyWS
-- | Swap with the previous or next workspace of the given type.
swapTo' :: Direction1D -> WSType -> X ()
swapTo' dir which = findWorkspace getSortByIndex dir which 1 >>= swapWithCurrent
-- | See 'XMonad.Actions.SwapWorkspaces.swapWithCurrent'. This is almost the
-- same with names.
swapWithCurrent :: WorkspaceId -> X ()
swapWithCurrent t = do
current <- gets (W.currentTag . windowset)
swapNames t current
windows $ Swap.swapWorkspaces t current
-- | Swap names of the two workspaces.
swapNames :: WorkspaceId -> WorkspaceId -> X ()
swapNames w1 w2 = do
WorkspaceNames m <- XS.get
let getname w = fromMaybe "" $ M.lookup w m
set w name m' = if null name then M.delete w m' else M.insert w name m'
XS.put $ WorkspaceNames $ set w1 (getname w2) $ set w2 (getname w1) $ m

View File

@@ -38,7 +38,7 @@ import qualified Data.Map as M
-- > import qualified Data.Map as M
-- > main = xmonad someConfig { keys = \c -> azertyKeys c `M.union` keys someConfig c }
azertyConfig = defaultConfig { keys = \c -> azertyKeys c `M.union` keys defaultConfig c }
azertyConfig = defaultConfig { keys = azertyKeys <+> keys defaultConfig }
azertyKeys conf@(XConfig {modMask = modm}) = M.fromList $
[((modm, xK_semicolon), sendMessage (IncMasterN (-1)))]

View File

@@ -26,7 +26,6 @@ module XMonad.Config.Bluetile (
import XMonad hiding ( (|||) )
import XMonad.Layout hiding ( (|||) )
import XMonad.Layout.BorderResize
import XMonad.Layout.BoringWindows
import XMonad.Layout.ButtonDecoration
@@ -49,8 +48,9 @@ import XMonad.Actions.WindowMenu
import XMonad.Hooks.CurrentWorkspaceOnTop
import XMonad.Hooks.EwmhDesktops
import XMonad.Hooks.ManageDocks
import XMonad.Hooks.ManageHelpers
import XMonad.Hooks.PositionStoreHooks
import XMonad.Hooks.RestoreMinimized
import XMonad.Hooks.Minimize
import XMonad.Hooks.ServerMode
import XMonad.Hooks.WorkspaceByPos
@@ -118,8 +118,8 @@ bluetileKeys conf@(XConfig {XMonad.modMask = modMask'}) = M.fromList $
, ((modMask' , xK_period), sendMessage (IncMasterN (-1))) -- %! Deincrement the number of windows in the master area
-- quit, or restart
, ((modMask' .|. shiftMask, xK_q ), io (exitWith ExitSuccess)) -- %! Quit bluetile
, ((modMask' , xK_q ), spawn "bluetile --restart") -- %! Restart bluetile
, ((modMask' .|. shiftMask, xK_q ), io (exitWith ExitSuccess)) -- %! Quit
, ((modMask' , xK_q ), restart "xmonad" True) -- %! Restart
-- Metacity-like workspace switching
, ((mod1Mask .|. controlMask, xK_Left), prevWS)
@@ -141,7 +141,7 @@ bluetileKeys conf@(XConfig {XMonad.modMask = modMask'}) = M.fromList $
, ((modMask' , xK_z), withFocused (sendMessage . maximizeRestore))
-- Minimizing
, ((modMask', xK_m ), withFocused (\f -> sendMessage (MinimizeWin f)))
, ((modMask', xK_m ), withFocused minimizeWindow)
, ((modMask' .|. shiftMask, xK_m ), sendMessage RestoreNextMinimizedWin)
]
++
@@ -177,11 +177,12 @@ isFloating w = do
bluetileManageHook :: ManageHook
bluetileManageHook = composeAll
[ workspaceByPos, positionStoreManageHook
[ workspaceByPos, positionStoreManageHook (Just defaultThemeWithButtons)
, className =? "MPlayer" --> doFloat
, isFullscreen --> doFullFloat
, manageDocks]
bluetileLayoutHook = avoidStruts $ boringAuto $ minimize $ (
bluetileLayoutHook = avoidStruts $ minimize $ boringWindows $ (
named "Floating" floating |||
named "Tiled1" tiled1 |||
named "Tiled2" tiled2 |||
@@ -203,13 +204,14 @@ bluetileConfig =
layoutHook = bluetileLayoutHook,
logHook = currentWorkspaceOnTop >> ewmhDesktopsLogHook,
handleEventHook = ewmhDesktopsEventHook
`mappend` restoreMinimizedEventHook
`mappend` fullscreenEventHook
`mappend` minimizeEventHook
`mappend` serverModeEventHook' bluetileCommands
`mappend` positionStoreEventHook,
workspaces = bluetileWorkspaces,
keys = bluetileKeys,
mouseBindings = bluetileMouseBindings,
focusFollowsMouse = False,
focusedBorderColor = "#ff5500",
focusFollowsMouse = False,
focusedBorderColor = "#000000",
terminal = "gnome-terminal"
}

View File

@@ -54,7 +54,6 @@ module XMonad.Config.Desktop (
) where
import XMonad
import XMonad.Config (defaultConfig)
import XMonad.Hooks.ManageDocks
import XMonad.Hooks.EwmhDesktops
import XMonad.Util.Cursor
@@ -127,7 +126,7 @@ import qualified Data.Map as M
-- To add to the logHook while still sending workspace and window information
-- to DE apps use something like:
--
-- > , logHook = myLogHook >> logHook desktopConfig
-- > , logHook = myLogHook <+> logHook desktopConfig
--
-- Or for more elaborate logHooks you can use @do@:
--
@@ -139,25 +138,23 @@ import qualified Data.Map as M
-- $eventHook
-- To customize xmonad's event handling while still having it respond
-- to EWMH events from pagers, task bars, etc. add to your imports:
-- to EWMH events from pagers, task bars:
--
-- > import Data.Monoid
-- > , handleEventHook = myEventHooks <+> handleEventHook desktopConfig
--
-- and use 'Data.Monoid.mappend' to combine event hooks (right to left application like @\<+\>@)
--
-- > , handleEventHook = mappend myEventHooks (handleEventHook desktopConfig)
--
-- or 'Data.Monoid.mconcat' (like @composeAll@)
-- or 'mconcat' if you write a list event of event hooks
--
-- > , handleEventHook = mconcat
-- > [ myMouseHandler
-- > , myMessageHandler
-- > , handleEventHook desktopConfig ]
--
-- Note that the event hooks are run left to right (in contrast to
-- 'ManageHook'S which are right to left)
-- $startupHook
-- To run the desktop startupHook, plus add further actions to be run each
-- time xmonad starts or restarts, use '>>' to combine actions as in the
-- time xmonad starts or restarts, use '<+>' to combine actions as in the
-- logHook example, or something like:
--
-- > , startupHook = do
@@ -170,7 +167,7 @@ desktopConfig = ewmh defaultConfig
{ startupHook = setDefaultCursor xC_left_ptr
, layoutHook = desktopLayoutModifiers $ layoutHook defaultConfig
, manageHook = manageHook defaultConfig <+> manageDocks
, keys = \c -> desktopKeys c `M.union` keys defaultConfig c }
, keys = desktopKeys <+> keys defaultConfig }
desktopKeys (XConfig {modMask = modm}) = M.fromList $
[ ((modm, xK_b), sendMessage ToggleStruts) ]

View File

@@ -1,4 +1,5 @@
{-# OPTIONS_GHC -fno-warn-missing-signatures -fglasgow-exts -fno-warn-orphans #-}
{-# LANGUAGE PatternGuards #-}
{-# OPTIONS_GHC -fno-warn-missing-signatures -fno-warn-orphans #-}
-----------------------------------------------------------------------------
-- |
-- Copyright : (c) Spencer Janssen 2007
@@ -10,7 +11,6 @@ module XMonad.Config.Droundy ( config, mytab ) where
import XMonad hiding (keys, config, (|||))
import qualified XMonad (keys)
import XMonad.Config ( defaultConfig )
import qualified XMonad.StackSet as W
import qualified Data.Map as M

View File

@@ -41,7 +41,7 @@ import System.Environment (getEnvironment)
gnomeConfig = desktopConfig
{ terminal = "gnome-terminal"
, keys = \c -> gnomeKeys c `M.union` keys desktopConfig c
, keys = gnomeKeys <+> keys desktopConfig
, startupHook = gnomeRegister >> startupHook desktopConfig }
gnomeKeys (XConfig {modMask = modm}) = M.fromList $

View File

@@ -40,11 +40,11 @@ import qualified Data.Map as M
kdeConfig = desktopConfig
{ terminal = "konsole"
, keys = \c -> kdeKeys c `M.union` keys desktopConfig c }
, keys = kdeKeys <+> keys desktopConfig }
kde4Config = desktopConfig
{ terminal = "konsole"
, keys = \c -> kde4Keys c `M.union` keys desktopConfig c }
, keys = kde4Keys <+> keys desktopConfig }
kdeKeys (XConfig {modMask = modm}) = M.fromList $
[ ((modm, xK_p), spawn "dcop kdesktop default popupExecuteCommand")

View File

@@ -6,7 +6,6 @@ import qualified XMonad.StackSet as W
import XMonad.Actions.CopyWindow
import XMonad.Layout.Tabbed
import XMonad.Layout.HintedTile
import XMonad.Config (defaultConfig)
import XMonad.Layout.NoBorders
import XMonad.Hooks.DynamicLog
import XMonad.Hooks.ManageDocks

View File

@@ -36,7 +36,7 @@ import qualified Data.Map as M
xfceConfig = desktopConfig
{ terminal = "Terminal"
, keys = \c -> xfceKeys c `M.union` keys desktopConfig c }
, keys = xfceKeys <+> keys desktopConfig }
xfceKeys (XConfig {modMask = modm}) = M.fromList $
[ ((modm, xK_p), spawn "xfrun4")

View File

@@ -256,6 +256,9 @@ xmonad contributed extensions.
* Code should be compilable with "ghc-options: -Wall -Werror" set in the
xmonad-contrib.cabal file. There should be no warnings.
* Code should be free of any warnings or errors from the Hlint tool; use your
best judgement on some warnings like eta-reduction or bracket removal, though.
* Partial functions should be avoided: the window manager should not
crash, so never call 'error' or 'undefined'.

View File

@@ -382,13 +382,17 @@ Here is a list of the modules found in @XMonad.Hooks@:
* "XMonad.Hooks.ManageHelpers": provide helper functions to be used
in @manageHook@.
* "XMonad.Hooks.Minimize":
Handles window manager hints to minimize and restore windows. Use
this with XMonad.Layout.Minimize.
* "XMonad.Hooks.Place":
Automatic placement of floating windows.
* "XMonad.Hooks.RestoreMinimized":
Lets you restore minimized windows (see "XMonad.Layout.Minimize")
by selecting them on a taskbar (listens for _NET_ACTIVE_WINDOW
and WM_CHANGE_STATE).
(Deprecated: Use XMonad.Hooks.Minimize) Lets you restore minimized
windows (see "XMonad.Layout.Minimize") by selecting them on a
taskbar (listens for _NET_ACTIVE_WINDOW and WM_CHANGE_STATE).
* "XMonad.Hooks.Script":
Provides a simple interface for running a ~\/.xmonad\/hooks script with the
@@ -932,16 +936,18 @@ example, you could write:
and provide an appropriate definition of @myKeys@, such as:
> myKeys conf@(XConfig {XMonad.modMask = modm}) =
> myKeys conf@(XConfig {XMonad.modMask = modm}) = M.fromList
> [ ((modm, xK_F12), xmonadPrompt defaultXPConfig)
> , ((modm, xK_F3 ), shellPrompt defaultXPConfig)
> ]
This particular definition also requires importing "XMonad.Prompt",
"XMonad.Prompt.Shell", and "XMonad.Prompt.XMonad":
"XMonad.Prompt.Shell", "XMonad.Prompt.XMonad", and "Data.Map":
> import XMonadPrompt
> import ... -- and so on
> import qualified Data.Map as M
> import XMonad.Prompt
> import XMonad.Prompt.Shell
> import XMonad.Prompt.XMonad
For a list of the names of particular keys (such as xK_F12, and so
on), see
@@ -977,7 +983,7 @@ module, before starting we must first import this modules:
For instance, if you have defined some additional key bindings like
these:
> myKeys conf@(XConfig {XMonad.modMask = modm}) =
> myKeys conf@(XConfig {XMonad.modMask = modm}) = M.fromList
> [ ((modm, xK_F12), xmonadPrompt defaultXPConfig)
> , ((modm, xK_F3 ), shellPrompt defaultXPConfig)
> ]
@@ -985,13 +991,19 @@ these:
then you can create a new key bindings map by joining the default one
with yours:
> newKeys x = M.union (keys defaultConfig x) (M.fromList (myKeys x))
> newKeys x = myKeys x `M.union` keys defaultConfig x
Finally, you can use @newKeys@ in the 'XMonad.Core.XConfig.keys' field
of the configuration:
> main = xmonad $ defaultConfig { keys = newKeys }
Alternatively, the '<+>' operator can be used which in this usage does exactly
the same as the explicit usage of 'M.union' and propagation of the config
argument, thanks to appropriate instances in "Data.Monoid".
> main = xmonad $ defaultConfig { keys = myKeys <+> keys defaultConfig }
All together, your @~\/.xmonad\/xmonad.hs@ would now look like this:
@@ -1006,11 +1018,9 @@ All together, your @~\/.xmonad\/xmonad.hs@ would now look like this:
> import XMonad.Prompt.XMonad
>
> main :: IO ()
> main = xmonad $ defaultConfig { keys = newKeys }
> main = xmonad $ defaultConfig { keys = myKeys <+> keys defaultConfig }
>
> newKeys x = M.union (keys defaultConfig x) (M.fromList (myKeys x))
>
> myKeys conf@(XConfig {XMonad.modMask = modm}) =
> myKeys conf@(XConfig {XMonad.modMask = modm}) = M.fromList
> [ ((modm, xK_F12), xmonadPrompt defaultXPConfig)
> , ((modm, xK_F3 ), shellPrompt defaultXPConfig)
> ]
@@ -1034,10 +1044,10 @@ For example, suppose you want to get rid of @mod-q@ and @mod-shift-q@
to define @newKeys@ as a 'Data.Map.difference' between the default
map and the map of the key bindings you want to remove. Like so:
> newKeys x = M.difference (keys defaultConfig x) (M.fromList $ keysToRemove x)
> newKeys x = keys defaultConfig x `M.difference` keysToRemove x
>
> keysToRemove :: XConfig Layout -> [((KeyMask, KeySym),X ())]
> keysToRemove x =
> keysToRemove :: XConfig Layout -> M.Map (KeyMask, KeySym) (X ())
> keysToRemove x = M.fromList
> [ ((modm , xK_q ), return ())
> , ((modm .|. shiftMask, xK_q ), return ())
> ]
@@ -1164,7 +1174,7 @@ Suppose we want a list with the 'XMonad.Layout.Full',
Then we create the combination of layouts we need:
> mylayoutHook = Full ||| tabbed shrinkText defaultTConf ||| Accordion
> mylayoutHook = Full ||| tabbed shrinkText defaultTheme ||| Accordion
Now, all we need to do is change the 'XMonad.Core.layoutHook'
@@ -1178,11 +1188,11 @@ example, suppose we want to use the
'XMonad.Layout.NoBorders.noBorders' layout modifier, from the
"XMonad.Layout.NoBorders" module (which must be imported):
> mylayoutHook = noBorders (Full ||| tabbed shrinkText defaultTConf ||| Accordion)
> mylayoutHook = noBorders (Full ||| tabbed shrinkText defaultTheme ||| Accordion)
If we want only the tabbed layout without borders, then we may write:
> mylayoutHook = Full ||| noBorders (tabbed shrinkText defaultTConf) ||| Accordion
> mylayoutHook = Full ||| noBorders (tabbed shrinkText defaultTheme) ||| Accordion
Our @~\/.xmonad\/xmonad.hs@ will now look like this:
@@ -1192,7 +1202,7 @@ Our @~\/.xmonad\/xmonad.hs@ will now look like this:
> import XMonad.Layout.Accordion
> import XMonad.Layout.NoBorders
>
> mylayoutHook = Full ||| noBorders (tabbed shrinkText defaultTConf) ||| Accordion
> mylayoutHook = Full ||| noBorders (tabbed shrinkText defaultTheme) ||| Accordion
>
> main = xmonad $ defaultConfig { layoutHook = mylayoutHook }

View File

@@ -26,6 +26,7 @@ import XMonad
import qualified XMonad.StackSet as S
import qualified XMonad.Util.ExtensibleState as XS
import Control.Monad(when)
import qualified Data.Map as M
-- $usage
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
@@ -50,12 +51,18 @@ currentWorkspaceOnTop = withDisplay $ \d -> do
(CWOTS lastTag) <- XS.get
let curTag = S.tag . S.workspace . S.current $ ws
when (curTag /= lastTag) $ do
-- the following is more or less a reimplementation of what's happening in "XMonad.Operation"
let s = S.current ws
wsp = S.workspace s
viewrect = screenRect $ S.screenDetail s
tmpStack = S.stack . S.workspace $ s
(rs, _) <- runLayout wsp { S.stack = tmpStack } viewrect
let wins = map fst rs
tmpStack = (S.stack wsp) >>= S.filter (`M.notMember` S.floating ws)
(rs, ml') <- runLayout wsp { S.stack = tmpStack } viewrect
updateLayout curTag ml'
let this = S.view curTag ws
fltWins = filter (flip M.member (S.floating ws)) $ S.index this
wins = fltWins ++ (map fst rs) -- order: first all floating windows, then the order the layout returned
-- end of reimplementation
when (not . null $ wins) $ do
io $ raiseWindow d (head wins) -- raise first window of current workspace to the very top,
io $ restackWindows d wins -- then use restackWindows to let all other windows from the workspace follow

View File

@@ -0,0 +1,107 @@
-----------------------------------------------------------------------------
-- |
-- Module : XMonad.Hooks.DebugKeyEvents
-- Copyright : (c) 2011 Brandon S Allbery <allbery.b@gmail.com>
-- License : BSD
--
-- Maintainer : Brandon S Allbery <allbery.b@gmail.com>
-- Stability : unstable
-- Portability : unportable
--
-- A debugging module to track key events, useful when you can't tell whether
-- xmonad is processing some or all key events.
-----------------------------------------------------------------------------
module XMonad.Hooks.DebugKeyEvents (-- * Usage
-- $usage
debugKeyEvents
) where
import XMonad.Core
import XMonad.Operations (cleanMask)
import Graphics.X11.Xlib
import Graphics.X11.Xlib.Extras
import Control.Monad.State (gets)
import Data.Bits
import Data.List (intercalate)
import Data.Monoid
import Numeric (showHex)
import System.IO (hPutStrLn
,stderr)
-- $usage
-- Add this to your handleEventHook to print received key events to the
-- log (the console if you use @startx@/@xinit@, otherwise usually
-- @~/.xsession-errors@).
--
-- > , handleEventHook = debugKeyEvents
--
-- If you already have a handleEventHook then you should append it:
--
-- > , handleEventHook = ... <+> debugKeyEvents
--
-- 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.)
--
-- For more detailed instructions on editing the logHook see:
--
-- "XMonad.Doc.Extending#The_log_hook_and_external_status_bars"
-- | Print key events to stderr for debugging
debugKeyEvents :: Event -> X All
debugKeyEvents (KeyEvent {ev_event_type = t, ev_state = m, ev_keycode = code})
| t == keyPress =
withDisplay $ \dpy -> do
sym <- io $ keycodeToKeysym dpy code 0
msk <- cleanMask m
nl <- gets numberlockMask
io $ hPutStrLn stderr $ intercalate " " ["keycode"
,show code
,"sym"
,show sym
," ("
,hex sym
," \""
,keysymToString sym
,"\") mask"
,hex m
,"(" ++ vmask nl m ++ ")"
,"clean"
,hex msk
,"(" ++ vmask nl msk ++ ")"
]
return (All True)
debugKeyEvents _ = return (All True)
-- | Convenient showHex variant
hex :: (Integral n, Show n) => n -> String
hex v = "0x" ++ showHex v ""
-- | Convert a modifier mask into a useful string
vmask :: KeyMask -> KeyMask -> String
vmask numLockMask msk = intercalate " " $
reverse $
fst $
foldr vmask' ([],msk) masks
where
masks = map (\m -> (m,show m)) [0..toEnum (bitSize msk - 1)] ++
[(numLockMask,"num" )
,( lockMask,"lock" )
,(controlMask,"ctrl" )
,( shiftMask,"shift")
,( mod5Mask,"mod5" )
,( mod4Mask,"mod4" )
,( mod3Mask,"mod3" )
,( mod2Mask,"mod2" )
,( mod1Mask,"mod1" )
]
vmask' _ a@( _,0) = a
vmask' (m,s) (ss,v) | v .&. m == m = (s:ss,v .&. complement m)
vmask' _ r = r

View File

@@ -29,6 +29,7 @@ module XMonad.Hooks.DynamicLog (
dynamicLog,
dynamicLogXinerama,
xmonadPropLog',
xmonadPropLog,
-- * Build your own formatter
@@ -53,27 +54,26 @@ module XMonad.Hooks.DynamicLog (
) where
--
-- Useful imports
--
import XMonad
import Control.Monad
import Codec.Binary.UTF8.String (encodeString)
import Control.Monad (liftM2)
import Data.Char ( isSpace, ord )
import Data.List (intersperse, isPrefixOf, sortBy)
import Data.Maybe ( isJust, catMaybes )
import Data.List
import qualified Data.Map as M
import Data.Ord ( comparing )
import qualified Data.Map as M
import qualified XMonad.StackSet as S
import System.IO
import Foreign.C (CChar)
import XMonad
import XMonad.Util.WorkspaceCompare
import XMonad.Util.NamedWindows
import XMonad.Util.Run
import XMonad.Layout.LayoutModifier
import XMonad.Util.Font
import XMonad.Hooks.UrgencyHook
import XMonad.Hooks.ManageDocks
@@ -209,20 +209,24 @@ statusBar cmd pp k conf = do
where
keys' = (`M.singleton` sendMessage ToggleStruts) . k
-- | Write a string to the property _XMONAD_LOG on the root window. This
-- property is of type UTF8_STRING. The string must have been processed by
-- encodeString (dynamicLogString does this).
xmonadPropLog :: String -> X ()
xmonadPropLog msg = do
-- | Write a string to a property on the root window. This property is of
-- type UTF8_STRING. The string must have been processed by encodeString
-- (dynamicLogString does this).
xmonadPropLog' :: String -> String -> X ()
xmonadPropLog' prop msg = do
d <- asks display
r <- asks theRoot
xlog <- getAtom "_XMONAD_LOG"
xlog <- getAtom prop
ustring <- getAtom "UTF8_STRING"
io $ changeProperty8 d r xlog ustring propModeReplace (encodeCChar msg)
where
encodeCChar :: String -> [CChar]
encodeCChar = map (fromIntegral . ord)
-- | Write a string to the _XMONAD_LOG property on the root window.
xmonadPropLog :: String -> X ()
xmonadPropLog = xmonadPropLog' "_XMONAD_LOG"
-- |
-- Helper function which provides ToggleStruts keybinding
--
@@ -270,9 +274,9 @@ dynamicLogString pp = do
wt <- maybe (return "") (fmap show . getName) . S.peek $ winset
-- run extra loggers, ignoring any that generate errors.
extras <- sequence $ map (flip catchX (return Nothing)) $ ppExtras pp
extras <- mapM (flip catchX (return Nothing)) $ ppExtras pp
return $ encodeOutput . sepBy (ppSep pp) . ppOrder pp $
return $ encodeString . sepBy (ppSep pp) . ppOrder pp $
[ ws
, ppLayout pp ld
, ppTitle pp wt
@@ -303,10 +307,15 @@ pprWindowSet sort' urgents pp s = sepBy (ppWsSep pp) . map fmt . sort' $
-- where 1, 9, and 3 are the workspaces on screens 1, 2 and 3, respectively,
-- and 2 and 7 are non-visible, non-empty workspaces.
--
-- Unfortunately, at the present time, the current layout and window title
-- are not shown, and there is no way to incorporate the xinerama
-- workspace format shown above with 'dynamicLogWithPP'. Hopefully this
-- will change soon.
-- At the present time, the current layout and window title
-- are not shown. The xinerama workspace format shown above can be (mostly) replicated
-- using 'dynamicLogWithPP' by setting 'ppSort' to /getSortByXineramaRule/ from
-- "XMonad.Util.WorkspaceCompare". For example,
--
-- > defaultPP { ppCurrent = dzenColor "red" "#efebe7"
-- > , ppVisible = wrap "[" "]"
-- > , ppSort = getSortByXineramaRule
-- > }
dynamicLogXinerama :: X ()
dynamicLogXinerama = withWindowSet $ io . putStrLn . pprWindowSetXinerama
@@ -337,7 +346,7 @@ trim = f . f
-- | Limit a string to a certain length, adding "..." if truncated.
shorten :: Int -> String -> String
shorten n xs | length xs < n = xs
| otherwise = (take (n - length end) xs) ++ end
| otherwise = take (n - length end) xs ++ end
where
end = "..."
@@ -476,11 +485,11 @@ dzenPP = defaultPP { ppCurrent = dzenColor "white" "#2b4f98" . pad
, ppWsSep = ""
, ppSep = ""
, ppLayout = dzenColor "black" "#cccccc" .
(\ x -> case x of
"TilePrime Horizontal" -> " TTT "
"TilePrime Vertical" -> " []= "
"Hinted Full" -> " [ ] "
_ -> pad x
(\ x -> pad $ case x of
"TilePrime Horizontal" -> "TTT"
"TilePrime Vertical" -> "[]="
"Hinted Full" -> "[ ]"
_ -> x
)
, ppTitle = ("^bg(#324c80) " ++) . dzenEscape
}
@@ -515,4 +524,3 @@ byorgeyPP = defaultPP { ppHiddenNoWindows = showNamedWorkspaces
where showNamedWorkspaces wsId = if any (`elem` wsId) ['a'..'z']
then pad wsId
else ""

View File

@@ -19,7 +19,8 @@ module XMonad.Hooks.EwmhDesktops (
ewmhDesktopsStartup,
ewmhDesktopsLogHook,
ewmhDesktopsLogHookCustom,
ewmhDesktopsEventHook
ewmhDesktopsEventHook,
fullscreenEventHook
) where
import Codec.Binary.UTF8.String (encode)
@@ -34,6 +35,7 @@ import qualified XMonad.StackSet as W
import XMonad.Hooks.SetWMName
import XMonad.Util.XUtils (fi)
import XMonad.Util.WorkspaceCompare
import XMonad.Util.WindowProperties (getProp32)
-- $usage
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
@@ -154,6 +156,36 @@ handle ClientMessageEvent {
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.
fullscreenEventHook :: Event -> X All
fullscreenEventHook (ClientMessageEvent _ _ _ dpy win typ (action:dats)) = do
state <- getAtom "_NET_WM_STATE"
fullsc <- getAtom "_NET_WM_STATE_FULLSCREEN"
wstate <- fromMaybe [] `fmap` getProp32 state win
let isFull = fromIntegral fullsc `elem` wstate
-- Constants for the _NET_WM_STATE protocol:
remove = 0
add = 1
toggle = 2
ptype = 4 -- The atom property type for changeProperty
chWstate f = io $ changeProperty32 dpy win state ptype propModeReplace (f wstate)
when (typ == state && 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
when (action == remove || (action == toggle && isFull)) $ do
chWstate $ delete (fi fullsc)
windows $ W.sink win
return $ All True
fullscreenEventHook _ = return $ All True
setNumberOfDesktops :: (Integral a) => a -> X ()
setNumberOfDesktops n = withDisplay $ \dpy -> do

221
XMonad/Hooks/FadeWindows.hs Normal file
View File

@@ -0,0 +1,221 @@
{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}
-----------------------------------------------------------------------------
-- |
-- Module : XMonad.Hooks.FadeWindows
-- Copyright : Brandon S Allbery KF8NH <allbery.b@gmail.com>
-- License : BSD
--
-- Maintainer : Brandon S Allbery KF8NH
-- Stability : unstable
-- Portability : unportable
--
-- A more flexible and general compositing interface than FadeInactive.
-- Windows can be selected and opacity specified by means of FadeHooks,
-- which are very similar to ManageHooks and use the same machinery.
--
-----------------------------------------------------------------------------
module XMonad.Hooks.FadeWindows (-- * Usage
-- $usage
-- * The 'logHook' for window fading
fadeWindowsLogHook
-- * The 'FadeHook'
,FadeHook
,Opacity
,idFadeHook
-- * Predefined 'FadeHook's
,opaque
,solid
,transparent
,invisible
,transparency
,translucence
,fadeBy
,opacity
,fadeTo
-- * 'handleEventHook' for mapped/unmapped windows
,fadeWindowsEventHook
-- * 'doF' for simple hooks
,doS
-- * Useful 'Query's for 'FadeHook's
,isFloating
,isUnfocused
) where
import XMonad.Core
import XMonad.ManageHook (liftX)
import qualified XMonad.StackSet as W
import XMonad.Hooks.FadeInactive (setOpacity
,isUnfocused
)
import Control.Monad (forM_)
import Control.Monad.Reader (ask
,asks)
import Control.Monad.State (gets)
import qualified Data.Map as M
import Data.Monoid
import Graphics.X11.Xlib.Extras (Event(..))
-- $usage
-- To use this module, make sure your @xmonad@ core supports generalized
-- 'ManageHook's (check the type of 'idHook'; if it's @ManageHook@ then
-- your @xmonad@ is too old) and then add @fadeWindowsLogHook@ to your
-- 'logHook' and @fadeWindowsEventHook@ to your 'handleEventHook':
--
-- > , logHook = fadeWindowsLogHook myFadeHook
-- > , handleEventHook = fadeWindowsEventHook
-- > {- ... -}
-- >
-- > myFadeHook = composeAll [isUnfocused --> transparency 0.2
-- > , opaque
-- > ]
--
-- The above is like FadeInactive with a fade value of 0.2.
--
-- FadeHooks do not accumulate; instead, they compose from right to
-- left like 'ManageHook's, so the above example @myFadeHook@ will
-- render unfocused windows at 4/5 opacity and the focused window
-- as opaque. The 'opaque' hook above is optional, by the way, as any
-- unmatched window will be opaque by default.
--
-- This module is best used with "XMonad.Hooks.MoreManageHelpers", which
-- exports a number of Queries that can be used in either @ManageHook@
-- or @FadeHook@.
--
-- Note that you need a compositing manager such as @xcompmgr@,
-- @dcompmgr@, or @cairo-compmgr@ for window fading to work. If you
-- aren't running a compositing manager, the opacity will be recorded
-- but won't take effect until a compositing manager is started.
--
-- For more detailed instructions on editing the 'logHook' see:
--
-- "XMonad.Doc.Extending#The_log_hook_and_external_status_bars"
--
-- For more detailed instructions on editing the 'handleEventHook',
-- see:
--
-- "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
-- window is unmapped and remapped, to the machine becoming sluggish
-- until the compositing manager is restarted (at which point a
-- popup/dialog will suddenly appear; apparently it's getting into
-- a tight loop trying to fade the popup in). I find it useful to
-- have a key binding to restart the compositing manager; for example,
--
-- main = xmonad $ defaultConfig {
-- {- ... -}
-- }
-- `additionalKeysP`
-- [("M-S-4",spawn "killall xcompmgr; sleep 1; xcompmgr -cCfF &")]
-- {- ... -}
-- ]
--
-- (See "XMonad.Util.EZConfig" for 'additionalKeysP'.)
-- a window opacity to be carried in a Query. OEmpty is sort of a hack
-- to make it obay the monoid laws
data Opacity = Opacity Rational | OEmpty
instance Monoid Opacity where
mempty = OEmpty
r `mappend` OEmpty = r
_ `mappend` r = r
-- | A FadeHook is similar to a ManageHook, but records window opacity.
type FadeHook = Query Opacity
-- | Render a window fully opaque.
opaque :: FadeHook
opaque = doS (Opacity 1)
-- | Render a window fully transparent.
transparent :: FadeHook
transparent = doS (Opacity 0)
-- | Specify a window's transparency.
transparency :: Rational -- ^ The window's transparency as a fraction.
-- @transparency 1@ is the same as 'transparent',
-- whereas @transparency 0@ is the same as 'opaque'.
-> FadeHook
transparency = doS . Opacity . (1-) . clampRatio
-- | Specify a window's opacity; this is the inverse of 'transparency'.
opacity :: Rational -- ^ The opacity of a window as a fraction.
-- @opacity 1@ is the same as 'opaque',
-- whereas @opacity 0@ is the same as 'transparent'.
-> FadeHook
opacity = doS . Opacity . clampRatio
fadeTo, translucence, fadeBy :: Rational -> FadeHook
-- ^ An alias for 'transparency'.
fadeTo = transparency
-- ^ An alias for 'transparency'.
translucence = transparency
-- ^ An alias for 'transparency'.
fadeBy = opacity
invisible, solid :: FadeHook
-- ^ An alias for 'transparent'.
invisible = transparent
-- ^ An alias for 'opaque'.
solid = opaque
-- | Like 'doF', but usable with 'ManageHook'-like hooks that
-- aren't 'Query' wrapped around transforming functions ('Endo').
doS :: Monoid m => m -> Query m
doS = return
-- | The identity 'FadeHook', which renders windows 'opaque'.
idFadeHook :: FadeHook
idFadeHook = opaque
-- | A Query to determine if a window is floating.
isFloating :: Query Bool
isFloating = ask >>= \w -> liftX . gets $ M.member w . W.floating . windowset
-- boring windows can't be seen outside of a layout, so we watch messages with
-- a dummy LayoutModifier and stow them in a persistent bucket. this is not
-- entirely reliable given that boringAuto still isn't observable; we just hope
-- those aren't visible and won;t be affected anyway
-- @@@ punted for now, will be a separate module. it's still slimy, though
-- | A 'logHook' to fade windows under control of a 'FadeHook', which is
-- similar to but not identical to 'ManageHook'.
fadeWindowsLogHook :: FadeHook -> X ()
fadeWindowsLogHook h = withWindowSet $ \s -> do
let visibleWins = (W.integrate' . W.stack . W.workspace . W.current $ s) ++
concatMap (W.integrate' . W.stack . W.workspace) (W.visible s)
forM_ visibleWins $ \w -> do
o <- userCodeDef (Opacity 1) (runQuery h w)
setOpacity w $ case o of
OEmpty -> 0.93
Opacity r -> r
-- | A 'handleEventHook' to handle fading and unfading of newly mapped
-- or unmapped windows; this avoids problems with layouts such as
-- "XMonad.Layout.Full" or "XMonad.Layout.Tabbed". This hook may
-- also be useful with "XMonad.Hooks.FadeInactive".
fadeWindowsEventHook :: Event -> X All
fadeWindowsEventHook (MapNotifyEvent {}) =
-- we need to run the fadeWindowsLogHook. only one way...
asks config >>= logHook >> return (All True)
fadeWindowsEventHook _ = return (All True)
-- A utility to clamp opacity fractions to the range (0,1)
clampRatio :: Rational -> Rational
clampRatio r | r >= 0 && r <= 1 = r
| r < 0 = 0
| otherwise = 1

View File

@@ -2,10 +2,10 @@
-----------------------------------------------------------------------------
-- |
-- Module : XMonad.Hooks.FloatNext
-- Copyright : Quentin Moser <quentin.moser@unifr.ch>
-- Copyright : Quentin Moser <moserq@gmail.com>
-- License : BSD-style (see LICENSE)
--
-- Maintainer : Quentin Moser <quentin.moser@unifr.ch>
-- Maintainer : orphaned
-- Stability : unstable
-- Portability : unportable
--
@@ -36,38 +36,11 @@ module XMonad.Hooks.FloatNext ( -- * Usage
, willFloatAllNewPP
, runLogHook ) where
import Prelude hiding (all)
import XMonad
import qualified XMonad.Util.ExtensibleState as XS
import XMonad.Hooks.ToggleHook
import Control.Monad (join,guard)
import Control.Applicative ((<$>))
import Control.Arrow (first, second)
{- Helper functions -}
_set :: ((a -> a) -> (Bool, Bool) -> (Bool, Bool)) -> a -> X ()
_set f b = modify' (f $ const b)
_toggle :: ((Bool -> Bool) -> (Bool, Bool) -> (Bool, Bool)) -> X ()
_toggle f = modify' (f not)
_get :: ((Bool, Bool) -> a) -> X a
_get f = XS.gets (f . getFloatMode)
_pp :: ((Bool, Bool) -> Bool) -> String -> (String -> String) -> X (Maybe String)
_pp f s st = (\b -> guard b >> Just (st s)) <$> _get f
{- The current state is kept here -}
data FloatMode = FloatMode { getFloatMode :: (Bool,Bool) } deriving (Typeable)
instance ExtensionClass FloatMode where
initialValue = FloatMode (False,False)
modify' :: ((Bool,Bool) -> (Bool,Bool)) -> X ()
modify' f = XS.modify (FloatMode . f . getFloatMode)
hookName :: String
hookName = "__float"
-- $usage
-- This module provides actions (that can be set as keybindings)
@@ -95,33 +68,31 @@ modify' f = XS.modify (FloatMode . f . getFloatMode)
-- | This 'ManageHook' will selectively float windows as set
-- by 'floatNext' and 'floatAllNew'.
floatNextHook :: ManageHook
floatNextHook = do (next, all) <- liftX $ XS.gets getFloatMode
liftX $ XS.put $ FloatMode (False, all)
if next || all then doFloat else idHook
floatNextHook = toggleHook hookName doFloat
-- | @floatNext True@ arranges for the next spawned window to be
-- sent to the floating layer, @floatNext False@ cancels it.
floatNext :: Bool -> X ()
floatNext = _set first
floatNext = hookNext hookName
toggleFloatNext :: X ()
toggleFloatNext = _toggle first
toggleFloatNext = toggleHookNext hookName
-- | @floatAllNew True@ arranges for new windows to be
-- sent to the floating layer, @floatAllNew False@ cancels it
floatAllNew :: Bool -> X ()
floatAllNew = _set second
floatAllNew = hookAllNew hookName
toggleFloatAllNew :: X ()
toggleFloatAllNew = _toggle second
toggleFloatAllNew = toggleHookAllNew hookName
-- | Whether the next window will be set floating
willFloatNext :: X Bool
willFloatNext = _get fst
willFloatNext = willHookNext hookName
-- | Whether new windows will be set floating
willFloatAllNew :: X Bool
willFloatAllNew = _get snd
willFloatAllNew = willHookAllNew hookName
-- $pp
-- The following functions are used to display the current
@@ -143,10 +114,7 @@ willFloatAllNew = _get snd
-- pass them 'id'.
willFloatNextPP :: (String -> String) -> X (Maybe String)
willFloatNextPP = _pp fst "Next"
willFloatNextPP = willHookNextPP hookName
willFloatAllNewPP :: (String -> String) -> X (Maybe String)
willFloatAllNewPP = _pp snd "All"
runLogHook :: X ()
runLogHook = join $ asks $ logHook . config
willFloatAllNewPP = willHookAllNewPP hookName

View File

@@ -0,0 +1,57 @@
-----------------------------------------------------------------------------
-- |
-- Module : XMonad.Hooks.ICCCMFocus
-- License : BSD
--
-- Maintainer : Tony Morris <haskell@tmorris.net>
--
-- Implemented in your @logHook@, Java swing applications will not misbehave
-- when it comes to taking and losing focus.
--
-- This has been done by taking the patch in <http://code.google.com/p/xmonad/issues/detail?id=177> and refactoring it so that it can be included in @~\/.xmonad\/xmonad.hs@.
--
-- @
-- conf' =
-- conf {
-- logHook = takeTopFocus
-- }
-- @
-----------------------------------------------------------------------------
module XMonad.Hooks.ICCCMFocus
(
atom_WM_TAKE_FOCUS
, takeFocusX
, takeTopFocus
) where
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
-- | The value to add to your log hook configuration.
takeTopFocus ::
X ()
takeTopFocus =
(withWindowSet $ maybe (setFocusX =<< asks theRoot) takeFocusX . W.peek) >> setWMName "LG3D"

View File

@@ -1,5 +1,4 @@
{-# LANGUAGE PatternGuards, FlexibleInstances, MultiParamTypeClasses #-}
{-# OPTIONS -fglasgow-exts #-}
{-# LANGUAGE DeriveDataTypeable, PatternGuards, FlexibleInstances, MultiParamTypeClasses, CPP #-}
-- deriving Typeable for ghc-6.6 compatibility, which is retained in the core
-----------------------------------------------------------------------------
-- |
@@ -18,10 +17,17 @@ module XMonad.Hooks.ManageDocks (
-- * Usage
-- $usage
manageDocks, checkDock, AvoidStruts, avoidStruts, avoidStrutsOn,
docksEventHook,
ToggleStruts(..),
SetStruts(..),
module XMonad.Util.Types,
#ifdef TESTING
r2c,
c2r,
RectC(..),
#endif
-- for XMonad.Actions.FloatSnap
calcGap
) where
@@ -30,10 +36,11 @@ module XMonad.Hooks.ManageDocks (
-----------------------------------------------------------------------------
import XMonad
import Foreign.C.Types (CLong)
import Control.Monad
import XMonad.Layout.LayoutModifier
import XMonad.Util.Types
import XMonad.Util.WindowProperties (getProp32s)
import XMonad.Util.XUtils (fi)
import Data.Monoid (All(..))
import qualified Data.Set as S
@@ -56,6 +63,11 @@ import qualified Data.Set as S
-- > layoutHook = avoidStruts (tall ||| mirror tall ||| ...)
-- > where tall = Tall 1 (3/100) (1/2)
--
-- The third component is an event hook that causes new docks to appear
-- immediately, instead of waiting for the next focus change.
--
-- > handleEventHook = ... <+> docksEventHook
--
-- 'AvoidStruts' also supports toggling the dock gaps; add a keybinding
-- similar to:
--
@@ -102,6 +114,14 @@ checkDock = ask >>= \w -> liftX $ do
Just [r] -> return $ elem (fromIntegral r) [dock, desk]
_ -> return False
-- | Whenever a new dock appears, refresh the layout immediately to avoid the
-- new dock.
docksEventHook :: Event -> X All
docksEventHook (MapNotifyEvent {ev_window = w}) = do
whenX ((not `fmap` (isClient w)) <&&> runQuery checkDock w) refresh
return (All True)
docksEventHook _ = return (All True)
-- | Gets the STRUT config, if present, in xmonad gap order
getStrut :: Window -> X [Strut]
getStrut w = do
@@ -210,32 +230,22 @@ type Strut = (Direction2D, CLong, CLong, CLong)
-- | (Initial x pixel, initial y pixel,
-- final x pixel, final y pixel).
type RectC = (CLong, CLong, CLong, CLong)
fi :: (Integral a, Num b) => a -> b
fi = fromIntegral
newtype RectC = RectC (CLong, CLong, CLong, CLong) deriving (Eq,Show)
-- | Invertible conversion.
r2c :: Rectangle -> RectC
r2c (Rectangle x y w h) = (fi x, fi y, fi x + fi w - 1, fi y + fi h - 1)
r2c (Rectangle x y w h) = RectC (fi x, fi y, fi x + fi w - 1, fi y + fi h - 1)
-- | Invertible conversion.
c2r :: RectC -> Rectangle
c2r (x1, y1, x2, y2) = Rectangle (fi x1) (fi y1) (fi $ x2 - x1 + 1) (fi $ y2 - y1 + 1)
c2r (RectC (x1, y1, x2, y2)) = Rectangle (fi x1) (fi y1) (fi $ x2 - x1 + 1) (fi $ y2 - y1 + 1)
-- TODO: Add these QuickCheck properties to the test suite, along with
-- suitable Arbitrary instances.
-- prop_r2c_c2r :: RectC -> Bool
-- prop_r2c_c2r r = r2c (c2r r) == r
-- prop_c2r_r2c :: Rectangle -> Bool
-- prop_c2r_r2c r = c2r (r2c r) == r
reduce :: RectC -> Strut -> RectC -> RectC
reduce (sx0, sy0, sx1, sy1) (s, n, l, h) (x0, y0, x1, y1) = case s of
reduce (RectC (sx0, sy0, sx1, sy1)) (s, n, l, h) (RectC (x0, y0, x1, y1)) =
RectC $ case s of
L | p (y0, y1) && qh x1 -> (mx x0 sx0, y0 , x1 , y1 )
R | p (y0, y1) && qv sx1 x0 -> (x0 , y0 , mn x1 sx1, y1 )
U | p (x0, x1) && qh y1 -> (x0 , mx y0 sy0, x1 , y1 )

View File

@@ -45,7 +45,8 @@ module XMonad.Hooks.ManageHelpers (
doSideFloat,
doFloatAt,
doFloatDep,
doHideIgnore
doHideIgnore,
Match,
) where
import XMonad

53
XMonad/Hooks/Minimize.hs Normal file
View File

@@ -0,0 +1,53 @@
----------------------------------------------------------------------------
-- |
-- Module : XMonad.Hooks.Minimize
-- Copyright : (c) Justin Bogner 2010
-- License : BSD3-style (see LICENSE)
--
-- Maintainer : Justin Bogner <mail@justinbogner.com>
-- Stability : unstable
-- Portability : not portable
--
-- Handles window manager hints to minimize and restore windows. Use
-- this with XMonad.Layout.Minimize.
--
-----------------------------------------------------------------------------
module XMonad.Hooks.Minimize
( -- * Usage
-- $usage
minimizeEventHook
) where
import Data.Monoid
import Control.Monad(when)
import XMonad
import XMonad.Layout.Minimize
-- $usage
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
--
-- > import XMonad.Hooks.Minimize
-- > import XMonad.Layout.Minimize
-- >
-- > myHandleEventHook = minimizeEventHook
-- > myLayout = minimize (Tall 1 (3/100) (1/2)) ||| Full ||| etc..
-- > main = xmonad defaultConfig { layoutHook = myLayout
-- > , handleEventHook = myHandleEventHook }
minimizeEventHook :: Event -> X All
minimizeEventHook (ClientMessageEvent {ev_window = w,
ev_message_type = mt,
ev_data = dt}) = do
a_aw <- getAtom "_NET_ACTIVE_WINDOW"
a_cs <- getAtom "WM_CHANGE_STATE"
when (mt == a_aw) $ sendMessage (RestoreMinimizedWin w)
when (mt == a_cs) $ do
let message = fromIntegral . head $ dt
when (message == normalState) $ sendMessage (RestoreMinimizedWin w)
when (message == iconicState) $ minimizeWindow w
return (All True)
minimizeEventHook _ = return (All True)

View File

@@ -1,10 +1,10 @@
-----------------------------------------------------------------------------
-- |
-- Module : XMonad.Hooks.Place
-- Copyright : Quentin Moser <quentin.moser@unifr.ch>
-- Copyright : Quentin Moser <moserq@gmail.com>
-- License : BSD-style (see LICENSE)
--
-- Maintainer : Quentin Moser <quentin.moser@unifr.ch>
-- Maintainer : orphaned
-- Stability : unstable
-- Portability : unportable
--
@@ -38,11 +38,12 @@ import qualified XMonad.StackSet as S
import XMonad.Layout.WindowArranger
import XMonad.Actions.FloatKeys
import XMonad.Util.XUtils
import qualified Data.Map as M
import Data.Ratio ((%))
import Data.List (sortBy, minimumBy, partition)
import Data.Maybe (maybe, fromMaybe, catMaybes)
import Data.Maybe (fromMaybe, catMaybes)
import Data.Monoid (Endo(..))
import Control.Monad (guard, join)
import Control.Monad.Trans (lift)
@@ -262,8 +263,6 @@ checkBounds (Rectangle x1 y1 w1 h1) (Rectangle x2 y2 w2 h2)
scale :: (RealFrac a, Integral b) => a -> b -> b -> b
scale r n1 n2 = truncate $ r * fi n2 + (1 - r) * fi n1
fi :: (Integral a, Num b) => a -> b
fi = fromIntegral
r2rr :: Rectangle -> Rectangle -> S.RationalRect
r2rr (Rectangle x0 y0 w0 h0) (Rectangle x y w h)

View File

@@ -36,13 +36,15 @@ module XMonad.Hooks.PositionStoreHooks (
import XMonad
import qualified XMonad.StackSet as W
import XMonad.Util.PositionStore
import XMonad.Util.XUtils (fi)
import XMonad.Hooks.ManageDocks
import XMonad.Layout.Decoration
import System.Random(randomRIO)
import Control.Applicative((<$>))
import Control.Monad(when)
import Data.Maybe
import Data.Monoid
import qualified Data.Set as S
-- $usage
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
@@ -50,9 +52,13 @@ import Data.Monoid
-- > import XMonad.Hooks.PositionStoreHooks
--
-- and adding 'positionStoreManageHook' to your 'ManageHook' as well
-- as 'positionStoreEventHook' to your event hooks:
-- as 'positionStoreEventHook' to your event hooks. To be accurate
-- about window sizes, the module needs to know if any decoration is in effect.
-- This is specified with the first argument: Supply 'Nothing' for no decoration,
-- otherwise use 'Just defaultTheme' or similar to inform the module about the
-- decoration theme used.
--
-- > myManageHook = positionStoreManageHook <+> manageHook defaultConfig
-- > myManageHook = positionStoreManageHook Nothing <+> manageHook defaultConfig
-- > myHandleEventHook = positionStoreEventHook
-- >
-- > main = xmonad defaultConfig { manageHook = myManageHook
@@ -60,11 +66,13 @@ import Data.Monoid
-- > }
--
positionStoreManageHook :: ManageHook
positionStoreManageHook = ask >>= liftX . positionStoreInit >> idHook
positionStoreManageHook :: Maybe Theme -> ManageHook
positionStoreManageHook mDecoTheme = ask >>= liftX . positionStoreInit mDecoTheme >> idHook
positionStoreInit :: Window -> X ()
positionStoreInit w = withDisplay $ \d -> do
positionStoreInit :: Maybe Theme -> Window -> X ()
positionStoreInit mDecoTheme w = withDisplay $ \d -> do
let decoH = maybe 0 decoHeight mDecoTheme -- take decoration into account, which - in its current
-- form - makes windows smaller to make room for it
wa <- io $ getWindowAttributes d w
ws <- gets windowset
arbitraryOffsetX <- randomIntOffset
@@ -76,13 +84,16 @@ positionStoreInit w = withDisplay $ \d -> do
(Rectangle (srX + fi arbitraryOffsetX)
(srY + fi arbitraryOffsetY)
(fi $ wa_width wa)
(fi $ wa_height wa)) sr )
(decoH + fi (wa_height wa))) sr )
else do
sc <- fromMaybe (W.current ws) <$> pointScreen (fi $ wa_x wa) (fi $ wa_y wa)
let sr = screenRect . W.screenDetail $ sc
sr' <- fmap ($ sr) (calcGap $ S.fromList [minBound .. maxBound]) -- take docks into account, accepting
-- a somewhat unfortunate inter-dependency
-- with 'XMonad.Hooks.ManageDocks'
modifyPosStore (\ps -> posStoreInsert ps w
(Rectangle (fi $ wa_x wa) (fi $ wa_y wa)
(fi $ wa_width wa) (fi $ wa_height wa)) sr )
(Rectangle (fi $ wa_x wa) (fi (wa_y wa) - fi decoH)
(fi $ wa_width wa) (decoH + fi (wa_height wa))) sr' )
where
randomIntOffset :: X (Int)
randomIntOffset = io $ randomRIO (42, 242)

View File

@@ -8,9 +8,9 @@
-- Stability : unstable
-- Portability : not portable
--
-- Lets you restore minimized windows (see "XMonad.Layout.Minimize")
-- by selecting them on a taskbar (listens for _NET_ACTIVE_WINDOW
-- and WM_CHANGE_STATE).
-- (Deprecated: Use XMonad.Hooks.Minimize) Lets you restore minimized
-- windows (see "XMonad.Layout.Minimize") by selecting them on a
-- taskbar (listens for _NET_ACTIVE_WINDOW and WM_CHANGE_STATE).
--
-----------------------------------------------------------------------------

View File

@@ -0,0 +1,170 @@
{-# LANGUAGE DeriveDataTypeable #-}
-----------------------------------------------------------------------------
-- |
-- Module : XMonad.Hooks.ScreenCorners
-- Copyright : (c) 2009 Nils Schweinsberg
-- License : BSD3-style (see LICENSE)
--
-- Maintainer : Nils Schweinsberg <mail@n-sch.de>
-- Stability : unstable
-- Portability : unportable
--
-- Run @X ()@ actions by touching the edge of your screen with your mouse.
--
-----------------------------------------------------------------------------
module XMonad.Hooks.ScreenCorners
(
-- * Usage
-- $usage
-- * Adding screen corners
ScreenCorner (..)
, addScreenCorner
, addScreenCorners
-- * Event hook
, screenCornerEventHook
) where
import Data.Monoid
import Data.List (find)
import XMonad
import XMonad.Util.XUtils (fi)
import qualified Data.Map as M
import qualified XMonad.Util.ExtensibleState as XS
data ScreenCorner = SCUpperLeft
| SCUpperRight
| SCLowerLeft
| SCLowerRight
deriving (Eq, Ord, Show)
--------------------------------------------------------------------------------
-- ExtensibleState modifications
--------------------------------------------------------------------------------
newtype ScreenCornerState = ScreenCornerState (M.Map Window (ScreenCorner, X ()))
deriving Typeable
instance ExtensionClass ScreenCornerState where
initialValue = ScreenCornerState M.empty
-- | Add one single @X ()@ action to a screen corner
addScreenCorner :: ScreenCorner -> X () -> X ()
addScreenCorner corner xF = do
ScreenCornerState m <- XS.get
(win,xFunc) <- case find (\(_,(sc,_)) -> sc == corner) (M.toList m) of
Just (w, (_,xF')) -> return (w, xF' >> xF) -- chain X actions
Nothing -> flip (,) xF `fmap` createWindowAt corner
XS.modify $ \(ScreenCornerState m') -> ScreenCornerState $ M.insert win (corner,xFunc) m'
-- | Add a list of @(ScreenCorner, X ())@ tuples
addScreenCorners :: [ (ScreenCorner, X ()) ] -> X ()
addScreenCorners = mapM_ (\(corner, xF) -> addScreenCorner corner xF)
--------------------------------------------------------------------------------
-- Xlib functions
--------------------------------------------------------------------------------
-- "Translate" a ScreenCorner to real (x,y) Positions
createWindowAt :: ScreenCorner -> X Window
createWindowAt SCUpperLeft = createWindowAt' 0 0
createWindowAt SCUpperRight = withDisplay $ \dpy ->
let w = displayWidth dpy (defaultScreen dpy) - 1
in createWindowAt' (fi w) 0
createWindowAt SCLowerLeft = withDisplay $ \dpy ->
let h = displayHeight dpy (defaultScreen dpy) - 1
in createWindowAt' 0 (fi h)
createWindowAt SCLowerRight = withDisplay $ \dpy ->
let w = displayWidth dpy (defaultScreen dpy) - 1
h = displayHeight dpy (defaultScreen dpy) - 1
in createWindowAt' (fi w) (fi h)
-- Create a new X window at a (x,y) Position
createWindowAt' :: Position -> Position -> X Window
createWindowAt' x y = withDisplay $ \dpy -> io $ do
rootw <- rootWindow dpy (defaultScreen dpy)
let
visual = defaultVisualOfScreen $ defaultScreenOfDisplay dpy
attrmask = cWOverrideRedirect
w <- allocaSetWindowAttributes $ \attributes -> do
set_override_redirect attributes True
createWindow dpy -- display
rootw -- parent window
x -- x
y -- y
1 -- width
1 -- height
0 -- border width
0 -- depth
inputOnly -- class
visual -- visual
attrmask -- valuemask
attributes -- attributes
-- we only need mouse entry events
selectInput dpy w enterWindowMask
mapWindow dpy w
sync dpy False
return w
--------------------------------------------------------------------------------
-- Event hook
--------------------------------------------------------------------------------
-- | Handle screen corner events
screenCornerEventHook :: Event -> X All
screenCornerEventHook CrossingEvent { ev_window = win } = do
ScreenCornerState m <- XS.get
case M.lookup win m of
Just (_, xF) -> xF
Nothing -> return ()
return (All True)
screenCornerEventHook _ = return (All True)
--------------------------------------------------------------------------------
-- $usage
--
-- This extension adds KDE-like screen corners to XMonad. By moving your cursor
-- into one of your screen corners you can trigger an @X ()@ action, for
-- example @"XMonad.Actions.GridSelect".goToSelected@ or
-- @"XMonad.Actions.CycleWS".nextWS@ etc.
--
-- To use it, import it on top of your @xmonad.hs@:
--
-- > import XMonad.Hooks.ScreenCorners
--
-- Then add your screen corners in our startup hook:
--
-- > myStartupHook = do
-- > ...
-- > addScreenCorner SCUpperRight (goToSelected defaultGSConfig { gs_cellwidth = 200})
-- > addScreenCorners [ (SCLowerRight, nextWS)
-- > , (SCLowerLeft, prevWS)
-- > ]
--
-- And finally wait for screen corner events in your event hook:
--
-- > myEventHook e = do
-- > ...
-- > screenCornerEventHook e

View File

@@ -26,7 +26,6 @@ module XMonad.Hooks.Script (
--
import XMonad
import Control.Monad.Trans
import System.Directory
-- $usage

View File

@@ -64,7 +64,6 @@ module XMonad.Hooks.ServerMode
) where
import Control.Monad (when)
import Data.List
import Data.Monoid
import System.IO

169
XMonad/Hooks/ToggleHook.hs Normal file
View File

@@ -0,0 +1,169 @@
{-# LANGUAGE DeriveDataTypeable #-}
-----------------------------------------------------------------------------
-- |
-- Module : XMonad.Hooks.ToggleHook
-- Copyright : Ben Boeckel <mathstuf@gmail.com>
-- License : BSD-style (see LICENSE)
--
-- Maintainer : Ben Boeckel <mathstuf@gmail.com>
-- Stability : unstable
-- Portability : unportable
--
-- Hook and keybindings for toggling hook behavior.
-----------------------------------------------------------------------------
module XMonad.Hooks.ToggleHook ( -- * Usage
-- $usage
-- * The hook
toggleHook
, toggleHook'
-- * Actions
, hookNext
, toggleHookNext
, hookAllNew
, toggleHookAllNew
-- * Queries
, willHook
, willHookNext
, willHookAllNew
-- * 'DynamicLog' utilities
-- $pp
, willHookNextPP
, willHookAllNewPP
, runLogHook ) where
import Prelude hiding (all)
import XMonad
import qualified XMonad.Util.ExtensibleState as XS
import Control.Monad (join,guard)
import Control.Applicative ((<$>))
import Control.Arrow (first, second)
import Data.Map
{- Helper functions -}
_set :: String -> ((a -> a) -> (Bool, Bool) -> (Bool, Bool)) -> a -> X ()
_set n f b = modify' n (f $ const b)
_toggle :: String -> ((Bool -> Bool) -> (Bool, Bool) -> (Bool, Bool)) -> X ()
_toggle n f = modify' n (f not)
_get :: String -> ((Bool, Bool) -> a) -> X a
_get n f = XS.gets $ f . (findWithDefault (False, False) n . hooks)
_pp :: String -> ((Bool, Bool) -> Bool) -> String -> (String -> String) -> X (Maybe String)
_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)
instance ExtensionClass HookState where
initialValue = HookState empty
modify' :: String -> ((Bool, Bool) -> (Bool, Bool)) -> X ()
modify' n f = XS.modify (HookState . setter . hooks)
where
setter m = insert n (f (findWithDefault (False, False) n m)) m
-- $usage
-- This module provides actions (that can be set as keybindings)
-- to be able to cause hooks to be occur on a conditional basis.
--
-- You can use it by including the following in your @~\/.xmonad\/xmonad.hs@:
--
-- > import XMonad.Hooks.ToggleHook
--
-- and adding 'toggleHook name hook' to your 'ManageHook' where @name@ is the
-- name of the hook and @hook@ is the hook to execute based on the state.
--
-- > myManageHook = toggleHook "float" doFloat <+> manageHook defaultConfig
--
-- Additionally, toggleHook' is provided to toggle between two hooks (rather
-- than on/off).
--
-- > myManageHook = toggleHook' "oldfocus" (const id) W.focusWindow <+> manageHook defaultConfig
--
-- The 'hookNext' and 'toggleHookNext' functions can be used in key
-- bindings to set whether the hook is applied or not.
--
-- > , ((modm, xK_e), toggleHookNext "float")
--
-- 'hookAllNew' and 'toggleHookAllNew' are similar but float all
-- spawned windows until disabled again.
--
-- > , ((modm, xK_r), toggleHookAllNew "float")
-- | This 'ManageHook' will selectively apply a hook as set
-- by 'hookNext' and 'hookAllNew'.
toggleHook :: String -> ManageHook -> ManageHook
toggleHook n h = toggleHook' n h idHook
toggleHook' :: String -> ManageHook -> ManageHook -> ManageHook
toggleHook' n th fh = do m <- liftX $ XS.gets hooks
(next, all) <- return $ findWithDefault (False, False) n m
liftX $ XS.put $ HookState $ insert n (False, all) m
if next || all then th else fh
-- | @hookNext name True@ arranges for the next spawned window to
-- have the hook @name@ applied, @hookNext name False@ cancels it.
hookNext :: String -> Bool -> X ()
hookNext n = _set n first
toggleHookNext :: String -> X ()
toggleHookNext n = _toggle n first
-- | @hookAllNew name True@ arranges for new windows to
-- have the hook @name@ applied, @hookAllNew name False@ cancels it
hookAllNew :: String -> Bool -> X ()
hookAllNew n = _set n second
toggleHookAllNew :: String -> X ()
toggleHookAllNew n = _toggle n second
-- | Query what will happen at the next ManageHook call for the hook @name@.
willHook :: String -> X Bool
willHook n = willHookNext n <||> willHookAllNew n
-- | Whether the next window will trigger the hook @name@.
willHookNext :: String -> X Bool
willHookNext n = _get n fst
-- | Whether new windows will trigger the hook @name@.
willHookAllNew :: String -> X Bool
willHookAllNew n = _get n snd
-- $pp
-- The following functions are used to display the current
-- state of 'hookNext' and 'hookAllNew' in your
-- 'XMonad.Hooks.DynamicLog.dynamicLogWithPP'.
-- 'willHookNextPP' and 'willHookAllNewPP' should be added
-- to the 'XMonad.Hooks.DynamicLog.ppExtras' field of your
-- 'XMonad.Hooks.DynamicLog.PP'.
--
-- Use 'runLogHook' to refresh the output of your 'logHook', so
-- that the effects of a 'hookNext'/... will be visible
-- immediately:
--
-- > , ((modm, xK_e), toggleHookNext "float" >> runLogHook)
--
-- The @String -> String@ parameters to 'willHookNextPP' and
-- 'willHookAllNewPP' will be applied to their output, you
-- can use them to set the text color, etc., or you can just
-- pass them 'id'.
willHookNextPP :: String -> (String -> String) -> X (Maybe String)
willHookNextPP n = _pp n fst "Next"
willHookAllNewPP :: String -> (String -> String) -> X (Maybe String)
willHookAllNewPP n = _pp n snd "All"
runLogHook :: X ()
runLogHook = join $ asks $ logHook . config

View File

@@ -65,7 +65,8 @@ module XMonad.Hooks.UrgencyHook (
readUrgents, withUrgents,
StdoutUrgencyHook(..),
SpawnUrgencyHook(..),
UrgencyHook(urgencyHook)
UrgencyHook(urgencyHook),
Interval,
) where
import XMonad
@@ -79,7 +80,7 @@ import XMonad.Util.Timer (TimerId, startTimer, handleTimer)
import Control.Applicative ((<$>))
import Control.Monad (when)
import Data.Bits (testBit)
import Data.List (delete)
import Data.List (delete, (\\))
import Data.Maybe (listToMaybe, maybeToList)
import qualified Data.Set as S
@@ -372,7 +373,9 @@ shouldSuppress :: SuppressWhen -> Window -> X Bool
shouldSuppress sw w = elem w <$> suppressibleWindows sw
cleanupUrgents :: SuppressWhen -> X ()
cleanupUrgents sw = mapM_ clearUrgency =<< suppressibleWindows sw
cleanupUrgents sw = do
sw' <- suppressibleWindows sw
adjustUrgents (\\ sw') >> adjustReminders (filter $ ((`notElem` sw') . window))
suppressibleWindows :: SuppressWhen -> X [Window]
suppressibleWindows Visible = gets $ S.toList . mapped

View File

@@ -1,3 +1,4 @@
{-# LANGUAGE ScopedTypeVariables #-}
-----------------------------------------------------------------------------
-- |
-- Module : XMonad.Hooks.XPropManage
@@ -17,14 +18,14 @@ module XMonad.Hooks.XPropManage (
xPropManageHook, XPropMatch, pmX, pmP
) where
import Prelude hiding (catch)
import Control.Exception
import Data.Char (chr)
import Data.List (concat)
import Data.Monoid (mconcat, Endo(..))
import Control.Monad.Trans (lift)
import XMonad
import XMonad.ManageHook ((-->))
-- $usage
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
@@ -75,7 +76,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) (\_ -> return [[]])
prop <- io $ 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

@@ -42,7 +42,7 @@ data Accordion a = Accordion deriving ( Read, Show )
instance LayoutClass Accordion Window where
pureLayout _ sc ws = zip ups tops ++ [(W.focus ws, mainPane)] ++ zip dns bottoms
where
ups = W.up ws
ups = reverse $ W.up ws
dns = W.down ws
(top, allButTop) = splitVerticallyBy (1%8 :: Ratio Int) sc
(center, bottom) = splitVerticallyBy (6%7 :: Ratio Int) allButTop

View File

@@ -18,7 +18,7 @@
module XMonad.Layout.AutoMaster (
-- * Usage
-- $usage
autoMaster
autoMaster, AutoMaster
) where
import Control.Monad
@@ -48,7 +48,7 @@ import XMonad.Layout.LayoutModifier
data AutoMaster a = AutoMaster Int Float Float
deriving (Read,Show)
instance LayoutModifier AutoMaster Window where
instance (Eq w) => LayoutModifier AutoMaster w where
modifyLayout (AutoMaster k bias _) = autoLayout k bias
pureMess = autoMess
@@ -61,12 +61,12 @@ autoMess (AutoMaster k bias delta) m = msum [fmap resize (fromMessage m),
resize Shrink = AutoMaster k (max (-0.4) $ bias-delta) delta
-- | Main layout function
autoLayout :: (LayoutClass l Window) =>
autoLayout :: (Eq w, LayoutClass l w) =>
Int ->
Float ->
W.Workspace WorkspaceId (l Window) Window
W.Workspace WorkspaceId (l w) w
-> Rectangle
-> X ([(Window, Rectangle)], Maybe (l Window))
-> X ([(w, Rectangle)], Maybe (l w))
autoLayout k bias wksp rect = do
let stack = W.stack wksp
let ws = W.integrate' stack

View File

@@ -24,6 +24,7 @@ module XMonad.Layout.BorderResize
-- $usage
borderResize
, BorderResize (..)
, RectWithBorders, BorderInfo,
) where
import XMonad
@@ -63,15 +64,6 @@ brBorderOffset = 5
brBorderSize :: Dimension
brBorderSize = 10
brCursorRightSide :: Glyph
brCursorRightSide = 96
brCursorLeftSide :: Glyph
brCursorLeftSide = 70
brCursorTopSide :: Glyph
brCursorTopSide = 138
brCursorBottomSide :: Glyph
brCursorBottomSide = 16
borderResize :: l a -> ModifiedLayout BorderResize l a
borderResize = ModifiedLayout (BR M.empty)
@@ -155,10 +147,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), brCursorRightSide , RightSideBorder),
((Rectangle (x - brBorderOffset) y brBorderSize ht) , brCursorLeftSide , LeftSideBorder),
((Rectangle x (y - brBorderOffset) wh brBorderSize) , brCursorTopSide , TopSideBorder),
((Rectangle x (y + fi ht - brBorderOffset) wh brBorderSize), brCursorBottomSide , BottomSideBorder)
[((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)
]
handleResize :: [(Window, (BorderType, Window, Rectangle))] -> Event -> X ()

View File

@@ -24,6 +24,10 @@ module XMonad.Layout.BoringWindows (
UpdateBoring(UpdateBoring),
BoringMessage(Replace,Merge),
BoringWindows()
-- * Tips
-- ** variant of 'Full'
-- $simplest
) where
import XMonad.Layout.LayoutModifier(ModifiedLayout(..),
@@ -134,3 +138,12 @@ instance LayoutModifier BoringWindows Window where
focusMaster' :: W.Stack a -> W.Stack a
focusMaster' c@(W.Stack _ [] _) = c
focusMaster' (W.Stack t ls rs) = W.Stack x [] (xs ++ t : rs) where (x:xs) = reverse ls
{- $simplest
An alternative to 'Full' is "XMonad.Layout.Simplest". Less windows are
ignored by 'focusUp' and 'focusDown'. This may be helpful when you want windows
to be uninteresting by some other layout modifier (ex.
"XMonad.Layout.Minimize")
-}

View File

@@ -22,7 +22,8 @@
module XMonad.Layout.ButtonDecoration
( -- * Usage:
-- $usage
buttonDeco
buttonDeco,
ButtonDecoration,
) where
import XMonad

View File

@@ -21,7 +21,8 @@ module XMonad.Layout.CenteredMaster (
-- $usage
centerMaster,
topRightMaster
topRightMaster,
CenteredMaster, TopRightMaster,
) where
import XMonad

View File

@@ -61,9 +61,9 @@ columnLayout (Column q) rect stack = zip ws rects
mkRect :: Rectangle -> (Dimension,Position) -> Rectangle
mkRect (Rectangle xs ys ws _) (h,y) = Rectangle xs (ys+fromIntegral y) ws h
xn :: Int -> Rectangle -> Float -> Int -> Dimension
xn n (Rectangle _ _ _ h) q k = if q==1 then
xn n (Rectangle _ _ _ h) q k = if q==1 then
h `div` (fromIntegral n)
else
round ((fromIntegral h)*q^(n-k)*(1-q)/(1-q^n))

View File

@@ -27,7 +27,7 @@ import Data.List ( delete, intersect, (\\) )
import Data.Maybe ( isJust )
import Control.Monad
import XMonad hiding (focus)
import XMonad.StackSet ( integrate, Workspace (..), Stack(..) )
import XMonad.StackSet ( Workspace (..), Stack(..) )
import XMonad.Layout.WindowNavigation
import XMonad.Util.WindowProperties
import qualified XMonad.StackSet as W

View File

@@ -27,6 +27,7 @@ module XMonad.Layout.Decoration
, isInStack, isVisible, isInvisible, isWithin, fi
, findWindowByDecoration
, module XMonad.Layout.LayoutModifier
, DecorationState, OrigWin
) where
import Control.Monad (when)
@@ -43,6 +44,7 @@ import XMonad.Util.NamedWindows (getName)
import XMonad.Util.Invisible
import XMonad.Util.XUtils
import XMonad.Util.Font
import XMonad.Util.Image
-- $usage
-- This module is intended for layout developers, who want to decorate
@@ -66,19 +68,22 @@ decoration s t ds = ModifiedLayout (Decoration (I Nothing) s t ds)
--
-- For a collection of 'Theme's see "XMonad.Util.Themes"
data Theme =
Theme { activeColor :: String -- ^ Color of the active window
, inactiveColor :: String -- ^ Color of the inactive window
, urgentColor :: String -- ^ Color of the urgent window
, activeBorderColor :: String -- ^ Color of the border of the active window
, inactiveBorderColor :: String -- ^ Color of the border of the inactive window
, urgentBorderColor :: String -- ^ Color of the border of the urgent window
, activeTextColor :: String -- ^ Color of the text of the active window
, inactiveTextColor :: String -- ^ Color of the text of the inactive window
, urgentTextColor :: String -- ^ Color of the text of the urgent window
, fontName :: String -- ^ Font name
, decoWidth :: Dimension -- ^ Maximum width of the decorations (if supported by the 'DecorationStyle')
, decoHeight :: Dimension -- ^ Height of the decorations
, windowTitleAddons :: [(String, Align)] -- ^ Extra text to appear in a window's title bar
Theme { activeColor :: String -- ^ Color of the active window
, inactiveColor :: String -- ^ Color of the inactive window
, urgentColor :: String -- ^ Color of the urgent window
, activeBorderColor :: String -- ^ Color of the border of the active window
, inactiveBorderColor :: String -- ^ Color of the border of the inactive window
, urgentBorderColor :: String -- ^ Color of the border of the urgent window
, activeTextColor :: String -- ^ Color of the text of the active window
, inactiveTextColor :: String -- ^ Color of the text of the inactive window
, urgentTextColor :: String -- ^ Color of the text of the urgent window
, fontName :: String -- ^ Font name
, decoWidth :: Dimension -- ^ Maximum width of the decorations (if supported by the 'DecorationStyle')
, decoHeight :: Dimension -- ^ Height of the decorations
, windowTitleAddons :: [(String, Align)] -- ^ Extra text to appear in a window's title bar.
-- Refer to for a use "XMonad.Layout.ImageButtonDecoration"
, windowTitleIcons :: [([[Bool]], Placement)] -- ^ Extra icons to appear in a window's title bar.
-- Inner @[Bool]@ is a row in a icon bitmap.
} deriving (Show, Read)
-- | The default xmonad 'Theme'.
@@ -97,6 +102,7 @@ defaultTheme =
, decoWidth = 200
, decoHeight = 20
, windowTitleAddons = []
, windowTitleIcons = []
}
-- | A 'Decoration' layout modifier will handle 'SetTheme', a message
@@ -362,7 +368,7 @@ createDecoWindow t r = let mask = Just (exposureMask .|. buttonPressMask) in
createNewWindow r mask (inactiveColor t) True
showDecos :: [DecoWin] -> X ()
showDecos = showWindows . catMaybes . map fst
showDecos = showWindows . catMaybes . map fst . filter (isJust . snd)
hideDecos :: [DecoWin] -> X ()
hideDecos = hideWindows . catMaybes . map fst
@@ -393,7 +399,9 @@ updateDeco sh t fs ((w,_),(Just dw,Just (Rectangle _ _ wh ht))) = do
return $ size > fromIntegral wh - fromIntegral (ht `div` 2)) (show nw)
let als = AlignCenter : map snd (windowTitleAddons t)
strs = name : map fst (windowTitleAddons t)
paintAndWrite dw fs wh ht 1 bc borderc tc bc als strs
i_als = map snd (windowTitleIcons t)
icons = map fst (windowTitleIcons t)
paintTextAndIcons dw fs wh ht 1 bc borderc tc bc als strs i_als icons
updateDeco _ _ _ (_,(Just w,Nothing)) = hideWindow w
updateDeco _ _ _ _ = return ()

View File

@@ -29,7 +29,6 @@ import XMonad.Layout.Maximize
import XMonad.Hooks.ManageDocks
import XMonad.Util.Font
import XMonad.Util.PositionStore
import XMonad.Util.XUtils (fi)
import Control.Applicative((<$>))
import Data.Maybe
@@ -63,7 +62,7 @@ titleBarButtonHandler mainw distFromLeft distFromRight = do
then focus mainw >> sendMessage (maximizeRestore mainw) >> return True
else if (fi distFromRight >= minimizeButtonOffset &&
fi distFromRight <= minimizeButtonOffset + buttonSize)
then focus mainw >> sendMessage (MinimizeWin mainw) >> return True
then focus mainw >> minimizeWindow mainw >> return True
else return False
action
@@ -88,7 +87,7 @@ handleScreenCrossing w decoWin = withDisplay $ \d -> do
ws <- gets windowset
sc <- fromMaybe (W.current ws) <$> pointScreen (fi px) (fi py)
maybeWksp <- screenWorkspace $ W.screen sc
let targetWksp = maybeWksp >>= \wksp ->
let targetWksp = maybeWksp >>= \wksp ->
W.findTag w ws >>= \currentWksp ->
if (currentWksp /= wksp)
then Just wksp

View File

@@ -94,7 +94,6 @@ import XMonad.Layout.TabBarDecoration
import XMonad.Layout.Accordion
import XMonad.Layout.Circle
import XMonad.Layout.ResizeScreen
import XMonad.Layout.WindowArranger
import XMonad.Layout.SimpleFloat

View File

@@ -21,7 +21,6 @@ module XMonad.Layout.Dishes (
Dishes (..)
) where
import Data.List
import XMonad
import XMonad.StackSet (integrate)
import Control.Monad (ap)

View File

@@ -17,7 +17,8 @@
module XMonad.Layout.DraggingVisualizer
( draggingVisualizer,
DraggingVisualizerMsg (..)
DraggingVisualizerMsg (..),
DraggingVisualizer,
) where
import XMonad

131
XMonad/Layout/Drawer.hs Normal file
View File

@@ -0,0 +1,131 @@
{-# LANGUAGE MultiParamTypeClasses, TypeSynonymInstances, FlexibleInstances, FlexibleContexts #-}
-----------------------------------------------------------------------------
-- |
-- Module : XMonad.Layout.Drawer
-- Copyright : (c) 2009 Max Rabkin
-- License : BSD-style (see xmonad/LICENSE)
--
-- Maintainer : max.rabkin@gmail.com
-- Stability : unstable
-- Portability : unportable
--
-- A layout modifier that puts some windows in a "drawer" which retracts and
-- expands depending on whether any window in it has focus.
--
-- Useful for music players, tool palettes, etc.
--
-----------------------------------------------------------------------------
module XMonad.Layout.Drawer
( -- * Usage
-- $usage
-- * Drawers
simpleDrawer
, drawer
-- * Placing drawers
-- The drawer can be placed on any side of the screen with these functions
, onLeft, onTop, onRight, onBottom
, module XMonad.Util.WindowProperties
, Drawer, Reflected
) where
import XMonad
import XMonad.Layout.LayoutModifier
import XMonad.Util.WindowProperties
import XMonad.StackSet as S
import XMonad.Layout.Reflect
-- $usage
-- To use this module, add the following import to @~\/.xmonad\/xmonad.hs@:
--
-- > import XMonad.Layout.Drawer
--
-- > myLayout = drawer `onTop` (Tall 1 0.03 0.5) ||| Full ||| RandomOtherLayout...
-- > where
-- > drawer = simpleDrawer 0.01 0.3 (ClassName "Rhythmbox" `Or` ClassName "Xchat")
-- >
-- > main = xmonad defaultConfig { layoutHook = myLayout }
--
-- This will place the Rhythmbox and Xchat windows in at the top of the screen
-- only when using the 'Tall' layout. See "XMonad.Util.WindowProperties" for
-- more information on selecting windows.
data Drawer l a = Drawer Rational Rational Property (l a)
deriving (Read, Show)
-- | filter : filterM :: partition : partitionM
partitionM :: Monad m => (a -> m Bool) -> [a] -> m ([a], [a])
partitionM _ [] = return ([], [])
partitionM f (x:xs) = do
b <- f x
(ys, zs) <- partitionM f xs
return $ if b
then (x:ys, zs)
else (ys, x:zs)
instance (LayoutClass l Window, Read (l Window)) => LayoutModifier (Drawer l) Window where
modifyLayout (Drawer rs rb p l) ws rect =
case stack ws of
Nothing -> runLayout ws rect
Just stk@(Stack { up=up_, down=down_, S.focus=w }) -> do
(upD, upM) <- partitionM (hasProperty p) up_
(downD, downM) <- partitionM (hasProperty p) down_
b <- hasProperty p w
focusedWindow <- gets (fmap S.focus . stack . workspace . current . windowset)
let rectD = if b && Just w == focusedWindow then rectB else rectS
let (stackD, stackM) = if b
then ( Just $ stk { up=upD, down=downD }
, mkStack upM downM )
else ( mkStack upD downD
, Just $ stk { up=upM, down=downM } )
(winsD, _) <- runLayout (ws { layout=l, stack=stackD }) rectD
(winsM, u') <- runLayout (ws { stack=stackM }) rectM
return (winsD ++ winsM, u')
where
mkStack [] [] = Nothing
mkStack xs (y:ys) = Just (Stack { up=xs, S.focus=y, down=ys })
mkStack (x:xs) ys = Just (Stack { up=xs, S.focus=x, down=ys })
rectB = rect { rect_width=round $ fromIntegral (rect_width rect) * rb }
rectS = rectB { rect_x=rect_x rectB - (round $ (rb - rs) * fromIntegral (rect_width rect)) }
rectM = rect { rect_x=rect_x rect + round (fromIntegral (rect_width rect) * rs)
, rect_width=rect_width rect - round (fromIntegral (rect_width rect) * rs) }
type Reflected l = ModifiedLayout Reflect l
-- | Construct a drawer with a simple layout of the windows inside
simpleDrawer :: Rational -- ^ The portion of the screen taken up by the drawer when closed
-> Rational -- ^ The portion of the screen taken up by the drawer when open
-> Property -- ^ Which windows to put in the drawer
-> Drawer Tall a
simpleDrawer rs rb p = Drawer rs rb p vertical
where
vertical = Tall 0 0 0
-- Export a synonym for the constructor as a Haddock workaround
-- | Construct a drawer with an arbitrary layout for windows inside
drawer :: Rational -- ^ The portion of the screen taken up by the drawer when closed
-> Rational -- ^ The portion of the screen taken up by the drawer when open
-> Property -- ^ Which windows to put in the drawer
-> (l a) -- ^ The layout of windows in the drawer
-> Drawer l a
drawer = Drawer
onLeft :: Drawer l a -> l' a -> ModifiedLayout (Drawer l) l' a
onLeft = ModifiedLayout
onRight :: Drawer l a -> l' a -> Reflected (ModifiedLayout (Drawer l) (Reflected l')) a
onRight d = reflectHoriz . onLeft d . reflectHoriz
onTop :: Drawer l a -> l' a -> Mirror (ModifiedLayout (Drawer l) (Mirror l')) a
onTop d = Mirror . onLeft d . Mirror
onBottom :: Drawer l a -> l' a -> Reflected (Mirror (ModifiedLayout (Drawer l) (Mirror (Reflected l')))) a
onBottom d = reflectVert . onTop d . reflectVert

227
XMonad/Layout/Fullscreen.hs Normal file
View File

@@ -0,0 +1,227 @@
{-# LANGUAGE DeriveDataTypeable, MultiParamTypeClasses, FlexibleInstances, TypeSynonymInstances #-}
-----------------------------------------------------------------------------
-- |
-- Module : XMonad.Layout.Decoration
-- Copyright : (c) 2010 Audun Skaugen
-- License : BSD-style (see xmonad/LICENSE)
--
-- Maintainer : audunskaugen@gmail.com
-- Stability : unstable
-- Portability : unportable
--
-- Hooks for sending messages about fullscreen windows to layouts, and
-- a few example layout modifier that implement fullscreen windows.
-----------------------------------------------------------------------------
module XMonad.Layout.Fullscreen
( -- * Usage:
-- $usage
fullscreenFull
,fullscreenFocus
,fullscreenFullRect
,fullscreenFocusRect
,fullscreenFloat
,fullscreenFloatRect
,fullscreenEventHook
,fullscreenManageHook
,fullscreenManageHookWith
,FullscreenMessage(..)
-- * Types for reference
,FullscreenFloat, FullscreenFocus, FullscreenFull
) where
import XMonad
import XMonad.Layout.LayoutModifier
import XMonad.Util.WindowProperties
import XMonad.Hooks.ManageHelpers (isFullscreen)
import qualified XMonad.StackSet as W
import Data.List
import Data.Maybe
import Data.Monoid
import qualified Data.Map as M
import Control.Monad
import Control.Arrow (second)
-- $usage
-- Provides a ManageHook and an EventHook that sends layout messages
-- with information about fullscreening windows. This allows layouts
-- to make their own decisions about what they should to with a
-- window that requests fullscreen.
--
-- The module also includes a few layout modifiers as an illustration
-- of how such layouts should behave.
--
-- To use this module, add 'fullscreenEventHook' and 'fullscreenManageHook'
-- to your config, i.e.
--
-- > xmonad defaultconfig { eventHook = fullscreenEventHook,
-- > manageHook = fullscreenManageHook,
-- > layoutHook = myLayouts }
--
-- Now you can use layouts that respect fullscreen, for example the
-- provided 'fullscreenFull':
--
-- > myLayouts = fullscreenFull someLayout
--
-- | Messages that control the fullscreen state of the window.
-- AddFullscreen and RemoveFullscreen are sent to all layouts
-- when a window wants or no longer wants to be fullscreen.
-- FullscreenChanged is sent to the current layout after one
-- of the above have been sent.
data FullscreenMessage = AddFullscreen Window
| RemoveFullscreen Window
| FullscreenChanged
deriving (Typeable)
instance Message FullscreenMessage
data FullscreenFull a = FullscreenFull W.RationalRect [a]
deriving (Read, Show)
data FullscreenFocus a = FullscreenFocus W.RationalRect [a]
deriving (Read, Show)
data FullscreenFloat a = FullscreenFloat W.RationalRect (M.Map a (W.RationalRect, Bool))
deriving (Read, Show)
instance LayoutModifier FullscreenFull Window where
pureMess ff@(FullscreenFull frect fulls) m = case fromMessage m of
Just (AddFullscreen win) -> Just $ FullscreenFull frect $ nub $ win:fulls
Just (RemoveFullscreen win) -> Just $ FullscreenFull frect $ delete win $ fulls
Just FullscreenChanged -> Just ff
_ -> Nothing
pureModifier (FullscreenFull frect fulls) rect _ list =
(map (flip (,) rect') visfulls ++ rest, Nothing)
where visfulls = intersect fulls $ map fst list
rest = filter (flip notElem visfulls . fst) list
rect' = scaleRationalRect rect frect
instance LayoutModifier FullscreenFocus Window where
pureMess ff@(FullscreenFocus frect fulls) m = case fromMessage m of
Just (AddFullscreen win) -> Just $ FullscreenFocus frect $ nub $ win:fulls
Just (RemoveFullscreen win) -> Just $ FullscreenFocus frect $ delete win $ fulls
Just FullscreenChanged -> Just ff
_ -> Nothing
pureModifier (FullscreenFocus frect fulls) rect (Just (W.Stack {W.focus = f})) list
| f `elem` fulls = ((f, rect') : rest, Nothing)
| otherwise = (list, Nothing)
where rest = filter ((/= f) . fst) list
rect' = scaleRationalRect rect frect
pureModifier _ _ Nothing list = (list, Nothing)
instance LayoutModifier FullscreenFloat Window where
handleMess (FullscreenFloat frect fulls) m = case fromMessage m of
Just (AddFullscreen win) -> do
mrect <- (M.lookup win . W.floating) `fmap` gets windowset
return $ case mrect of
Just rect -> Just $ FullscreenFloat frect $ M.insert win (rect,True) fulls
Nothing -> Nothing
Just (RemoveFullscreen win) ->
return $ Just $ FullscreenFloat frect $ M.adjust (second $ const False) win fulls
-- Modify the floating member of the stack set directly; this is the hackish part.
Just FullscreenChanged -> do
state <- get
let ws = windowset state
flt = W.floating ws
flt' = M.intersectionWith doFull fulls flt
put state {windowset = ws {W.floating = M.union flt' flt}}
return $ Just $ FullscreenFloat frect $ M.filter snd fulls
where doFull (_, True) _ = frect
doFull (rect, False) _ = rect
Nothing -> return Nothing
-- | Layout modifier that makes fullscreened window fill the
-- entire screen.
fullscreenFull :: LayoutClass l a =>
l a -> ModifiedLayout FullscreenFull l a
fullscreenFull = fullscreenFullRect $ W.RationalRect 0 0 1 1
-- | As above, but the fullscreened window will fill the
-- specified rectangle instead of the entire screen.
fullscreenFullRect :: LayoutClass l a =>
W.RationalRect -> l a -> ModifiedLayout FullscreenFull l a
fullscreenFullRect r = ModifiedLayout $ FullscreenFull r []
-- | Layout modifier that makes the fullscreened window fill
-- the entire screen only if it is currently focused.
fullscreenFocus :: LayoutClass l a =>
l a -> ModifiedLayout FullscreenFocus l a
fullscreenFocus = fullscreenFocusRect $ W.RationalRect 0 0 1 1
-- | As above, but the fullscreened window will fill the
-- specified rectangle instead of the entire screen.
fullscreenFocusRect :: LayoutClass l a =>
W.RationalRect -> l a -> ModifiedLayout FullscreenFocus l a
fullscreenFocusRect r = ModifiedLayout $ FullscreenFocus r []
-- | Hackish layout modifier that makes floating fullscreened
-- windows fill the entire screen.
fullscreenFloat :: LayoutClass l a =>
l a -> ModifiedLayout FullscreenFloat l a
fullscreenFloat = fullscreenFloatRect $ W.RationalRect 0 0 1 1
-- | As above, but the fullscreened window will fill the
-- specified rectangle instead of the entire screen.
fullscreenFloatRect :: LayoutClass l a =>
W.RationalRect -> l a -> ModifiedLayout FullscreenFloat l a
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"
fullsc <- getAtom "_NET_WM_STATE_FULLSCREEN"
wstate <- fromMaybe [] `fmap` getProp32 state win
let fi :: (Integral i, Num n) => i -> n
fi = fromIntegral
isFull = fi fullsc `elem` wstate
remove = 0
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
when (action == add || (action == toggle && not isFull)) $ do
chWState (fi fullsc:)
broadcastMessage $ AddFullscreen win
sendMessage FullscreenChanged
when (action == remove || (action == toggle && isFull)) $ do
chWState $ delete (fi fullsc)
broadcastMessage $ RemoveFullscreen win
sendMessage FullscreenChanged
return $ All True
fullscreenEventHook (DestroyWindowEvent {ev_window = w}) = do
-- When a window is destroyed, the layouts should remove that window
-- from their states.
broadcastMessage $ RemoveFullscreen w
cw <- (W.workspace . W.current) `fmap` gets windowset
sendMessageWithNoRefresh FullscreenChanged cw
return $ All True
fullscreenEventHook _ = return $ All True
-- | Manage hook that sets the fullscreen property for
-- windows that are initially fullscreen
fullscreenManageHook :: ManageHook
fullscreenManageHook = fullscreenManageHook' isFullscreen
-- | A version of fullscreenManageHook that lets you specify
-- your own query to decide whether a window should be fullscreen.
fullscreenManageHookWith :: Query Bool -> ManageHook
fullscreenManageHookWith h = fullscreenManageHook' $ isFullscreen <||> h
fullscreenManageHook' :: Query Bool -> ManageHook
fullscreenManageHook' isFull = isFull --> do
w <- ask
liftX $ do
broadcastMessage $ AddFullscreen w
cw <- (W.workspace . W.current) `fmap` gets windowset
sendMessageWithNoRefresh FullscreenChanged cw
idHook

View File

@@ -28,7 +28,7 @@
module XMonad.Layout.Gaps (
-- * Usage
-- $usage
Direction2D(..),
Direction2D(..), Gaps,
GapSpec, gaps, GapMessage(..)
) where
@@ -38,6 +38,7 @@ import Graphics.X11 (Rectangle(..))
import XMonad.Layout.LayoutModifier
import XMonad.Util.Types (Direction2D(..))
import XMonad.Util.XUtils (fi)
import Data.List (delete)
@@ -56,8 +57,8 @@ import Data.List (delete)
--
-- > , ((modm .|. controlMask, xK_g), sendMessage $ ToggleGaps) -- toggle all gaps
-- > , ((modm .|. controlMask, xK_t), sendMessage $ ToggleGap U) -- toggle the top gap
-- > , ((modm .|. controlMask, xK_w), sendMessage $ IncGap R 5) -- increment the right-hand gap
-- > , ((modm .|. controlMask, xK_q), sendMessage $ DecGap R 5) -- decrement the right-hand gap
-- > , ((modm .|. controlMask, xK_w), sendMessage $ IncGap 5 R) -- increment the right-hand gap
-- > , ((modm .|. controlMask, xK_q), sendMessage $ DecGap 5 R) -- decrement the right-hand gap
--
-- If you want complete control over all gaps, you could include
-- something like this in your keybindings, assuming in this case you
@@ -133,9 +134,6 @@ toggleGap conf cur d | d `elem` cur = delete d cur
incGap :: GapSpec -> Direction2D -> Int -> GapSpec
incGap gs d i = map (\(dir,j) -> if dir == d then (dir,max (j+i) 0) else (dir,j)) gs
fi :: (Num b, Integral a) => a -> b
fi = fromIntegral
-- | Add togglable manual gaps to a layout.
gaps :: GapSpec -- ^ The gaps to allow, paired with their initial sizes.
-> l a -- ^ The layout to modify.

View File

@@ -19,7 +19,8 @@
module XMonad.Layout.GridVariants ( -- * Usage
-- $usage
ChangeMasterGeom(..)
ChangeMasterGridGeom(..)
, ChangeGridGeom(..)
, Grid(..)
, TallGrid(..)
, SplitGrid(..)
@@ -68,9 +69,24 @@ instance LayoutClass Grid a where
nwins = length wins
rects = arrangeAspectGrid rect nwins aspect
pureMessage layout msg = fmap (changeGridAspect layout) (fromMessage msg)
description _ = "Grid"
-- | SplitGrid layout. Parameters are
changeGridAspect :: Grid a -> ChangeGridGeom -> Grid a
changeGridAspect (Grid _) (SetGridAspect aspect) = Grid aspect
changeGridAspect (Grid aspect) (ChangeGridAspect delta) =
Grid (max 0.00001 (aspect + delta))
-- |Geometry change messages understood by Grid and SplitGrid
data ChangeGridGeom
= SetGridAspect !Rational
| ChangeGridAspect !Rational
deriving Typeable
instance Message ChangeGridGeom
-- |SplitGrid layout. Parameters are
--
-- - side where the master is
-- - number of master rows
@@ -81,8 +97,8 @@ instance LayoutClass Grid a where
data SplitGrid a = SplitGrid Orientation !Int !Int !Rational !Rational !Rational
deriving (Read, Show)
-- | Type to specify the side of the screen that holds
-- the master area of a SplitGrid.
-- |Type to specify the side of the screen that holds
-- the master area of a SplitGrid.
data Orientation = T | B | L | R
deriving (Eq, Read, Show)
@@ -95,18 +111,23 @@ instance LayoutClass SplitGrid a where
rects = arrangeSplitGrid rect o nwins mrows mcols mfrac saspect
pureMessage layout msg =
msum [ fmap (resizeMaster layout) (fromMessage msg)
, fmap (changeMasterGrid layout) (fromMessage msg) ]
msum [ fmap (resizeMaster layout) (fromMessage msg)
, fmap (changeMasterGrid layout) (fromMessage msg)
, fmap (changeSlaveGridAspect layout) (fromMessage msg)
]
description _ = "SplitGrid"
-- |The geometry change message understood by the master grid
data ChangeMasterGeom
= IncMasterRows !Int -- ^Change the number of master rows
| IncMasterCols !Int -- ^Change the number of master columns
data ChangeMasterGridGeom
= IncMasterRows !Int -- ^Change the number of master rows
| IncMasterCols !Int -- ^Change the number of master columns
| SetMasterRows !Int -- ^Set the number of master rows to absolute value
| SetMasterCols !Int -- ^Set the number of master columns to absolute value
| SetMasterFraction !Rational -- ^Set the fraction of the screen used by the master grid
deriving Typeable
instance Message ChangeMasterGeom
instance Message ChangeMasterGridGeom
arrangeSplitGrid :: Rectangle -> Orientation -> Int -> Int -> Int -> Rational -> Rational -> [Rectangle]
arrangeSplitGrid rect@(Rectangle rx ry rw rh) o nwins mrows mcols mfrac saspect
@@ -183,11 +204,23 @@ resizeMaster (SplitGrid o mrows mcols mfrac saspect delta) Shrink =
resizeMaster (SplitGrid o mrows mcols mfrac saspect delta) Expand =
SplitGrid o mrows mcols (min 1 (mfrac + delta)) saspect delta
changeMasterGrid :: SplitGrid a -> ChangeMasterGeom -> SplitGrid a
changeMasterGrid :: SplitGrid a -> ChangeMasterGridGeom -> SplitGrid a
changeMasterGrid (SplitGrid o mrows mcols mfrac saspect delta) (IncMasterRows d) =
SplitGrid o (max 0 (mrows + d)) mcols mfrac saspect delta
changeMasterGrid (SplitGrid o mrows mcols mfrac saspect delta) (IncMasterCols d) =
SplitGrid o mrows (max 0 (mcols + d)) mfrac saspect delta
changeMasterGrid (SplitGrid o _ mcols mfrac saspect delta) (SetMasterRows mrows) =
SplitGrid o (max 0 mrows) mcols mfrac saspect delta
changeMasterGrid (SplitGrid o mrows _ mfrac saspect delta) (SetMasterCols mcols) =
SplitGrid o mrows (max 0 mcols) mfrac saspect delta
changeMasterGrid (SplitGrid o mrows mcols _ saspect delta) (SetMasterFraction mfrac) =
SplitGrid o mrows mcols mfrac saspect delta
changeSlaveGridAspect :: SplitGrid a -> ChangeGridGeom -> SplitGrid a
changeSlaveGridAspect (SplitGrid o mrows mcols mfrac _ delta) (SetGridAspect saspect) =
SplitGrid o mrows mcols mfrac saspect delta
changeSlaveGridAspect (SplitGrid o mrows mcols mfrac saspect delta) (ChangeGridAspect sdelta) =
SplitGrid o mrows mcols mfrac (max 0.00001 (saspect + sdelta)) delta
-- | TallGrid layout. Parameters are
--

510
XMonad/Layout/Groups.hs Normal file
View File

@@ -0,0 +1,510 @@
{-# OPTIONS_GHC -fno-warn-name-shadowing -fno-warn-unused-binds #-}
{-# LANGUAGE StandaloneDeriving, FlexibleContexts, DeriveDataTypeable
, UndecidableInstances, FlexibleInstances, MultiParamTypeClasses
, PatternGuards, Rank2Types, TypeSynonymInstances #-}
-----------------------------------------------------------------------------
-- |
-- Module : XMonad.Layout.Groups
-- Copyright : Quentin Moser <moserq@gmail.com>
-- License : BSD-style (see LICENSE)
--
-- Maintainer : orphaned
-- Stability : unstable
-- Portability : unportable
--
-- Two-level layout with windows split in individual layout groups,
-- themselves managed by a user-provided layout.
--
-----------------------------------------------------------------------------
module XMonad.Layout.Groups ( -- * Usage
-- $usage
-- * Creation
group
-- * Messages
, GroupsMessage(..)
, ModifySpec
-- ** Useful 'ModifySpec's
, swapUp
, swapDown
, swapMaster
, focusUp
, focusDown
, focusMaster
, swapGroupUp
, swapGroupDown
, swapGroupMaster
, focusGroupUp
, focusGroupDown
, focusGroupMaster
, moveToGroupUp
, moveToGroupDown
, moveToNewGroupUp
, moveToNewGroupDown
, splitGroup
-- * Types
, Groups
, Group(..)
, onZipper
, onLayout
, WithID
, sameID
) where
import XMonad
import qualified XMonad.StackSet as W
import XMonad.Util.Stack
import Data.Maybe (isJust, isNothing, fromMaybe, catMaybes, fromJust)
import Data.List ((\\))
import Control.Arrow ((>>>))
import Control.Applicative ((<$>))
import Control.Monad (forM)
-- $usage
-- This module provides a layout combinator that allows you
-- to manage your windows in independent groups. You can provide
-- both the layout with which to arrange the windows inside each
-- 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
-- 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
-- notion of the order of windows, which is completely separate
-- from XMonad's. For this reason, operations like 'XMonad.StackSet.SwapUp'
-- 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
-- by this module.
--
-- If you use both 'Groups'-based and other layouts, The "XMonad.Layout.Groups.Helpers"
-- module provides actions that can work correctly with both, defined using
-- functions from "XMonad.Actions.MessageFeedback".
-- | Create a 'Groups' layout.
--
-- Note that the second parameter (the layout for arranging the
-- groups) is not used on 'Windows', but on 'Group's. For this
-- reason, you can only use layouts that don't specifically
-- need to manage 'Window's. This is obvious, when you think
-- about it.
group :: l Window -> l2 (Group l Window) -> Groups l l2 Window
group l l2 = Groups l l2 startingGroups (U 1 0)
where startingGroups = fromJust $ singletonZ $ G (ID (U 0 0) l) emptyZ
-- * Stuff with unique keys
data Uniq = U Integer Integer
deriving (Eq, Show, Read)
-- | 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)
gen :: Uniq -> (Uniq, [Uniq])
gen (U i1 i2) = (U (i1+1) i2, zipWith U (repeat i1) [i2..])
-- | Split an infinite list into two. I ended up not
-- needing this, but let's keep it just in case.
-- split :: [a] -> ([a], [a])
-- split as = snd $ foldr step (True, ([], [])) as
-- where step a (True, (as1, as2)) = (False, (a:as1, as2))
-- step a (False, (as1, as2)) = (True, (as1, a:as2))
-- | Add a unique identity to a layout so we can
-- follow it around.
data WithID l a = ID { getID :: Uniq
, unID :: (l a)}
deriving (Show, Read)
-- | Compare the ids of two 'WithID' values
sameID :: WithID l a -> WithID l a -> Bool
sameID (ID id1 _) (ID id2 _) = id1 == id2
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
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
-- ** Datatypes
-- | A group of windows and its layout algorithm.
data Group l a = G { gLayout :: WithID l a
, gZipper :: Zipper a }
deriving (Show, Read, Eq)
onLayout :: (WithID l a -> WithID l a) -> Group l a -> Group l a
onLayout f g = g { gLayout = f $ gLayout g }
onZipper :: (Zipper a -> Zipper a) -> Group l a -> Group l a
onZipper f g = g { gZipper = f $ gZipper g }
-- | The type of our layouts.
data Groups l l2 a = Groups { -- | The starting layout for new groups
baseLayout :: l a
-- | The layout for placing each group on the screen
, partitioner :: l2 (Group l a)
-- | The window groups
, groups :: W.Stack (Group l a)
-- | A seed for generating unique ids
, seed :: Uniq
}
deriving instance (Show a, Show (l a), Show (l2 (Group l a))) => Show (Groups l l2 a)
deriving instance (Read a, Read (l a), Read (l2 (Group l a))) => Read (Groups l l2 a)
-- | Messages accepted by 'Groups'-based layouts.
-- All other messages are forwarded to the layout of the currently
-- focused subgroup (as if they had been wrapped in 'ToFocused').
data GroupsMessage = ToEnclosing SomeMessage -- ^ Send a message to the enclosing layout
-- (the one that places the groups themselves)
| ToGroup Int SomeMessage -- ^ Send a message to the layout for nth group
-- (starting at 0)
| ToFocused SomeMessage -- ^ Send a message to the layout for the focused
-- group
| ToAll SomeMessage -- ^ Send a message to all the sub-layouts
| Refocus -- ^ Refocus the window which should be focused according
-- to the layout.
| Modify ModifySpec -- ^ Modify the ordering\/grouping\/focusing
-- of windows according to a 'ModifySpec'
deriving Typeable
instance Show GroupsMessage where
show (ToEnclosing _) = "ToEnclosing {...}"
show (ToGroup i _) = "ToGroup "++show i++" {...}"
show (ToFocused _) = "ToFocused {...}"
show (ToAll _) = "ToAll {...}"
show Refocus = "Refocus"
show (Modify _) = "Modify {...}"
instance Message GroupsMessage
modifyGroups :: (Zipper (Group l a) -> Zipper (Group l a))
-> Groups l l2 a -> Groups l l2 a
modifyGroups f g = let (seed', id:_) = gen (seed g)
defaultGroups = fromJust $ singletonZ $ G (ID id $ baseLayout g) emptyZ
in g { groups = fromMaybe defaultGroups . f . Just $ groups g
, seed = seed' }
-- ** Readaptation
-- | 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
-- 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
(seed', id:_) = gen $ seed g
g' = g { seed = seed' }
in flip modifyGroups g' $ mapZ_ (onZipper $ removeDeleted z)
>>> filterKeepLast (isJust . gZipper)
>>> findNewWindows (W.integrate' z)
>>> addWindows (ID id $ baseLayout g)
>>> focusGroup mf
>>> onFocusedZ (onZipper $ focusWindow mf)
where filterKeepLast _ Nothing = Nothing
filterKeepLast f z@(Just s) = maybe (singletonZ $ W.focus s) Just
$ filterZ_ f z
-- | Remove the windows from a group which are no longer present in
-- the stack.
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)
-> (Zipper (Group l a), [a])
findNewWindows as gs = (gs, foldrZ_ removePresent as gs)
where removePresent g as' = filter (not . flip elemZ (gZipper g)) as'
-- | Add windows to the focused group. If you need to create one,
-- use the given layout and an id from the given list.
addWindows :: WithID l a -> (Zipper (Group l a), [a]) -> Zipper (Group l a)
addWindows l (Nothing, as) = singletonZ $ G l (W.differentiate as)
addWindows _ (z, as) = onFocusedZ (onZipper add) z
where add z = foldl (flip insertUpZ) z as
-- | Focus the group containing the given window
focusGroup :: Eq a => Maybe a -> Zipper (Group l a) -> Zipper (Group l a)
focusGroup Nothing = id
focusGroup (Just a) = fromTags . map (tagBy $ elemZ a . gZipper) . W.integrate'
-- | Focus the given window
focusWindow :: Eq a => Maybe a -> Zipper a -> Zipper a
focusWindow Nothing = id
focusWindow (Just a) = fromTags . map (tagBy (==a)) . W.integrate'
-- * Interface
-- ** Layout instance
instance (LayoutClass l Window, LayoutClass l2 (Group l Window))
=> LayoutClass (Groups l l2) Window where
description (Groups _ p gs _) = s1++" by "++s2
where s1 = description $ gLayout $ W.focus gs
s2 = description p
runLayout ws@(W.Workspace _ _l z) r = let l = readapt z _l in
do (areas, mpart') <- runLayout ws { W.layout = partitioner l
, W.stack = Just $ groups l } r
results <- forM areas $ \(g, r') -> runLayout ws { W.layout = gLayout g
, W.stack = gZipper g } r'
let hidden = map gLayout (W.integrate $ groups l) \\ map (gLayout . fst) areas
hidden' <- mapM (flip handleMessage $ SomeMessage Hide) hidden
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
= do mp' <- handleMessage p sm'
return $ maybeMakeNew l mp' []
handleMessage l@(Groups _ p gs _) sm | Just (ToAll sm') <- fromMessage sm
= do mp' <- handleMessage p sm'
mg's <- mapZM_ (handle sm') $ Just gs
return $ maybeMakeNew l mp' $ W.integrate' mg's
where handle sm (G l _) = handleMessage l sm
handleMessage l sm | Just a <- fromMessage sm
= let _rightType = a == Hide -- Is there a better-looking way
-- of doing this?
in handleMessage l $ SomeMessage $ ToAll sm
handleMessage l@(Groups _ _ z _) sm = case fromMessage sm of
Just (ToFocused sm') -> do mg's <- W.integrate' <$> handleOnFocused sm' z
return $ maybeMakeNew l Nothing mg's
Just (ToGroup i sm') -> do mg's <- handleOnIndex i sm' z
return $ maybeMakeNew l Nothing mg's
Just (Modify spec) -> case applySpec spec l of
Just l' -> refocus l' >> return (Just l')
Nothing -> return $ Just l
Just Refocus -> refocus l >> return (Just l)
Just _ -> return Nothing
Nothing -> handleMessage l $ SomeMessage (ToFocused sm)
where handleOnFocused sm z = mapZM step $ Just z
where step True (G l _) = handleMessage l sm
step False _ = return Nothing
handleOnIndex i sm z = mapM step $ zip [0..] $ W.integrate z
where step (j, (G l _)) | i == j = handleMessage l sm
step _ = return Nothing
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 }
where combine z ml's = let table = map (\(ID id a) -> (id, a)) $ catMaybes ml's
in flip mapS_ z $ \(G (ID id l) ws) -> case lookup id table of
Nothing -> G (ID id l) ws
Just l' -> G (ID id l') ws
mapS_ f = fromJust . mapZ_ f . Just
maybeMakeNew :: Groups l l2 a -> Maybe (l2 (Group l a)) -> [Maybe (WithID l a)]
-> Maybe (Groups l l2 a)
maybeMakeNew _ Nothing ml's | all isNothing ml's = Nothing
maybeMakeNew g mpart' ml's = justMakeNew g mpart' ml's
refocus :: Groups l l2 Window -> X ()
refocus g = case getFocusZ $ gZipper $ W.focus $ groups g
of Just w -> focus w
Nothing -> return ()
-- ** ModifySpec type
-- | Type of functions describing modifications to a 'Groups' layout. They
-- are transformations on 'Zipper's of groups.
--
-- Things you shouldn't do:
--
-- * Forge new windows (they will be ignored)
--
-- * Duplicate windows (whatever happens is your problem)
--
-- * Remove windows (they will be added again)
--
-- * Duplicate layouts (only one will be kept, the rest will
-- get the base layout)
--
-- Note that 'ModifySpec' is a rank-2 type (indicating that 'ModifySpec's must
-- be polymorphic in the layout type), so if you define functions taking
-- '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)
-- | Apply a ModifySpec.
applySpec :: ModifySpec -> Groups l l2 Window -> Maybe (Groups l l2 Window)
applySpec f g = let (seed', id:ids) = gen $ seed g
g' = flip modifyGroups g $ f (ID id $ baseLayout g)
>>> toTags
>>> foldr reID ((ids, []), [])
>>> snd
>>> fromTags
in case groups g == groups g' of
True -> Nothing
False -> Just g' { seed = seed' }
where reID eg ((id:ids, seen), egs)
= let myID = getID $ gLayout $ fromE eg
in case elem myID seen of
False -> ((id:ids, myID:seen), eg:egs)
True -> ((ids, seen), mapE_ (setID id) eg:egs)
where setID id (G (ID _ _) z) = G (ID id $ baseLayout g) z
reID _ (([], _), _) = undefined -- The list of ids is infinite
-- ** Misc. ModifySpecs
-- | helper
onFocused :: (Zipper Window -> Zipper Window) -> ModifySpec
onFocused f _ gs = onFocusedZ (onZipper f) gs
-- | Swap the focused window with the previous one.
swapUp :: ModifySpec
swapUp = onFocused swapUpZ
-- | Swap the focused window with the next one.
swapDown :: ModifySpec
swapDown = onFocused swapDownZ
-- | Swap the focused window with the (group's) master
-- window.
swapMaster :: ModifySpec
swapMaster = onFocused swapMasterZ
-- | Swap the focused group with the previous one.
swapGroupUp :: ModifySpec
swapGroupUp _ = swapUpZ
-- | Swap the focused group with the next one.
swapGroupDown :: ModifySpec
swapGroupDown _ = swapDownZ
-- | Swap the focused group with the master group.
swapGroupMaster :: ModifySpec
swapGroupMaster _ = swapMasterZ
-- | Move focus to the previous window in the group.
focusUp :: ModifySpec
focusUp = onFocused focusUpZ
-- | Move focus to the next window in the group.
focusDown :: ModifySpec
focusDown = onFocused focusDownZ
-- | Move focus to the group's master window.
focusMaster :: ModifySpec
focusMaster = onFocused focusMasterZ
-- | Move focus to the previous group.
focusGroupUp :: ModifySpec
focusGroupUp _ = focusUpZ
-- | Move focus to the next group.
focusGroupDown :: ModifySpec
focusGroupDown _ = focusDownZ
-- | Move focus to the master group.
focusGroupMaster :: ModifySpec
focusGroupMaster _ = focusMasterZ
-- | helper
_removeFocused :: W.Stack a -> (a, Zipper a)
_removeFocused (W.Stack f (u:up) down) = (f, Just $ W.Stack u up down)
_removeFocused (W.Stack f [] (d:down)) = (f, Just $ W.Stack d [] down)
_removeFocused (W.Stack f [] []) = (f, Nothing)
-- helper
_moveToNewGroup :: WithID l Window -> W.Stack (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
= let (w, f') = _removeFocused f
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
moveToNewGroupUp l0 (Just s) = _moveToNewGroup l0 s insertUpZ
-- | Move the focused window to a new group after the current one.
moveToNewGroupDown :: ModifySpec
moveToNewGroupDown _ Nothing = Nothing
moveToNewGroupDown l0 (Just s) = _moveToNewGroup l0 s insertDownZ
-- | Move the focused window to the previous group.
-- If 'True', when in the first group, wrap around to the last one.
-- If 'False', create a new group before it.
moveToGroupUp :: Bool -> ModifySpec
moveToGroupUp _ _ Nothing = Nothing
moveToGroupUp False l0 (Just s) = if null (W.up s) then moveToNewGroupUp l0 (Just s)
else moveToGroupUp True l0 (Just s)
moveToGroupUp True _ (Just s@(W.Stack _ [] [])) = Just s
moveToGroupUp True _ (Just s@(W.Stack (G l (Just f)) _ _))
= let (w, f') = _removeFocused f
in onFocusedZ (onZipper $ insertUpZ w) $ focusUpZ $ Just s { W.focus = G l f' }
moveToGroupUp True _ gs = gs
-- | Move the focused window to the next group.
-- If 'True', when in the last group, wrap around to the first one.
-- If 'False', create a new group after it.
moveToGroupDown :: Bool -> ModifySpec
moveToGroupDown _ _ Nothing = Nothing
moveToGroupDown False l0 (Just s) = if null (W.down s) then moveToNewGroupDown l0 (Just s)
else moveToGroupDown True l0 (Just s)
moveToGroupDown True _ (Just s@(W.Stack _ [] [])) = Just s
moveToGroupDown True _ (Just s@(W.Stack (G l (Just f)) _ _))
= let (w, f') = _removeFocused f
in onFocusedZ (onZipper $ insertUpZ w) $ focusDownZ $ Just s { W.focus = G l f' }
moveToGroupDown True _ gs = gs
-- | Split the focused group into two at the position of the focused window (below it,
-- unless it's the last window - in that case, above it).
splitGroup :: ModifySpec
splitGroup _ Nothing = Nothing
splitGroup l0 z@(Just s) | G l (Just ws) <- W.focus s
= case ws of
W.Stack _ [] [] -> z
W.Stack f (u:up) [] -> let g1 = G l $ Just $ W.Stack f [] []
g2 = G l0 $ Just $ W.Stack u up []
in insertDownZ g1 $ onFocusedZ (const g2) z
W.Stack f up (d:down) -> let g1 = G l $ Just $ W.Stack f up []
g2 = G l0 $ Just $ W.Stack d [] down
in insertUpZ g1 $ onFocusedZ (const g2) z
splitGroup _ _ = Nothing

View File

@@ -0,0 +1,240 @@
{-# OPTIONS_GHC -fno-warn-missing-signatures #-}
{-# LANGUAGE MultiParamTypeClasses, Rank2Types #-}
-----------------------------------------------------------------------------
-- |
-- Module : XMonad.Layout.Groups.Examples
-- Copyright : Quentin Moser <moserq@gmail.com>
-- License : BSD-style (see LICENSE)
--
-- Maintainer : orphaned
-- Stability : unstable
-- Portability : unportable
--
-- Example layouts for "XMonad.Layout.Groups".
--
-----------------------------------------------------------------------------
module XMonad.Layout.Groups.Examples ( -- * Usage
-- $usage
-- * Example: Row of columns
-- $example1
rowOfColumns
, zoomColumnIn
, zoomColumnOut
, zoomColumnReset
, toggleColumnFull
, zoomWindowIn
, zoomWindowOut
, zoomWindowReset
, toggleWindowFull
-- * Example: Tiled tab groups
-- $example2
, tallTabs
, mirrorTallTabs
, fullTabs
, TiledTabsConfig(..)
, defaultTiledTabsConfig
, increaseNMasterGroups
, decreaseNMasterGroups
, shrinkMasterGroups
, expandMasterGroups
, nextOuterLayout
-- * Useful re-exports and utils
, module XMonad.Layout.Groups.Helpers
, shrinkText
, defaultTheme
, GroupEQ(..)
, zoomRowG
) where
import XMonad hiding ((|||))
import qualified XMonad.Layout.Groups as G
import XMonad.Layout.Groups.Helpers
import XMonad.Layout.ZoomRow
import XMonad.Layout.Tabbed
import XMonad.Layout.Named
import XMonad.Layout.Renamed
import XMonad.Layout.LayoutCombinators
import XMonad.Layout.Decoration
import XMonad.Layout.Simplest
-- $usage
-- 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@.
--
-- 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
-- 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
-- re-exported by this module.
--
-- For more information on how to extend your layour hook and key bindings, see
-- "XMonad.Doc.Extending".
-- * Helper: ZoomRow of Group elements
-- | Compare two 'Group's by comparing the ids of their layouts.
data GroupEQ a = GroupEQ
deriving (Show, Read)
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))
=> ZoomRow GroupEQ (G.Group l a)
zoomRowG = zoomRowWith GroupEQ
-- * Example 1: Row of columns
-- $example1
-- A layout that arranges windows in a row of columns. It uses 'ZoomRow's for
-- both, allowing you to:
--
-- * Freely change the proportion of the screen width allocated to each column
--
-- * Freely change the proportion of a column's heigth allocated to each of its windows
--
-- * Set a column to occupy the whole screen space whenever it has focus
--
-- * Set a window to occupy its whole column whenever it has focus
--
-- to use this layout, add 'rowOfColumns' to your layout hook, for example:
--
-- > myLayout = rowOfColumns
--
-- To be able to change the sizes of columns and windows, you can create key bindings
-- for the relevant actions:
--
-- > ((modMask, xK_minus), zoomWindowOut)
--
-- and so on.
rowOfColumns = G.group column zoomRowG
where column = renamed [CutWordsLeft 2, PrependWords "ZoomColumn"] $ Mirror zoomRow
-- | Increase the width of the focused column
zoomColumnIn :: X ()
zoomColumnIn = sendMessage $ G.ToEnclosing $ SomeMessage $ zoomIn
-- | Decrease the width of the focused column
zoomColumnOut :: X ()
zoomColumnOut = sendMessage $ G.ToEnclosing $ SomeMessage $ zoomOut
-- | Reset the width of the focused column
zoomColumnReset :: X ()
zoomColumnReset = sendMessage $ G.ToEnclosing $ SomeMessage $ zoomReset
-- | Toggle whether the currently focused column should
-- take up all available space whenever it has focus
toggleColumnFull :: X ()
toggleColumnFull = sendMessage $ G.ToEnclosing $ SomeMessage $ ZoomFullToggle
-- | Increase the heigth of the focused window
zoomWindowIn :: X ()
zoomWindowIn = sendMessage zoomIn
-- | Decrease the height of the focused window
zoomWindowOut :: X ()
zoomWindowOut = sendMessage zoomOut
-- | Reset the height of the focused window
zoomWindowReset :: X ()
zoomWindowReset = sendMessage zoomReset
-- | Toggle whether the currently focused window should
-- take up the whole column whenever it has focus
toggleWindowFull :: X ()
toggleWindowFull = sendMessage ZoomFullToggle
-- * Example 2: Tabbed groups in a Tall/Full layout.
-- $example2
-- A layout which arranges windows into tabbed groups, and the groups
-- 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
-- case you can freely switch between the three afterwards.
--
-- You can use any of these three layouts by including it in your layout hook.
-- You will need to provide it with a 'TiledTabsConfig' containing the size
-- parameters for 'Tall' and 'Mirror' 'Tall', and the shrinker and decoration theme
-- for the tabs. If you're happy with defaults, you can use 'defaultTiledTabsConfig':
--
-- > myLayout = tallTabs defaultTiledTabsConfig
--
-- To be able to increase\/decrease the number of master groups and shrink\/expand
-- the master area, you can create key bindings for the relevant actions:
--
-- > ((modMask, xK_h), shrinkMasterGroups)
--
-- and so on.
-- | Configuration data for the "tiled tab groups" layout
data TiledTabsConfig s = TTC { vNMaster :: Int
, vRatio :: Rational
, vIncrement :: Rational
, hNMaster :: Int
, hRatio :: Rational
, hIncrement :: Rational
, tabsShrinker :: s
, tabsTheme :: Theme }
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
tallTabs c = _tab c $ G.group _tabs $ _vert c ||| _horiz c ||| Full
mirrorTallTabs c = _tab c $ G.group _tabs $ _horiz c ||| Full ||| _vert c
_tabs = named "Tabs" Simplest
_tab c l = renamed [CutWordsLeft 1] $ addTabs (tabsShrinker c) (tabsTheme c) l
_vert c = named "Vertical" $ Tall (vNMaster c) (vIncrement c) (vRatio c)
_horiz c = named "Horizontal" $ Mirror $ Tall (hNMaster c) (hIncrement c) (hRatio c)
-- | Increase the number of master groups by one
increaseNMasterGroups :: X ()
increaseNMasterGroups = sendMessage $ G.ToEnclosing $ SomeMessage $ IncMasterN 1
-- | Decrease the number of master groups by one
decreaseNMasterGroups :: X ()
decreaseNMasterGroups = sendMessage $ G.ToEnclosing $ SomeMessage $ IncMasterN (-1)
-- | Shrink the master area
shrinkMasterGroups :: X ()
shrinkMasterGroups = sendMessage $ G.ToEnclosing $ SomeMessage $ Shrink
-- | Expand the master area
expandMasterGroups :: X ()
expandMasterGroups = sendMessage $ G.ToEnclosing $ SomeMessage $ Expand
-- | Rotate the available outer layout algorithms
nextOuterLayout :: X ()
nextOuterLayout = sendMessage $ G.ToEnclosing $ SomeMessage $ NextLayout

View File

@@ -0,0 +1,232 @@
{-# OPTIONS_GHC -fno-warn-missing-signatures #-}
{-# LANGUAGE MultiParamTypeClasses, Rank2Types #-}
-----------------------------------------------------------------------------
-- |
-- Module : XMonad.Layout.Groups.Helpers
-- Copyright : Quentin Moser <moserq@gmail.com>
-- License : BSD-style (see LICENSE)
--
-- Maintainer : orphaned
-- Stability : stable
-- Portability : unportable
--
-- Utility functions for "XMonad.Layout.Groups".
--
-----------------------------------------------------------------------------
module XMonad.Layout.Groups.Helpers ( -- * Usage
-- $usage
-- ** Layout-generic actions
swapUp
, swapDown
, swapMaster
, focusUp
, focusDown
, focusMaster
, toggleFocusFloat
-- ** 'G.Groups'-secific actions
, swapGroupUp
, swapGroupDown
, swapGroupMaster
, focusGroupUp
, focusGroupDown
, focusGroupMaster
, moveToGroupUp
, moveToGroupDown
, moveToNewGroupUp
, moveToNewGroupDown
, splitGroup ) where
import XMonad hiding ((|||))
import qualified XMonad.StackSet as W
import qualified XMonad.Layout.Groups as G
import XMonad.Actions.MessageFeedback
import Control.Monad (unless)
import qualified Data.Map as M
-- $usage
--
-- This module provides helpers functions for use with "XMonad.Layout.Groups"-based
-- layouts. You can use its contents by adding
--
-- > import XMonad.Layout.Groups.Helpers
--
-- to the top of your @.\/.xmonad\/xmonad.hs@.
--
-- "XMonad.Layout.Groups"-based layouts do not have the same notion
-- of window ordering as the rest of XMonad. For this reason, the usual
-- ways of reordering windows and moving focus do not work with them.
-- "XMonad.Layout.Groups" provides 'Message's that can be used to obtain
-- the right effect.
--
-- But what if you want to use both 'G.Groups' and other layouts?
-- 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
-- write @sendMessage $ Modify $ ...@ everytime.
--
-- This module exports many operations with the same names as
-- 'G.ModifySpec's from "XMonad.Layout.Groups", so if you want
-- to import both, we suggest to import "XMonad.Layout.Groups"
-- qualified:
--
-- > import qualified XMonad.Layout.Groups as G
--
-- For more information on how to extend your layour hook and key bindings, see
-- "XMonad.Doc.Extending".
-- ** Layout-generic actions
-- #Layout-generic actions#
alt :: G.ModifySpec -> (WindowSet -> WindowSet) -> X ()
alt f g = alt2 (G.Modify f) $ windows g
alt2 :: G.GroupsMessage -> X () -> X ()
alt2 m x = do b <- send m
unless b x
-- | Swap the focused window with the previous one
swapUp :: X ()
swapUp = alt G.swapUp W.swapUp
-- | Swap the focused window with the next one
swapDown :: X ()
swapDown = alt G.swapDown W.swapDown
-- | Swap the focused window with the master window
swapMaster :: X ()
swapMaster = alt G.swapMaster W.swapMaster
-- | If the focused window is floating, focus the next floating
-- window. otherwise, focus the next non-floating one.
focusUp :: X ()
focusUp = ifFloat focusFloatUp focusNonFloatUp
-- | If the focused window is floating, focus the next floating
-- window. otherwise, focus the next non-floating one.
focusDown :: X ()
focusDown = ifFloat focusFloatDown focusNonFloatDown
-- | Move focus to the master window
focusMaster :: X ()
focusMaster = alt G.focusMaster W.shiftMaster
-- | Move focus between the floating and non-floating layers
toggleFocusFloat :: X ()
toggleFocusFloat = ifFloat focusNonFloat focusFloatUp
-- *** Floating layer helpers
getFloats :: X [Window]
getFloats = gets $ M.keys . W.floating . windowset
getWindows :: X [Window]
getWindows = gets $ W.integrate' . W.stack . W.workspace . W.current . windowset
ifFloat :: X () -> X () -> X ()
ifFloat x1 x2 = withFocused $ \w -> do floats <- getFloats
if elem w floats then x1 else x2
focusNonFloat :: X ()
focusNonFloat = alt2 G.Refocus helper
where helper = withFocused $ \w -> do
ws <- getWindows
floats <- getFloats
let (before, after) = span (/=w) ws
case filter (flip notElem floats) $ after ++ before of
[] -> return ()
w':_ -> focus w'
focusHelper :: (Bool -> Bool) -- ^ if you want to focus a floating window, 'id'.
-- if you want a non-floating one, 'not'.
-> ([Window] -> [Window]) -- ^ if you want the next window, 'id'.
-- if you want the previous one, 'reverse'.
-> X ()
focusHelper f g = withFocused $ \w -> do
ws <- getWindows
let (before, _:after) = span (/=w) ws
let toFocus = g $ after ++ before
floats <- getFloats
case filter (f . flip elem floats) toFocus of
[] -> return ()
w':_ -> focus w'
focusNonFloatUp :: X ()
focusNonFloatUp = alt2 (G.Modify G.focusUp) $ focusHelper not reverse
focusNonFloatDown :: X ()
focusNonFloatDown = alt2 (G.Modify G.focusDown) $ focusHelper not id
focusFloatUp :: X ()
focusFloatUp = focusHelper id reverse
focusFloatDown :: X ()
focusFloatDown = focusHelper id id
-- ** Groups-specific actions
wrap :: G.ModifySpec -> X ()
wrap = sendMessage . G.Modify
-- | Swap the focused group with the previous one
swapGroupUp :: X ()
swapGroupUp = wrap G.swapGroupUp
-- | Swap the focused group with the next one
swapGroupDown :: X ()
swapGroupDown = wrap G.swapGroupDown
-- | Swap the focused group with the master group
swapGroupMaster :: X ()
swapGroupMaster = wrap G.swapGroupMaster
-- | Move the focus to the previous group
focusGroupUp :: X ()
focusGroupUp = wrap G.focusGroupUp
-- | Move the focus to the next group
focusGroupDown :: X ()
focusGroupDown = wrap G.focusGroupDown
-- | Move the focus to the master group
focusGroupMaster :: X ()
focusGroupMaster = wrap G.focusGroupMaster
-- | Move the focused window to the previous group. The 'Bool' argument
-- determines what will be done if the focused window is in the very first
-- group: Wrap back to the end ('True'), or create a new group before
-- it ('False').
moveToGroupUp :: Bool -> X ()
moveToGroupUp b = wrap (G.moveToGroupUp b)
-- | Move the focused window to the next group. The 'Bool' argument
-- determines what will be done if the focused window is in the very last
-- group: Wrap back to the beginning ('True'), or create a new group after
-- it ('False').
moveToGroupDown :: Bool -> X ()
moveToGroupDown b = wrap (G.moveToGroupDown b)
-- | Move the focused window to a new group before the current one
moveToNewGroupUp :: X ()
moveToNewGroupUp = wrap G.moveToNewGroupUp
-- | Move the focused window to a new group after the current one
moveToNewGroupDown :: X ()
moveToNewGroupDown = wrap G.moveToNewGroupDown
-- | Split the focused group in two at the position of the focused
-- window.
splitGroup :: X ()
splitGroup = wrap G.splitGroup

View File

@@ -0,0 +1,133 @@
{-# OPTIONS_GHC -fno-warn-missing-signatures #-}
{-# LANGUAGE MultiParamTypeClasses, Rank2Types #-}
-----------------------------------------------------------------------------
-- |
-- Module : XMonad.Layout.Groups.Wmii
-- Copyright : Quentin Moser <moserq@gmail.com>
-- License : BSD-style (see LICENSE)
--
-- Maintainer : orphaned
-- Stability : stable
-- Portability : unportable
--
-- A wmii-like layout algorithm.
--
-----------------------------------------------------------------------------
module XMonad.Layout.Groups.Wmii ( -- * Usage
-- $usage
wmii
, zoomGroupIn
, zoomGroupOut
, zoomGroupReset
, toggleGroupFull
, groupToNextLayout
, groupToFullLayout
, groupToTabbedLayout
, groupToVerticalLayout
-- * Useful re-exports
, shrinkText
, defaultTheme
, module XMonad.Layout.Groups.Helpers ) where
import XMonad hiding ((|||))
import qualified XMonad.Layout.Groups as G
import XMonad.Layout.Groups.Examples
import XMonad.Layout.Groups.Helpers
import XMonad.Layout.Tabbed
import XMonad.Layout.Named
import XMonad.Layout.Renamed
import XMonad.Layout.LayoutCombinators
import XMonad.Layout.MessageControl
import XMonad.Layout.Simplest
-- $usage
-- 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
-- its windows
--
-- * by maximizing the focused one
--
-- * by tabbing them (wmii uses a stacked layout, but I'm too lazy to write it)
--
-- * 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
-- 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
-- parameters) to your layout hook, for example:
--
-- > myLayout = wmii shrinkText defaultTheme
--
-- To be able to zoom in and out of groups, change their inner layout, etc.,
-- create key bindings for the relevant actions:
--
-- > ((modMask, xK_f), toggleGroupFull)
--
-- and so on.
--
-- For more information on how to extend your layout hook and key bindings, see
-- "XMonad.Doc.Extending".
--
-- Finally, you will probably want to be able to move focus and windows
-- between groups in a consistent fashion. For this, you should take a look
-- at the "XMonad.Layout.Groups.Helpers" module, whose contents are re-exported
-- by this module.
-- | A layout inspired by wmii
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]
$ addTabs s t
$ ignore NextLayout
$ ignore (JumpToLayout "") $ unEscape
$ column ||| tabs ||| Full
-- | Increase the width of the focused group
zoomGroupIn :: X ()
zoomGroupIn = zoomColumnIn
-- | Decrease the size of the focused group
zoomGroupOut :: X ()
zoomGroupOut = zoomColumnOut
-- | Reset the size of the focused group to the default
zoomGroupReset :: X ()
zoomGroupReset = zoomColumnReset
-- | Toggle whether the currently focused group should be maximized
-- whenever it has focus.
toggleGroupFull :: X ()
toggleGroupFull = toggleGroupFull
-- | Rotate the layouts in the focused group.
groupToNextLayout :: X ()
groupToNextLayout = sendMessage $ escape NextLayout
-- | Switch the focused group to the \"maximized\" layout.
groupToFullLayout :: X ()
groupToFullLayout = sendMessage $ escape $ JumpToLayout "Full"
-- | Switch the focused group to the \"tabbed\" layout.
groupToTabbedLayout :: X ()
groupToTabbedLayout = sendMessage $ escape $ JumpToLayout "Tabs"
-- | Switch the focused group to the \"column\" layout.
groupToVerticalLayout :: X ()
groupToVerticalLayout = sendMessage $ escape $ JumpToLayout "Column"

View File

@@ -65,7 +65,7 @@ instance LayoutClass Grid Window where
doLayout (GridRatio d m) r w = flip (,) Nothing . arrange d m r (integrate w)
replicateS :: Int -> (a -> (b, a)) -> a -> ([b], a)
replicateS n = runState . replicateM n . State
replicateS n f = runState . replicateM n $ do (a,s) <- gets f; put s; return a
doColumn :: Dimension -> Dimension -> Dimension -> [(D -> D)] -> [D]
doColumn width height k adjs =

View File

@@ -25,12 +25,11 @@ module XMonad.Layout.IM (
-- * TODO
-- $todo
Property(..), IM(..), withIM, gridIM,
AddRoster,
) where
import XMonad
import qualified XMonad.StackSet as S
import Data.List
import XMonad.Layout (splitHorizontallyBy)
import XMonad.Layout.Grid
import XMonad.Layout.LayoutModifier
import XMonad.Util.WindowProperties

View File

@@ -0,0 +1,183 @@
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
----------------------------------------------------------------------------
-- |
-- Module : XMonad.Layout.ImageButtonDecoration
-- Copyright : (c) Jan Vornberger 2009
-- Alejandro Serrano 2010
-- License : BSD3-style (see LICENSE)
--
-- Maintainer : trupill@gmail.com
-- Stability : unstable
-- Portability : not portable
--
-- A decoration that includes small image buttons on both ends which invoke
-- various actions when clicked on: Show a window menu (see
-- "XMonad.Actions.WindowMenu"), minimize, maximize or close the window.
--
-- Note: For maximizing and minimizing to actually work, you will need
-- to integrate "XMonad.Layout.Maximize" and "XMonad.Layout.Minimize" into your
-- setup. See the documentation of those modules for more information.
--
-----------------------------------------------------------------------------
-- This module is mostly derived from "XMonad.Layout.DecorationAddons"
-- and "XMonad.Layout.ButtonDecoration"
module XMonad.Layout.ImageButtonDecoration
( -- * Usage:
-- $usage
imageButtonDeco
, defaultThemeWithImageButtons
, imageTitleBarButtonHandler
, ImageButtonDecoration
) where
import XMonad
import XMonad.Layout.Decoration
import XMonad.Layout.DecorationAddons
import XMonad.Util.Image
import XMonad.Actions.WindowMenu
import XMonad.Layout.Minimize
import XMonad.Layout.Maximize
-- $usage
-- You can use this module with the following in your
-- @~\/.xmonad\/xmonad.hs@:
--
-- > import XMonad.Layout.ImageButtonDecoration
--
-- Then edit your @layoutHook@ by adding the ImageButtonDecoration to
-- your layout:
--
-- > myL = imageButtonDeco shrinkText defaultThemeWithImageButtons (layoutHook defaultConfig)
-- > main = xmonad defaultConfig { layoutHook = myL }
--
-- The buttons' dimension and placements
buttonSize :: Int
buttonSize = 10
menuButtonOffset :: Int
menuButtonOffset = 4
minimizeButtonOffset :: Int
minimizeButtonOffset = 32
maximizeButtonOffset :: Int
maximizeButtonOffset = 18
closeButtonOffset :: Int
closeButtonOffset = 4
-- The images in a 0-1 scale to make
-- it easier to visualize
convertToBool' :: [Int] -> [Bool]
convertToBool' = map (\x -> x == 1)
convertToBool :: [[Int]] -> [[Bool]]
convertToBool = map convertToBool'
menuButton' :: [[Int]]
menuButton' = [[1,1,1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1,1,1],
[1,1,0,0,0,0,0,0,1,1],
[1,1,0,0,0,0,0,0,1,1],
[1,1,1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1,1,1]]
menuButton :: [[Bool]]
menuButton = convertToBool menuButton'
miniButton' :: [[Int]]
miniButton' = [[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[1,1,1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1,1,1]]
miniButton :: [[Bool]]
miniButton = convertToBool miniButton'
maxiButton' :: [[Int]]
maxiButton' = [[1,1,1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1,1,1],
[1,1,0,0,0,0,0,0,1,1],
[1,1,0,0,0,0,0,0,1,1],
[1,1,0,0,0,0,0,0,1,1],
[1,1,0,0,0,0,0,0,1,1],
[1,1,0,0,0,0,0,0,1,1],
[1,1,0,0,0,0,0,0,1,1],
[1,1,1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1,1,1]]
maxiButton :: [[Bool]]
maxiButton = convertToBool maxiButton'
closeButton' :: [[Int]]
closeButton' = [[1,1,0,0,0,0,0,0,1,1],
[1,1,1,0,0,0,0,1,1,1],
[0,1,1,1,0,0,1,1,1,0],
[0,0,1,1,1,1,1,1,0,0],
[0,0,0,1,1,1,1,0,0,0],
[0,0,0,1,1,1,1,0,0,0],
[0,0,1,1,1,1,1,1,0,0],
[0,1,1,1,0,0,1,1,1,0],
[1,1,1,0,0,0,0,1,1,1],
[1,1,0,0,0,0,0,0,1,1]]
closeButton :: [[Bool]]
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.
-- To actually see the buttons, you will need to use a theme that includes them.
-- See 'defaultThemeWithImageButtons' below.
imageTitleBarButtonHandler :: Window -> Int -> Int -> X Bool
imageTitleBarButtonHandler mainw distFromLeft distFromRight = do
let action = if (fi distFromLeft >= menuButtonOffset &&
fi distFromLeft <= menuButtonOffset + buttonSize)
then focus mainw >> windowMenu >> return True
else if (fi distFromRight >= closeButtonOffset &&
fi distFromRight <= closeButtonOffset + buttonSize)
then focus mainw >> kill >> return True
else if (fi distFromRight >= maximizeButtonOffset &&
fi distFromRight <= maximizeButtonOffset + buttonSize)
then focus mainw >> sendMessage (maximizeRestore mainw) >> return True
else if (fi distFromRight >= minimizeButtonOffset &&
fi distFromRight <= minimizeButtonOffset + buttonSize)
then focus mainw >> minimizeWindow mainw >> return True
else return False
action
defaultThemeWithImageButtons :: Theme
defaultThemeWithImageButtons = defaultTheme {
windowTitleIcons = [ (menuButton, CenterLeft 3),
(closeButton, CenterRight 3),
(maxiButton, CenterRight 18),
(miniButton, CenterRight 33) ]
}
imageButtonDeco :: (Eq a, Shrinker s) => s -> Theme
-> l a -> ModifiedLayout (Decoration ImageButtonDecoration s) l a
imageButtonDeco s c = decoration s c $ NFD True
data ImageButtonDecoration a = NFD Bool deriving (Show, Read)
instance Eq a => DecorationStyle ImageButtonDecoration a where
describeDeco _ = "ImageButtonDeco"
decorationCatchClicksHook _ mainw dFL dFR = imageTitleBarButtonHandler mainw dFL dFR
decorationAfterDraggingHook _ (mainw, _) decoWin = focus mainw >> handleScreenCrossing mainw decoWin >> return ()

View File

@@ -19,18 +19,23 @@ module XMonad.Layout.IndependentScreens (
VirtualWorkspace, PhysicalWorkspace,
workspaces',
withScreens, onCurrentScreen,
marshallPP,
countScreens,
marshall, unmarshall
-- * Converting between virtual and physical workspaces
-- $converting
marshall, unmarshall, unmarshallS, unmarshallW,
marshallWindowSpace, unmarshallWindowSpace
) where
-- for the screen stuff
import Control.Applicative((<*), liftA2)
import Control.Arrow hiding ((|||))
import Control.Monad
import Control.Monad.Instances
import Data.List
import Graphics.X11.Xinerama
import XMonad
import XMonad.StackSet hiding (workspaces)
import XMonad.StackSet hiding (filter, workspaces)
import XMonad.Hooks.DynamicLog
-- $usage
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
@@ -48,15 +53,19 @@ import XMonad.StackSet hiding (workspaces)
-- to specific workspace names. In the default configuration, only
-- the keybindings for changing workspace do this:
--
-- > [((m .|. modm, k), windows $ f i)
-- > | (i, k) <- zip (XMonad.workspaces conf) [xK_1 .. xK_9]
-- > , (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)]]
-- > keyBindings conf = let m = modMask conf in fromList $
-- > {- lots of other keybindings -}
-- > [((m .|. modm, k), windows $ f i)
-- > | (i, k) <- zip (XMonad.workspaces conf) [xK_1 .. xK_9]
-- > , (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)]]
--
-- This should change to
--
-- > [((m .|. modm, k), windows $ onCurrentScreen f i)
-- > | (i, k) <- zip (workspaces' conf) [xK_1 .. xK_9]
-- > , (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)]]
-- > keyBindings conf = let m = modMask conf in fromList $
-- > {- lots of other keybindings -}
-- > [((m .|. modm, k), windows $ onCurrentScreen f i)
-- > | (i, k) <- zip (workspaces' conf) [xK_1 .. xK_9]
-- > , (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)]]
--
-- In particular, the analogue of @XMonad.workspaces@ is
-- @workspaces'@, and you can use @onCurrentScreen@ to convert functions
@@ -67,17 +76,24 @@ import XMonad.StackSet hiding (workspaces)
type VirtualWorkspace = WorkspaceId
type PhysicalWorkspace = WorkspaceId
-- $converting
-- You shouldn't need to use the functions below very much. They are used
-- internally. However, in some cases, they may be useful, and so are exported
-- just in case. In general, the \"marshall\" functions convert the convenient
-- form (like \"web\") you would like to use in your configuration file to the
-- inconvenient form (like \"2_web\") that xmonad uses internally. Similarly,
-- the \"unmarshall\" functions convert in the other direction.
marshall :: ScreenId -> VirtualWorkspace -> PhysicalWorkspace
marshall (S sc) vws = show sc ++ '_':vws
unmarshall :: PhysicalWorkspace -> (ScreenId, VirtualWorkspace)
unmarshall = ((S . read) *** drop 1) . break (=='_')
unmarshall :: PhysicalWorkspace -> (ScreenId, VirtualWorkspace)
unmarshallS :: PhysicalWorkspace -> ScreenId
unmarshallW :: PhysicalWorkspace -> VirtualWorkspace
-- ^ You shouldn't need to use @marshall@ and @unmarshall@ very much.
-- They simply convert between the physical and virtual worlds. For
-- example, you might want to use them as part of a status bar
-- configuration. The function @snd . unmarshall@ would discard the
-- screen information from an otherwise unsightly workspace name.
unmarshall = ((S . read) *** drop 1) . break (=='_')
unmarshallS = fst . unmarshall
unmarshallW = snd . unmarshall
workspaces' :: XConfig l -> [VirtualWorkspace]
workspaces' = nub . map (snd . unmarshall) . workspaces
@@ -101,4 +117,39 @@ onCurrentScreen f vws = screen . current >>= f . flip marshall vws
-- > }
--
countScreens :: (MonadIO m, Integral i) => m i
countScreens = liftM genericLength . liftIO $ openDisplay "" >>= getScreenInfo
countScreens = liftM genericLength . liftIO $ openDisplay "" >>= liftA2 (<*) getScreenInfo closeDisplay
-- | This turns a naive pretty-printer into one that is aware of the
-- independent screens. That is, you can write your pretty printer to behave
-- the way you want on virtual workspaces; this function will convert that
-- pretty-printer into one that first filters out physical workspaces on other
-- screens, then converts all the physical workspaces on this screen to their
-- virtual names.
--
-- For example, 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 . marshallPP screen . pp $ handle
-- > in log 0 hLeft >> log 1 hRight
marshallPP :: ScreenId -> PP -> PP
marshallPP s pp = pp {
ppCurrent = ppCurrent pp . snd . unmarshall,
ppVisible = ppVisible pp . snd . unmarshall,
ppHidden = ppHidden pp . snd . unmarshall,
ppHiddenNoWindows = ppHiddenNoWindows pp . snd . unmarshall,
ppUrgent = ppUrgent pp . snd . unmarshall,
ppSort = fmap (marshallSort s) (ppSort pp)
}
marshallSort :: ScreenId -> ([WindowSpace] -> [WindowSpace]) -> ([WindowSpace] -> [WindowSpace])
marshallSort s vSort = pScreens . vSort . vScreens where
onScreen ws = unmarshallS (tag ws) == s
vScreens = map unmarshallWindowSpace . filter onScreen
pScreens = map (marshallWindowSpace s)
-- | Convert the tag of the 'WindowSpace' from a 'VirtualWorkspace' to a 'PhysicalWorkspace'.
marshallWindowSpace :: ScreenId -> WindowSpace -> WindowSpace
-- | Convert the tag of the 'WindowSpace' from a 'PhysicalWorkspace' to a 'VirtualWorkspace'.
unmarshallWindowSpace :: WindowSpace -> WindowSpace
marshallWindowSpace s ws = ws { tag = marshall s (tag ws) }
unmarshallWindowSpace ws = ws { tag = unmarshallW (tag ws) }

View File

@@ -24,15 +24,13 @@ module XMonad.Layout.LayoutBuilder (
SubMeasure (..),
SubBox (..),
absBox,
relBox
relBox,
LayoutN,
) where
import XMonad
import XMonad.Layout
import qualified XMonad.StackSet as W
import Graphics.X11.Xlib
import Data.Maybe (isJust)
import Control.Monad
-- $usage
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:

View File

@@ -0,0 +1,210 @@
{-# LANGUAGE TypeSynonymInstances, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, UndecidableInstances, PatternGuards, DeriveDataTypeable, ScopedTypeVariables #-}
-----------------------------------------------------------------------------
-- |
-- Module : XMonad.Layout.LayoutBuilderP
-- Copyright : (c) 2009 Anders Engstrom <ankaan@gmail.com>, 2011 Ilya Portnov <portnov84@rambler.ru>
-- License : BSD3-style (see LICENSE)
--
-- Maintainer : Ilya Portnov <portnov84@rambler.ru>
-- Stability : unstable
-- Portability : unportable
--
-- A layout combinator that sends windows matching given predicate to one rectangle
-- and the rest to another.
--
-----------------------------------------------------------------------------
module XMonad.Layout.LayoutBuilderP (
LayoutP (..),
layoutP, layoutAll,
B.relBox, B.absBox,
-- * Overloading ways to select windows
-- $selectWin
Predicate (..), Proxy(..),
) where
import Control.Monad
import Data.Maybe (isJust)
import XMonad
import qualified XMonad.StackSet as W
import XMonad.Util.WindowProperties
import qualified XMonad.Layout.LayoutBuilder as B
-- $selectWin
--
-- 'Predicate' exists because layouts are required to be serializable, and
-- "XMonad.Util.WindowProperties" is not sufficient (for example it does not
-- allow using regular expressions).
--
-- compare "XMonad.Util.Invisible"
-- | 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
class Predicate p w where
alwaysTrue :: Proxy w -> p -- ^ A predicate that is always True.
checkPredicate :: p -> w -> X Bool -- ^ Check if given object (window or smth else) matches that predicate
-- | Contains no actual data, but is needed to help select the correct instance
-- of 'Predicate'
data Proxy a = Proxy
-- | Data type for our layout.
data LayoutP p l1 l2 a =
LayoutP (Maybe a) (Maybe a) p B.SubBox (Maybe B.SubBox) (l1 a) (Maybe (l2 a))
deriving (Show,Read)
-- | Use the specified layout in the described area windows that match given predicate and send the rest of the windows to the next layout in the chain.
-- It is possible to supply an alternative area that will then be used instead, if there are no windows to send to the next layout.
layoutP :: (Read a, Eq a, LayoutClass l1 a, LayoutClass l2 a, LayoutClass l3 a, Predicate p a) =>
p
-> B.SubBox -- ^ The box to place the windows in
-> Maybe B.SubBox -- ^ Possibly an alternative box that is used when this layout handles all windows that are left
-> l1 a -- ^ The layout to use in the specified area
-> LayoutP p l2 l3 a -- ^ Where to send the remaining windows
-> LayoutP p l1 (LayoutP p l2 l3) a -- ^ The resulting layout
layoutP prop box mbox sub next = LayoutP Nothing Nothing prop box mbox sub (Just next)
-- | Use the specified layout in the described area for all remaining windows.
layoutAll :: forall l1 p a. (Read a, Eq a, LayoutClass l1 a, Predicate p a) =>
B.SubBox -- ^ The box to place the windows in
-> l1 a -- ^ The layout to use in the specified area
-> LayoutP p l1 Full a -- ^ The resulting layout
layoutAll box sub =
let a = alwaysTrue (Proxy :: Proxy a)
in LayoutP Nothing Nothing a box Nothing sub Nothing
instance (LayoutClass l1 w, LayoutClass l2 w, Predicate p w, Show w, Read w, Eq w, Typeable w, Show p) =>
LayoutClass (LayoutP p l1 l2) w where
-- | Update window locations.
runLayout (W.Workspace _ (LayoutP subf nextf prop box mbox sub next) s) rect
= do (subs,nexts,subf',nextf') <- splitStack s prop subf nextf
let selBox = if isJust nextf'
then box
else maybe box id mbox
(sublist,sub') <- handle sub subs $ calcArea selBox rect
(nextlist,next') <- case next of Nothing -> return ([],Nothing)
Just n -> do (res,l) <- handle n nexts rect
return (res,Just l)
return (sublist++nextlist, Just $ LayoutP subf' nextf' prop box mbox sub' next' )
where
handle l s' r = do (res,ml) <- runLayout (W.Workspace "" l s') r
l' <- return $ maybe l id ml
return (res,l')
-- | Propagate messages.
handleMessage l m
| Just (IncMasterN _) <- fromMessage m = sendFocus l m
| Just (Shrink) <- fromMessage m = sendFocus l m
| Just (Expand) <- fromMessage m = sendFocus l m
| otherwise = sendBoth l m
-- | Descriptive name for layout.
description (LayoutP _ _ _ _ _ sub (Just next)) = "layoutP "++ description sub ++" "++ description next
description (LayoutP _ _ _ _ _ sub Nothing) = "layoutP "++ description sub
sendSub :: (LayoutClass l1 a, LayoutClass l2 a, Read a, Show a, Eq a, Typeable a, Predicate p a)
=> LayoutP p l1 l2 a -> SomeMessage -> X (Maybe (LayoutP p l1 l2 a))
sendSub (LayoutP subf nextf prop box mbox sub next) m =
do sub' <- handleMessage sub m
return $ if isJust sub'
then Just $ LayoutP subf nextf prop box mbox (maybe sub id sub') next
else Nothing
sendBoth :: (LayoutClass l1 a, LayoutClass l2 a, Read a, Show a, Eq a, Typeable a, Predicate p a)
=> LayoutP p l1 l2 a -> SomeMessage -> X (Maybe (LayoutP p l1 l2 a))
sendBoth l@(LayoutP _ _ _ _ _ _ Nothing) m = sendSub l m
sendBoth (LayoutP subf nextf prop box mbox sub (Just next)) m =
do sub' <- handleMessage sub m
next' <- handleMessage next m
return $ if isJust sub' || isJust next'
then Just $ LayoutP subf nextf prop box mbox (maybe sub id sub') (Just $ maybe next id next')
else Nothing
sendNext :: (LayoutClass l1 a, LayoutClass l2 a, Read a, Show a, Eq a, Typeable a, Predicate p a)
=> LayoutP p l1 l2 a -> SomeMessage -> X (Maybe (LayoutP p l1 l2 a))
sendNext (LayoutP _ _ _ _ _ _ Nothing) _ = return Nothing
sendNext (LayoutP subf nextf prop box mbox sub (Just next)) m =
do next' <- handleMessage next m
return $ if isJust next'
then Just $ LayoutP subf nextf prop box mbox sub next'
else Nothing
sendFocus :: (LayoutClass l1 a, LayoutClass l2 a, Read a, Show a, Eq a, Typeable a, Predicate p a)
=> LayoutP p l1 l2 a -> SomeMessage -> X (Maybe (LayoutP p l1 l2 a))
sendFocus l@(LayoutP subf _ _ _ _ _ _) m = do foc <- isFocus subf
if foc then sendSub l m
else sendNext l m
isFocus :: (Show a) => Maybe a -> X Bool
isFocus Nothing = return False
isFocus (Just w) = do ms <- (W.stack . W.workspace . W.current) `fmap` gets windowset
return $ maybe False (\s -> show w == (show $ W.focus s)) ms
-- | Split given list of objects (i.e. windows) using predicate.
splitBy :: (Predicate p w) => p -> [w] -> X ([w], [w])
splitBy prop ws = foldM step ([], []) ws
where
step (good, bad) w = do
ok <- checkPredicate prop w
return $ if ok
then (w:good, bad)
else (good, w:bad)
splitStack :: (Predicate p w, Eq w) => Maybe (W.Stack w) -> p -> Maybe w -> Maybe w -> X (Maybe (W.Stack w),Maybe (W.Stack w),Maybe w,Maybe w)
splitStack Nothing _ _ _ = return (Nothing,Nothing,Nothing,Nothing)
splitStack (Just s) prop subf nextf = do
let ws = W.integrate s
(good, other) <- splitBy prop ws
let subf' = foc good subf
nextf' = foc other nextf
return ( differentiate' subf' good
, differentiate' nextf' other
, subf'
, nextf'
)
where
foc [] _ = Nothing
foc l f = if W.focus s `elem` l
then Just $ W.focus s
else if maybe False (`elem` l) f
then f
else Just $ head l
calcArea :: B.SubBox -> Rectangle -> Rectangle
calcArea (B.SubBox xpos ypos width height) rect = Rectangle (rect_x rect + fromIntegral xpos') (rect_y rect + fromIntegral ypos') width' height'
where
xpos' = calc False xpos $ rect_width rect
ypos' = calc False ypos $ rect_height rect
width' = calc True width $ rect_width rect - xpos'
height' = calc True height $ rect_height rect - ypos'
calc zneg val tot = fromIntegral $ min (fromIntegral tot) $ max 0 $
case val of B.Rel v -> floor $ v * fromIntegral tot
B.Abs v -> if v<0 || (zneg && v==0)
then (fromIntegral tot)+v
else v
differentiate' :: Eq q => Maybe q -> [q] -> Maybe (W.Stack q)
differentiate' _ [] = Nothing
differentiate' Nothing w = W.differentiate w
differentiate' (Just f) w
| f `elem` w = Just $ W.Stack { W.focus = f
, W.up = reverse $ takeWhile (/=f) w
, W.down = tail $ dropWhile (/=f) w
}
| otherwise = W.differentiate w
instance Predicate Property Window where
alwaysTrue _ = Const True
checkPredicate = hasProperty

View File

@@ -47,6 +47,9 @@ module XMonad.Layout.LayoutCombinators
-- $jtl
, (|||)
, JumpToLayout(..)
-- * Types
, NewSelect
) where
import Data.Maybe ( isJust, isNothing )
@@ -76,6 +79,7 @@ import XMonad.Layout.DragPane
-- > import XMonad hiding ( (|||) )
-- > import XMonad.Layout.LayoutCombinators
--
-- If you import XMonad.Layout, you will need to hide it from there as well.
-- Then bind some keys to a 'JumpToLayout' message:
--
-- > , ((modm .|. controlMask, xK_f), sendMessage $ JumpToLayout "Full") -- jump directly to the Full layout
@@ -179,9 +183,16 @@ infixr 5 |||
-- The standard xmonad core exports a layout combinator @|||@ which
-- represents layout choice. This is a reimplementation which also
-- provides the capability to support 'JumpToLayout' messages. To use
-- it, be sure to hide the import of @|||@ from the xmonad core:
-- it, be sure to hide the import of @|||@ from the xmonad core; if either of
-- these two lines appear in your configuration:
--
-- > import XMonad
-- > import XMonad.Layout
--
-- replace them with these instead, respectively:
--
-- > import XMonad hiding ( (|||) )
-- > import XMonad.Layout hiding ( (|||) )
--
-- The argument given to a 'JumpToLayout' message should be the
-- @description@ of the layout to be selected. If you use
@@ -213,7 +224,7 @@ infixr 5 |||
data NewSelect l1 l2 a = NewSelect Bool (l1 a) (l2 a) deriving ( Read, Show )
-- |
-- |
data JumpToLayout = JumpToLayout String -- ^ A message to jump to a particular layout
-- , specified by its description string..
| NextLayoutNoWrap

View File

@@ -20,10 +20,15 @@ module XMonad.Layout.LayoutHints
, layoutHintsWithPlacement
, layoutHintsToCenter
, LayoutHints
, LayoutHintsToCenter
, hintsEventHook
) where
import XMonad(LayoutClass(runLayout), mkAdjust, Window,
Dimension, Position, Rectangle(Rectangle),D)
Dimension, Position, Rectangle(Rectangle), D,
X, refresh, Event(..), propertyNotify, wM_NORMAL_HINTS,
(<&&>), io, applySizeHints, whenX, isClient, withDisplay,
getWindowAttributes, getWMNormalHints, WindowAttributes(..))
import qualified XMonad.StackSet as W
import XMonad.Layout.Decoration(isInStack)
@@ -32,9 +37,10 @@ import XMonad.Layout.LayoutModifier(ModifiedLayout(..),
import XMonad.Util.Types(Direction2D(..))
import Control.Applicative((<$>))
import Control.Arrow(Arrow((***), first, second))
import Control.Monad(Monad(return), mapM, join)
import Control.Monad(join)
import Data.Function(on)
import Data.List(sortBy)
import Data.Monoid(All(..))
import Data.Set (Set)
import qualified Data.Set as Set
@@ -62,6 +68,14 @@ import qualified Data.Set as Set
-- For more detailed instructions on editing the layoutHook see:
--
-- "XMonad.Doc.Extending#Editing_the_layout_hook"
--
-- To make XMonad reflect changes in window hints immediately, add
-- 'hintsEventHook' to your 'handleEventHook'.
--
-- > myHandleEventHook = hintsEventHook <+> ...
-- >
-- > main = xmonad defaultConfig { handleEventHook = myHandleEventHook
-- > , ... }
layoutHints :: (LayoutClass l a) => l a -> ModifiedLayout LayoutHints l a
layoutHints = ModifiedLayout (LayoutHints (0, 0))
@@ -235,3 +249,19 @@ centerPlacement' cf root assigned
= (cf $ cx - cwx, cf $ cy - cwy)
where (cx,cy) = center root
(cwx,cwy) = center assigned
-- | Event hook that refreshes the layout whenever a window changes its hints.
hintsEventHook :: Event -> X All
hintsEventHook (PropertyEvent { ev_event_type = t, ev_atom = a, ev_window = w })
| t == propertyNotify && a == wM_NORMAL_HINTS = do
whenX (isClient w <&&> hintsMismatch w) $ refresh
return (All True)
hintsEventHook _ = return (All True)
-- | True if the window's current size does not satisfy its size hints.
hintsMismatch :: Window -> X Bool
hintsMismatch w = withDisplay $ \d -> io $ do
wa <- getWindowAttributes d w
sh <- getWMNormalHints d w
let dim = (fromIntegral $ wa_width wa, fromIntegral $ wa_height wa)
return $ dim /= applySizeHints 0 sh dim

View File

@@ -29,6 +29,8 @@ module XMonad.Layout.LayoutModifier (
LayoutModifier(..), ModifiedLayout(..)
) where
import Control.Monad
import XMonad
import XMonad.StackSet ( Stack, Workspace (..) )
@@ -106,6 +108,22 @@ class (Show (m a), Read (m a)) => LayoutModifier m a where
-> X ([(a, Rectangle)], Maybe (l a))
modifyLayout _ w r = runLayout w r
-- | Similar to 'modifyLayout', but this function also allows you
-- update the state of your layout modifier(the second value in the
-- outer tuple).
--
-- If both 'modifyLayoutWithUpdate' and 'redoLayout' return a
-- modified state of the layout modifier, 'redoLayout' takes
-- precedence. If this function returns a modified state, this
-- state will internally be used in the subsequent call to
-- 'redoLayout' as well.
modifyLayoutWithUpdate :: (LayoutClass l a) =>
m a
-> Workspace WorkspaceId (l a) a
-> Rectangle
-> X (([(a,Rectangle)], Maybe (l a)), Maybe (m a))
modifyLayoutWithUpdate m w r = flip (,) Nothing `fmap` modifyLayout m w r
-- | 'handleMess' allows you to spy on messages to the underlying
-- layout, in order to have an effect in the X monad, or alter
-- the layout modifier state in some way (by returning @Just
@@ -234,9 +252,9 @@ class (Show (m a), Read (m a)) => LayoutModifier m a where
-- semantics of a 'LayoutModifier' applied to an underlying layout.
instance (LayoutModifier m a, LayoutClass l a) => LayoutClass (ModifiedLayout m l) a where
runLayout (Workspace i (ModifiedLayout m l) ms) r =
do (ws, ml') <- modifyLayout m (Workspace i l ms) r
(ws', mm') <- redoLayout m r ms ws
let ml'' = case mm' of
do ((ws, ml'),mm') <- modifyLayoutWithUpdate m (Workspace i l ms) r
(ws', mm'') <- redoLayout (maybe m id mm') r ms ws
let ml'' = case mm'' `mplus` mm' of
Just m' -> Just $ (ModifiedLayout m') $ maybe l id ml'
Nothing -> ModifiedLayout m `fmap` ml'
return (ws', ml'')

View File

@@ -16,7 +16,8 @@
module XMonad.Layout.LayoutScreens (
-- * Usage
-- $usage
layoutScreens, fixedLayout
layoutScreens, layoutSplitScreen, fixedLayout,
FixedLayout,
) where
import XMonad
@@ -55,6 +56,7 @@ import qualified XMonad.StackSet as W
-- For detailed instructions on editing your key bindings, see
-- "XMonad.Doc.Extending#Editing_key_bindings".
-- | Modify all screens.
layoutScreens :: LayoutClass l Int => Int -> l Int -> X ()
layoutScreens nscr _ | nscr < 1 = trace $ "Can't layoutScreens with only " ++ show nscr ++ " screens."
layoutScreens nscr l =
@@ -67,6 +69,20 @@ layoutScreens nscr l =
, W.visible = zipWith3 W.Screen xs [1 ..] $ map SD ss
, W.hidden = ys }
-- | Modify current screen.
layoutSplitScreen :: LayoutClass l Int => Int -> l Int -> X ()
layoutSplitScreen nscr _ | nscr < 1 = trace $ "Can't layoutSplitScreen with only " ++ show nscr ++ " screens."
layoutSplitScreen nscr l =
do rect <- gets $ screenRect . W.screenDetail . W.current . windowset
(wss, _) <- runLayout (W.Workspace "" l (Just $ W.Stack { W.focus=1, W.up=[],W.down=[1..nscr-1] })) rect
windows $ \ws@(W.StackSet { W.current = c, W.visible = vs, W.hidden = hs }) ->
let (x:xs, ys) = splitAt nscr $ W.workspace c : hs
s:ss = map snd wss
in ws { W.current = W.Screen x (W.screen c) (SD s)
, W.visible = (zipWith3 W.Screen xs [(W.screen c+1) ..] $ map SD ss) ++
map (\v -> if W.screen v>W.screen c then v{W.screen = W.screen v + fromIntegral (nscr-1)} else v) vs
, W.hidden = ys }
getWindowRectangle :: Window -> X Rectangle
getWindowRectangle w = withDisplay $ \d ->
do a <- io $ getWindowAttributes d w

View File

@@ -1,4 +1,4 @@
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, DeriveDataTypeable, PatternGuards #-}
{-# LANGUAGE CPP, FlexibleInstances, MultiParamTypeClasses, DeriveDataTypeable, PatternGuards #-}
-----------------------------------------------------------------------------
-- |
-- Module : XMonad.Layout.LimitWindows
@@ -23,13 +23,20 @@ module XMonad.Layout.LimitWindows (
limitWindows,limitSlice,limitSelect,
-- * Change the number of windows
increaseLimit,decreaseLimit,setLimit
increaseLimit,decreaseLimit,setLimit,
#ifdef TESTING
-- * For tests
select,update,Selection(..),updateAndSelect,
#endif
-- * Types
LimitWindows, Selection,
) where
import XMonad.Layout.LayoutModifier
import XMonad
import qualified XMonad.StackSet as W
import XMonad.Layout (IncMasterN (..))
import Control.Monad((<=<),guard)
import Control.Applicative((<$>))
import Data.Maybe(fromJust)
@@ -139,7 +146,7 @@ select s stk
downs = W.down stk
ups = reverse $ W.up stk
lups = length ups
updateStart :: Selection l -> W.Stack a -> Int
updateStart s stk
| lups < nMaster s -- the focussed window is in the master pane

View File

@@ -20,7 +20,8 @@ module XMonad.Layout.MagicFocus
promoteWarp,
promoteWarp',
followOnlyIf,
disableFollowOnWS
disableFollowOnWS,
MagicFocus,
) where
import XMonad

View File

@@ -26,12 +26,14 @@ module XMonad.Layout.Magnifier
magnifiercz,
magnifiercz',
maximizeVertical,
MagnifyMsg (..)
MagnifyMsg (..),
Magnifier,
) where
import XMonad
import XMonad.StackSet
import XMonad.Layout.LayoutModifier
import XMonad.Util.XUtils
-- $usage
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
@@ -159,6 +161,3 @@ fit (Rectangle sx sy sw sh) (Rectangle x y w h) = Rectangle x' y' w' h'
y' = max sy (y - (max 0 (y + fi h - sy - fi sh)))
w' = min sw w
h' = min sh h
fi :: (Num b, Integral a) => a -> b
fi = fromIntegral

View File

@@ -17,7 +17,9 @@ module XMonad.Layout.Master (
-- * Usage
-- $usage
mastered
mastered,
multimastered,
AddMaster,
) where
import XMonad
@@ -34,6 +36,10 @@ import XMonad.Layout.LayoutModifier
--
-- > mastered (1/100) (1/2) $ Grid
--
-- Or if you want multiple (here two) master windows from the beginning:
--
-- > multimastered 2 (1/100) (1/2) $ Grid
--
-- This will use the left half of your screen for a master window and let
-- Grid manage the right half.
--
@@ -45,42 +51,68 @@ import XMonad.Layout.LayoutModifier
-- | Data type for LayoutModifier which converts given layout to a mastered
-- layout
data AddMaster a = AddMaster Rational Rational deriving (Show, Read)
data AddMaster a = AddMaster Int Rational Rational deriving (Show, Read)
-- | Modifier which converts given layout to a mastered one
multimastered :: (LayoutClass l a) =>
Int -- ^ @k@, number of master windows
-> Rational -- ^ @delta@, the ratio of the screen to resize by
-> Rational -- ^ @frac@, what portion of the screen to use for the master window
-> l a -- ^ the layout to be modified
-> ModifiedLayout AddMaster l a
multimastered k delta frac = ModifiedLayout $ AddMaster k delta frac
mastered :: (LayoutClass l a) =>
Rational -- ^ @delta@, the ratio of the screen to resize by
-> Rational -- ^ @frac@, what portion of the screen to use for the master window
-> l a -- ^ the layout to be modified
-> ModifiedLayout AddMaster l a
mastered delta frac = ModifiedLayout $ AddMaster delta frac
mastered delta frac = multimastered 1 delta frac
instance LayoutModifier AddMaster Window where
modifyLayout (AddMaster delta frac) = applyMaster delta frac
modifyLayout (AddMaster k delta frac) = applyMaster k delta frac
modifierDescription _ = "Mastered"
pureMess (AddMaster delta frac) m
| Just Shrink <- fromMessage m = Just $ AddMaster delta (frac-delta)
| Just Expand <- fromMessage m = Just $ AddMaster delta (frac+delta)
pureMess (AddMaster k delta frac) m
| Just Shrink <- fromMessage m = Just $ AddMaster k delta (frac-delta)
| Just Expand <- fromMessage m = Just $ AddMaster k delta (frac+delta)
| Just (IncMasterN d) <- fromMessage m = Just $ AddMaster (max 1 (k+d)) delta frac
pureMess _ _ = Nothing
-- | Internal function for adding a master window and let the modified
-- layout handle the rest of the windows
applyMaster :: (LayoutClass l Window) =>
Rational
Int
-> Rational
-> Rational
-> S.Workspace WorkspaceId (l Window) Window
-> Rectangle
-> X ([(Window, Rectangle)], Maybe (l Window))
applyMaster _ frac wksp rect = do
applyMaster k _ frac wksp rect = do
let st= S.stack wksp
let ws = S.integrate' $ st
if length ws > 1 then do
let m = head ws
let (mr, sr) = splitHorizontallyBy frac rect
let nst = st>>= S.filter (m/=)
wrs <- runLayout (wksp {S.stack = nst}) sr
return ((m, mr) : fst wrs, snd wrs)
let n = length ws
if n > 1 then do
if(n<=k) then
return ((divideCol rect ws), Nothing)
else do
let m = take k ws
let (mr, sr) = splitHorizontallyBy frac rect
let nst = st>>= S.filter (\w -> not (w `elem` m))
wrs <- runLayout (wksp {S.stack = nst}) sr
return ((divideCol mr m) ++ (fst wrs), snd wrs)
else runLayout wksp rect
-- | Shift rectangle down
shiftD :: Position -> Rectangle -> Rectangle
shiftD s (Rectangle x y w h) = Rectangle x (y+s) w h
-- | Divide rectangle between windows
divideCol :: Rectangle -> [a] -> [(a, Rectangle)]
divideCol (Rectangle x y w h) ws = zip ws rects
where n = length ws
oneH = fromIntegral h `div` n
oneRect = Rectangle x y w (fromIntegral oneH)
rects = take n $ iterate (shiftD (fromIntegral oneH)) oneRect

View File

@@ -19,7 +19,8 @@ module XMonad.Layout.Maximize (
-- * Usage
-- $usage
maximize,
maximizeRestore
maximizeRestore,
Maximize, MaximizeRestore,
) where
import XMonad
@@ -64,12 +65,14 @@ instance LayoutModifier Maximize Window where
pureModifier (Maximize (Just target)) rect (Just (S.Stack focused _ _)) wrs =
if focused == target
then (maxed ++ rest, Nothing)
else (rest ++ maxed, Nothing)
else (rest ++ maxed, lay)
where
(toMax, rest) = partition (\(w, _) -> w == target) wrs
maxed = map (\(w, _) -> (w, maxRect)) toMax
maxRect = Rectangle (rect_x rect + 25) (rect_y rect + 25)
(rect_width rect - 50) (rect_height rect - 50)
lay | null maxed = Just (Maximize Nothing)
| otherwise = Nothing
pureModifier _ _ _ wrs = (wrs, Nothing)
pureMess (Maximize mw) m = case fromMessage m of

View File

@@ -6,7 +6,7 @@
-- Copyright : (c) 2008 Quentin Moser
-- License : BSD3
--
-- Maintainer : <quentin.moser@unifr.ch>
-- Maintainer : orphaned
-- Stability : unstable
-- Portability : unportable
--
@@ -41,7 +41,7 @@ import Control.Arrow (second)
-- > import XMonad.Layout.MessageEscape
--
-- Then, if you use a modified layout where the modifier would intercept
-- a message, but you'd want to be able to send it to the inner layout
-- a message, but you'd want to be able to send it to the inner layout
-- only, add the 'unEscape' modifier to the inner layout like so:
--
-- > import XMonad.Layout.Master (mastered)
@@ -60,7 +60,7 @@ import Control.Arrow (second)
-- layout, use the 'ignore' modifier:
--
-- > myLayout = Tall ||| (ignore NextLayout $ ignore (JumpToLayout "") $
-- > unEscape $ mastered 0.01 0.5
-- > unEscape $ mastered 0.01 0.5
-- > $ Full ||| simpleTabbed)
--
-- /IMPORTANT NOTE:/ The standard '(|||)' operator from "XMonad.Layout"
@@ -121,6 +121,6 @@ unEscape l = ModifiedLayout UE l
-- | Applies the Ignore layout modifier to a layout, blocking
-- all messages of the same type as the one passed as its first argument.
ignore :: (Message m, LayoutClass l w)
ignore :: (Message m, LayoutClass l w)
=> m -> l w -> (Ignore m l w)
ignore _ l = I l

View File

@@ -1,8 +1,8 @@
{-# LANGUAGE MultiParamTypeClasses, DeriveDataTypeable, TypeSynonymInstances, FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses, DeriveDataTypeable, TypeSynonymInstances, FlexibleContexts, PatternGuards #-}
----------------------------------------------------------------------------
-- |
-- Module : XMonad.Layout.Minimize
-- Copyright : (c) Jan Vornberger 2009
-- Copyright : (c) Jan Vornberger 2009, Alejandro Serrano 2010
-- License : BSD3-style (see LICENSE)
--
-- Maintainer : jan.vornberger@informatik.uni-oldenburg.de
@@ -18,14 +18,20 @@ module XMonad.Layout.Minimize (
-- * Usage
-- $usage
minimize,
MinimizeMsg(..)
minimizeWindow,
MinimizeMsg(RestoreMinimizedWin,RestoreNextMinimizedWin),
Minimize,
) where
import XMonad
import qualified XMonad.StackSet as W
import XMonad.Layout.LayoutModifier
import XMonad.Layout.BoringWindows as BW
import XMonad.Util.WindowProperties (getProp32)
import Data.List
import qualified Data.Map as M
import Data.Maybe
import Foreign.C.Types (CLong)
-- $usage
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
@@ -43,7 +49,7 @@ import Data.List
--
-- In the key-bindings, do something like:
--
-- > , ((modm, xK_m ), withFocused (\f -> sendMessage (MinimizeWin f)))
-- > , ((modm, xK_m ), withFocused minimizeWindow)
-- > , ((modm .|. shiftMask, xK_m ), sendMessage RestoreNextMinimizedWin)
--
-- The first action will minimize the focused window, while the second one will restore
@@ -54,15 +60,16 @@ import Data.List
-- "XMonad.Doc.Extending#Editing_key_bindings".
--
-- The module is designed to work together with "XMonad.Layout.BoringWindows" so
-- that minimized windows will be skipped when switching the focus window with
-- the keyboard. Use the 'BW.boringAuto' function.
-- that minimized windows will be skipped over when switching the focused window with
-- the keyboard. Include 'BW.boringWindows' in your layout hook and see the
-- documentation of "XMonad.Layout.BoringWindows" on how to modify your keybindings.
--
-- Also see "XMonad.Hooks.RestoreMinimized" if you want to be able to restore
-- minimized windows from your taskbar.
-- Also see "XMonad.Hooks.Minimize" if you want to be able to minimize
-- and restore windows from your taskbar.
data Minimize a = Minimize [Window] deriving ( Read, Show )
data Minimize a = Minimize [Window] (M.Map Window W.RationalRect) deriving ( Read, Show )
minimize :: LayoutClass l Window => l Window -> ModifiedLayout Minimize l Window
minimize = ModifiedLayout $ Minimize []
minimize = ModifiedLayout $ Minimize [] M.empty
data MinimizeMsg = MinimizeWin Window
| RestoreMinimizedWin Window
@@ -70,25 +77,68 @@ data MinimizeMsg = MinimizeWin Window
deriving (Typeable, Eq)
instance Message MinimizeMsg
instance LayoutModifier Minimize Window where
modifierDescription (Minimize _) = "Minimize"
minimizeWindow :: Window -> X ()
minimizeWindow w = sendMessage (MinimizeWin w) >> BW.focusDown
modifyLayout (Minimize minimized) wksp rect = do
setMinimizedState :: Window -> Int -> (CLong -> [CLong] -> [CLong]) -> X ()
setMinimizedState win st f = do
setWMState win st
withDisplay $ \dpy -> do
state <- getAtom "_NET_WM_STATE"
mini <- getAtom "_NET_WM_STATE_HIDDEN"
wstate <- fromMaybe [] `fmap` getProp32 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)
setMinimized :: Window -> X ()
setMinimized win = setMinimizedState win iconicState (:)
setNotMinimized :: Window -> X ()
setNotMinimized win = setMinimizedState win normalState delete
instance LayoutModifier Minimize Window where
modifierDescription _ = "Minimize"
modifyLayout (Minimize minimized _) wksp rect = do
let stack = W.stack wksp
filtStack = stack >>=W.filter (\w -> not (w `elem` minimized))
runLayout (wksp {W.stack = filtStack}) rect
handleMess (Minimize minimized) m = case fromMessage m of
Just (MinimizeWin w)
| not (w `elem` minimized) -> do
BW.focusDown
return $ Just $ Minimize (w:minimized)
| otherwise -> return Nothing
Just (RestoreMinimizedWin w) ->
return $ Just $ Minimize (minimized \\ [w])
Just (RestoreNextMinimizedWin)
| not (null minimized) -> do
focus (head minimized)
return $ Just $ Minimize (tail minimized)
| otherwise -> return Nothing
_ -> return Nothing
handleMess (Minimize minimized unfloated) m
| Just (MinimizeWin w) <- fromMessage m, not (w `elem` minimized) = do
setMinimized w
ws <- gets windowset
case M.lookup w (W.floating ws) of
Nothing -> return $ Just $ Minimize (w:minimized) unfloated
Just r -> do
modify (\s -> s { windowset = W.sink w ws})
return $ Just $ Minimize (w:minimized) (M.insert w r unfloated)
| Just (RestoreMinimizedWin w) <- fromMessage m = do
setNotMinimized w
case M.lookup w unfloated of
Nothing -> return $ Just $ Minimize (minimized \\ [w]) unfloated
Just r -> do
ws <- gets windowset
modify (\s -> s { windowset = W.float w r ws})
return $ Just $ Minimize (minimized \\ [w]) (M.delete w unfloated)
| Just RestoreNextMinimizedWin <- fromMessage m = do
ws <- gets windowset
if not (null minimized)
then case M.lookup (head minimized) unfloated of
Nothing -> do
let w = head minimized
setNotMinimized w
modify (\s -> s { windowset = W.focusWindow w ws})
return $ Just $ Minimize (tail minimized) unfloated
Just r -> do
let w = head minimized
setNotMinimized w
modify (\s -> s { windowset = (W.focusWindow w . W.float w r) ws})
return $ Just $ Minimize (tail minimized) (M.delete w unfloated)
else return Nothing
| Just BW.UpdateBoring <- fromMessage m = do
ws <- gets (W.workspace . W.current . windowset)
flip sendMessageWithNoRefresh ws $ BW.Replace "Minimize" minimized
return Nothing
| otherwise = return Nothing

View File

@@ -21,6 +21,8 @@ module XMonad.Layout.Mosaic (
,mosaic
,changeMaster
,changeFocused
,Mosaic
)
where

View File

@@ -25,6 +25,9 @@ module XMonad.Layout.MosaicAlt (
, tallWindowAlt
, wideWindowAlt
, resetAlt
, Params, Param
, HandleWindowAlt
) where
import XMonad

Some files were not shown because too many files have changed in this diff Show More