{-# 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"