    {-# OPTIONS_GHC -fno-warn-missing-signatures #-}
    -- TODO: Remove when we depend on a version of xmonad that has unGrab.
    {-# OPTIONS_GHC -Wno-deprecations  #-}
    {-# OPTIONS_GHC -Wno-dodgy-imports #-}
    -----------------------------------------------------------------------------
    -- |
    -- Module       : XMonad.Config.Mate
    -- Description  : Config for integrating xmonad with MATE.
    -- Copyright    : (c) Brandon S Allbery KF8NH, 2014
    -- License      : BSD
    --
    -- Maintainer   : allbery.b@gmail.com
    -- Stability    :  unstable
    -- Portability  :  unportable
    --
    -- This module provides a config suitable for use with the MATE desktop
    -- environment.
    --
    -----------------------------------------------------------------------------

    module XMonad.Config.Mate (
        -- * Usage
        -- $usage
        mateConfig,
        mateRun,
        matePanel,
        mateRegister,
        mateLogout,
        mateShutdown,
        desktopLayoutModifiers
        ) where

    import System.Environment (getEnvironment)
    import qualified Data.Map as M

    import XMonad hiding (unGrab)
    import XMonad.Config.Desktop
    import XMonad.Prelude (toUpper)
    import XMonad.Util.Run (safeSpawn)
    import XMonad.Util.Ungrab (unGrab)

    -- $usage
    -- To use this module, start with the following @xmonad.hs@:
    --
    -- > import XMonad
    -- > import XMonad.Config.Mate
    -- >
    -- > main = xmonad mateConfig
    --
    -- For examples of how to further customize @mateConfig@ see "XMonad.Config.Desktop".

    mateConfig :: XConfig
      (ModifiedLayout
         AvoidStruts (Choose Tall (Choose (Mirror Tall) Full)))
    mateConfig = XConfig
      (ModifiedLayout
         AvoidStruts (Choose Tall (Choose (Mirror Tall) Full)))
    desktopConfig
        { terminal = "mate-terminal"
        , keys     = mateKeys <> keys desktopConfig
        , startupHook = mateRegister >> startupHook desktopConfig }

    mateKeys :: XConfig l -> Map (ButtonMask, Time) (X ())
    mateKeys XConfig{modMask :: forall (l :: * -> *). XConfig l -> ButtonMask
    modMask = ButtonMask
    modm} = [((ButtonMask, Time), X ())] -> Map (ButtonMask, Time) (X ())
    forall k a. Ord k => [(k, a)] -> Map k a
    M.fromList
        [ ((ButtonMask
    modm, Time
    xK_p), X ()
    mateRun)
        , ((ButtonMask
    modm, Time
    xK_d), X ()
    unGrab X () -> X () -> X ()
    forall a b. X a -> X b -> X b
    forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
    >> String -> X ()
    matePanel String
    "MAIN_MENU")
        , ((ButtonMask
    modm ButtonMask -> ButtonMask -> ButtonMask
    forall a. Bits a => a -> a -> a
    .|. ButtonMask
    shiftMask, Time
    xK_q), X ()
    forall (m :: * -> *). MonadIO m => m ()
    mateLogout) ]

    -- | Launch the "Run Application" dialog.  mate-panel must be running for this
    -- to work.  partial application for existing keybinding compatibility.
    mateRun :: X ()
    mateRun :: X ()
    mateRun = String -> X ()
    matePanel String
    "RUN_DIALOG"

    -- | Launch a panel action. Either the "Run Application" dialog ("run_dialog" parameter,
    -- see above) or the main menu ("main_menu" parameter).  mate-panel must be running
    -- for this to work.
    matePanel :: String -> X ()
    matePanel :: String -> X ()
    matePanel String
    action = (Display -> X ()) -> X ()
    forall a. (Display -> X a) -> X a
    withDisplay ((Display -> X ()) -> X ()) -> (Display -> X ()) -> X ()
    forall a b. (a -> b) -> a -> b
    $ \Display
    dpy -> do
        let panel :: String
    panel = String
    "_MATE_PANEL_ACTION"
        Time
    rw <- (XConf -> Time) -> X Time
    forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
    asks XConf -> Time
    theRoot
        Time
    mate_panel <- String -> X Time
    getAtom String
    panel
        Time
    panel_action <- String -> X Time
    getAtom (String
    panel String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    "_" String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ (Char -> Char) -> String -> String
    forall a b. (a -> b) -> [a] -> [b]
    map Char -> Char
    toUpper String
    action)

        IO () -> X ()
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    io (IO () -> X ()) -> IO () -> X ()
    forall a b. (a -> b) -> a -> b
    $ (XEventPtr -> IO ()) -> IO ()
    forall a. (XEventPtr -> IO a) -> IO a
    allocaXEvent ((XEventPtr -> IO ()) -> IO ()) -> (XEventPtr -> IO ()) -> IO ()
    forall a b. (a -> b) -> a -> b
    $ \XEventPtr
    e -> do
            XEventPtr -> EventType -> IO ()
    setEventType XEventPtr
    e EventType
    clientMessage
            XEventPtr -> Time -> Time -> CInt -> Time -> Time -> IO ()
    setClientMessageEvent XEventPtr
    e Time
    rw Time
    mate_panel CInt
    32 Time
    panel_action Time
    0
            Display -> Time -> Bool -> Time -> XEventPtr -> IO ()
    sendEvent Display
    dpy Time
    rw Bool
    False Time
    structureNotifyMask XEventPtr
    e
            Display -> Bool -> IO ()
    sync Display
    dpy Bool
    False

    -- | Register xmonad with mate. 'dbus-send' must be in the $PATH with which
    -- xmonad is started.
    --
    -- This action reduces a delay on startup only if you have configured
    -- mate-session to start xmonad with a command such as (check local
    -- documentation):
    --
    -- > dconf write /org/mate/desktop/session/required_components/windowmanager "'xmonad'"
    --
    -- (the extra quotes are required by dconf)
    mateRegister :: MonadIO m => m ()
    mateRegister :: forall (m :: * -> *). MonadIO m => m ()
    mateRegister = IO () -> m ()
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    io (IO () -> m ()) -> IO () -> m ()
    forall a b. (a -> b) -> a -> b
    $ do
        Maybe String
    x <- String -> [(String, String)] -> Maybe String
    forall a b. Eq a => a -> [(a, b)] -> Maybe b
    lookup String
    "DESKTOP_AUTOSTART_ID" ([(String, String)] -> Maybe String)
    -> IO [(String, String)] -> IO (Maybe String)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> IO [(String, String)]
    getEnvironment
        Maybe String -> (String -> IO ()) -> IO ()
    forall (m :: * -> *) a. Monad m => Maybe a -> (a -> m ()) -> m ()
    whenJust Maybe String
    x ((String -> IO ()) -> IO ()) -> (String -> IO ()) -> IO ()
    forall a b. (a -> b) -> a -> b
    $ \String
    sessionId -> String -> [String] -> IO ()
    forall (m :: * -> *). MonadIO m => String -> [String] -> m ()
    safeSpawn String
    "dbus-send"
                [String
    "--session"
                ,String
    "--print-reply=literal"
                ,String
    "--dest=org.mate.SessionManager"
                ,String
    "/org/mate/SessionManager"
                ,String
    "org.mate.SessionManager.RegisterClient"
                ,String
    "string:xmonad"
                ,String
    "string:"String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++String
    sessionId]

    -- | Display MATE logout dialog. This is the default mod-q action.
    mateLogout :: MonadIO m => m ()
    mateLogout :: forall (m :: * -> *). MonadIO m => m ()
    mateLogout = String -> m ()
    forall (m :: * -> *). MonadIO m => String -> m ()
    spawn String
    "mate-session-save --logout-dialog"

    -- | Display MATE shutdown dialog. You can override mod-q to invoke this, or bind it
    -- to another key if you prefer.
    mateShutdown :: MonadIO m => m ()
    mateShutdown :: forall (m :: * -> *). MonadIO m => m ()
    mateShutdown = String -> m ()
    forall (m :: * -> *). MonadIO m => String -> m ()
    spawn String
    "mate-session-save --shutdown-dialog"