    {-# LANGUAGE DeriveDataTypeable #-}
    -----------------------------------------------------------------------------
    -- |
    -- Module      :  XMonad.Util.Replace
    -- Copyright   :  (c) Jan Vornberger 2009
    -- License     :  BSD3-style (see LICENSE)
    --
    -- Maintainer  :  Adam Vogt <vogt.adam@gmail.com>
    -- Stability   :  unstable
    -- Portability :  unportable
    --
    -- Implements a @--replace@ behavior outside of core.
    --
    -----------------------------------------------------------------------------

    -- refer to core patches:
    -- http://article.gmane.org/gmane.comp.lang.haskell.xmonad/8358
    module XMonad.Util.Replace
        ( -- * Usage
          -- $usage
          replace

          -- * Notes
          -- $shortcomings

          -- ** Implementing a @--replace@ flag
          -- $getArgs
        ) where

    import XMonad
    import Data.Function
    import Control.Monad

    -- $usage
    -- You must run the 'replace' action before starting xmonad proper, this
    -- results in xmonad replacing the currently running WM regardless of the
    -- arguments it is run with:
    --
    -- > import XMonad
    -- > import XMonad.Util.Replace
    -- > main = do
    -- >    replace
    -- >    xmonad $ defaultConfig { .... }
    --

    -- $shortcomings
    -- This doesn't seem to work for replacing WMs that have been started
    -- from within xmonad, such as with @'restart' "openbox" False@, but no other
    -- WMs that implements --replace manage this either. 'replace' works for
    -- replacing metacity when the full gnome-session is started at least.

    -- $getArgs
    -- You can use 'System.Environment.getArgs' to watch for an explicit
    -- @--replace@ flag:
    --
    -- > import XMonad
    -- > import XMonad.Util.Replace (replace)
    -- > import Control.Monad (when)
    -- > import System.Environment (getArgs)
    -- >
    -- > main = do
    -- >    args <- getArgs
    -- >    when ("--replace" `elem` args) replace
    -- >    xmonad $ defaultConfig { .... }
    --
    --
    -- Note that your @~\/.xmonad/xmonad-$arch-$os@ binary is not run with the same
    -- flags as the @xmonad@ binary that calls it. You may be able to work around
    -- this by running your @~\/.xmonad/xmonad-$arch-$os@ binary directly, which is
    -- otherwise not recommended.

    -- | @replace@ must be run before xmonad starts to signals to compliant window
    -- managers that they must exit and let xmonad take over.
    replace :: IO ()
    replace = do
        dpy   <- openDisplay ""
        let dflt = defaultScreen dpy

        rootw  <- rootWindow dpy dflt

        -- check for other WM
        wmSnAtom <- internAtom dpy ("WM_S" ++ (show dflt)) False
        currentWmSnOwner <- xGetSelectionOwner dpy wmSnAtom
        when (currentWmSnOwner /= 0) $ do
            putStrLn $ "Screen " ++ (show dflt) ++ " on display \""
                        ++ (displayString dpy) ++ "\" already has a window manager."

            -- prepare to receive destroyNotify for old WM
            selectInput dpy currentWmSnOwner structureNotifyMask

            -- create off-screen window
            netWmSnOwner <- allocaSetWindowAttributes $ \attributes -> do
                set_override_redirect attributes True
                set_event_mask attributes propertyChangeMask
                let screen = defaultScreenOfDisplay dpy
                let visual = defaultVisualOfScreen screen
                let attrmask = cWOverrideRedirect .|. cWEventMask
                createWindow dpy rootw (-100) (-100) 1 1 0 copyFromParent copyFromParent visual attrmask attributes

            -- try to acquire wmSnAtom, this should signal the old WM to terminate
            putStrLn $ "Replacing existing window manager..."
            xSetSelectionOwner dpy wmSnAtom netWmSnOwner currentTime

            -- SKIPPED: check if we acquired the selection
            -- SKIPPED: send client message indicating that we are now the WM

            -- wait for old WM to go away
            putStr $ "Waiting for other window manager to terminate... "
            fix $ \again -> do
                evt <- allocaXEvent $ \event -> do
                    windowEvent dpy currentWmSnOwner structureNotifyMask event
                    get_EventType event

                when (evt /= destroyNotify) again
            putStrLn $ "done"
        closeDisplay dpy