aboutsummaryrefslogtreecommitdiffstats
path: root/XMonad/Util/RemoteWindows.hs
diff options
context:
space:
mode:
Diffstat (limited to 'XMonad/Util/RemoteWindows.hs')
-rw-r--r--XMonad/Util/RemoteWindows.hs90
1 files changed, 90 insertions, 0 deletions
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