----------------------------------------------------------------------------- -- | -- Module : XMonad.Util.RemoteWindows -- Copyright : (c) Anton Vorontsov 2014 -- License : BSD-style (as xmonad) -- -- Maintainer : Anton Vorontsov -- 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