packages feed

xmonad 0.17.2 → 0.18.0

raw patch · 14 files changed

+344/−145 lines, 14 files

Files

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