aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnton Vorontsov <anton@enomsg.org>2014-09-01 09:21:41 +0200
committerAnton Vorontsov <anton@enomsg.org>2014-09-01 09:21:41 +0200
commit925a7ecf632eddb21ff92e6b6f73850d6d2d1ee0 (patch)
treee95a75e3f0e8449f99ce61cb58c19edb7a73b5a1
parent1c870f828837d3558420bbb84c9e0f085f0ec352 (diff)
downloadXMonadContrib-925a7ecf632eddb21ff92e6b6f73850d6d2d1ee0.tar.gz
XMonadContrib-925a7ecf632eddb21ff92e6b6f73850d6d2d1ee0.tar.xz
XMonadContrib-925a7ecf632eddb21ff92e6b6f73850d6d2d1ee0.zip
Add Stoppable layout for power saving
Ignore-this: a52805d9f3095cd7af48507847ed2fe3 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 darcs-hash:20140901072141-1836e-f65c7bdad9b87883e41a526f496d93f0171ed149.gz
-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