content: -----------------------------------------------------------------------------
  -- |
  -- 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
  