-------------------------------------------------------------------------------
-- |
-- Module      :  XMonad.Util.GrabModifiers
-- Copyright   :  (c) Jochen Keil <jochen.keil@gmail.com>
-- License     :  BSD3-style (see LICENSE)
--
-- Maintainer  :  jochen.keil@gmail.com
-- Stability   :  unstable
-- Portability :  unportable
--
-- Put a passive grab on one (or a list of) modifier keys.
-- Useful if one wants to receive e.g. keyPress/keyRelease Events from withing
-- XMonad.
--
-------------------------------------------------------------------------------

module XMonad.Util.GrabModifiers (
    -- * Usage
    -- $usage
    grabModifier,
    extractModifiers,
    extractKeyCode
    ) where

import qualified Data.Map as M

import XMonad
import Data.Bits((.&.))
import Control.Monad(forM)

-- $usage
--
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@ file:
--
-- > import XMonad.Hooks.GrabModifiers
--
-- and
--
-- > startupHook = ... >> grabModifier mod1Mask
--
-- or
--
-- > startupHook = ... >> grabModifier (mod1Mask .|. shiftMask)
--
-- For detailed instructions on editing your hooks, see
-- "XMonad.Doc.Extending#g:8".
--
-- For detailed instructions on editing your key bindings, see
-- "XMonad.Doc.Extending#Editing_key_bindings".
--
-- A word of warning: a grabbed key will be invisible to other applications
-- So for example after grabbing shift it won't be possible to type capital
-- letters.

-- | Grab one Modifier
grabModifier :: KeyMask -- ^ A KeyMask for which the corresponding keys should be grabbed
             -> X ()
grabModifier mask = do
    XConf { display = dpy, theRoot = root } <- ask

    io (extractKeyCode dpy mask >>=
        mapM_ (\kc -> grabKey dpy kc 0 root False grabModeAsync grabModeAsync))


-- | Extract single KeyMasks from OR'ed KeyMask Values
-- E.g. (mod1Mask .|. shiftMask) => [mod1MapIndex, shiftMapIndex]
extractModifiers :: KeyMask -> [Modifier]
extractModifiers mask = M.elems $ M.filterWithKey (\k -> \_ -> k .&. mask /= 0) modMap


-- | Do the same as extractModifiers but resolve to KeyCodes immediately
extractKeyCode :: Display -> KeyMask -> IO [KeyCode]
extractKeyCode dpy mask = (concat . map snd . filter (flip elem (extractModifiers mask) . fst))
    `fmap` getModifierMapping dpy -- one ugly beast


modMap :: M.Map KeyMask Modifier
modMap = M.fromList
    [ (shiftMask, shiftMapIndex)
    , (lockMask, lockMapIndex)
    , (controlMask, controlMapIndex)
    , (mod1Mask, mod1MapIndex)
    , (mod2Mask, mod2MapIndex)
    , (mod3Mask, mod3MapIndex)
    , (mod4Mask, mod4MapIndex)
    , (mod5Mask, mod5MapIndex)
    ]
