aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--XMonad/Layout/Stoppable.hs130
-rw-r--r--xmonad-contrib.cabal1
2 files changed, 131 insertions, 0 deletions
diff --git a/XMonad/Layout/Stoppable.hs b/XMonad/Layout/Stoppable.hs
new file mode 100644
index 0000000..35c3c01
--- /dev/null
+++ b/XMonad/Layout/Stoppable.hs
@@ -0,0 +1,130 @@
+{-# LANGUAGE MultiParamTypeClasses, TypeSynonymInstances #-}
+{-# LANGUAGE PatternGuards #-}
+-----------------------------------------------------------------------------
+-- |
+-- Module : XMonad.Layout.Stoppable
+-- Copyright : (c) Anton Vorontsov <anton@enomsg.org> 2014
+-- License : BSD-style (as xmonad)
+--
+-- Maintainer : Anton Vorontsov <anton@enomsg.org>
+-- Stability : unstable
+-- Portability : unportable
+--
+-- This module implements a special kind of layout modifier, which when
+-- applied to a layout, causes xmonad to stop all non-visible processes.
+-- In a way, this is a sledge-hammer for applications that drain power.
+-- For example, given a web browser on a stoppable workspace, once the
+-- workspace is hidden the web browser will be stopped.
+--
+-- Note that the stopped application won't be able to communicate with X11
+-- clipboard. For this, the module actually stops applications after a
+-- certain delay, giving a chance for a user to complete copy-paste
+-- sequence. By default, the delay equals to 15 seconds, it is
+-- configurable via 'Stoppable' constructor.
+--
+-- The stoppable modifier prepends a mark (by default equals to
+-- \"Stoppable\") to the layout description (alternatively, you can choose
+-- your own mark and use it with 'Stoppable' constructor). The stoppable
+-- layout (identified by a mark) spans to multiple workspaces, letting you
+-- to create groups of stoppable workspaces that only stop processes when
+-- none of the workspaces are visible, and conversely, unfreezing all
+-- processes even if one of the stoppable workspaces are visible.
+--
+-- To stop the process we use signals, which works for most cases. For
+-- processes that tinker with signal handling (debuggers), another
+-- (Linux-centric) approach may be used. See
+-- <https://www.kernel.org/doc/Documentation/cgroups/freezer-subsystem.txt>
+--
+-----------------------------------------------------------------------------
+
+module XMonad.Layout.Stoppable
+ ( -- $usage
+ Stoppable(..)
+ , stoppable
+ ) where
+
+import XMonad
+import XMonad.Actions.WithAll
+import XMonad.Util.WindowProperties
+import XMonad.Util.Timer
+import XMonad.StackSet hiding (filter)
+import XMonad.Layout.LayoutModifier
+import System.Posix.Env
+import System.Posix.Signals
+import Data.Maybe
+import Control.Monad
+
+-- $usage
+-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
+--
+-- > import XMonad
+-- > import XMonad.Layout.Stoppable
+-- >
+-- > main = xmonad def
+-- > { layoutHook = layoutHook def ||| stoppable (layoutHook def) }
+--
+-- Note that the module has to distinguish between local and remote
+-- proccesses, which means that it needs to know the hostname, so it looks
+-- for environment variables (e.g. HOST).
+--
+-- Beware that as of now this module does not support dynamically changing
+-- hostnames.
+--
+-- For more detailed instructions on editing the layoutHook see:
+--
+-- "XMonad.Doc.Extending#Editing_the_layout_hook"
+
+signalWindow :: Signal -> Window -> X ()
+signalWindow s w = do
+ pid <- getProp32s "_NET_WM_PID" w
+ io $ (signalProcess s . fromIntegral) `mapM_` fromMaybe [] pid
+
+signalLocalWindow :: Signal -> Window -> X ()
+signalLocalWindow s w = do
+ host <- io $ pickOneMaybe `liftM` (getEnv `mapM` vars)
+ hasProperty (Machine host) w >>= flip when (signalWindow s w)
+ where
+ pickOneMaybe = last . (mzero:) . take 1 . catMaybes
+ vars = ["XAUTHLOCALHOSTNAME","HOST","HOSTNAME"]
+
+withAllOn :: (a -> X ()) -> Workspace i l a -> X ()
+withAllOn f wspc = f `mapM_` integrate' (stack wspc)
+
+withAllFiltered :: (Workspace i l a -> Bool)
+ -> [Workspace i l a]
+ -> (a -> X ()) -> X ()
+withAllFiltered p wspcs f = withAllOn f `mapM_` filter p wspcs
+
+sigStoppableWorkspacesHook :: String -> X ()
+sigStoppableWorkspacesHook k = do
+ ws <- gets windowset
+ withAllFiltered isStoppable (hidden ws) (signalLocalWindow sigSTOP)
+ where
+ isStoppable ws = k `elem` words (description $ layout ws)
+
+-- | Data type for ModifiedLayout. The constructor lets you to specify a
+-- custom mark/description modifier and a delay. You can also use
+-- 'stoppable' helper function.
+data Stoppable a = Stoppable
+ { mark :: String
+ , delay :: Rational
+ , timer :: Maybe TimerId
+ } deriving (Show,Read)
+
+instance LayoutModifier Stoppable Window where
+ modifierDescription = mark
+
+ hook _ = withAll $ signalLocalWindow sigCONT
+
+ handleMess (Stoppable m _ (Just tid)) msg
+ | Just ev <- fromMessage msg = handleTimer tid ev run
+ where run = sigStoppableWorkspacesHook m >> return Nothing
+ handleMess (Stoppable m d _) msg
+ | Just Hide <- fromMessage msg =
+ (Just . Stoppable m d . Just) `liftM` startTimer d
+ | otherwise = return Nothing
+
+-- | Convert a layout to a stoppable layout using the default mark
+-- (\"Stoppable\") and a delay of 15 seconds.
+stoppable :: l a -> ModifiedLayout Stoppable l a
+stoppable = ModifiedLayout (Stoppable "Stoppable" 15 Nothing)
diff --git a/xmonad-contrib.cabal b/xmonad-contrib.cabal
index 9fdf16f..166d217 100644
--- a/xmonad-contrib.cabal
+++ b/xmonad-contrib.cabal
@@ -261,6 +261,7 @@ library
XMonad.Layout.Spiral
XMonad.Layout.Square
XMonad.Layout.StackTile
+ XMonad.Layout.Stoppable
XMonad.Layout.SubLayouts
XMonad.Layout.TabBarDecoration
XMonad.Layout.Tabbed