module XMonad.Hooks.PerWindowKbdLayout (
perWindowKbdLayout) where
import Foreign
import Foreign.C.Types (CUChar,CUShort,CUInt(..),CInt(..))
import Control.Monad (when)
import Data.List (find)
import qualified Data.Map as M
import Data.Monoid (All(..))
import Data.Traversable (traverse)
import XMonad
import qualified XMonad.StackSet as W
import qualified XMonad.Util.ExtensibleState as XS
data XkbStateRec = XkbStateRec {
group :: CUChar,
locked_group :: CUChar,
base_group :: CUShort,
latched_group :: CUShort,
mods :: CUChar,
base_mods :: CUChar,
latched_mods :: CUChar,
locked_mods :: CUChar,
compat_state :: CUChar,
grab_mods :: CUChar,
compat_grab_mods :: CUChar,
lookup_mods :: CUChar,
compat_lookup_mods :: CUChar,
ptr_buttons :: CUShort
}
instance Storable XkbStateRec where
sizeOf _ = ((18))
alignment _ = alignment (undefined :: CUShort)
peek ptr = do
r_group <- ((\hsc_ptr -> peekByteOff hsc_ptr 0)) ptr
r_locked_group <- ((\hsc_ptr -> peekByteOff hsc_ptr 1)) ptr
r_base_group <- ((\hsc_ptr -> peekByteOff hsc_ptr 2)) ptr
r_latched_group <- ((\hsc_ptr -> peekByteOff hsc_ptr 4)) ptr
r_mods <- ((\hsc_ptr -> peekByteOff hsc_ptr 6)) ptr
r_base_mods <- ((\hsc_ptr -> peekByteOff hsc_ptr 7)) ptr
r_latched_mods <- ((\hsc_ptr -> peekByteOff hsc_ptr 8)) ptr
r_locked_mods <- ((\hsc_ptr -> peekByteOff hsc_ptr 9)) ptr
r_compat_state <- ((\hsc_ptr -> peekByteOff hsc_ptr 10)) ptr
r_grab_mods <- ((\hsc_ptr -> peekByteOff hsc_ptr 11)) ptr
r_compat_grab_mods <- ((\hsc_ptr -> peekByteOff hsc_ptr 12)) ptr
r_lookup_mods <- ((\hsc_ptr -> peekByteOff hsc_ptr 13)) ptr
r_compat_lookup_mods <- ((\hsc_ptr -> peekByteOff hsc_ptr 14)) ptr
r_ptr_buttons <- ((\hsc_ptr -> peekByteOff hsc_ptr 16)) ptr
return XkbStateRec {
group = r_group,
locked_group = r_locked_group,
base_group = r_base_group,
latched_group = r_latched_group,
mods = r_mods,
base_mods = r_base_mods,
latched_mods = r_latched_mods,
locked_mods = r_locked_mods,
compat_state = r_compat_state,
grab_mods = r_grab_mods,
compat_grab_mods = r_compat_grab_mods,
lookup_mods = r_lookup_mods,
compat_lookup_mods = r_compat_lookup_mods,
ptr_buttons = r_ptr_buttons
}
foreign import ccall unsafe "X11/XKBlib.h XkbGetState"
xkbGetState :: Display -> CUInt -> Ptr XkbStateRec -> IO CInt
foreign import ccall unsafe "XkbLockGroup" xkbLockGroup :: Display -> CUInt -> CUInt -> IO ()
type KbdLayout = Int
getKbdLayout :: Display -> IO KbdLayout
getKbdLayout d = alloca $ \stRecPtr -> do
xkbGetState d (256) stRecPtr
st <- peek stRecPtr
return $ fromIntegral (group st)
setKbdLayout :: Display -> KbdLayout -> IO ()
setKbdLayout d l = xkbLockGroup d (256) $ fromIntegral l
data LayoutStorage = LayoutStorage (Maybe Window) (M.Map Window KbdLayout) deriving (Typeable,Read,Show)
instance ExtensionClass LayoutStorage where initialValue = LayoutStorage Nothing M.empty
perWindowKbdLayout :: Event -> X All
perWindowKbdLayout (DestroyWindowEvent {ev_window = w, ev_event_type = et}) = do
when (et == destroyNotify) $
XS.modify $ \(LayoutStorage mpf wtl) -> (LayoutStorage mpf (M.delete w wtl))
return (All True)
perWindowKbdLayout _ = do
mst <- gets (W.stack . W.workspace . W.current . windowset)
traverse update $ W.focus `fmap` mst
return (All True)
update :: Window -> X()
update foc = withDisplay $ \dpy -> do
(LayoutStorage mpf wtl) <- XS.get
curLayout <- io $ getKbdLayout dpy
case mpf of
Nothing ->
XS.put (LayoutStorage (Just foc) (M.insert foc curLayout wtl))
Just pf -> when (pf /= foc) $ do
XS.put (LayoutStorage (Just foc) (M.insert pf curLayout wtl))
io $ whenJust (M.lookup foc wtl) (setKbdLayout dpy)