diff options
-rw-r--r-- | XMonad/Layout/Stoppable.hs | 15 | ||||
-rw-r--r-- | XMonad/Util/RemoteWindows.hs | 90 | ||||
-rw-r--r-- | xmonad-contrib.cabal | 1 |
3 files changed, 97 insertions, 9 deletions
diff --git a/XMonad/Layout/Stoppable.hs b/XMonad/Layout/Stoppable.hs index 35c3c01..5c95aa1 100644 --- a/XMonad/Layout/Stoppable.hs +++ b/XMonad/Layout/Stoppable.hs @@ -46,10 +46,10 @@ module XMonad.Layout.Stoppable import XMonad import XMonad.Actions.WithAll import XMonad.Util.WindowProperties +import XMonad.Util.RemoteWindows 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 @@ -67,8 +67,10 @@ import Control.Monad -- 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. +-- Environment variables will work for most cases, but won't work if the +-- hostname changes. To cover dynamic hostnames case, in addition to +-- layoutHook you have to provide manageHook from +-- "XMonad.Util.RemoteWindows" module. -- -- For more detailed instructions on editing the layoutHook see: -- @@ -80,12 +82,7 @@ signalWindow s w = do 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"] +signalLocalWindow s w = isLocalWindow w >>= flip when (signalWindow s w) withAllOn :: (a -> X ()) -> Workspace i l a -> X () withAllOn f wspc = f `mapM_` integrate' (stack wspc) diff --git a/XMonad/Util/RemoteWindows.hs b/XMonad/Util/RemoteWindows.hs new file mode 100644 index 0000000..8eeb025 --- /dev/null +++ b/XMonad/Util/RemoteWindows.hs @@ -0,0 +1,90 @@ +----------------------------------------------------------------------------- +-- | +-- Module : XMonad.Util.RemoteWindows +-- 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 proper way of finding out whether the window +-- is remote or local. +-- +-- Just checking for a hostname and WM_CLIENT_MACHINE being equal is often +-- not enough because the hostname is a changing subject (without any +-- established notification mechanisms), and thus WM_CLIENT_MACHINE and +-- the hostname can diverge even for a local window. +-- +-- This module solves the problem. As soon as there is a new window +-- created, we check the hostname and WM_CLIENT_MACHINE, and then we cache +-- the result into the XMONAD_REMOTE property. +-- +-- Notice that XMonad itself does not know anything about hostnames, nor +-- does it have any dependency on Network.* modules. For this module it is +-- not a problem: you can provide a mean to get the hostname through your +-- config file (see usage). Or, if you don't like the hassle of handling +-- dynamic hostnames (suppose your hostname never changes), it is also +-- fine: this module will fallback to using environment variables. +-- +----------------------------------------------------------------------------- + +module XMonad.Util.RemoteWindows + ( -- $usage + isLocalWindow + , manageRemote + , manageRemoteG + ) where + +import XMonad +import XMonad.Util.WindowProperties +import Data.Monoid +import Data.Maybe +import Control.Monad +import System.Posix.Env + +-- $usage +-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@: +-- +-- > import XMonad +-- > import XMonad.Util.RemoteWindows +-- > import Network.BSD +-- > +-- > main = xmonad def +-- > { manageHook = manageRemote =<< io getHostName } + +guessHostName :: IO String +guessHostName = pickOneMaybe `liftM` (getEnv `mapM` vars) + where + pickOneMaybe = last . (mzero:) . take 1 . catMaybes + vars = ["XAUTHLOCALHOSTNAME","HOST","HOSTNAME"] + +setRemoteProp :: Window -> String -> X () +setRemoteProp w host = do + d <- asks display + p <- getAtom "XMONAD_REMOTE" + t <- getAtom "CARDINAL" + v <- hasProperty (Machine host) w + io $ changeProperty32 d w p t propModeReplace + [fromIntegral . fromEnum $ not v] + +-- | Given a window, tell if it is a local or a remote process. Normally, +-- it checks XMONAD_REMOTE property. If it does not exist (i.e. the +-- manageRemote hook was not deployed in user's config), it falls back to +-- checking environment variables and assuming that hostname never +-- changes. +isLocalWindow :: Window -> X Bool +isLocalWindow w = getProp32s "XMONAD_REMOTE" w >>= \p -> case p of + Just [y] -> return $ y == 0 + _ -> io guessHostName >>= \host -> hasProperty (Machine host) w + +-- | Use this hook to let XMonad properly track remote/local windows. For +-- example, @manageHook = manageRemote =<< io getHostName@. +manageRemote :: String -> ManageHook +manageRemote host = ask >>= \w -> liftX (setRemoteProp w host) >> return mempty + +-- | Use this hook if you want to manage XMONAD_REMOTE properties, but +-- don't want to use an external getHostName in your config. That way you +-- are retreating to environment variables. +manageRemoteG :: ManageHook +manageRemoteG = manageRemote =<< io guessHostName diff --git a/xmonad-contrib.cabal b/xmonad-contrib.cabal index 166d217..cc605b5 100644 --- a/xmonad-contrib.cabal +++ b/xmonad-contrib.cabal @@ -307,6 +307,7 @@ library XMonad.Util.NamedWindows XMonad.Util.Paste XMonad.Util.PositionStore + XMonad.Util.RemoteWindows XMonad.Util.Replace XMonad.Util.Run XMonad.Util.Scratchpad |