1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
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
|