xmonad 0.17.2 → 0.18.0
raw patch · 14 files changed
+344/−145 lines, 14 files
Files
- CHANGES.md +33/−3
- INSTALL.md +18/−15
- MAINTAINERS.md +6/−1
- README.md +3/−3
- TUTORIAL.md +83/−26
- src/XMonad/Core.hs +67/−24
- src/XMonad/Layout.hs +5/−1
- src/XMonad/Main.hs +1/−54
- src/XMonad/ManageHook.hs +1/−5
- src/XMonad/Operations.hs +84/−5
- src/XMonad/StackSet.hs +4/−1
- tests/Properties.hs +6/−1
- tests/Properties/Layout/Tall.hs +28/−4
- xmonad.cabal +5/−2
CHANGES.md view
@@ -1,5 +1,35 @@ # Change Log / Release Notes +## 0.18.0 (February 3, 2024)++### Breaking Changes++* Dropped support for GHC 8.4.++### Enhancements++* Exported `sendRestart` and `sendReplace` from `XMonad.Operations`.++* Exported `buildLaunch` from `XMonad.Main`.++* `Tall` does not draw windows with zero area.++* `XMonad.Operations.floatLocation` now applies size hints. This means windows+ will snap to these hints as soon as they're floated (mouse move, keybinding).+ Previously that only happened on mouse resize.++* Recompilation now detects `flake.nix` and `default.nix` (can be a+ symlink) and switches to using `nix build` as appropriate.++* Added `unGrab` to `XMonad.Operations`; this releases XMonad's passive+ keyboard grab, so other applications (like `scrot`) can do their+ thing.++### Bug Fixes++* Duplicated floats (e.g. from X.A.CopyToAll) no longer escape to inactive+ screens.+ ## 0.17.2 (April 2, 2023) ### Bug Fixes@@ -14,6 +44,9 @@ * Exported `cacheNumlockMask` and `mkGrabs` from `XMonad.Operations`. + * Added `willFloat` function to `XMonad.ManageHooks` to detect whether the+ (about to be) managed window will be a floating window or not.+ ### Bug Fixes * Fixed border color of windows with alpha channel. Now all windows have the@@ -70,9 +103,6 @@ * Added `withUnfocused` function to `XMonad.Operations`, allowing for `X` operations to be applied to unfocused windows.-- * Added `willFloat` function to `XMonad.ManageHooks` to detect whether the- (about to be) managed window will be a floating window or not [these build scripts]: https://github.com/xmonad/xmonad-testing/tree/master/build-scripts
INSTALL.md view
@@ -123,7 +123,9 @@ #### Install Stack -The easiest way to get [stack] is probably via your system's package+Probably one of the best ways to get [stack] is to use [GHCup], which is the main Haskell installer according to language's official [website][GHCup] and community [survey]. GHCup is [widely available] and is considered less error prone than other installation options.++You can also use your system's package manager: ``` console@@ -145,9 +147,6 @@ $ curl -sSL https://get.haskellstack.org/ | sh ``` -Yet another way would be via [ghcup]; this is similar to installers like-`rustup`, in case you prefer that.- #### Create a New Project Let's create a stack project. Since we're already in the correct@@ -222,7 +221,9 @@ #### Install cabal-install -The easiest way to get [cabal-install] is probably via your system's package+Probably one of the best ways to get [cabal-install] is to use [GHCup], which is the main Haskell installer according to language's official [website][GHCup] and community [survey]. GHCup is [widely available] and is considered less error prone than other installation options.++You can also use your system's package manager: ``` console@@ -231,22 +232,22 @@ $ sudo pacman -S cabal-install # Arch ``` -If your distribution does not package cabal-install, [ghcup][] is another-option. See also <https://www.haskell.org/cabal/#install-upgrade>.+See also <https://www.haskell.org/cabal/#install-upgrade>. #### Create a New Project -Let's create a cabal project. Since we're already in the correct-directory (`~/.config/xmonad`) with `xmonad` and `xmonad-contrib`-subdirectories, we'll instruct cabal to use them. Create a file named-`cabal.project` containing:+If you want to use `xmonad` or `xmonad-contrib` from git, you will need a+`cabal.project` file. If you want to use both from [Hackage][], you should+skip this step. +Create a file named `cabal.project` containing:+ ``` packages: */*.cabal ``` -(If you skip this step, cabal will use the latest releases from [Hackage][]-instead.)+(If you do this step without using [git] checkouts, you will get an error from+cabal in the next step. Simply remove `cabal.project` and try again.) #### Install Everything @@ -255,7 +256,7 @@ ``` console $ cabal update-$ cabal install --package-env=$HOME/.config/xmonad --lib xmonad xmonad-contrib+$ cabal install --package-env=$HOME/.config/xmonad --lib base xmonad xmonad-contrib $ cabal install --package-env=$HOME/.config/xmonad xmonad ``` @@ -393,7 +394,9 @@ [git]: https://git-scm.com/ [stack]: https://docs.haskellstack.org/en/stable/README/ [cabal-install]: https://www.haskell.org/cabal/-[ghcup]: https://www.haskell.org/ghcup/+[GHCup]: https://www.haskell.org/ghcup/+[survey]: https://taylor.fausak.me/2022/11/18/haskell-survey-results/+[widely available]: https://www.haskell.org/ghcup/install/#supported-platforms [what xmonad would do]: https://github.com/xmonad/xmonad/blob/master/src/XMonad/Core.hs#L659-L667 [Hackage]: https://hackage.haskell.org/ [scripts/build]: https://github.com/xmonad/xmonad-contrib/blob/master/scripts/build
MAINTAINERS.md view
@@ -117,7 +117,12 @@ See [old announcements][old-announce] ([even older][older-announce]) for inspiration. - 7. Bump version for development (add `.9`) and prepare fresh sections in+ 7. Trigger xmonad-docs build to generate and persist docs for the just+ released version:++ - https://github.com/xmonad/xmonad-docs/actions/workflows/stack.yml++ 8. Bump version for development (add `.9`) and prepare fresh sections in [`CHANGES.md`](CHANGES.md). [packdeps]: https://hackage.haskell.org/package/packdeps
README.md view
@@ -6,9 +6,9 @@ <a href="https://github.com/xmonad/xmonad/blob/readme/LICENSE"><img alt="License" src="https://img.shields.io/github/license/xmonad/xmonad"></a> <a href="https://haskell.org/"><img alt="Made in Haskell" src="https://img.shields.io/badge/Made%20in-Haskell-%235e5086?logo=haskell"></a> <br>- <a href="https://github.com/xmonad/xmonad/actions/workflows/stack.yml"><img alt="Stack" src="https://img.shields.io/github/workflow/status/xmonad/xmonad/Stack?label=Stack&logo=githubactions&logoColor=white"></a>- <a href="https://github.com/xmonad/xmonad/actions/workflows/haskell-ci.yml"><img alt="Cabal" src="https://img.shields.io/github/workflow/status/xmonad/xmonad/Haskell-CI?label=Cabal&logo=githubactions&logoColor=white"></a>- <a href="https://github.com/xmonad/xmonad/actions/workflows/nix.yml"><img alt="Nix" src="https://img.shields.io/github/workflow/status/xmonad/xmonad/Nix?label=Nix&logo=githubactions&logoColor=white"></a>+ <a href="https://github.com/xmonad/xmonad/actions/workflows/stack.yml"><img alt="Stack" src="https://img.shields.io/github/actions/workflow/status/xmonad/xmonad/stack.yml?label=Stack&logo=githubactions&logoColor=white"></a>+ <a href="https://github.com/xmonad/xmonad/actions/workflows/haskell-ci.yml"><img alt="Cabal" src="https://img.shields.io/github/actions/workflow/status/xmonad/xmonad/haskell-ci.yml?label=Cabal&logo=githubactions&logoColor=white"></a>+ <a href="https://github.com/xmonad/xmonad/actions/workflows/nix.yml"><img alt="Nix" src="https://img.shields.io/github/actions/workflow/status/xmonad/xmonad/nix.yml?label=Nix&logo=githubactions&logoColor=white"></a> <br> <a href="https://github.com/sponsors/xmonad"><img alt="GitHub Sponsors" src="https://img.shields.io/github/sponsors/xmonad?label=GitHub%20Sponsors&logo=githubsponsors"></a> <a href="https://opencollective.com/xmonad"><img alt="Open Collective" src="https://img.shields.io/opencollective/all/xmonad?label=Open%20Collective&logo=opencollective"></a>
TUTORIAL.md view
@@ -77,6 +77,11 @@ `~/.xinitrc` as directed in the xmonad guided tour, you should be good to go! If not, just smack an `exec xmonad` at the bottom of that file. +In particular, it might be a good idea to set a wallpaper beforehand.+Otherwise, when switching workspaces or closing windows, you might start+seeing "shadows" of windows that were there before, unable to interact+with them.+ ## Installing Xmobar What we need to do now—provided we want to use a bar at all—is to@@ -263,7 +268,7 @@ ``` The so-called `where`-clause above simply consists of local declarations-that might clutter things up where they all declared at the top-level+that might clutter things up were they all declared at the top-level like this ``` haskell@@ -379,12 +384,16 @@ some [Hacks] to make this work), we will also add the relevant function to get "proper" fullscreen behaviour here. +---+ _IF YOU ARE ON A VERSION `< 0.17.0`_: The `ewmhFullscreen` function does not exist in these versions. Instead of it, you can try to add `fullscreenEventHook` to your `handleEventHook` to achieve similar functionality (how to do this is explained in the documentation of [XMonad.Hooks.EwmhDesktops]). +---+ To use the two combinators, we compose them with the `xmonad` function in the following way: @@ -430,7 +439,7 @@ ## Make XMonad and Xmobar Talk to Each Other Onto the main dish. First, we have to import the necessary modules.-Add the following to your list of imports:+Add the following to your list of imports ``` haskell import XMonad.Hooks.DynamicLog@@ -438,24 +447,28 @@ import XMonad.Hooks.StatusBar.PP ``` -_IF YOU ARE ON A VERSION `< 0.17.0`_: The `XMonad.Hooks.StatusBar` and- `XMonad.Hooks.StatusBar.PP` modules don't exist yet. You can find- everything you need in the `XMonad.Hooks.DynamicLog` module, so remove- these two imports.--Replace your `main` function above with:+and replace your `main` function above with: ``` haskell main :: IO () main = xmonad $ ewmhFullscreen $ ewmh $ xmobarProp $ myConfig ``` -_IF YOU ARE ON A VERSION `< 0.17.0`_: The `xmobarProp` function does not- exist in these versions. Instead of it, use `xmobar` via- `main = xmonad . ewmh =<< xmobar myConfig` and carefully read the part- about pipes later on (`xmobar` uses pipes to make xmobar talk to- xmonad).+--- +_IF YOU ARE ON A VERSION `< 0.17.0`_: The `XMonad.Hooks.StatusBar` and+ `XMonad.Hooks.StatusBar.PP` modules don't exist yet. You can find+ everything you need in the `XMonad.Hooks.DynamicLog` module, so remove+ these two imports.++ Further, the `xmobarProp` function does not exist in older versions.+ Instead of it, use `xmobar` via `main = xmonad . ewmh =<< xmobar+ myConfig` and carefully read the part about pipes later on (`xmobar`+ uses pipes to make xmobar talk to xmonad). Do note the lack of+ `ewmhFullscreen`, as explained above!++---+ As a quick side-note, we could have also written ``` haskell@@ -540,11 +553,15 @@ (this is useful, for example, for [XMonad.Util.ClickableWorkspaces], which is a new feature in `0.17.0`). +---+ _IF YOU ARE ON A VERSION `< 0.17.0`_: As discussed above, the `xmobar` function uses pipes, so you actually do want to use the `StdinReader`. Simply replace _all_ occurences of `XMonadLog` with `StdinReader` below (don't forget the template!) +---+ ## Configuring Xmobar Now, before this will work, we have to configure xmobar. Here's a nice@@ -660,6 +677,8 @@ $ myConfig ``` +---+ _IF YOU ARE ON A VERSION `< 0.17.0`_: `xmobar` has a similar definition, relying on `statusBar` alone: `xmobar = statusBar "xmobar" xmobarPP toggleStrutsKey`. Sadly, the `defToggleStrutsKey` function is not yet@@ -668,7 +687,6 @@ ``` haskell main :: IO () main = xmonad- . ewmhFullscreen . ewmh =<< statusBar "xmobar" def toggleStrutsKey myConfig where@@ -676,6 +694,8 @@ toggleStrutsKey XConfig{ modMask = m } = (m, xK_b) ``` +---+ The `defToggleStrutsKey` here is just the key with which you can toggle the bar; it is bound to `M-b`. If you want to change this, you can also define your own:@@ -772,6 +792,8 @@ lowWhite = xmobarColor "#bbbbbb" "" ``` +---+ _IF YOU ARE ON A VERSION `< 0.17`_: Both `logTitles` and `xmobarBorder` are not available yet, so you will have to remove them. As an alternative to `xmobarBorder`, a common way to "mark" the currently@@ -779,6 +801,8 @@ `ppCurrent = wrap (blue "[") (blue "]")` and see if you like it. Also read the bit about `ppOrder` further down! +---+ That's a lot! But don't worry, take a deep breath and remind yourself of what you read above in the documentation of the [PP record]. Even if you haven't read the documentation yet, most of the fields should be@@ -882,10 +906,10 @@ First, configure xscreensaver how you like it with the `xscreensaver-demo` command. Now, we will set these things up in-`~/.xinitrc` (we could also do most of this in xmonad's `startupHook`,-but `~/.xinitrc` is perhaps more standard). If you want to use xmonad-with a desktop environment, see [Basic Desktop Environment Integration]-for how to do this.+`~/.xinitrc`. If you want to use XMonad with a desktop environment, see+[Basic Desktop Environment Integration] for how to do this. For a+version using XMonad's built in functionality instead, see the [next+section][using-the-startupHook]. Your `~/.xinitrc` may wind up looking like this: @@ -933,6 +957,38 @@ Of course substitute the wallpaper for one of your own. If you like the one used above, you can find it [here](https://i.imgur.com/9MQHuZx.png). +### Using the `startupHook`++Instead of the `.xinitrc` file, one can also use XMonad's built in+`startupHook` in order to auto start programs. The+[XMonad.Util.SpawnOnce] library is perfect for this use case, allowing+programs to start only once, and not with every invocation of `M-q`!++This requires but a small change in `myConfig`:++``` haskell+-- import XMonad.Util.SpawnOnce (spawnOnce)++myConfig = def+ { modMask = mod4Mask -- Rebind Mod to the Super key+ , layoutHook = myLayout -- Use custom layouts+ , startupHook = myStartupHook+ }+ `additionalKeysP`+ [ ("M-S-z", spawn "xscreensaver-command -lock")+ , ("M-C-s", unGrab *> spawn "scrot -s" )+ , ("M-f" , spawn "firefox" )+ ]++myStartupHook :: X ()+myStartupHook = do+ spawnOnce "trayer --edge top --align right --SetDockType true \+ \--SetPartialStrut true --expand true --width 10 \+ \--transparent true --tint 0x5f5f5f --height 18"+ spawnOnce "feh --bg-fill --no-fehbg ~/.wallpapers/haskell-red-noise.png"+ -- … and so on …+```+ ## Final Touches There may be some programs that you don't want xmonad to tile. The@@ -1238,21 +1294,22 @@ [Basic Desktop Environment Integration]: https://wiki.haskell.org/Xmonad/Basic_Desktop_Environment_Integration [Hacks]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Util-Hacks.html-[PP record]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Hooks-DynamicLog.html#t:PP [INSTALL.md]: INSTALL.md+[PP record]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Hooks-DynamicLog.html#t:PP [XMonad.Config]: https://github.com/xmonad/xmonad/blob/master/src/XMonad/Config.hs-[XMonad.ManageHook]: https://xmonad.github.io/xmonad-docs/xmonad/XMonad-ManageHook.html-[XMonad.Util.Loggers]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Util-Loggers.html-[XMonad.Util.EZConfig]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Util-EZConfig.html-[XMonad.Layout.Renamed]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Layout-Renamed.html-[XMonad.Layout.Magnifier]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Layout-Magnifier.html [XMonad.Doc.Contributing]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Doc-Configuring.html [XMonad.Hooks.EwmhDesktops]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Hooks-EwmhDesktops.html-[XMonad.Layout.ThreeColumns]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Layout-ThreeColumns.html [XMonad.Hooks.ManageHelpers]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Hooks-ManageHelpers.html+[XMonad.Layout.Magnifier]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Layout-Magnifier.html+[XMonad.Layout.Renamed]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Layout-Renamed.html+[XMonad.Layout.ThreeColumns]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Layout-ThreeColumns.html+[XMonad.ManageHook]: https://xmonad.github.io/xmonad-docs/xmonad/XMonad-ManageHook.html [XMonad.Util.ClickableWorkspaces]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Util-ClickableWorkspaces.html+[XMonad.Util.EZConfig]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Util-EZConfig.html+[XMonad.Util.Loggers]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Util-Loggers.html+[XMonad.Util.SpawnOnce]: https://hackage.haskell.org/package/xmonad-contrib/docs/XMonad-Util-SpawnOnce.html -[xmobar]: https://xmobar.org/+[xmobar]: https://codeberg.org/xmobar/xmobar [battery]: https://codeberg.org/xmobar/xmobar/src/branch/master/doc/plugins.org#batteryp-dirs-args-refreshrate [xmobar.hs]: https://codeberg.org/xmobar/xmobar/src/branch/master/etc/xmobar.hs [Wikipedia page]: https://en.wikipedia.org/wiki/ICAO_airport_code#Prefixes
src/XMonad/Core.hs view
@@ -7,6 +7,7 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE DerivingVia #-} ----------------------------------------------------------------------------- -- |@@ -32,7 +33,7 @@ StateExtension(..), ExtensionClass(..), ConfExtension(..), runX, catchX, userCode, userCodeDef, io, catchIO, installSignalHandlers, uninstallSignalHandlers, withDisplay, withWindowSet, isRoot, runOnWorkspaces,- getAtom, spawn, spawnPID, xfork, xmessage, recompile, trace, whenJust, whenX,+ getAtom, spawn, spawnPID, xfork, xmessage, recompile, trace, whenJust, whenX, ifM, getXMonadDir, getXMonadCacheDir, getXMonadDataDir, stateFileName, binFileName, atom_WM_STATE, atom_WM_PROTOCOLS, atom_WM_DELETE_WINDOW, atom_WM_TAKE_FOCUS, withWindowAttributes, ManageHook, Query(..), runQuery, Directories'(..), Directories, getDirectories,@@ -48,12 +49,13 @@ import Control.Monad.Fix (fix) import Control.Monad.State import Control.Monad.Reader-import Control.Monad (filterM, guard, liftM2, void, when)+import Control.Monad (filterM, guard, void, when) import Data.Semigroup import Data.Traversable (for) import Data.Time.Clock (UTCTime) import Data.Default.Class import System.Environment (lookupEnv)+import Data.List (isInfixOf, intercalate, (\\)) import System.FilePath import System.IO import System.Info@@ -68,8 +70,8 @@ import Graphics.X11.Xlib import Graphics.X11.Xlib.Extras (getWindowAttributes, WindowAttributes, Event) import Data.Typeable-import Data.List (isInfixOf, (\\)) import Data.Maybe (isJust,fromMaybe)+import Data.Monoid (Ap(..)) import qualified Data.Map as M import qualified Data.Set as S@@ -165,12 +167,7 @@ -- newtype X a = X (ReaderT XConf (StateT XState IO) a) deriving (Functor, Applicative, Monad, MonadFail, MonadIO, MonadState XState, MonadReader XConf)--instance Semigroup a => Semigroup (X a) where- (<>) = liftM2 (<>)--instance (Monoid a) => Monoid (X a) where- mempty = pure mempty+ deriving (Semigroup, Monoid) via Ap X a instance Default a => Default (X a) where def = return def@@ -178,16 +175,11 @@ type ManageHook = Query (Endo WindowSet) newtype Query a = Query (ReaderT Window X a) deriving (Functor, Applicative, Monad, MonadReader Window, MonadIO)+ deriving (Semigroup, Monoid) via Ap Query a runQuery :: Query a -> Window -> X a runQuery (Query m) = runReaderT m -instance Semigroup a => Semigroup (Query a) where- (<>) = liftM2 (<>)--instance Monoid a => Monoid (Query a) where- mempty = pure mempty- instance Default a => Default (Query a) where def = return def @@ -423,9 +415,13 @@ data ConfExtension = forall a. Typeable a => ConfExtension a -- ------------------------------------------------------------------------ | General utilities------ Lift an 'IO' action into the 'X' monad+-- General utilities++-- | If-then-else lifted to a 'Monad'.+ifM :: Monad m => m Bool -> m a -> m a -> m a+ifM mb t f = mb >>= \b -> if b then t else f++-- | Lift an 'IO' action into the 'X' monad io :: MonadIO m => IO a -> m a io = liftIO @@ -584,21 +580,31 @@ srcFileName Directories{ cfgDir } = cfgDir </> "xmonad.hs" libFileName Directories{ cfgDir } = cfgDir </> "lib" -buildScriptFileName, stackYamlFileName :: Directories -> FilePath+buildScriptFileName, stackYamlFileName, nixFlakeFileName, nixDefaultFileName :: Directories -> FilePath buildScriptFileName Directories{ cfgDir } = cfgDir </> "build" stackYamlFileName Directories{ cfgDir } = cfgDir </> "stack.yaml"+nixFlakeFileName Directories{ cfgDir } = cfgDir </> "flake.nix"+nixDefaultFileName Directories{ cfgDir } = cfgDir </> "default.nix" -- | Compilation method for xmonad configuration.-data Compile = CompileGhc | CompileStackGhc FilePath | CompileScript FilePath+data Compile+ = CompileGhc+ | CompileStackGhc FilePath+ | CompileNixFlake+ | CompileNixDefault+ | CompileScript FilePath deriving (Show) -- | Detect compilation method by looking for known file names in xmonad -- configuration directory. detectCompile :: Directories -> IO Compile-detectCompile dirs = tryScript <|> tryStack <|> useGhc+detectCompile dirs =+ tryScript <|> tryStack <|> tryNixFlake <|> tryNixDefault <|> useGhc where buildScript = buildScriptFileName dirs stackYaml = stackYamlFileName dirs+ flakeNix = nixFlakeFileName dirs+ defaultNix = nixDefaultFileName dirs tryScript = do guard =<< doesFileExist buildScript@@ -612,6 +618,18 @@ trace $ "Suggested resolution to use it: chmod u+x " <> show buildScript empty + tryNixFlake = do+ guard =<< doesFileExist flakeNix+ canonNixFlake <- canonicalizePath flakeNix+ trace $ "XMonad will use nix flake at " <> show canonNixFlake <> " to recompile"+ pure CompileNixFlake++ tryNixDefault = do+ guard =<< doesFileExist defaultNix+ canonNixDefault <- canonicalizePath defaultNix+ trace $ "XMonad will use nix file at " <> show canonNixDefault <> " to recompile"+ pure CompileNixDefault+ tryStack = do guard =<< doesFileExist stackYaml canonStackYaml <- canonicalizePath stackYaml@@ -619,11 +637,18 @@ pure $ CompileStackGhc canonStackYaml useGhc = do- trace $ "XMonad will use ghc to recompile, because neither "- <> show buildScript <> " nor " <> show stackYaml <> " exists."+ trace $ "XMonad will use ghc to recompile, because none of "+ <> intercalate ", "+ [ show buildScript+ , show stackYaml+ , show flakeNix+ , show defaultNix+ ] <> " exist." pure CompileGhc - isExecutable f = E.catch (executable <$> getPermissions f) (\(SomeException _) -> return False)+-- | Determine whether or not the file found at the provided filepath is executable.+isExecutable :: FilePath -> IO Bool+isExecutable f = E.catch (executable <$> getPermissions f) (\(SomeException _) -> return False) -- | Should we recompile xmonad configuration? Is it newer than the compiled -- binary?@@ -648,6 +673,8 @@ if binT < stackYamlT then True <$ trace "XMonad recompiling because some files have changed." else shouldCompile dirs CompileGhc+shouldCompile _dirs CompileNixFlake{} = True <$ trace "XMonad recompiling because flake recompilation is being used."+shouldCompile _dirs CompileNixDefault{} = True <$ trace "XMonad recompiling because nix recompilation is being used." shouldCompile _dirs CompileScript{} = True <$ trace "XMonad recompiling because a custom build script is being used." @@ -667,6 +694,10 @@ CompileStackGhc stackYaml -> run "stack" ["build", "--silent", "--stack-yaml", stackYaml] .&&. run "stack" ("ghc" : "--stack-yaml" : stackYaml : "--" : ghcArgs)+ CompileNixFlake ->+ run "nix" ["build"] >>= andCopyFromResultDir+ CompileNixDefault ->+ run "nix-build" [] >>= andCopyFromResultDir CompileScript script -> run script [binFileName dirs] where@@ -680,6 +711,18 @@ , "-outputdir", buildDirName dirs , "-o", binFileName dirs ]+ andCopyFromResultDir exitCode = do+ if exitCode == ExitSuccess then copyFromResultDir else return exitCode+ findM p = foldr (\x -> ifM (p x) (pure $ Just x)) (pure Nothing)+ catchAny :: IO a -> (SomeException -> IO a) -> IO a+ catchAny = E.catch+ copyFromResultDir = do+ let binaryDirectory = cfgDir dirs </> "result" </> "bin"+ binFiles <- map (binaryDirectory </>) <$> catchAny (listDirectory binaryDirectory) (\_ -> return [])+ mfilepath <- findM isExecutable binFiles+ case mfilepath of+ Just filepath -> copyFile filepath (binFileName dirs) >> return ExitSuccess+ Nothing -> return $ ExitFailure 1 -- waitForProcess =<< System.Process.runProcess, but without closing the err handle runProc cwd err exe args = do
src/XMonad/Layout.hs view
@@ -62,9 +62,13 @@ -- a nice pure layout, lots of properties for the layout, and its messages, in Properties.hs instance LayoutClass Tall a where- pureLayout (Tall nmaster _ frac) r s = zip ws rs+ pureLayout (Tall nmaster _ frac) r s+ | frac == 0 = drop nmaster layout+ | frac == 1 = take nmaster layout+ | otherwise = layout where ws = W.integrate s rs = tile frac r nmaster (length ws)+ layout = zip ws rs pureMessage (Tall nmaster delta frac) m = msum [fmap resize (fromMessage m)
src/XMonad/Main.hs view
@@ -15,14 +15,13 @@ -- ----------------------------------------------------------------------------- -module XMonad.Main (xmonad, launch) where+module XMonad.Main (xmonad, buildLaunch, launch) where import System.Locale.SetLocale import qualified Control.Exception as E import Data.Bits import Data.List ((\\)) import Data.Foldable (traverse_)-import Data.Function import qualified Data.Map as M import qualified Data.Set as S import Control.Monad.Reader@@ -132,25 +131,6 @@ args <- getArgs executeFile bin False args Nothing -sendRestart :: IO ()-sendRestart = do- dpy <- openDisplay ""- rw <- rootWindow dpy $ defaultScreen dpy- xmonad_restart <- internAtom dpy "XMONAD_RESTART" False- allocaXEvent $ \e -> do- setEventType e clientMessage- setClientMessageEvent' e rw xmonad_restart 32 []- sendEvent dpy rw False structureNotifyMask e- sync dpy False---- | a wrapper for 'replace'-sendReplace :: IO ()-sendReplace = do- dpy <- openDisplay ""- let dflt = defaultScreen dpy- rootw <- rootWindow dpy dflt- replace dpy dflt rootw- -- | Entry point into xmonad for custom builds. -- -- This function isn't meant to be called by the typical xmonad user@@ -485,36 +465,3 @@ ems <- extraModifiers ba <- asks buttonActions mapM_ (\(m,b) -> mapM_ (grab b . (m .|.)) ems) (M.keys ba)---- | @replace@ to signals compliant window managers to exit.-replace :: Display -> ScreenNumber -> Window -> IO ()-replace dpy dflt rootw = do- -- check for other WM- wmSnAtom <- internAtom dpy ("WM_S" ++ show dflt) False- currentWmSnOwner <- xGetSelectionOwner dpy wmSnAtom- when (currentWmSnOwner /= 0) $ do- -- prepare to receive destroyNotify for old WM- selectInput dpy currentWmSnOwner structureNotifyMask-- -- create off-screen window- netWmSnOwner <- allocaSetWindowAttributes $ \attributes -> do- set_override_redirect attributes True- set_event_mask attributes propertyChangeMask- let screen = defaultScreenOfDisplay dpy- visual = defaultVisualOfScreen screen- attrmask = cWOverrideRedirect .|. cWEventMask- createWindow dpy rootw (-100) (-100) 1 1 0 copyFromParent copyFromParent visual attrmask attributes-- -- try to acquire wmSnAtom, this should signal the old WM to terminate- xSetSelectionOwner dpy wmSnAtom netWmSnOwner currentTime-- -- SKIPPED: check if we acquired the selection- -- SKIPPED: send client message indicating that we are now the WM-- -- wait for old WM to go away- fix $ \again -> do- evt <- allocaXEvent $ \event -> do- windowEvent dpy currentWmSnOwner structureNotifyMask event- get_EventType event-- when (evt /= destroyNotify) again
src/XMonad/ManageHook.hs view
@@ -64,10 +64,6 @@ (<||>) :: Monad m => m Bool -> m Bool -> m Bool x <||> y = ifM x (pure True) y --- | If-then-else lifted to a 'Monad'.-ifM :: Monad m => m Bool -> m a -> m a -> m a-ifM mb t f = mb >>= \b -> if b then t else f- -- | Return the window title. title :: Query String title = ask >>= \w -> liftX $ do@@ -77,7 +73,7 @@ (internAtom d "_NET_WM_NAME" False >>= getTextProperty d w) `E.catch` \(SomeException _) -> getTextProperty d w wM_NAME extract prop = do l <- wcTextPropertyToTextList d prop- return $ if null l then "" else head l+ return $ fromMaybe "" $ listToMaybe l io $ bracket getProp (xFree . tp_value) extract `E.catch` \(SomeException _) -> return "" -- | Return the application name; i.e., the /first/ string returned by
src/XMonad/Operations.hs view
@@ -33,10 +33,11 @@ -- * Keyboard and Mouse cleanMask, extraModifiers, mouseDrag, mouseMoveWindow, mouseResizeWindow,- setButtonGrab, setFocusX, cacheNumlockMask, mkGrabs,+ setButtonGrab, setFocusX, cacheNumlockMask, mkGrabs, unGrab, -- * Messages sendMessage, broadcastMessage, sendMessageWithNoRefresh,+ sendRestart, sendReplace, -- * Save and Restore State StateFile (..), writeStateToFile, readStateFile, restart,@@ -196,6 +197,7 @@ let m = W.floating ws flt = [(fw, scaleRationalRect viewrect r) | fw <- filter (`M.member` m) (W.index this)+ , fw `notElem` vis , Just r <- [M.lookup fw m]] vs = flt ++ rs @@ -473,6 +475,28 @@ , extraMod <- extraMods ] +-- | Release XMonad's keyboard grab, so other grabbers can do their thing.+--+-- Start a keyboard action with this if it is going to run something+-- that needs to do a keyboard, pointer, or server grab. For example,+--+-- > , ((modm .|. controlMask, xK_p), unGrab >> spawn "scrot")+--+-- (Other examples are certain screen lockers and "gksu".)+-- This avoids needing to insert a pause/sleep before running the+-- command.+--+-- XMonad retains the keyboard grab during key actions because if they+-- use a submap, they need the keyboard to be grabbed, and if they had+-- to assert their own grab then the asynchronous nature of X11 allows+-- race conditions between XMonad, other clients, and the X server that+-- would cause keys to sometimes be "leaked" to the focused window.+unGrab :: X ()+unGrab = withDisplay $ \d -> io $ do+ ungrabKeyboard d currentTime+ ungrabPointer d currentTime+ sync d False+ ------------------------------------------------------------------------ -- Message handling @@ -519,6 +543,59 @@ handleMessage (W.layout ws) (SomeMessage ReleaseResources) windows $ const $ ss{ W.current = c{ W.workspace = ws{ W.layout = l } } } +-- | Signal xmonad to restart itself.+sendRestart :: IO ()+sendRestart = do+ dpy <- openDisplay ""+ rw <- rootWindow dpy $ defaultScreen dpy+ xmonad_restart <- internAtom dpy "XMONAD_RESTART" False+ allocaXEvent $ \e -> do+ setEventType e clientMessage+ setClientMessageEvent' e rw xmonad_restart 32 []+ sendEvent dpy rw False structureNotifyMask e+ sync dpy False++-- | Signal compliant window managers to exit.+sendReplace :: IO ()+sendReplace = do+ dpy <- openDisplay ""+ let dflt = defaultScreen dpy+ rootw <- rootWindow dpy dflt+ replace dpy dflt rootw++-- | Signal compliant window managers to exit.+replace :: Display -> ScreenNumber -> Window -> IO ()+replace dpy dflt rootw = do+ -- check for other WM+ wmSnAtom <- internAtom dpy ("WM_S" ++ show dflt) False+ currentWmSnOwner <- xGetSelectionOwner dpy wmSnAtom+ when (currentWmSnOwner /= 0) $ do+ -- prepare to receive destroyNotify for old WM+ selectInput dpy currentWmSnOwner structureNotifyMask++ -- create off-screen window+ netWmSnOwner <- allocaSetWindowAttributes $ \attributes -> do+ set_override_redirect attributes True+ set_event_mask attributes propertyChangeMask+ let screen = defaultScreenOfDisplay dpy+ visual = defaultVisualOfScreen screen+ attrmask = cWOverrideRedirect .|. cWEventMask+ createWindow dpy rootw (-100) (-100) 1 1 0 copyFromParent copyFromParent visual attrmask attributes++ -- try to acquire wmSnAtom, this should signal the old WM to terminate+ xSetSelectionOwner dpy wmSnAtom netWmSnOwner currentTime++ -- SKIPPED: check if we acquired the selection+ -- SKIPPED: send client message indicating that we are now the WM++ -- wait for old WM to go away+ fix $ \again -> do+ evt <- allocaXEvent $ \event -> do+ windowEvent dpy currentWmSnOwner structureNotifyMask event+ get_EventType event++ when (evt /= destroyNotify) again+ ------------------------------------------------------------------------ -- Utilities @@ -649,6 +726,7 @@ where go = withDisplay $ \d -> do ws <- gets windowset+ sh <- io $ getWMNormalHints d w wa <- io $ getWindowAttributes d w let bw = (fromIntegral . wa_border_width) wa point_sc <- pointScreen (fi $ wa_x wa) (fi $ wa_y wa)@@ -663,13 +741,14 @@ sr = screenRect . W.screenDetail $ sc x = (fi (wa_x wa) - fi (rect_x sr)) % fi (rect_width sr) y = (fi (wa_y wa) - fi (rect_y sr)) % fi (rect_height sr)- width = fi (wa_width wa + bw*2) % fi (rect_width sr)- height = fi (wa_height wa + bw*2) % fi (rect_height sr)+ (width, height) = applySizeHintsContents sh (wa_width wa, wa_height wa)+ rwidth = fi (width + bw*2) % fi (rect_width sr)+ rheight = fi (height + bw*2) % fi (rect_height sr) -- adjust x/y of unmanaged windows if we ignored or didn't get pointScreen, -- it might be out of bounds otherwise rr = if managed || point_sc `sr_eq` Just sc- then W.RationalRect x y width height- else W.RationalRect (0.5 - width/2) (0.5 - height/2) width height+ then W.RationalRect x y rwidth rheight+ else W.RationalRect (0.5 - rwidth/2) (0.5 - rheight/2) rwidth rheight return (W.screen sc, rr)
src/XMonad/StackSet.hs view
@@ -106,7 +106,10 @@ -- -- <https://dspace.library.uu.nl/handle/1874/2532 R. Hinze and J. Jeuring, Functional Pearl: Weaving a Web> ----- and Conor McBride's zipper differentiation paper.+-- and+--+-- <http://strictlypositive.org/diff.pdf Conor McBride, The Derivative of a Regular Type is its Type of One-Hole Contexts>.+-- -- Another good reference is: <https://wiki.haskell.org/Zipper The Zipper, Haskell wikibook> -- $xinerama
tests/Properties.hs view
@@ -166,11 +166,16 @@ -- tall layout ,("tile 1 window fullsize", property prop_tile_fullscreen)+ ,("tile max ratio", property prop_tile_max_ratio)+ ,("tile min ratio", property prop_tile_min_ratio) ,("tiles never overlap", property prop_tile_non_overlap) ,("split horizontal", property prop_split_horizontal) ,("split vertical", property prop_split_vertical) - ,("pure layout tall", property prop_purelayout_tall)+ ,("pure layout tall", property prop_purelayout_tall)+ {- Following two test cases should be automatically generated by QuickCheck ideally, but it fails. -}+ ,("pure layout tall: ratio = 0", property (\n d rect -> prop_purelayout_tall n d 0 rect))+ ,("pure layout tall: ratio = 1", property (\n d rect -> prop_purelayout_tall n d 1 rect)) ,("send shrink tall", property prop_shrink_tall) ,("send expand tall", property prop_expand_tall) ,("send incmaster tall", property prop_incmaster_tall)
tests/Properties/Layout/Tall.hs view
@@ -11,8 +11,9 @@ import Graphics.X11.Xlib.Types (Rectangle(..)) -import Data.Maybe+import Control.Applicative import Data.List (sort)+import Data.Maybe import Data.Ratio ------------------------------------------------------------------------@@ -27,6 +28,22 @@ where _ = rect :: Rectangle pct = 3 % 100 +-- with a ratio of 1, no stack windows are drawn of there is at least+-- one master window around.+prop_tile_max_ratio = extremeRatio 1 drop++-- with a ratio of 0, no master windows are drawn at all if there are+-- any stack windows around.+prop_tile_min_ratio = extremeRatio 0 take++extremeRatio amount getRects rect = do+ w@(NonNegative windows) <- arbitrary `suchThat` (> NonNegative 0)+ NonNegative nmaster <- arbitrary `suchThat` (< w)+ let tiled = tile amount rect nmaster windows+ pure $ if nmaster == 0+ then prop_tile_non_overlap rect windows nmaster+ else all ((== 0) . rect_width) $ getRects nmaster tiled+ -- splitting horizontally yields sensible results prop_split_horizontal (NonNegative n) x = noOverflows (+) (rect_x x) (rect_width x) ==>@@ -49,13 +66,20 @@ -- pureLayout works.-prop_purelayout_tall n r1 r2 rect = do+prop_purelayout_tall n d r rect = do x <- (arbitrary :: Gen T) `suchThat` (isJust . peek)- let layout = Tall n r1 r2+ let layout = Tall n d r st = fromJust . stack . workspace . current $ x ts = pureLayout layout rect st+ ntotal = length (index x) return $- length ts == length (index x)+ (if r == 0 then+ -- (<=) for Bool is the logical implication+ (0 <= n && n <= ntotal) <= (length ts == ntotal - n)+ else if r == 1 then+ (0 <= n && n <= ntotal) <= (length ts == n)+ else+ length ts == ntotal) && noOverlaps (map snd ts) &&
xmonad.cabal view
@@ -1,5 +1,5 @@ name: xmonad-version: 0.17.2+version: 0.18.0 synopsis: A tiling window manager description: xmonad is a tiling window manager for X. Windows are arranged automatically to tile the screen without gaps or overlap, maximising@@ -27,7 +27,7 @@ Ondřej Súkup, Paul Hebble, Shachaf Ben-Kiki, Siim Põder, Tim McIver, Trevor Elliott, Wouter Swierstra, Conrad Irwin, Tim Thelion, Tony Zorman maintainer: xmonad@haskell.org-tested-with: GHC == 8.6.5 || == 8.8.4 || == 8.10.7 || == 9.0.2 || == 9.2.7 || == 9.4.4 || == 9.6.1+tested-with: GHC == 8.6.5 || == 8.8.4 || == 8.10.7 || == 9.0.2 || == 9.2.8 || == 9.4.8 || == 9.6.4 || == 9.8.1 category: System homepage: http://xmonad.org bug-reports: https://github.com/xmonad/xmonad/issues@@ -128,6 +128,9 @@ , containers , xmonad default-language: Haskell2010++ if impl(ghc > 9.8)+ ghc-options: -Wno-x-partial if flag(pedantic) ghc-options: -Werror