wild-bind-x11 0.1.0.7 → 0.2.0.0
raw patch · 14 files changed
+1523/−294 lines, 14 filesdep +asyncdep +mtldep +semigroupsdep −QuickCheckdep ~textdep ~transformersPVP ok
version bump matches the API change (PVP)
Dependencies added: async, mtl, semigroups
Dependencies removed: QuickCheck
Dependency ranges changed: text, transformers
API changes (from Hackage documentation)
- WildBind.X11.Internal.Key: class KeySymLike k
- WildBind.X11.Internal.Key: class ModifierLike k
- WildBind.X11.Internal.Key: fromKeySym :: KeySymLike k => KeySym -> Maybe k
- WildBind.X11.Internal.Key: instance GHC.Classes.Eq WildBind.X11.Internal.Key.ModifierKey
- WildBind.X11.Internal.Key: instance GHC.Classes.Ord WildBind.X11.Internal.Key.ModifierKey
- WildBind.X11.Internal.Key: instance GHC.Enum.Bounded WildBind.X11.Internal.Key.ModifierKey
- WildBind.X11.Internal.Key: instance GHC.Enum.Enum WildBind.X11.Internal.Key.ModifierKey
- WildBind.X11.Internal.Key: instance GHC.Show.Show WildBind.X11.Internal.Key.ModifierKey
- WildBind.X11.Internal.Key: instance WildBind.X11.Internal.Key.KeySymLike Graphics.X11.Types.KeySym
- WildBind.X11.Internal.Key: instance WildBind.X11.Internal.Key.KeySymLike WildBind.Input.NumPad.NumPadLocked
- WildBind.X11.Internal.Key: instance WildBind.X11.Internal.Key.KeySymLike WildBind.Input.NumPad.NumPadUnlocked
- WildBind.X11.Internal.Key: instance WildBind.X11.Internal.Key.ModifierLike WildBind.Input.NumPad.NumPadLocked
- WildBind.X11.Internal.Key: instance WildBind.X11.Internal.Key.ModifierLike WildBind.Input.NumPad.NumPadUnlocked
- WildBind.X11.Internal.Key: xEventToKeySymLike :: KeySymLike k => XEventPtr -> MaybeT IO k
+ WildBind.X11: Alt :: XMod
+ WildBind.X11: Ctrl :: XMod
+ WildBind.X11: KeyPress :: KeyEventType
+ WildBind.X11: KeyRelease :: KeyEventType
+ WildBind.X11: Shift :: XMod
+ WildBind.X11: Super :: XMod
+ WildBind.X11: XKeyEvent :: KeyEventType -> Set XMod -> KeySym -> XKeyEvent
+ WildBind.X11: [xKeyEventKeySym] :: XKeyEvent -> KeySym
+ WildBind.X11: [xKeyEventMods] :: XKeyEvent -> Set XMod
+ WildBind.X11: [xKeyEventType] :: XKeyEvent -> KeyEventType
+ WildBind.X11: addXMod :: ToXKeyEvent k => XMod -> k -> XKeyEvent
+ WildBind.X11: alt :: ToXKeyEvent k => k -> XKeyEvent
+ WildBind.X11: class ToXKeyEvent k
+ WildBind.X11: class XKeyInput k
+ WildBind.X11: ctrl :: ToXKeyEvent k => k -> XKeyEvent
+ WildBind.X11: data KeyEventType
+ WildBind.X11: data X11Front k
+ WildBind.X11: data XKeyEvent
+ WildBind.X11: data XMod
+ WildBind.X11: defaultRootWindow :: X11Front k -> Window
+ WildBind.X11: fromKeyEvent :: XKeyInput k => KeyMaskMap -> KeyEventType -> KeySym -> KeyMask -> Maybe k
+ WildBind.X11: makeFrontEnd :: (XKeyInput k, Describable k, Ord k) => X11Front k -> FrontEnd ActiveWindow k
+ WildBind.X11: press :: ToXKeyEvent k => k -> XKeyEvent
+ WildBind.X11: release :: ToXKeyEvent k => k -> XKeyEvent
+ WildBind.X11: shift :: ToXKeyEvent k => k -> XKeyEvent
+ WildBind.X11: super :: ToXKeyEvent k => k -> XKeyEvent
+ WildBind.X11: toKeySym :: XKeyInput k => k -> KeySym
+ WildBind.X11: toModifierMasks :: XKeyInput k => KeyMaskMap -> k -> NonEmpty KeyMask
+ WildBind.X11: toXKeyEvent :: ToXKeyEvent k => k -> XKeyEvent
+ WildBind.X11: withX11Front :: (X11Front k -> IO a) -> IO a
+ WildBind.X11.Emulate: push :: (ToXKeyEvent k, MonadIO m, MonadReader Window m) => X11Front i -> k -> m ()
+ WildBind.X11.Emulate: pushTo :: (ToXKeyEvent k, MonadIO m) => X11Front i -> Window -> k -> m ()
+ WildBind.X11.Emulate: remap :: (ToXKeyEvent from, ToXKeyEvent to) => X11Front i -> from -> to -> Binding' bs ActiveWindow XKeyEvent
+ WildBind.X11.Emulate: remapR :: (ToXKeyEvent from, ToXKeyEvent to) => X11Front i -> from -> to -> Binding' bs ActiveWindow XKeyEvent
+ WildBind.X11.Emulate: sendKey :: (ToXKeyEvent k, MonadIO m, MonadReader Window m) => X11Front i -> k -> m ()
+ WildBind.X11.Emulate: sendKeyTo :: (ToXKeyEvent k, MonadIO m) => X11Front i -> Window -> k -> m ()
+ WildBind.X11.Emulate.Example: main :: IO ()
+ WildBind.X11.Emulate.Example: myBinding :: X11Front XKeyEvent -> Binding ActiveWindow XKeyEvent
+ WildBind.X11.Internal.Key: Alt :: XMod
+ WildBind.X11.Internal.Key: Ctrl :: XMod
+ WildBind.X11.Internal.Key: KeyMaskMap :: KeyMask -> KeyMask -> KeyMask -> KeyMask -> KeyMask -> KeyMask -> KeyMask -> KeyMask -> KeyMaskMap
+ WildBind.X11.Internal.Key: KeyPress :: KeyEventType
+ WildBind.X11.Internal.Key: KeyRelease :: KeyEventType
+ WildBind.X11.Internal.Key: Shift :: XMod
+ WildBind.X11.Internal.Key: Super :: XMod
+ WildBind.X11.Internal.Key: XKeyEvent :: KeyEventType -> Set XMod -> KeySym -> XKeyEvent
+ WildBind.X11.Internal.Key: [maskAlt] :: KeyMaskMap -> KeyMask
+ WildBind.X11.Internal.Key: [maskCapsLock] :: KeyMaskMap -> KeyMask
+ WildBind.X11.Internal.Key: [maskControl] :: KeyMaskMap -> KeyMask
+ WildBind.X11.Internal.Key: [maskNumLock] :: KeyMaskMap -> KeyMask
+ WildBind.X11.Internal.Key: [maskScrollLock] :: KeyMaskMap -> KeyMask
+ WildBind.X11.Internal.Key: [maskShiftLock] :: KeyMaskMap -> KeyMask
+ WildBind.X11.Internal.Key: [maskShift] :: KeyMaskMap -> KeyMask
+ WildBind.X11.Internal.Key: [maskSuper] :: KeyMaskMap -> KeyMask
+ WildBind.X11.Internal.Key: [xKeyEventKeySym] :: XKeyEvent -> KeySym
+ WildBind.X11.Internal.Key: [xKeyEventMods] :: XKeyEvent -> Set XMod
+ WildBind.X11.Internal.Key: [xKeyEventType] :: XKeyEvent -> KeyEventType
+ WildBind.X11.Internal.Key: addXMod :: ToXKeyEvent k => XMod -> k -> XKeyEvent
+ WildBind.X11.Internal.Key: alt :: ToXKeyEvent k => k -> XKeyEvent
+ WildBind.X11.Internal.Key: class ToXKeyEvent k
+ WildBind.X11.Internal.Key: class XKeyInput k
+ WildBind.X11.Internal.Key: ctrl :: ToXKeyEvent k => k -> XKeyEvent
+ WildBind.X11.Internal.Key: data KeyEventType
+ WildBind.X11.Internal.Key: data KeyMaskMap
+ WildBind.X11.Internal.Key: data XKeyEvent
+ WildBind.X11.Internal.Key: data XMod
+ WildBind.X11.Internal.Key: fromKeyEvent :: XKeyInput k => KeyMaskMap -> KeyEventType -> KeySym -> KeyMask -> Maybe k
+ WildBind.X11.Internal.Key: getKeyMaskMap :: Display -> IO KeyMaskMap
+ WildBind.X11.Internal.Key: instance (WildBind.X11.Internal.Key.ToXKeyEvent a, WildBind.X11.Internal.Key.ToXKeyEvent b) => WildBind.X11.Internal.Key.ToXKeyEvent (Data.Either.Either a b)
+ WildBind.X11.Internal.Key: instance (WildBind.X11.Internal.Key.XKeyInput a, WildBind.X11.Internal.Key.XKeyInput b) => WildBind.X11.Internal.Key.XKeyInput (Data.Either.Either a b)
+ WildBind.X11.Internal.Key: instance GHC.Classes.Eq WildBind.X11.Internal.Key.KeyEventType
+ WildBind.X11.Internal.Key: instance GHC.Classes.Eq WildBind.X11.Internal.Key.KeyMaskMap
+ WildBind.X11.Internal.Key: instance GHC.Classes.Eq WildBind.X11.Internal.Key.XKeyEvent
+ WildBind.X11.Internal.Key: instance GHC.Classes.Eq WildBind.X11.Internal.Key.XMod
+ WildBind.X11.Internal.Key: instance GHC.Classes.Ord WildBind.X11.Internal.Key.KeyEventType
+ WildBind.X11.Internal.Key: instance GHC.Classes.Ord WildBind.X11.Internal.Key.KeyMaskMap
+ WildBind.X11.Internal.Key: instance GHC.Classes.Ord WildBind.X11.Internal.Key.XKeyEvent
+ WildBind.X11.Internal.Key: instance GHC.Classes.Ord WildBind.X11.Internal.Key.XMod
+ WildBind.X11.Internal.Key: instance GHC.Enum.Bounded WildBind.X11.Internal.Key.KeyEventType
+ WildBind.X11.Internal.Key: instance GHC.Enum.Bounded WildBind.X11.Internal.Key.XMod
+ WildBind.X11.Internal.Key: instance GHC.Enum.Enum WildBind.X11.Internal.Key.KeyEventType
+ WildBind.X11.Internal.Key: instance GHC.Enum.Enum WildBind.X11.Internal.Key.XMod
+ WildBind.X11.Internal.Key: instance GHC.Show.Show WildBind.X11.Internal.Key.KeyEventType
+ WildBind.X11.Internal.Key: instance GHC.Show.Show WildBind.X11.Internal.Key.KeyMaskMap
+ WildBind.X11.Internal.Key: instance GHC.Show.Show WildBind.X11.Internal.Key.XKeyEvent
+ WildBind.X11.Internal.Key: instance GHC.Show.Show WildBind.X11.Internal.Key.XMod
+ WildBind.X11.Internal.Key: instance WildBind.Description.Describable WildBind.X11.Internal.Key.XKeyEvent
+ WildBind.X11.Internal.Key: instance WildBind.X11.Internal.Key.ToXKeyEvent Graphics.X11.Types.KeySym
+ WildBind.X11.Internal.Key: instance WildBind.X11.Internal.Key.ToXKeyEvent WildBind.X11.Internal.Key.XKeyEvent
+ WildBind.X11.Internal.Key: instance WildBind.X11.Internal.Key.XKeyInput WildBind.Input.NumPad.NumPadLocked
+ WildBind.X11.Internal.Key: instance WildBind.X11.Internal.Key.XKeyInput WildBind.Input.NumPad.NumPadUnlocked
+ WildBind.X11.Internal.Key: instance WildBind.X11.Internal.Key.XKeyInput WildBind.X11.Internal.Key.XKeyEvent
+ WildBind.X11.Internal.Key: press :: ToXKeyEvent k => k -> XKeyEvent
+ WildBind.X11.Internal.Key: release :: ToXKeyEvent k => k -> XKeyEvent
+ WildBind.X11.Internal.Key: shift :: ToXKeyEvent k => k -> XKeyEvent
+ WildBind.X11.Internal.Key: super :: ToXKeyEvent k => k -> XKeyEvent
+ WildBind.X11.Internal.Key: toModifierMasks :: XKeyInput k => KeyMaskMap -> k -> NonEmpty KeyMask
+ WildBind.X11.Internal.Key: toXKeyEvent :: ToXKeyEvent k => k -> XKeyEvent
+ WildBind.X11.Internal.Key: xKeyEventToXKeyInput :: XKeyInput k => KeyMaskMap -> KeyEventType -> XKeyEventPtr -> MaybeT IO k
+ WildBind.X11.Internal.Key: xSendKeyEvent :: KeyMaskMap -> Display -> Window -> XKeyEvent -> IO ()
+ WildBind.X11.Internal.Window: defaultRootWindowForDisplay :: Display -> Window
+ WildBind.X11.Internal.Window: fromWinID :: Window -> Window
+ WildBind.X11.Internal.Window: winID :: Window -> Window
+ WildBind.X11.KeySym: type KeySym = XID
+ WildBind.X11.KeySym: xK_0 :: KeySym
+ WildBind.X11.KeySym: xK_1 :: KeySym
+ WildBind.X11.KeySym: xK_2 :: KeySym
+ WildBind.X11.KeySym: xK_3 :: KeySym
+ WildBind.X11.KeySym: xK_4 :: KeySym
+ WildBind.X11.KeySym: xK_5 :: KeySym
+ WildBind.X11.KeySym: xK_6 :: KeySym
+ WildBind.X11.KeySym: xK_7 :: KeySym
+ WildBind.X11.KeySym: xK_8 :: KeySym
+ WildBind.X11.KeySym: xK_9 :: KeySym
+ WildBind.X11.KeySym: xK_A :: KeySym
+ WildBind.X11.KeySym: xK_AE :: KeySym
+ WildBind.X11.KeySym: xK_Aacute :: KeySym
+ WildBind.X11.KeySym: xK_Acircumflex :: KeySym
+ WildBind.X11.KeySym: xK_Adiaeresis :: KeySym
+ WildBind.X11.KeySym: xK_Agrave :: KeySym
+ WildBind.X11.KeySym: xK_Alt_L :: KeySym
+ WildBind.X11.KeySym: xK_Alt_R :: KeySym
+ WildBind.X11.KeySym: xK_Aring :: KeySym
+ WildBind.X11.KeySym: xK_Atilde :: KeySym
+ WildBind.X11.KeySym: xK_B :: KeySym
+ WildBind.X11.KeySym: xK_BackSpace :: KeySym
+ WildBind.X11.KeySym: xK_Begin :: KeySym
+ WildBind.X11.KeySym: xK_Break :: KeySym
+ WildBind.X11.KeySym: xK_C :: KeySym
+ WildBind.X11.KeySym: xK_Cancel :: KeySym
+ WildBind.X11.KeySym: xK_Caps_Lock :: KeySym
+ WildBind.X11.KeySym: xK_Ccedilla :: KeySym
+ WildBind.X11.KeySym: xK_Clear :: KeySym
+ WildBind.X11.KeySym: xK_Codeinput :: KeySym
+ WildBind.X11.KeySym: xK_Control_L :: KeySym
+ WildBind.X11.KeySym: xK_Control_R :: KeySym
+ WildBind.X11.KeySym: xK_D :: KeySym
+ WildBind.X11.KeySym: xK_Delete :: KeySym
+ WildBind.X11.KeySym: xK_Down :: KeySym
+ WildBind.X11.KeySym: xK_E :: KeySym
+ WildBind.X11.KeySym: xK_ETH :: KeySym
+ WildBind.X11.KeySym: xK_Eacute :: KeySym
+ WildBind.X11.KeySym: xK_Ecircumflex :: KeySym
+ WildBind.X11.KeySym: xK_Ediaeresis :: KeySym
+ WildBind.X11.KeySym: xK_Egrave :: KeySym
+ WildBind.X11.KeySym: xK_End :: KeySym
+ WildBind.X11.KeySym: xK_Escape :: KeySym
+ WildBind.X11.KeySym: xK_Eth :: KeySym
+ WildBind.X11.KeySym: xK_Execute :: KeySym
+ WildBind.X11.KeySym: xK_F :: KeySym
+ WildBind.X11.KeySym: xK_F1 :: KeySym
+ WildBind.X11.KeySym: xK_F10 :: KeySym
+ WildBind.X11.KeySym: xK_F11 :: KeySym
+ WildBind.X11.KeySym: xK_F12 :: KeySym
+ WildBind.X11.KeySym: xK_F13 :: KeySym
+ WildBind.X11.KeySym: xK_F14 :: KeySym
+ WildBind.X11.KeySym: xK_F15 :: KeySym
+ WildBind.X11.KeySym: xK_F16 :: KeySym
+ WildBind.X11.KeySym: xK_F17 :: KeySym
+ WildBind.X11.KeySym: xK_F18 :: KeySym
+ WildBind.X11.KeySym: xK_F19 :: KeySym
+ WildBind.X11.KeySym: xK_F2 :: KeySym
+ WildBind.X11.KeySym: xK_F20 :: KeySym
+ WildBind.X11.KeySym: xK_F21 :: KeySym
+ WildBind.X11.KeySym: xK_F22 :: KeySym
+ WildBind.X11.KeySym: xK_F23 :: KeySym
+ WildBind.X11.KeySym: xK_F24 :: KeySym
+ WildBind.X11.KeySym: xK_F25 :: KeySym
+ WildBind.X11.KeySym: xK_F26 :: KeySym
+ WildBind.X11.KeySym: xK_F27 :: KeySym
+ WildBind.X11.KeySym: xK_F28 :: KeySym
+ WildBind.X11.KeySym: xK_F29 :: KeySym
+ WildBind.X11.KeySym: xK_F3 :: KeySym
+ WildBind.X11.KeySym: xK_F30 :: KeySym
+ WildBind.X11.KeySym: xK_F31 :: KeySym
+ WildBind.X11.KeySym: xK_F32 :: KeySym
+ WildBind.X11.KeySym: xK_F33 :: KeySym
+ WildBind.X11.KeySym: xK_F34 :: KeySym
+ WildBind.X11.KeySym: xK_F35 :: KeySym
+ WildBind.X11.KeySym: xK_F4 :: KeySym
+ WildBind.X11.KeySym: xK_F5 :: KeySym
+ WildBind.X11.KeySym: xK_F6 :: KeySym
+ WildBind.X11.KeySym: xK_F7 :: KeySym
+ WildBind.X11.KeySym: xK_F8 :: KeySym
+ WildBind.X11.KeySym: xK_F9 :: KeySym
+ WildBind.X11.KeySym: xK_Find :: KeySym
+ WildBind.X11.KeySym: xK_G :: KeySym
+ WildBind.X11.KeySym: xK_H :: KeySym
+ WildBind.X11.KeySym: xK_Help :: KeySym
+ WildBind.X11.KeySym: xK_Home :: KeySym
+ WildBind.X11.KeySym: xK_Hyper_L :: KeySym
+ WildBind.X11.KeySym: xK_Hyper_R :: KeySym
+ WildBind.X11.KeySym: xK_I :: KeySym
+ WildBind.X11.KeySym: xK_Iacute :: KeySym
+ WildBind.X11.KeySym: xK_Icircumflex :: KeySym
+ WildBind.X11.KeySym: xK_Idiaeresis :: KeySym
+ WildBind.X11.KeySym: xK_Igrave :: KeySym
+ WildBind.X11.KeySym: xK_Insert :: KeySym
+ WildBind.X11.KeySym: xK_J :: KeySym
+ WildBind.X11.KeySym: xK_K :: KeySym
+ WildBind.X11.KeySym: xK_KP_0 :: KeySym
+ WildBind.X11.KeySym: xK_KP_1 :: KeySym
+ WildBind.X11.KeySym: xK_KP_2 :: KeySym
+ WildBind.X11.KeySym: xK_KP_3 :: KeySym
+ WildBind.X11.KeySym: xK_KP_4 :: KeySym
+ WildBind.X11.KeySym: xK_KP_5 :: KeySym
+ WildBind.X11.KeySym: xK_KP_6 :: KeySym
+ WildBind.X11.KeySym: xK_KP_7 :: KeySym
+ WildBind.X11.KeySym: xK_KP_8 :: KeySym
+ WildBind.X11.KeySym: xK_KP_9 :: KeySym
+ WildBind.X11.KeySym: xK_KP_Add :: KeySym
+ WildBind.X11.KeySym: xK_KP_Begin :: KeySym
+ WildBind.X11.KeySym: xK_KP_Decimal :: KeySym
+ WildBind.X11.KeySym: xK_KP_Delete :: KeySym
+ WildBind.X11.KeySym: xK_KP_Divide :: KeySym
+ WildBind.X11.KeySym: xK_KP_Down :: KeySym
+ WildBind.X11.KeySym: xK_KP_End :: KeySym
+ WildBind.X11.KeySym: xK_KP_Enter :: KeySym
+ WildBind.X11.KeySym: xK_KP_Equal :: KeySym
+ WildBind.X11.KeySym: xK_KP_F1 :: KeySym
+ WildBind.X11.KeySym: xK_KP_F2 :: KeySym
+ WildBind.X11.KeySym: xK_KP_F3 :: KeySym
+ WildBind.X11.KeySym: xK_KP_F4 :: KeySym
+ WildBind.X11.KeySym: xK_KP_Home :: KeySym
+ WildBind.X11.KeySym: xK_KP_Insert :: KeySym
+ WildBind.X11.KeySym: xK_KP_Left :: KeySym
+ WildBind.X11.KeySym: xK_KP_Multiply :: KeySym
+ WildBind.X11.KeySym: xK_KP_Next :: KeySym
+ WildBind.X11.KeySym: xK_KP_Page_Down :: KeySym
+ WildBind.X11.KeySym: xK_KP_Page_Up :: KeySym
+ WildBind.X11.KeySym: xK_KP_Prior :: KeySym
+ WildBind.X11.KeySym: xK_KP_Right :: KeySym
+ WildBind.X11.KeySym: xK_KP_Separator :: KeySym
+ WildBind.X11.KeySym: xK_KP_Space :: KeySym
+ WildBind.X11.KeySym: xK_KP_Subtract :: KeySym
+ WildBind.X11.KeySym: xK_KP_Tab :: KeySym
+ WildBind.X11.KeySym: xK_KP_Up :: KeySym
+ WildBind.X11.KeySym: xK_L :: KeySym
+ WildBind.X11.KeySym: xK_L1 :: KeySym
+ WildBind.X11.KeySym: xK_L10 :: KeySym
+ WildBind.X11.KeySym: xK_L2 :: KeySym
+ WildBind.X11.KeySym: xK_L3 :: KeySym
+ WildBind.X11.KeySym: xK_L4 :: KeySym
+ WildBind.X11.KeySym: xK_L5 :: KeySym
+ WildBind.X11.KeySym: xK_L6 :: KeySym
+ WildBind.X11.KeySym: xK_L7 :: KeySym
+ WildBind.X11.KeySym: xK_L8 :: KeySym
+ WildBind.X11.KeySym: xK_L9 :: KeySym
+ WildBind.X11.KeySym: xK_Left :: KeySym
+ WildBind.X11.KeySym: xK_Linefeed :: KeySym
+ WildBind.X11.KeySym: xK_M :: KeySym
+ WildBind.X11.KeySym: xK_Menu :: KeySym
+ WildBind.X11.KeySym: xK_Meta_L :: KeySym
+ WildBind.X11.KeySym: xK_Meta_R :: KeySym
+ WildBind.X11.KeySym: xK_Mode_switch :: KeySym
+ WildBind.X11.KeySym: xK_Multi_key :: KeySym
+ WildBind.X11.KeySym: xK_MultipleCandidate :: KeySym
+ WildBind.X11.KeySym: xK_N :: KeySym
+ WildBind.X11.KeySym: xK_Next :: KeySym
+ WildBind.X11.KeySym: xK_Ntilde :: KeySym
+ WildBind.X11.KeySym: xK_Num_Lock :: KeySym
+ WildBind.X11.KeySym: xK_O :: KeySym
+ WildBind.X11.KeySym: xK_Oacute :: KeySym
+ WildBind.X11.KeySym: xK_Ocircumflex :: KeySym
+ WildBind.X11.KeySym: xK_Odiaeresis :: KeySym
+ WildBind.X11.KeySym: xK_Ograve :: KeySym
+ WildBind.X11.KeySym: xK_Ooblique :: KeySym
+ WildBind.X11.KeySym: xK_Otilde :: KeySym
+ WildBind.X11.KeySym: xK_P :: KeySym
+ WildBind.X11.KeySym: xK_Page_Down :: KeySym
+ WildBind.X11.KeySym: xK_Page_Up :: KeySym
+ WildBind.X11.KeySym: xK_Pause :: KeySym
+ WildBind.X11.KeySym: xK_PreviousCandidate :: KeySym
+ WildBind.X11.KeySym: xK_Print :: KeySym
+ WildBind.X11.KeySym: xK_Prior :: KeySym
+ WildBind.X11.KeySym: xK_Q :: KeySym
+ WildBind.X11.KeySym: xK_R :: KeySym
+ WildBind.X11.KeySym: xK_R1 :: KeySym
+ WildBind.X11.KeySym: xK_R10 :: KeySym
+ WildBind.X11.KeySym: xK_R11 :: KeySym
+ WildBind.X11.KeySym: xK_R12 :: KeySym
+ WildBind.X11.KeySym: xK_R13 :: KeySym
+ WildBind.X11.KeySym: xK_R14 :: KeySym
+ WildBind.X11.KeySym: xK_R15 :: KeySym
+ WildBind.X11.KeySym: xK_R2 :: KeySym
+ WildBind.X11.KeySym: xK_R3 :: KeySym
+ WildBind.X11.KeySym: xK_R4 :: KeySym
+ WildBind.X11.KeySym: xK_R5 :: KeySym
+ WildBind.X11.KeySym: xK_R6 :: KeySym
+ WildBind.X11.KeySym: xK_R7 :: KeySym
+ WildBind.X11.KeySym: xK_R8 :: KeySym
+ WildBind.X11.KeySym: xK_R9 :: KeySym
+ WildBind.X11.KeySym: xK_Redo :: KeySym
+ WildBind.X11.KeySym: xK_Return :: KeySym
+ WildBind.X11.KeySym: xK_Right :: KeySym
+ WildBind.X11.KeySym: xK_S :: KeySym
+ WildBind.X11.KeySym: xK_Scroll_Lock :: KeySym
+ WildBind.X11.KeySym: xK_Select :: KeySym
+ WildBind.X11.KeySym: xK_Shift_L :: KeySym
+ WildBind.X11.KeySym: xK_Shift_Lock :: KeySym
+ WildBind.X11.KeySym: xK_Shift_R :: KeySym
+ WildBind.X11.KeySym: xK_SingleCandidate :: KeySym
+ WildBind.X11.KeySym: xK_Super_L :: KeySym
+ WildBind.X11.KeySym: xK_Super_R :: KeySym
+ WildBind.X11.KeySym: xK_Sys_Req :: KeySym
+ WildBind.X11.KeySym: xK_T :: KeySym
+ WildBind.X11.KeySym: xK_THORN :: KeySym
+ WildBind.X11.KeySym: xK_Tab :: KeySym
+ WildBind.X11.KeySym: xK_Thorn :: KeySym
+ WildBind.X11.KeySym: xK_U :: KeySym
+ WildBind.X11.KeySym: xK_Uacute :: KeySym
+ WildBind.X11.KeySym: xK_Ucircumflex :: KeySym
+ WildBind.X11.KeySym: xK_Udiaeresis :: KeySym
+ WildBind.X11.KeySym: xK_Ugrave :: KeySym
+ WildBind.X11.KeySym: xK_Undo :: KeySym
+ WildBind.X11.KeySym: xK_Up :: KeySym
+ WildBind.X11.KeySym: xK_V :: KeySym
+ WildBind.X11.KeySym: xK_VoidSymbol :: KeySym
+ WildBind.X11.KeySym: xK_W :: KeySym
+ WildBind.X11.KeySym: xK_X :: KeySym
+ WildBind.X11.KeySym: xK_Y :: KeySym
+ WildBind.X11.KeySym: xK_Yacute :: KeySym
+ WildBind.X11.KeySym: xK_Z :: KeySym
+ WildBind.X11.KeySym: xK_a :: KeySym
+ WildBind.X11.KeySym: xK_aacute :: KeySym
+ WildBind.X11.KeySym: xK_acircumflex :: KeySym
+ WildBind.X11.KeySym: xK_acute :: KeySym
+ WildBind.X11.KeySym: xK_adiaeresis :: KeySym
+ WildBind.X11.KeySym: xK_ae :: KeySym
+ WildBind.X11.KeySym: xK_agrave :: KeySym
+ WildBind.X11.KeySym: xK_ampersand :: KeySym
+ WildBind.X11.KeySym: xK_apostrophe :: KeySym
+ WildBind.X11.KeySym: xK_aring :: KeySym
+ WildBind.X11.KeySym: xK_asciicircum :: KeySym
+ WildBind.X11.KeySym: xK_asciitilde :: KeySym
+ WildBind.X11.KeySym: xK_asterisk :: KeySym
+ WildBind.X11.KeySym: xK_at :: KeySym
+ WildBind.X11.KeySym: xK_atilde :: KeySym
+ WildBind.X11.KeySym: xK_b :: KeySym
+ WildBind.X11.KeySym: xK_backslash :: KeySym
+ WildBind.X11.KeySym: xK_bar :: KeySym
+ WildBind.X11.KeySym: xK_braceleft :: KeySym
+ WildBind.X11.KeySym: xK_braceright :: KeySym
+ WildBind.X11.KeySym: xK_bracketleft :: KeySym
+ WildBind.X11.KeySym: xK_bracketright :: KeySym
+ WildBind.X11.KeySym: xK_brokenbar :: KeySym
+ WildBind.X11.KeySym: xK_c :: KeySym
+ WildBind.X11.KeySym: xK_ccedilla :: KeySym
+ WildBind.X11.KeySym: xK_cedilla :: KeySym
+ WildBind.X11.KeySym: xK_cent :: KeySym
+ WildBind.X11.KeySym: xK_colon :: KeySym
+ WildBind.X11.KeySym: xK_comma :: KeySym
+ WildBind.X11.KeySym: xK_copyright :: KeySym
+ WildBind.X11.KeySym: xK_currency :: KeySym
+ WildBind.X11.KeySym: xK_d :: KeySym
+ WildBind.X11.KeySym: xK_degree :: KeySym
+ WildBind.X11.KeySym: xK_diaeresis :: KeySym
+ WildBind.X11.KeySym: xK_division :: KeySym
+ WildBind.X11.KeySym: xK_dollar :: KeySym
+ WildBind.X11.KeySym: xK_e :: KeySym
+ WildBind.X11.KeySym: xK_eacute :: KeySym
+ WildBind.X11.KeySym: xK_ecircumflex :: KeySym
+ WildBind.X11.KeySym: xK_ediaeresis :: KeySym
+ WildBind.X11.KeySym: xK_egrave :: KeySym
+ WildBind.X11.KeySym: xK_equal :: KeySym
+ WildBind.X11.KeySym: xK_eth :: KeySym
+ WildBind.X11.KeySym: xK_exclam :: KeySym
+ WildBind.X11.KeySym: xK_exclamdown :: KeySym
+ WildBind.X11.KeySym: xK_f :: KeySym
+ WildBind.X11.KeySym: xK_g :: KeySym
+ WildBind.X11.KeySym: xK_grave :: KeySym
+ WildBind.X11.KeySym: xK_greater :: KeySym
+ WildBind.X11.KeySym: xK_guillemotleft :: KeySym
+ WildBind.X11.KeySym: xK_guillemotright :: KeySym
+ WildBind.X11.KeySym: xK_h :: KeySym
+ WildBind.X11.KeySym: xK_hyphen :: KeySym
+ WildBind.X11.KeySym: xK_i :: KeySym
+ WildBind.X11.KeySym: xK_iacute :: KeySym
+ WildBind.X11.KeySym: xK_icircumflex :: KeySym
+ WildBind.X11.KeySym: xK_idiaeresis :: KeySym
+ WildBind.X11.KeySym: xK_igrave :: KeySym
+ WildBind.X11.KeySym: xK_j :: KeySym
+ WildBind.X11.KeySym: xK_k :: KeySym
+ WildBind.X11.KeySym: xK_l :: KeySym
+ WildBind.X11.KeySym: xK_less :: KeySym
+ WildBind.X11.KeySym: xK_m :: KeySym
+ WildBind.X11.KeySym: xK_macron :: KeySym
+ WildBind.X11.KeySym: xK_masculine :: KeySym
+ WildBind.X11.KeySym: xK_minus :: KeySym
+ WildBind.X11.KeySym: xK_mu :: KeySym
+ WildBind.X11.KeySym: xK_multiply :: KeySym
+ WildBind.X11.KeySym: xK_n :: KeySym
+ WildBind.X11.KeySym: xK_nobreakspace :: KeySym
+ WildBind.X11.KeySym: xK_notsign :: KeySym
+ WildBind.X11.KeySym: xK_ntilde :: KeySym
+ WildBind.X11.KeySym: xK_numbersign :: KeySym
+ WildBind.X11.KeySym: xK_o :: KeySym
+ WildBind.X11.KeySym: xK_oacute :: KeySym
+ WildBind.X11.KeySym: xK_ocircumflex :: KeySym
+ WildBind.X11.KeySym: xK_odiaeresis :: KeySym
+ WildBind.X11.KeySym: xK_ograve :: KeySym
+ WildBind.X11.KeySym: xK_onehalf :: KeySym
+ WildBind.X11.KeySym: xK_onequarter :: KeySym
+ WildBind.X11.KeySym: xK_onesuperior :: KeySym
+ WildBind.X11.KeySym: xK_ordfeminine :: KeySym
+ WildBind.X11.KeySym: xK_oslash :: KeySym
+ WildBind.X11.KeySym: xK_otilde :: KeySym
+ WildBind.X11.KeySym: xK_p :: KeySym
+ WildBind.X11.KeySym: xK_paragraph :: KeySym
+ WildBind.X11.KeySym: xK_parenleft :: KeySym
+ WildBind.X11.KeySym: xK_parenright :: KeySym
+ WildBind.X11.KeySym: xK_percent :: KeySym
+ WildBind.X11.KeySym: xK_period :: KeySym
+ WildBind.X11.KeySym: xK_periodcentered :: KeySym
+ WildBind.X11.KeySym: xK_plus :: KeySym
+ WildBind.X11.KeySym: xK_plusminus :: KeySym
+ WildBind.X11.KeySym: xK_q :: KeySym
+ WildBind.X11.KeySym: xK_question :: KeySym
+ WildBind.X11.KeySym: xK_questiondown :: KeySym
+ WildBind.X11.KeySym: xK_quotedbl :: KeySym
+ WildBind.X11.KeySym: xK_quoteleft :: KeySym
+ WildBind.X11.KeySym: xK_quoteright :: KeySym
+ WildBind.X11.KeySym: xK_r :: KeySym
+ WildBind.X11.KeySym: xK_registered :: KeySym
+ WildBind.X11.KeySym: xK_s :: KeySym
+ WildBind.X11.KeySym: xK_script_switch :: KeySym
+ WildBind.X11.KeySym: xK_section :: KeySym
+ WildBind.X11.KeySym: xK_semicolon :: KeySym
+ WildBind.X11.KeySym: xK_slash :: KeySym
+ WildBind.X11.KeySym: xK_space :: KeySym
+ WildBind.X11.KeySym: xK_ssharp :: KeySym
+ WildBind.X11.KeySym: xK_sterling :: KeySym
+ WildBind.X11.KeySym: xK_t :: KeySym
+ WildBind.X11.KeySym: xK_thorn :: KeySym
+ WildBind.X11.KeySym: xK_threequarters :: KeySym
+ WildBind.X11.KeySym: xK_threesuperior :: KeySym
+ WildBind.X11.KeySym: xK_twosuperior :: KeySym
+ WildBind.X11.KeySym: xK_u :: KeySym
+ WildBind.X11.KeySym: xK_uacute :: KeySym
+ WildBind.X11.KeySym: xK_ucircumflex :: KeySym
+ WildBind.X11.KeySym: xK_udiaeresis :: KeySym
+ WildBind.X11.KeySym: xK_ugrave :: KeySym
+ WildBind.X11.KeySym: xK_underscore :: KeySym
+ WildBind.X11.KeySym: xK_v :: KeySym
+ WildBind.X11.KeySym: xK_w :: KeySym
+ WildBind.X11.KeySym: xK_x :: KeySym
+ WildBind.X11.KeySym: xK_y :: KeySym
+ WildBind.X11.KeySym: xK_yacute :: KeySym
+ WildBind.X11.KeySym: xK_ydiaeresis :: KeySym
+ WildBind.X11.KeySym: xK_yen :: KeySym
+ WildBind.X11.KeySym: xK_z :: KeySym
- WildBind.X11: withFrontEnd :: (KeySymLike i, ModifierLike i, Describable i) => (FrontEnd ActiveWindow i -> IO a) -> IO a
+ WildBind.X11: withFrontEnd :: (XKeyInput i, Describable i, Ord i) => (FrontEnd ActiveWindow i -> IO a) -> IO a
- WildBind.X11.Internal.Key: toKeySym :: KeySymLike k => k -> KeySym
+ WildBind.X11.Internal.Key: toKeySym :: XKeyInput k => k -> KeySym
- WildBind.X11.Internal.Key: xGrabKey :: (KeySymLike k, ModifierLike k) => Display -> Window -> k -> IO ()
+ WildBind.X11.Internal.Key: xGrabKey :: Display -> Window -> KeySym -> KeyMask -> IO ()
- WildBind.X11.Internal.Key: xUngrabKey :: (KeySymLike k, ModifierLike k) => Display -> Window -> k -> IO ()
+ WildBind.X11.Internal.Key: xUngrabKey :: Display -> Window -> KeySym -> KeyMask -> IO ()
Files
- ChangeLog.md +14/−0
- src/WildBind/X11.hs +40/−137
- src/WildBind/X11/Emulate.hs +100/−0
- src/WildBind/X11/Emulate/Example.hs +35/−0
- src/WildBind/X11/Internal/FrontEnd.hs +220/−0
- src/WildBind/X11/Internal/GrabMan.hs +128/−0
- src/WildBind/X11/Internal/Key.hs +329/−77
- src/WildBind/X11/Internal/Window.hs +32/−6
- src/WildBind/X11/KeySym.hs +371/−0
- test/WildBind/X11/EmulateSpec.hs +106/−0
- test/WildBind/X11/Internal/KeySpec.hs +0/−34
- test/WildBind/X11/TestUtil.hs +12/−1
- test/WildBind/X11Spec.hs +116/−26
- wild-bind-x11.cabal +20/−13
ChangeLog.md view
@@ -1,6 +1,20 @@ # Revision history for wild-bind-x11 +## 0.2.0.0 -- 2018-01-01++* Fix crash when some kind of window (e.g. xdvi) is active. +* Remove `KeySymLike` and `ModifierLike` classes.+* Add `XMod`, `KeyEventType`, `XKeyEvent`, `X11Front` types.+* Add `XKeyInput`, `ToXKeyEvent` classes.+* Add `withX11Front`, `makeFrontEnd`, `press`, `release`,+ `addXMod`, `shift`, `alt`, `ctrl`, `super`, `defaultRootWindow` functions.+* Add `WildBind.X11.Emulate`, `WildBind.X11.Emulate.Example` and `WildBind.X11.KeySym` modules.+* Changed signature of `xGrabKey` and `xUngrabKey` functions.++ ## 0.1.0.7 -- 2017-07-21++* Confirmed build with `time-1.8`. ## 0.1.0.6 -- 2017-02-10
src/WildBind/X11.hs view
@@ -7,150 +7,53 @@ module WildBind.X11 ( -- * X11 front-end withFrontEnd,+ XKeyInput(..), -- * Windows in X11 Window, ActiveWindow,- -- ** Accessor functions for Window+ -- ** Getters winInstance, winClass,- winName+ winName,+ -- * Keys in X11+ XKeyEvent(..),+ XMod(..),+ KeyEventType(..),+ ToXKeyEvent(..),+ -- ** Setters+ press,+ release,+ shift,+ ctrl,+ alt,+ super,+ addXMod,+ -- * X11Front+ X11Front,+ withX11Front,+ makeFrontEnd,+ defaultRootWindow ) where -import Control.Applicative ((<$>), empty)-import Control.Concurrent (rtsSupportsBoundThreads)-import Control.Concurrent.STM (atomically, TChan, newTChanIO, tryReadTChan, writeTChan)-import Control.Exception (bracket, throwIO)-import Control.Monad.IO.Class (liftIO)-import Control.Monad.Trans.Cont (ContT(ContT), runContT)-import Control.Monad.Trans.Maybe (MaybeT, runMaybeT)-import Control.Monad.Trans.List (ListT(ListT), runListT)-import Control.Monad.Trans.Writer (WriterT, execWriterT, tell)-import Data.Bits ((.|.))-import Data.IORef (IORef, newIORef, readIORef, writeIORef)-import qualified Graphics.X11.Xlib as Xlib--import WildBind- ( FrontEnd(FrontEnd, frontDefaultDescription, frontSetGrab, frontUnsetGrab, frontNextEvent),- FrontEvent(FEInput,FEChange)- )+import WildBind (FrontEnd) import qualified WildBind.Description as WBD -import WildBind.X11.Internal.Key (KeySymLike, ModifierLike, xEventToKeySymLike, xGrabKey, xUngrabKey)-import WildBind.X11.Internal.Window (ActiveWindow,getActiveWindow, Window, winInstance, winClass, winName, emptyWindow)-import qualified WildBind.X11.Internal.NotificationDebouncer as Ndeb---- | The X11 front-end-data X11Front k =- X11Front { x11Display :: Xlib.Display,- x11Debouncer :: Ndeb.Debouncer,- x11PrevActiveWindow :: IORef (Maybe ActiveWindow),- x11PendingEvents :: TChan (FrontEvent ActiveWindow k)- }--x11RootWindow :: X11Front k -> Xlib.Window-x11RootWindow = Xlib.defaultRootWindow . x11Display--x11PopPendingEvent :: X11Front k -> IO (Maybe (FrontEvent ActiveWindow k))-x11PopPendingEvent f = atomically $ tryReadTChan $ x11PendingEvents f--x11UnshiftPendingEvents :: X11Front k -> [FrontEvent ActiveWindow k] -> IO ()-x11UnshiftPendingEvents f = atomically . mapM_ (writeTChan $ x11PendingEvents f)--openMyDisplay :: IO Xlib.Display-openMyDisplay = Xlib.openDisplay ""---- | Initialize and obtain 'FrontEnd' for X11, and run the given--- action.------ The X11 'FrontEnd' watches and provides 'ActiveWindow' as the--- front-end state. 'ActiveWindow' keeps information about the window--- currently active. As for the input type @i@,--- 'WildBind.Input.NumPad.NumPadUnlocked' and--- 'WildBind.Input.NumPad.NumPadLocked' are currently supported.--- --- Code using this function must be compiled __with @-threaded@ option enabled__--- in @ghc@. Otherwise, it aborts.------ Note that bound actions are executed when the key is released. That--- way, you can deliver events to the window that originally has the--- keyboard focus.----withFrontEnd :: (KeySymLike i, ModifierLike i, WBD.Describable i) => (FrontEnd ActiveWindow i -> IO a) -> IO a-withFrontEnd = if rtsSupportsBoundThreads then impl else error_impl where- impl = runContT $ do- disp <- ContT $ bracket openMyDisplay Xlib.closeDisplay- notif_disp <- ContT $ bracket openMyDisplay Xlib.closeDisplay- debouncer <- ContT $ Ndeb.withDebouncer notif_disp- liftIO $ Xlib.selectInput disp (Xlib.defaultRootWindow disp)- (Xlib.substructureNotifyMask .|. Ndeb.xEventMask)- awin_ref <- liftIO $ newIORef Nothing- pending_events <- liftIO $ newTChanIO- liftIO $ Ndeb.notify debouncer- return $ makeFrontEnd $ X11Front disp debouncer awin_ref pending_events- error_impl _ = throwIO $ userError "You need to build with -threaded option when you use WildBind.X11.withFrontEnd function."--tellElem :: Monad m => a -> WriterT [a] m ()-tellElem a = tell [a]--convertEvent :: (KeySymLike k) => Xlib.Display -> Ndeb.Debouncer -> Xlib.XEventPtr -> ListT IO (FrontEvent ActiveWindow k)-convertEvent disp deb xev = ListT $ execWriterT $ convertEventWriter where- convertEventWriter :: KeySymLike k => WriterT [FrontEvent ActiveWindow k] IO ()- convertEventWriter = do- xtype <- liftIO $ Xlib.get_EventType xev- let is_key_event = xtype == Xlib.keyRelease- is_awin_event = xtype == Xlib.configureNotify || xtype == Xlib.destroyNotify- tellChangeEvent = (tellElem . FEChange) =<< (liftIO $ getActiveWindow disp)- is_deb_event <- liftIO $ Ndeb.isDebouncedEvent deb xev- if is_key_event- then do- tellChangeEvent- (maybe (return ()) tellElem) =<< (liftIO $ runMaybeT (FEInput <$> xEventToKeySymLike xev))- else if is_deb_event- then tellChangeEvent- else if is_awin_event- then liftIO (Ndeb.notify deb) >> return ()- else return ()--filterUnchangedEvent :: X11Front k -> FrontEvent ActiveWindow k -> ListT IO ()-filterUnchangedEvent front (FEChange new_state) = do- m_old_state <- liftIO $ readIORef $ x11PrevActiveWindow front- case m_old_state of- Nothing -> return ()- Just old_state -> if new_state == old_state then empty else return ()-filterUnchangedEvent _ _ = return ()--updateState :: X11Front k -> FrontEvent ActiveWindow k -> IO ()-updateState front fev = case fev of- (FEInput _) -> return ()- (FEChange s) -> writeIORef (x11PrevActiveWindow front) (Just s)--grabDef :: (KeySymLike k, ModifierLike k) => (Xlib.Display -> Xlib.Window -> k -> IO ()) -> X11Front k -> k -> IO ()-grabDef func front key = func (x11Display front) (x11RootWindow front) key--nextEvent :: (KeySymLike k) => X11Front k -> IO (FrontEvent ActiveWindow k)-nextEvent handle = loop where- loop = do- mpending <- x11PopPendingEvent handle- case mpending of- Just eve -> return eve- Nothing -> nextEventFromX11- nextEventFromX11 = Xlib.allocaXEvent $ \xev -> do- Xlib.nextEvent (x11Display handle) xev- got_events <- processEvents xev- case got_events of- [] -> loop- (eve : rest) -> do- x11UnshiftPendingEvents handle rest- return eve- processEvents xev = runListT $ do- fevent <- convertEvent (x11Display handle) (x11Debouncer handle) xev- filterUnchangedEvent handle fevent- liftIO $ updateState handle fevent- return fevent+import WildBind.X11.Internal.FrontEnd+ ( X11Front,+ withFrontEnd,+ withX11Front,+ makeFrontEnd,+ defaultRootWindow+ )+import WildBind.X11.Internal.Key+ ( XKeyInput(..),+ XKeyEvent(..),+ XMod(..),+ ToXKeyEvent(..),+ KeyEventType(..),+ press, release,+ addXMod,+ shift, ctrl, alt, super+ )+import WildBind.X11.Internal.Window (ActiveWindow, Window, winInstance, winClass, winName) -makeFrontEnd :: (KeySymLike k, ModifierLike k, WBD.Describable k) => X11Front k -> FrontEnd ActiveWindow k-makeFrontEnd f = FrontEnd { frontDefaultDescription = WBD.describe,- frontSetGrab = grabDef xGrabKey f,- frontUnsetGrab = grabDef xUngrabKey f,- frontNextEvent = nextEvent f- }
+ src/WildBind/X11/Emulate.hs view
@@ -0,0 +1,100 @@+{-# LANGUAGE FlexibleContexts #-}+-- |+-- Module: WildBind.X11.Emulate+-- Description: X11 event emulation functions+-- Maintainer: Toshio Ito <debug.ito@gmail.com>+--+-- This module defines functions to emulate key inputs.+--+-- See "WildBind.X11.Emulate.Example" for an example.+--+-- @since 0.2.0.0+module WildBind.X11.Emulate+ ( -- * Create key inputs+ sendKeyTo,+ sendKey,+ pushTo,+ push,+ -- * Key remap binding+ remap,+ remapR+ ) where++import Control.Monad.IO.Class (MonadIO(liftIO))+import Control.Monad.Reader.Class (MonadReader(ask))+import WildBind.Binding (Binding', bindsF, on, run)++import WildBind.X11.Internal.FrontEnd+ ( X11Front(..)+ )+import WildBind.X11.Internal.Key+ ( ToXKeyEvent(..), KeyEventType(..), XKeyEvent,+ xSendKeyEvent, press, release+ )+import WildBind.X11.Internal.Window+ ( Window, ActiveWindow, winID+ )++-- | Send a X11 key event to a 'Window'.+sendKeyTo :: (ToXKeyEvent k, MonadIO m)+ => X11Front i+ -> Window -- ^ target window+ -> k -- ^ Key event to send+ -> m ()+sendKeyTo front win key = liftIO $ xSendKeyEvent kmmap disp win_id key_event+ where+ kmmap = x11KeyMaskMap front+ disp = x11Display front+ win_id = winID win+ key_event = toXKeyEvent key++-- | Same as 'sendKeyTo', but the target window is obtained from+-- 'MonadReader'.+sendKey :: (ToXKeyEvent k, MonadIO m, MonadReader Window m) => X11Front i -> k -> m ()+sendKey front key = do+ win <- ask+ sendKeyTo front win key++-- | Send a \"key push\" event to a 'Window', that is, send 'KeyPress'+-- and 'KeyRelease' events.+pushTo :: (ToXKeyEvent k, MonadIO m) => X11Front i -> Window -> k -> m ()+pushTo front win key = do+ send $ press key+ send $ release key+ where+ send = sendKeyTo front win++-- | Same as 'pushTo', but the target window is obtained from+-- 'MonadReader'.+push :: (ToXKeyEvent k, MonadIO m, MonadReader Window m) => X11Front i -> k -> m ()+push front key = do+ win <- ask+ pushTo front win key++-- | Create a binding that remaps key event \"@from@\" to+-- \"@to@\".+--+-- This binding captures 'KeyPress' and 'KeyRelease' events of+-- \"@from@\", and sends respective events of \"@to@\" to the active+-- window.+--+-- Sometimes 'remap' doesn't work as you expect, because the+-- 'KeyPress' event is sent to the window while it doesn't have+-- keyboard focus. In that case, try using 'remapR'.+remap :: (ToXKeyEvent from, ToXKeyEvent to) => X11Front i -> from -> to -> Binding' bs ActiveWindow XKeyEvent+remap front from to = bindsF $ do+ on (press from) `run` sendKey' (press to)+ on (release from) `run` sendKey' (release to)+ where+ sendKey' = sendKey front++-- | remap on Release. Like 'remap', but this binding captures only+-- 'KeyRelease' event and sends a pair of 'KeyPress' and 'KeyRelease'+-- events.+--+-- Because the original 'KeyRelease' event occurs after the focus+-- returns to the window, the emulated events are sent to the window+-- with focus.+remapR :: (ToXKeyEvent from, ToXKeyEvent to) => X11Front i -> from -> to -> Binding' bs ActiveWindow XKeyEvent+remapR front from to = bindsF $ do+ on (release from) `run` push front to
+ src/WildBind/X11/Emulate/Example.hs view
@@ -0,0 +1,35 @@+{-# LANGUAGE OverloadedStrings #-}+-- |+-- Module: WildBind.X11.Emulate.Example+-- Description: Example of WildBind.X11.Emulate+-- Maintainer: Toshio Ito <debug.ito@gmail.com>+--+-- This is an example of using "WildBind.X11.Emulate". See the source.+--+-- @since 0.2.0.0+module WildBind.X11.Emulate.Example+ where++import WildBind+ ( Binding,+ wildBind, bindsF, on, as, run+ )+import WildBind.X11+ ( X11Front, XKeyEvent, ActiveWindow,+ withX11Front, makeFrontEnd,+ alt, ctrl, press, release+ )+import WildBind.X11.KeySym (xK_c, xK_w)+import WildBind.X11.Emulate (sendKey)++main :: IO ()+main = withX11Front $ \x11 -> wildBind (myBinding x11) (makeFrontEnd x11)++-- | To use emulation functions, you need to obtain an 'X11Front'+-- object by 'withX11Front', and call emulation functions on it.+--+-- 'bindsF' function is useful to send keyboard events to the current+-- 'ActiveWindow'.+myBinding :: X11Front XKeyEvent -> Binding ActiveWindow XKeyEvent+myBinding x11 = bindsF $ do+ on (alt xK_w) `as` "Copy" `run` sendKey x11 (ctrl xK_c)
+ src/WildBind/X11/Internal/FrontEnd.hs view
@@ -0,0 +1,220 @@+-- |+-- Module: WildBind.X11.Internal.FrontEnd+-- Description: WildBind FrontEnd implementation for X11+-- Maintainer: Toshio Ito <debug.ito@gmail.com>+--+-- __This is an internal module. Package users should not rely on this.__+module WildBind.X11.Internal.FrontEnd+ ( -- * X11Front+ X11Front(..),+ withFrontEnd,+ withX11Front,+ makeFrontEnd,+ defaultRootWindow+ ) where+++import Control.Applicative ((<$>), empty)+import Control.Concurrent (rtsSupportsBoundThreads)+import Control.Concurrent.STM (atomically, TChan, newTChanIO, tryReadTChan, writeTChan)+import Control.Exception (bracket, throwIO)+import Control.Monad (when)+import Control.Monad.IO.Class (liftIO)+import Control.Monad.Trans.Cont (ContT(ContT), runContT)+import Control.Monad.Trans.Maybe (MaybeT, runMaybeT)+import Control.Monad.Trans.List (ListT(ListT), runListT)+import Control.Monad.Trans.Writer (WriterT, execWriterT, tell)+import Data.Bits ((.|.))+import Data.IORef (IORef, newIORef, readIORef, writeIORef)+import qualified Graphics.X11.Xlib as Xlib++import WildBind+ ( FrontEnd(FrontEnd, frontDefaultDescription, frontSetGrab, frontUnsetGrab, frontNextEvent),+ FrontEvent(FEInput,FEChange)+ )+import qualified WildBind.Description as WBD++import WildBind.X11.Internal.Key+ ( xKeyEventToXKeyInput,+ xGrabKey, xUngrabKey,+ XKeyInput(..), KeyMaskMap, getKeyMaskMap,+ KeyEventType(..)+ )+import WildBind.X11.Internal.Window+ ( ActiveWindow,getActiveWindow, Window,+ winInstance, winClass, winName, emptyWindow,+ defaultRootWindowForDisplay+ )+import qualified WildBind.X11.Internal.NotificationDebouncer as Ndeb+import qualified WildBind.X11.Internal.GrabMan as GM++-- | The X11 front-end. @k@ is the input key type.+--+-- This is the implementation of the 'FrontEnd' given by+-- 'withFrontEnd' function. With this object, you can do more advanced+-- actions. See "WildBind.X11.Emulate".+--+-- 'X11Front' is relatively low-level interface, so it's more likely+-- for this API to change in the future than 'FrontEnd'.+--+-- @since 0.2.0.0+data X11Front k =+ X11Front { x11Display :: Xlib.Display,+ x11Debouncer :: Ndeb.Debouncer,+ x11PrevActiveWindow :: IORef (Maybe ActiveWindow),+ x11PendingEvents :: TChan (FrontEvent ActiveWindow k),+ x11KeyMaskMap :: KeyMaskMap,+ x11GrabMan :: IORef (GM.GrabMan k)+ }++x11PopPendingEvent :: X11Front k -> IO (Maybe (FrontEvent ActiveWindow k))+x11PopPendingEvent f = atomically $ tryReadTChan $ x11PendingEvents f++x11UnshiftPendingEvents :: X11Front k -> [FrontEvent ActiveWindow k] -> IO ()+x11UnshiftPendingEvents f = atomically . mapM_ (writeTChan $ x11PendingEvents f)++openMyDisplay :: IO Xlib.Display+openMyDisplay = Xlib.openDisplay ""++-- | Initialize and obtain 'FrontEnd' for X11, and run the given+-- action.+--+-- The X11 'FrontEnd' watches and provides 'ActiveWindow' as the+-- front-end state. 'ActiveWindow' keeps information about the window+-- currently active. As for the input type @i@, this 'FrontEnd' gets+-- keyboard events from the X server.+-- +-- CAVEATS+--+-- Code using this function must be compiled+-- __with @-threaded@ option enabled__ in @ghc@. Otherwise, it aborts.+--+-- Because this 'FrontEnd' currently uses @XGrabKey(3)@ to get the+-- input, it may cause some weird behavior such as:+--+-- - Every input event makes the active window lose focus+-- temporarily. This may result in flickering cursor, for example. See+-- also: https://stackoverflow.com/questions/15270420/+--+-- - Key input is captured only while the first grabbed key is+-- pressed. For example, if @(release xK_a)@ and @(release xK_b)@+-- are bound, and you input @(press xK_a)@, @(press xK_b)@, @(release xK_a)@,+-- @(release xK_b)@, the last @(release xK_b)@ is NOT captured+-- because key grab ends with @(release xK_a)@.+withFrontEnd :: (XKeyInput i, WBD.Describable i, Ord i) => (FrontEnd ActiveWindow i -> IO a) -> IO a+withFrontEnd action = withX11Front' "WildBind.X11.withFrontEnd" $ \x11front -> action (makeFrontEnd x11front)++-- | Same as 'withFrontEnd', but it creates 'X11Front'. To create+-- 'FrontEnd', use 'makeFrontEnd'.+--+-- @since 0.2.0.0+withX11Front :: (X11Front k -> IO a) -> IO a+withX11Front = withX11Front' "WildBind.X11.withX11Front"++withX11Front' :: String -- ^ function name used in the error message.+ -> (X11Front k -> IO a)+ -> IO a+withX11Front' func_name = if rtsSupportsBoundThreads then impl else error_impl where+ impl = runContT $ do+ disp <- ContT $ bracket openMyDisplay Xlib.closeDisplay+ keymask_map <- liftIO $ getKeyMaskMap disp+ notif_disp <- ContT $ bracket openMyDisplay Xlib.closeDisplay+ debouncer <- ContT $ Ndeb.withDebouncer notif_disp+ liftIO $ Xlib.selectInput disp (Xlib.defaultRootWindow disp)+ (Xlib.substructureNotifyMask .|. Ndeb.xEventMask)+ awin_ref <- liftIO $ newIORef Nothing+ pending_events <- liftIO $ newTChanIO+ grab_man <- liftIO $ GM.new keymask_map disp (Xlib.defaultRootWindow disp)+ liftIO $ Ndeb.notify debouncer+ return $ X11Front disp debouncer awin_ref pending_events keymask_map grab_man+ error_impl _ = throwIO $ userError ("You need to build with -threaded option when you use " ++ func_name ++ " function.")+++tellElem :: Monad m => a -> WriterT [a] m ()+tellElem a = tell [a]++data InternalEvent = IEKey KeyEventType+ | IEDebounced+ | IEActiveWindow+ | IEUnknown++identifyEvent :: Ndeb.Debouncer -> Xlib.XEventPtr -> IO InternalEvent+identifyEvent deb xev = do+ xtype <- Xlib.get_EventType xev+ identify xtype+ where+ identify xtype | xtype == Xlib.keyPress = return $ IEKey KeyPress+ | xtype == Xlib.keyRelease = return $ IEKey KeyRelease+ | xtype == Xlib.configureNotify || xtype == Xlib.destroyNotify = return $ IEActiveWindow+ | otherwise = do+ is_deb_event <- Ndeb.isDebouncedEvent deb xev+ if is_deb_event+ then return IEDebounced+ else return IEUnknown++convertEvent :: (XKeyInput k) => KeyMaskMap -> Xlib.Display -> Ndeb.Debouncer -> Xlib.XEventPtr -> ListT IO (FrontEvent ActiveWindow k)+convertEvent kmmap disp deb xev = ListT $ execWriterT $ convertEventWriter where+ tellChangeEvent = (tellElem . FEChange) =<< (liftIO $ getActiveWindow disp)+ convertEventWriter :: XKeyInput k => WriterT [FrontEvent ActiveWindow k] IO ()+ convertEventWriter = do+ in_event <- liftIO $ identifyEvent deb xev+ case in_event of+ IEKey ev_type -> do+ let key_ev = Xlib.asKeyEvent xev+ tellChangeEvent+ (maybe (return ()) tellElem) =<< (liftIO $ runMaybeT (FEInput <$> xKeyEventToXKeyInput kmmap ev_type key_ev))+ IEDebounced -> tellChangeEvent+ IEActiveWindow -> liftIO (Ndeb.notify deb) >> return ()+ IEUnknown -> return ()++filterUnchangedEvent :: X11Front k -> FrontEvent ActiveWindow k -> ListT IO ()+filterUnchangedEvent front (FEChange new_state) = do+ m_old_state <- liftIO $ readIORef $ x11PrevActiveWindow front+ case m_old_state of+ Nothing -> return ()+ Just old_state -> if new_state == old_state then empty else return ()+filterUnchangedEvent _ _ = return ()++updateState :: X11Front k -> FrontEvent ActiveWindow k -> IO ()+updateState front fev = case fev of+ (FEInput _) -> return ()+ (FEChange s) -> writeIORef (x11PrevActiveWindow front) (Just s)++nextEvent :: (XKeyInput k) => X11Front k -> IO (FrontEvent ActiveWindow k)+nextEvent handle = loop where+ loop = do+ mpending <- x11PopPendingEvent handle+ case mpending of+ Just eve -> return eve+ Nothing -> nextEventFromX11+ nextEventFromX11 = Xlib.allocaXEvent $ \xev -> do+ Xlib.nextEvent (x11Display handle) xev+ got_events <- processEvents xev+ case got_events of+ [] -> loop+ (eve : rest) -> do+ x11UnshiftPendingEvents handle rest+ return eve+ processEvents xev = runListT $ do+ fevent <- convertEvent (x11KeyMaskMap handle) (x11Display handle) (x11Debouncer handle) xev+ filterUnchangedEvent handle fevent+ liftIO $ updateState handle fevent+ return fevent++-- | Create 'FrontEnd' from 'X11Front' object.+--+-- @since 0.2.0.0+makeFrontEnd :: (XKeyInput k, WBD.Describable k, Ord k) => X11Front k -> FrontEnd ActiveWindow k+makeFrontEnd f = FrontEnd { frontDefaultDescription = WBD.describe,+ frontSetGrab = runGrab GM.DoSetGrab,+ frontUnsetGrab = runGrab GM.DoUnsetGrab,+ frontNextEvent = nextEvent f+ }+ where+ runGrab = GM.modify (x11GrabMan f)++-- | Get the default root window.+--+-- @since 0.2.0.0+defaultRootWindow :: X11Front k -> Window+defaultRootWindow = defaultRootWindowForDisplay . x11Display
+ src/WildBind/X11/Internal/GrabMan.hs view
@@ -0,0 +1,128 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+-- |+-- Module: WildBind.X11.Internal.GrabMan+-- Description: internal key grab manager+-- Maintainer: Toshio Ito <debug.ito@gmail.com>+--+-- __This is an internal module. Package users should not rely on this.__+--+-- 'GrabMan' is a \"grab manager\". It manages the state of key grabs.+--+-- Of course X11 server itself manages key grabs. The reason why we+-- need 'GrabMan' is that the input symbols for X11 FrontEnd do not+-- map one-to-one to X11 'GrabField's. For example, @(press $ ctrl xK_x)@+-- actually corresponds to multiple 'GrabField's, each with a different+-- 'Xlib.KeyMask'. In addition, @(release $ ctrl xK_x)@ has exactly the+-- same set of 'GrabField's as @(press $ ctrl xK_x)@. For each possible+-- 'GrabField', we need to grab it if and only if there is at least+-- one grabbed input symbol for the 'GrabField'.+module WildBind.X11.Internal.GrabMan+ ( GrabMan,+ GrabOp(..),+ new,+ modify+ ) where++import Control.Monad (forM_)+import Data.IORef (IORef, newIORef, readIORef, writeIORef)+import Data.Foldable (foldr)+import Data.List.NonEmpty (NonEmpty)+import qualified Data.Map.Strict as M+import Data.Monoid (Monoid(..), (<>))+import qualified Data.Set as S+import qualified Graphics.X11.Xlib as Xlib++import WildBind.X11.Internal.Key+ ( XKeyEvent(..), press, KeyEventType(..),+ KeyMaskMap, XKeyInput(..),+ xGrabKey, xUngrabKey+ )++-- | Unit of key grabs in X11. X server manages state of key grabs+-- independently for each 'GrabField'.+type GrabField = (Xlib.KeySym, Xlib.KeyMask)++-- | High-level grab state. For each 'GrabField', the 'M.Map' value is+-- non-empty set of input symbols (type @k@) currently grabbed.+type GrabbedInputs k = M.Map GrabField (S.Set k)++insertG :: Ord k => GrabField -> k -> GrabbedInputs k -> (GrabbedInputs k, Bool)+insertG field key inputs = (new_inputs, is_new_entry)+ where+ is_new_entry = not $ M.member field inputs+ new_inputs = M.insertWith S.union field (S.singleton key) inputs++deleteG :: Ord k => GrabField -> k -> GrabbedInputs k -> (GrabbedInputs k, Bool)+deleteG field key inputs = (new_inputs, is_entry_deleted)+ where+ (new_inputs, is_entry_deleted) = case M.lookup field inputs of+ Nothing -> (inputs, False)+ Just cur_grabbed -> let new_grabbed = S.delete key cur_grabbed+ removed = new_grabbed == mempty+ in ( if removed+ then M.delete field inputs+ else M.insert field new_grabbed inputs,+ + removed+ )++-- | Grab operation. Either \"set grab\" or \"unset grab\".+data GrabOp = DoSetGrab | DoUnsetGrab deriving (Show,Eq,Ord)++modifyG :: Ord k => GrabOp -> GrabField -> k -> GrabbedInputs k -> (GrabbedInputs k, Bool)+modifyG op = case op of+ DoSetGrab -> insertG+ DoUnsetGrab -> deleteG++-- | The key grab manager.+data GrabMan k =+ GrabMan+ { gmKeyMaskMap :: KeyMaskMap,+ gmDisplay :: Xlib.Display,+ gmRootWindow :: Xlib.Window,+ gmGrabbedInputs :: GrabbedInputs k+ }+ deriving (Show,Eq,Ord)++-- | Create a new 'GrabMan'.+new :: KeyMaskMap -> Xlib.Display -> Xlib.Window -> IO (IORef (GrabMan k))+new kmm disp win = newIORef $ GrabMan { gmKeyMaskMap = kmm,+ gmDisplay = disp,+ gmRootWindow = win,+ gmGrabbedInputs = mempty+ }++grabFieldsFor :: XKeyInput k => KeyMaskMap -> k -> NonEmpty GrabField+grabFieldsFor kmmap k = do+ sym <- return $ toKeySym k+ modmask <- toModifierMasks kmmap k+ return (sym, modmask)++-- | Pure version of 'modify'.+modifyGM :: (XKeyInput k, Ord k) => GrabOp -> k -> GrabMan k+ -> (GrabMan k, [GrabField])+ -- ^ the next state of 'GrabMan', and the list of+ -- 'GrabField's which needs modifying with the X server.+modifyGM op input gm = foldr modifySingle (gm, []) fields+ where+ fields = grabFieldsFor (gmKeyMaskMap gm) input+ modifySingle field (cur_gm, cur_changed) = (new_gm, new_changed)+ where+ (new_gi, modified) = modifyG op field input $ gmGrabbedInputs cur_gm+ new_gm = cur_gm { gmGrabbedInputs = new_gi }+ new_changed = if modified then (field : cur_changed) else cur_changed++-- | Modify the grab state. The modification operation is specified by+-- 'GrabOp'. It controls grabs of the X server if necessary.+modify :: (XKeyInput k, Ord k) => IORef (GrabMan k) -> GrabOp -> k -> IO ()+modify gm_ref op input = do+ cur_gm <- readIORef gm_ref+ let (new_gm, changed_fields) = modifyGM op input cur_gm+ disp = gmDisplay cur_gm+ rwin = gmRootWindow cur_gm+ writeIORef gm_ref new_gm+ forM_ changed_fields $ \(keysym, mask) -> do+ case op of+ DoSetGrab -> xGrabKey disp rwin keysym mask+ DoUnsetGrab -> xUngrabKey disp rwin keysym mask+
src/WildBind/X11/Internal/Key.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE GeneralizedNewtypeDeriving, TypeSynonymInstances, OverloadedStrings #-} -- | -- Module: WildBind.X11.Internal.Key -- Description: types and functions related to key symbols and their conversion@@ -5,41 +6,105 @@ -- -- __This is an internal module. Package users should not rely on this.__ module WildBind.X11.Internal.Key- ( -- * Conversion between key types- KeySymLike(..), - xEventToKeySymLike,- -- * Key grabs- ModifierLike,- xGrabKey, xUngrabKey+ ( -- * Key+ XKeyInput(..),+ xKeyEventToXKeyInput,+ KeyEventType(..),+ -- * Modifiers+ KeyMaskMap(..),+ getKeyMaskMap,+ -- * XKeyEvent+ XKeyEvent(..),+ XMod(..),+ ToXKeyEvent(..),+ addXMod,+ press,+ release,+ shift,+ ctrl,+ alt,+ super,+ -- * Grabs+ xGrabKey,+ xUngrabKey,+ -- * Event generation+ xSendKeyEvent ) where -import Control.Applicative ((<$>), (<*>))+import Control.Applicative ((<$>), (<*>), (<|>))+import Control.Monad (forM_)+import Control.Monad.IO.Class (liftIO) import Control.Monad.Trans.Maybe (MaybeT(MaybeT))-import Data.Bits ((.|.))+import Data.Bits ((.|.), (.&.)) import qualified Data.Bits as Bits+import Data.Foldable (foldr, fold)+import Data.List (nub)+import Data.List.NonEmpty (NonEmpty(..)) import qualified Data.Map as M import Data.Maybe (mapMaybe, listToMaybe)+import Data.Monoid ((<>))+import qualified Data.Set as S+import qualified Data.Text as T+import qualified Foreign import qualified Graphics.X11.Xlib as Xlib import qualified Graphics.X11.Xlib.Extras as XlibE +import WildBind.Description (Describable(..)) import qualified WildBind.Input.NumPad as NumPad --- | Convertible to/from Xlib's 'KeySym'+-- | Whether the key is pressed or released. ----- prop> fromKeySym . toKeySym == Just-class KeySymLike k where- fromKeySym :: Xlib.KeySym -> Maybe k- toKeySym :: k -> Xlib.KeySym+-- @since 0.2.0.0+data KeyEventType = KeyPress+ | KeyRelease+ deriving (Show,Eq,Ord,Bounded,Enum) -instance KeySymLike Xlib.KeySym where- fromKeySym = Just- toKeySym = id+-- | 'Xlib.KeyMask' values assigned to each modifier keys/states. If+-- the modifier doesn't exist, the mask is 0.+--+-- @since 0.2.0.0+data KeyMaskMap =+ KeyMaskMap+ { maskShift :: Xlib.KeyMask,+ maskControl :: Xlib.KeyMask,+ maskAlt :: Xlib.KeyMask,+ maskSuper :: Xlib.KeyMask,+ maskNumLock :: Xlib.KeyMask,+ maskCapsLock :: Xlib.KeyMask,+ maskShiftLock :: Xlib.KeyMask,+ maskScrollLock :: Xlib.KeyMask+ }+ deriving (Show,Eq,Ord) +isMasked :: KeyMaskMap -> (KeyMaskMap -> Xlib.KeyMask) -> Xlib.KeyMask -> Bool+isMasked kmmap accessor target = if (target .&. accessor kmmap) == 0+ then False+ else True++-- | Class of data types that can be handled by X11. The data type can+-- tell X11 to grab key with optional modifiers, and it can be+-- extracted from a X11 Event object.+--+-- @since 0.2.0.0+class XKeyInput k where+ toKeySym :: k -> Xlib.KeySym+ -- ^ Get the X11 keysym for this input.+ toModifierMasks :: KeyMaskMap -> k -> NonEmpty Xlib.KeyMask+ -- ^ Get modifer masks to grab the keysym. The grab action is+ -- repeated for all modifier masks. By default, it just returns 0.+ toModifierMasks _ _ = return 0+ fromKeyEvent :: KeyMaskMap -> KeyEventType -> Xlib.KeySym -> Xlib.KeyMask -> Maybe k+ -- ^ Create the input object from a key event type, a keysym and a+ -- modifier (got from XEvent.)++-- | Partial inverse of 'toKeySym'. fromKeySymDef :: (Bounded k, Enum k) => (k -> Xlib.KeySym) -> Xlib.KeySym -> Maybe k fromKeySymDef to_conv ks = M.lookup ks $ M.fromList $ map (\n -> (to_conv n, n)) $ enumFromTo minBound maxBound -instance KeySymLike NumPad.NumPadUnlocked where- fromKeySym = fromKeySymDef toKeySym+-- | This input event captures the 'KeyRelease' event only. That way,+-- you can deliver events to the window that originally has the+-- keyboard focus.+instance XKeyInput NumPad.NumPadUnlocked where toKeySym n = case n of NumPad.NumUp -> Xlib.xK_KP_Up NumPad.NumDown -> Xlib.xK_KP_Down@@ -57,17 +122,17 @@ NumPad.NumMulti -> Xlib.xK_KP_Multiply NumPad.NumMinus -> Xlib.xK_KP_Subtract NumPad.NumPlus -> Xlib.xK_KP_Add+ fromKeyEvent _ KeyPress _ _ = Nothing+ fromKeyEvent kmmask KeyRelease keysym mask = if is_numlocked+ then Nothing+ else fromKeySymDef toKeySym keysym+ where+ is_numlocked = isMasked kmmask maskNumLock mask -instance KeySymLike NumPad.NumPadLocked where- -- Xlib handles the [(.) (Delete)] key in a weird way. In the input- -- event, it brings XK_KP_Decimal when NumLock enabled, XK_KP_Delete- -- when NumLock disabled. However, XKeysymToKeycode() function won't- -- return the correct keycode for XK_KP_Decimal. (I'm not sure how- -- much this behavior depends on user's environment...) As a- -- workaround in this instance, we map NumLPeriod -> XK_KP_Delete,- -- but in the reverse map, we also respond to XK_KP_Decimal.- fromKeySym ks | ks == Xlib.xK_KP_Decimal = Just NumPad.NumLPeriod- | otherwise = (fromKeySymDef toKeySym) ks+-- | This input event captures the 'KeyRelease' event only. That way,+-- you can deliver events to the window that originally has the+-- keyboard focus.+instance XKeyInput NumPad.NumPadLocked where toKeySym n = case n of NumPad.NumL0 -> Xlib.xK_KP_0 NumPad.NumL1 -> Xlib.xK_KP_1@@ -86,75 +151,100 @@ NumPad.NumLEnter -> Xlib.xK_KP_Enter NumPad.NumLPeriod -> Xlib.xK_KP_Delete -- XKeysymToKeycode() didn't return the correct keycode for XK_KP_Decimal in numpaar code...----- | Extract the KeySym associated with the XEvent.-xEventToKeySym :: Xlib.XEventPtr -> MaybeT IO Xlib.KeySym-xEventToKeySym xev = MaybeT (fst <$> (Xlib.lookupString $ Xlib.asKeyEvent xev))---- | Extract the 'KeySymLike' associated with the XEvent.-xEventToKeySymLike :: KeySymLike k => Xlib.XEventPtr -> MaybeT IO k-xEventToKeySymLike xev = (MaybeT . return . fromKeySym) =<< xEventToKeySym xev---- | Internal abstract of key modifiers-data ModifierKey = ModNumLock deriving (Eq,Ord,Show,Bounded,Enum)---- | Convertible into a set of Modifiers.-class ModifierLike k where- toModifiers :: k -> [ModifierKey]--instance ModifierLike NumPad.NumPadUnlocked where- toModifiers _ = []+ + toModifierMasks kmmap _ = return $ maskNumLock kmmap -instance ModifierLike NumPad.NumPadLocked where- toModifiers _ = [ModNumLock]+ -- Xlib handles the [(.) (Delete)] key in a weird way. In the input+ -- event, it brings XK_KP_Decimal when NumLock enabled, XK_KP_Delete+ -- when NumLock disabled. However, XKeysymToKeycode() function won't+ -- return the correct keycode for XK_KP_Decimal. (I'm not sure how+ -- much this behavior depends on user's environment...) As a+ -- workaround in this instance, we map NumLPeriod -> XK_KP_Delete,+ -- but in the reverse map, we also respond to XK_KP_Decimal.+ fromKeyEvent _ KeyPress _ _ = Nothing+ fromKeyEvent kmmap KeyRelease keysym mask =+ if not $ is_num_locked+ then Nothing+ else if keysym == Xlib.xK_KP_Decimal+ then Just NumPad.NumLPeriod+ else fromKeySymDef toKeySym keysym+ where+ is_num_locked = isMasked kmmap maskNumLock mask --- | Convert a 'KeySymLike' into a KeyCode and ButtonMask for grabbing.-xKeyCode :: (KeySymLike k, ModifierLike k) => Xlib.Display -> k -> IO (Xlib.KeyCode, Xlib.ButtonMask)-xKeyCode disp key = (,) <$> Xlib.keysymToKeycode disp (toKeySym key) <*> createMask disp (toModifiers key)+-- | 'fromKeyEvent' first tries to create 'Left' (type @a@). If it+-- fails, then it tries to create 'Right' (type @b@).+instance (XKeyInput a, XKeyInput b) => XKeyInput (Either a b) where+ toKeySym = either toKeySym toKeySym+ toModifierMasks kmmap = either (toModifierMasks kmmap) (toModifierMasks kmmap)+ fromKeyEvent kmmap ev_type keysym mask =+ (fmap Left $ fromKeyEvent kmmap ev_type keysym mask) <|> (fmap Right $ fromKeyEvent kmmap ev_type keysym mask) -createMask :: Xlib.Display -> [ModifierKey] -> IO Xlib.ButtonMask-createMask _ [] = return 0-createMask disp (modkey:rest) = do- modifier_index <- fromIntegral <$> getXModifier disp modkey- (Bits.shift 1 modifier_index .|.) <$> createMask disp rest+-- | Extract the 'XKeyInput' from the XKeyEvent.+--+-- @since 0.2.0.0+xKeyEventToXKeyInput :: XKeyInput k => KeyMaskMap -> KeyEventType -> Xlib.XKeyEventPtr -> MaybeT IO k+xKeyEventToXKeyInput kmmap ev_type kev = do+ keysym <- MaybeT (fst <$> Xlib.lookupString kev)+ (_, _, _, _, _, _, _, status, _, _) <- liftIO $ Xlib.get_KeyEvent $ Foreign.castPtr kev+ MaybeT $ return $ fromKeyEvent kmmap ev_type keysym status type XModifierMap = [(Xlib.Modifier, [Xlib.KeyCode])] +-- | Get current 'KeyMaskMap'.+--+-- @since 0.2.0.0+getKeyMaskMap :: Xlib.Display -> IO KeyMaskMap+getKeyMaskMap disp = do+ xmodmap <- getXModifierMap disp+ let maskFor = lookupModifierKeyMask disp xmodmap+ numlock_mask <- maskFor Xlib.xK_Num_Lock+ capslock_mask <- maskFor Xlib.xK_Caps_Lock+ shiftlock_mask <- maskFor Xlib.xK_Shift_Lock+ scrolllock_mask <- maskFor Xlib.xK_Scroll_Lock+ alt_mask <- maskFor Xlib.xK_Alt_L+ super_mask <- maskFor Xlib.xK_Super_L+ return KeyMaskMap { maskShift = Xlib.shiftMask,+ maskControl = Xlib.controlMask,+ maskAlt = alt_mask,+ maskSuper = super_mask,+ maskNumLock = numlock_mask,+ maskCapsLock = capslock_mask,+ maskShiftLock = shiftlock_mask,+ maskScrollLock = scrolllock_mask+ }+ getXModifierMap :: Xlib.Display -> IO XModifierMap getXModifierMap = XlibE.getModifierMapping --- | Look up modifier for the given 'ModifierKey'. This is necessary--- especially for NumLock modifier, because it is highly dynamic in--- KeyCode realm. If no modifier is associated with the 'ModifierKey',--- it returns 0.+-- | Look up a modifier keymask associated to the given keysym. This+-- is necessary especially for NumLock modifier, because it is highly+-- dynamic in KeyCode realm. If no modifier is associated with the+-- 'ModifierKey', it returns 0. -- -- c.f: -- -- * grab_key.c of xbindkey package -- * http://tronche.com/gui/x/xlib/input/keyboard-grabbing.html -- * http://tronche.com/gui/x/xlib/input/keyboard-encoding.html-lookupXModifier :: Xlib.Display -> XModifierMap -> ModifierKey -> IO Xlib.Modifier-lookupXModifier disp xmmap ModNumLock = do- numlock_code <- Xlib.keysymToKeycode disp Xlib.xK_Num_Lock- return $ maybe 0 id $ listToMaybe $ mapMaybe (lookupXMod' numlock_code) xmmap+lookupModifierKeyMask :: Xlib.Display -> XModifierMap -> Xlib.KeySym -> IO Xlib.KeyMask+lookupModifierKeyMask disp xmmap keysym = do+ keycode <- Xlib.keysymToKeycode disp keysym+ return $ maybe 0 modifierToKeyMask $ listToMaybe $ mapMaybe (lookupXMod' keycode) xmmap where lookupXMod' key_code (xmod, codes) = if key_code `elem` codes then Just xmod else Nothing -getXModifier :: Xlib.Display -> ModifierKey -> IO Xlib.Modifier-getXModifier disp key = do- xmmap <- getXModifierMap disp- lookupXModifier disp xmmap key-------+modifierToKeyMask :: Xlib.Modifier -> Xlib.KeyMask+modifierToKeyMask = Bits.shift 1 . fromIntegral -- | Grab the specified key on the specified window. The key is -- captured from now on, so the window won't get that.-xGrabKey :: (KeySymLike k, ModifierLike k) => Xlib.Display -> Xlib.Window -> k -> IO ()-xGrabKey disp win key = do- (code, mask) <- xKeyCode disp key+--+-- @since 0.2.0.0+xGrabKey :: Xlib.Display -> Xlib.Window -> Xlib.KeySym -> Xlib.KeyMask -> IO ()+xGrabKey disp win key mask = do+ code <- Xlib.keysymToKeycode disp key Xlib.grabKey disp code mask win False Xlib.grabModeAsync Xlib.grabModeAsync -- grabKey throws an exception if that key for the window is already@@ -162,7 +252,169 @@ -- exception. -- | Release the grab on the specified key.-xUngrabKey :: (KeySymLike k, ModifierLike k) => Xlib.Display -> Xlib.Window -> k -> IO ()-xUngrabKey disp win key = do- (code, mask) <- xKeyCode disp key+--+-- @since 0.2.0.0+xUngrabKey :: Xlib.Display -> Xlib.Window -> Xlib.KeySym -> Xlib.KeyMask -> IO ()+xUngrabKey disp win key mask = do+ code <- Xlib.keysymToKeycode disp key Xlib.ungrabKey disp code mask win++-- | X11 key modifiers.+--+-- @since 0.2.0.0+data XMod = Shift+ | Ctrl+ | Alt+ | Super+ deriving (Show,Eq,Ord,Enum,Bounded)++-- | High-level X11 key event.+--+-- @since 0.2.0.0+data XKeyEvent =+ XKeyEvent+ { xKeyEventType :: KeyEventType, + xKeyEventMods :: S.Set XMod, -- ^ set of key modifiers enabled.+ xKeyEventKeySym :: Xlib.KeySym+ -- ^ X11 KeySym for the key. "WildBind.X11.KeySym" re-exports+ -- 'KeySym' values.+ }+ deriving (Show,Eq,Ord)++-- | 'fromKeyEvent' always returns 'Just'.+instance XKeyInput XKeyEvent where+ toKeySym (XKeyEvent _ _ ks) = ks+ toModifierMasks kmmap (XKeyEvent _ mods _) =+ fmap (.|. xModsToKeyMask kmmap mods) $ lockVariations kmmap+ fromKeyEvent kmmap ev_type keysym mask = Just $ XKeyEvent ev_type (keyMaskToXMods kmmap mask) keysym++-- | Something that can converted to 'XKeyEvent'.+--+-- @since 0.2.0.0+class ToXKeyEvent k where+ toXKeyEvent :: k -> XKeyEvent++instance ToXKeyEvent XKeyEvent where+ toXKeyEvent = id++-- | 'KeyPress' event of KeySym with empty 'XMod' set.+instance ToXKeyEvent Xlib.KeySym where+ toXKeyEvent keysym = XKeyEvent KeyPress mempty keysym++instance (ToXKeyEvent a, ToXKeyEvent b) => ToXKeyEvent (Either a b) where+ toXKeyEvent = either toXKeyEvent toXKeyEvent++instance Describable XKeyEvent where+ describe (XKeyEvent ev mods keysym) = ev_txt <> T.pack (mods_str ++ Xlib.keysymToString keysym)+ where+ mods_str = fold $ S.map (\m -> show m ++ "+") mods+ ev_txt = case ev of+ KeyPress -> "press "+ KeyRelease -> "release "++xModToKeyMask :: KeyMaskMap -> XMod -> Xlib.KeyMask+xModToKeyMask kmmap modi = case modi of+ Shift -> maskShift kmmap+ Ctrl -> maskControl kmmap+ Alt -> maskAlt kmmap+ Super -> maskSuper kmmap++xModsToKeyMask :: KeyMaskMap -> S.Set XMod -> Xlib.KeyMask+xModsToKeyMask kmmap = foldr f 0+ where+ f modi mask = xModToKeyMask kmmap modi .|. mask++lockVariations :: KeyMaskMap -> NonEmpty Xlib.KeyMask+lockVariations kmmap = toNonEmpty $ nub $ do+ numl <- [0, maskNumLock kmmap]+ capsl <- [0, maskCapsLock kmmap]+ shiftl <- [0, maskShiftLock kmmap]+ scl <- [0, maskScrollLock kmmap]+ return (numl .|. capsl .|. shiftl .|. scl)+ where+ toNonEmpty [] = return 0+ -- the result should always include 0, so the above case is not really necessary.+ toNonEmpty (x:rest) = x :| rest++keyMaskToXMods :: KeyMaskMap -> Xlib.KeyMask -> S.Set XMod+keyMaskToXMods kmmap mask = S.fromList$ toXMod =<< [ (maskShift, Shift),+ (maskControl, Ctrl),+ (maskAlt, Alt),+ (maskSuper, Super)+ ]+ where+ toXMod (acc, mod_symbol) = if isMasked kmmap acc mask+ then [mod_symbol]+ else []++-- | Add a 'XMod' to 'xKeyEventMods'.+--+-- @since 0.2.0.0+addXMod :: ToXKeyEvent k => XMod -> k -> XKeyEvent+addXMod modi mkey = case toXKeyEvent mkey of+ XKeyEvent ev_type mods ks -> XKeyEvent ev_type (S.insert modi mods) ks++-- | Set 'KeyPress' to 'xKeyEventType'.+--+-- @since 0.2.0.0+press :: ToXKeyEvent k => k -> XKeyEvent+press k = (toXKeyEvent k) { xKeyEventType = KeyPress }++-- | Set 'KeyRelease' to 'xKeyEventType'.+--+-- @since 0.2.0.0+release :: ToXKeyEvent k => k -> XKeyEvent+release k = (toXKeyEvent k) { xKeyEventType = KeyRelease }++-- | Add 'Shift' modifier to 'xKeyEventMods'.+--+-- @since 0.2.0.0+shift :: ToXKeyEvent k => k -> XKeyEvent+shift = addXMod Shift++-- | Add 'Ctrl' modifier to 'xKeyEventMods'.+--+-- @since 0.2.0.0+ctrl :: ToXKeyEvent k => k -> XKeyEvent+ctrl = addXMod Ctrl++-- | Add 'Alt' modifier to 'xKeyEventMods'.+--+-- @since 0.2.0.0+alt :: ToXKeyEvent k => k -> XKeyEvent+alt = addXMod Alt++-- | Add 'Super' modifier to 'xKeyEventMods'.+--+-- @since 0.2.0.0+super :: ToXKeyEvent k => k -> XKeyEvent+super = addXMod Super++-- | Send a 'XKeyEvent' to the window.+--+-- @since 0.2.0.0+xSendKeyEvent :: KeyMaskMap -> Xlib.Display -> Xlib.Window -> XKeyEvent -> IO ()+xSendKeyEvent kmmap disp target_win key_event = Xlib.allocaXEvent $ \xev -> do+ setupXEvent xev+ Xlib.sendEvent disp target_win propagate event_mask xev+ Xlib.sync disp False+ where+ propagate = True+ event_type = xKeyEventType key_event+ event_mask = case event_type of+ KeyPress -> Xlib.keyPressMask+ KeyRelease -> Xlib.keyReleaseMask+ xevent_type = case event_type of+ KeyPress -> Xlib.keyPress+ KeyRelease -> Xlib.keyRelease+ setupXEvent xev = do+ key_code <- Xlib.keysymToKeycode disp $ xKeyEventKeySym key_event+ XlibE.setEventType xev xevent_type+ XlibE.setKeyEvent xev target_win (Xlib.defaultRootWindow disp) subwindow key_mask key_code is_same_screen+ subwindow = 0 -- I mean, 'None' in Xlib. Graphics.X11 does not define 'None' window ID, I think...+ is_same_screen = True+ key_mask = xModsToKeyMask kmmap $ xKeyEventMods key_event++-- c.f. create_key_event function in xlib_wrapper.c from 'xremap'+-- https://github.com/k0kubun/xremap+
src/WildBind/X11/Internal/Window.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE OverloadedStrings #-} -- | -- Module: WildBind.X11.Internal.Window -- Description: types and functions related to X11 windows@@ -9,12 +10,16 @@ Window, ActiveWindow, emptyWindow,+ fromWinID, -- * Accessor functions for 'Window' winInstance, winClass, winName,+ -- ** project-internal accessor+ winID, -- * Functions- getActiveWindow+ getActiveWindow,+ defaultRootWindowForDisplay ) where import Control.Applicative ((<$>),(<|>),empty)@@ -24,6 +29,7 @@ import Data.Maybe (listToMaybe) import Data.Text (Text) import qualified Data.Text as Text+import qualified Foreign import qualified Graphics.X11.Xlib as Xlib import qualified Graphics.X11.Xlib.Extras as XlibE @@ -43,7 +49,11 @@ Window { winInstance :: Text, -- ^ name of the application instance (part of @WM_CLASS@ property) winClass :: Text, -- ^ name of the application class (part of @WM_CLASS@ property)- winName :: Text -- ^ what's shown in the title bar+ winName :: Text, -- ^ what's shown in the title bar+ winID :: Xlib.Window+ -- ^ X11 window ID.+ --+ -- @since 0.2.0.0 } deriving (Eq,Ord,Show) -- | Use this type especially when the 'Window' is active.@@ -51,8 +61,14 @@ -- | An empty Window instance used for fallback and/or default value. emptyWindow :: Window-emptyWindow = Window "" "" ""+emptyWindow = Window "" "" "" 0 +-- | Create 'Window' from X11's 'Xlib.Window'. Only for testing.+--+-- @since 0.2.0.0+fromWinID :: Xlib.Window -> Window+fromWinID wid = emptyWindow { winID = wid }+ -- | Get currently active 'Window'. getActiveWindow :: Xlib.Display -> IO ActiveWindow getActiveWindow disp = maybe emptyWindow id <$> runMaybeT getActiveWindowM where@@ -61,8 +77,14 @@ guard (awin /= 0) -- sometimes X11 returns 0 (NULL) as a window ID, which I think is always invalid name <- xGetWindowName disp awin class_hint <- liftIO $ xGetClassHint disp awin- return $ (uncurry Window) class_hint name+ return $ (uncurry Window) class_hint name awin +-- | Get the default root window of the display.+--+-- @since 0.2.0.0+defaultRootWindowForDisplay :: Xlib.Display -> Window+defaultRootWindowForDisplay disp = Window "" "" "" $ Xlib.defaultRootWindow disp+ -- | Check whether specified feature is supported by the window -- manager(?) Port of libxdo's @_xdo_ewmh_is_supported()@ function. ewmhIsSupported :: Xlib.Display -> String -> IO Bool@@ -89,7 +111,6 @@ [] -> empty (val:_) -> return $ fromIntegral val - xGetClassHint :: Xlib.Display -> Xlib.Window -> IO (Text, Text) xGetClassHint disp win = do hint <- XlibE.getClassHint disp win@@ -98,7 +119,12 @@ xGetTextProperty :: Xlib.Display -> Xlib.Window -> String -> MaybeT IO Text xGetTextProperty disp win prop_name = do req <- liftIO $ Xlib.internAtom disp prop_name False- Text.pack <$> MaybeT (listToMaybe <$> (XlibE.wcTextPropertyToTextList disp =<< XlibE.getTextProperty disp win req))+ text_prop <- MaybeT $ Foreign.alloca $ \ptr_prop -> do+ status <- XlibE.xGetTextProperty disp win ptr_prop req+ if status == 0+ then return Nothing+ else fmap Just $ Foreign.peek ptr_prop+ Text.pack <$> MaybeT (listToMaybe <$> (XlibE.wcTextPropertyToTextList disp text_prop)) -- | Get the window name for the X11 window. The window name refers to -- @_NET_WM_NAME@ or @WM_NAME@.
+ src/WildBind/X11/KeySym.hs view
@@ -0,0 +1,371 @@+-- |+-- Module: WildBind.X11.KeySym+-- Description: Re-export KeySyms+-- Maintainer: Toshio Ito <debug.ito@gmail.com>+--+-- This module re-exports X11 'KeySym's.+--+-- @since 0.2.0.0+module WildBind.X11.KeySym+ ( -- * The type+ KeySym,+ -- * Alphabet+ xK_a,+ xK_b,+ xK_c,+ xK_d,+ xK_e,+ xK_f,+ xK_g,+ xK_h,+ xK_i,+ xK_j,+ xK_k,+ xK_l,+ xK_m,+ xK_n,+ xK_o,+ xK_p,+ xK_q,+ xK_r,+ xK_s,+ xK_t,+ xK_u,+ xK_v,+ xK_w,+ xK_x,+ xK_y,+ xK_z,+ xK_A,+ xK_B,+ xK_C,+ xK_D,+ xK_E,+ xK_F,+ xK_G,+ xK_H,+ xK_I,+ xK_J,+ xK_K,+ xK_L,+ xK_M,+ xK_N,+ xK_O,+ xK_P,+ xK_Q,+ xK_R,+ xK_S,+ xK_T,+ xK_U,+ xK_V,+ xK_W,+ xK_X,+ xK_Y,+ xK_Z,+ -- * Numbers+ xK_0,+ xK_1,+ xK_2,+ xK_3,+ xK_4,+ xK_5,+ xK_6,+ xK_7,+ xK_8,+ xK_9,+ -- * ASCII symbols+ xK_space,+ xK_exclam,+ xK_quotedbl,+ xK_numbersign,+ xK_dollar,+ xK_percent,+ xK_ampersand,+ xK_apostrophe,+ xK_quoteright,+ xK_parenleft,+ xK_parenright,+ xK_asterisk,+ xK_plus,+ xK_comma,+ xK_minus,+ xK_period,+ xK_slash,+ xK_colon,+ xK_semicolon,+ xK_less,+ xK_equal,+ xK_greater,+ xK_question,+ xK_at,+ xK_bracketleft,+ xK_backslash,+ xK_bracketright,+ xK_asciicircum,+ xK_underscore,+ xK_grave,+ xK_quoteleft,+ xK_braceleft,+ xK_bar,+ xK_braceright,+ xK_asciitilde,+ -- * Control keys+ xK_BackSpace,+ xK_Tab,+ xK_Linefeed,+ xK_Clear,+ xK_Return,+ xK_Pause,+ xK_Scroll_Lock,+ xK_Sys_Req,+ xK_Escape,+ xK_Delete,+ xK_Multi_key,+ xK_Codeinput,+ xK_SingleCandidate,+ xK_MultipleCandidate,+ xK_PreviousCandidate,+ xK_Home,+ xK_Left,+ xK_Up,+ xK_Right,+ xK_Down,+ xK_Prior,+ xK_Page_Up,+ xK_Next,+ xK_Page_Down,+ xK_End,+ xK_Begin,+ xK_Select,+ xK_Print,+ xK_Execute,+ xK_Insert,+ xK_Undo,+ xK_Redo,+ xK_Menu,+ xK_Find,+ xK_Cancel,+ xK_Help,+ xK_Break,+ xK_Mode_switch,+ xK_script_switch,+ xK_Num_Lock,+ -- * Number pad keys+ xK_KP_Space,+ xK_KP_Tab,+ xK_KP_Enter,+ xK_KP_F1,+ xK_KP_F2,+ xK_KP_F3,+ xK_KP_F4,+ xK_KP_Home,+ xK_KP_Left,+ xK_KP_Up,+ xK_KP_Right,+ xK_KP_Down,+ xK_KP_Prior,+ xK_KP_Page_Up,+ xK_KP_Next,+ xK_KP_Page_Down,+ xK_KP_End,+ xK_KP_Begin,+ xK_KP_Insert,+ xK_KP_Delete,+ xK_KP_Equal,+ xK_KP_Multiply,+ xK_KP_Add,+ xK_KP_Separator,+ xK_KP_Subtract,+ xK_KP_Decimal,+ xK_KP_Divide,+ xK_KP_0,+ xK_KP_1,+ xK_KP_2,+ xK_KP_3,+ xK_KP_4,+ xK_KP_5,+ xK_KP_6,+ xK_KP_7,+ xK_KP_8,+ xK_KP_9,+ -- * Function keys+ xK_F1,+ xK_F2,+ xK_F3,+ xK_F4,+ xK_F5,+ xK_F6,+ xK_F7,+ xK_F8,+ xK_F9,+ xK_F10,+ xK_F11,+ xK_L1,+ xK_F12,+ xK_L2,+ xK_F13,+ xK_L3,+ xK_F14,+ xK_L4,+ xK_F15,+ xK_L5,+ xK_F16,+ xK_L6,+ xK_F17,+ xK_L7,+ xK_F18,+ xK_L8,+ xK_F19,+ xK_L9,+ xK_F20,+ xK_L10,+ xK_F21,+ xK_R1,+ xK_F22,+ xK_R2,+ xK_F23,+ xK_R3,+ xK_F24,+ xK_R4,+ xK_F25,+ xK_R5,+ xK_F26,+ xK_R6,+ xK_F27,+ xK_R7,+ xK_F28,+ xK_R8,+ xK_F29,+ xK_R9,+ xK_F30,+ xK_R10,+ xK_F31,+ xK_R11,+ xK_F32,+ xK_R12,+ xK_F33,+ xK_R13,+ xK_F34,+ xK_R14,+ xK_F35,+ xK_R15,+ -- * Modifier keys+ xK_Shift_L,+ xK_Shift_R,+ xK_Control_L,+ xK_Control_R,+ xK_Caps_Lock,+ xK_Shift_Lock,+ xK_Meta_L,+ xK_Meta_R,+ xK_Alt_L,+ xK_Alt_R,+ xK_Super_L,+ xK_Super_R,+ xK_Hyper_L,+ xK_Hyper_R,+ -- * Alphabet with accent and ligatures+ xK_Agrave,+ xK_Aacute,+ xK_Acircumflex,+ xK_Atilde,+ xK_Adiaeresis,+ xK_Aring,+ xK_AE,+ xK_Ccedilla,+ xK_Egrave,+ xK_Eacute,+ xK_Ecircumflex,+ xK_Ediaeresis,+ xK_Igrave,+ xK_Iacute,+ xK_Icircumflex,+ xK_Idiaeresis,+ xK_ETH,+ xK_Eth,+ xK_Ntilde,+ xK_Ograve,+ xK_Oacute,+ xK_Ocircumflex,+ xK_Otilde,+ xK_Odiaeresis,+ xK_multiply,+ xK_Ooblique,+ xK_Ugrave,+ xK_Uacute,+ xK_Ucircumflex,+ xK_Udiaeresis,+ xK_Yacute,+ xK_THORN,+ xK_Thorn,+ xK_ssharp,+ xK_agrave,+ xK_aacute,+ xK_acircumflex,+ xK_atilde,+ xK_adiaeresis,+ xK_aring,+ xK_ae,+ xK_ccedilla,+ xK_egrave,+ xK_eacute,+ xK_ecircumflex,+ xK_ediaeresis,+ xK_igrave,+ xK_iacute,+ xK_icircumflex,+ xK_idiaeresis,+ xK_eth,+ xK_ntilde,+ xK_ograve,+ xK_oacute,+ xK_ocircumflex,+ xK_otilde,+ xK_odiaeresis,+ xK_division,+ xK_oslash,+ xK_ugrave,+ xK_uacute,+ xK_ucircumflex,+ xK_udiaeresis,+ xK_yacute,+ xK_thorn,+ xK_ydiaeresis,+ -- * Other symbols+ xK_nobreakspace,+ xK_exclamdown,+ xK_cent,+ xK_sterling,+ xK_currency,+ xK_yen,+ xK_brokenbar,+ xK_section,+ xK_diaeresis,+ xK_copyright,+ xK_ordfeminine,+ xK_guillemotleft,+ xK_notsign,+ xK_hyphen,+ xK_registered,+ xK_macron,+ xK_degree,+ xK_plusminus,+ xK_twosuperior,+ xK_threesuperior,+ xK_acute,+ xK_mu,+ xK_paragraph,+ xK_periodcentered,+ xK_cedilla,+ xK_onesuperior,+ xK_masculine,+ xK_guillemotright,+ xK_onequarter,+ xK_onehalf,+ xK_threequarters,+ xK_questiondown,+ -- * special keysym+ xK_VoidSymbol,+ ) where++import Graphics.X11.Xlib
+ test/WildBind/X11/EmulateSpec.hs view
@@ -0,0 +1,106 @@+module WildBind.X11.EmulateSpec (main,spec) where++import Control.Exception (bracket)+import Control.Monad (forM_)+import Control.Monad.Trans.Maybe (runMaybeT)+import Data.Bits ((.|.))+import Data.Text (unpack)+import qualified Graphics.X11.Xlib as Xlib+import Test.Hspec++import WildBind (frontNextEvent, FrontEvent(..))+import qualified WildBind.Description as WBD+import WildBind.X11+ ( withX11Front, makeFrontEnd,+ XMod(..), release, press,+ defaultRootWindow,+ XKeyEvent(..), KeyEventType(..),+ alt, super, ctrl, shift+ )+import WildBind.X11.Emulate (sendKeyTo)+import WildBind.X11.Internal.Key (xKeyEventToXKeyInput, getKeyMaskMap, KeyMaskMap)+import WildBind.X11.Internal.Window (fromWinID)++import WildBind.X11.TestUtil (checkIfX11Available)++main :: IO ()+main = hspec spec++spec :: Spec+spec = checkIfX11Available $ describe "sendKeyTo" $ do+ let inputs = [ alt $ super Xlib.xK_r,+ release Xlib.xK_w,+ press Xlib.xK_Right,+ release $ ctrl $ shift Xlib.xK_F12,+ ctrl $ alt Xlib.xK_3+ + -- ctrl $ shift Xlib.xK_bracketleft++ ---- "Shift" modifier is tricky, because it affects+ ---- the keysym depending on the keyboard and key map+ ---- setting. E.g., with a typical Japanese keyboard,+ ---- bracketleft "[" and braceleft "{" share the same+ ---- key, and we use Shift to input the braceleft+ ---- "{". So, if you use Shift modifier, you have to+ ---- use xK_braceleft keysym.+ ]+ forM_ inputs $ \input -> specify (unpack $ WBD.describe input) $ withX11Front $ \x11 -> do+ bracket (Xlib.openDisplay "") Xlib.closeDisplay $ \disp -> do+ kmmap <- getKeyMaskMap disp+ win <- makeWindow disp+ -- putStrLn ("Window created: " ++ show win)+ Xlib.sync disp False+ -- putStrLn ("Do send input: " ++ show input)+ sendKeyTo x11 (fromWinID win) input+ -- putStrLn ("Receiving..")+ (nextKey kmmap disp) `shouldReturn` input++-- We have to create a dedicated window to receive events sent by+-- 'sendKeyTo', because XSendEvent ignores key grabs (so X11Front+-- cannot get the sent event.)+--+-- By the way, key events by XTEST extension emulate the real key+-- events from a real keyboard, so they are caught by key grabs.+--+-- c.f. http://t-sato.in.coocan.jp/xvkbd/events.html++makeWindow :: Xlib.Display -> IO Xlib.Window+makeWindow disp = do+ win <- Xlib.createSimpleWindow disp root x y w h border_width border_pixel bg_pixel+ -- Xlib.storeName disp win "test window"+ -- Xlib.mapWindow disp win+ Xlib.selectInput disp win select_mask+ Xlib.flush disp+ return win+ where+ root = Xlib.defaultRootWindow disp+ x = 0+ y = 0+ w = 50+ h = 50+ border_width = 1+ border_pixel = Xlib.blackPixel disp 0+ bg_pixel = Xlib.whitePixel disp 0+ select_mask = Xlib.keyPressMask .|. Xlib.keyReleaseMask++nextKey :: KeyMaskMap -> Xlib.Display -> IO XKeyEvent+nextKey kmmap disp = Xlib.allocaXEvent $ \xev -> do+ Xlib.nextEvent disp xev+ xtype <- Xlib.get_EventType xev+ -- putStrLn ("Got event type: " ++ show xtype)+ case toKeyType xtype of+ Nothing -> error ("Unknown event type: " ++ show xtype)+ Just key_type -> do+ -- putStrLn ("KeyEventType = " ++ show key_type)+ ret <- fmap unwrapMaybe $ runMaybeT $ xKeyEventToXKeyInput kmmap key_type $ Xlib.asKeyEvent xev+ -- putStrLn ("Converted: " ++ show ret)+ return ret+ where+ toKeyType xtype | xtype == Xlib.keyPress = Just KeyPress+ | xtype == Xlib.keyRelease = Just KeyRelease+ | otherwise = Nothing+ error_convert = error "Cannot convert the XEvent to XKeyEvent."+ unwrapMaybe = maybe error_convert id+++
− test/WildBind/X11/Internal/KeySpec.hs
@@ -1,34 +0,0 @@-module WildBind.X11.Internal.KeySpec (spec) where--import Test.Hspec-import Test.QuickCheck (Arbitrary(arbitrary), arbitraryBoundedEnum, property)--import WildBind.Input.NumPad (NumPadUnlocked, NumPadLocked)-import WildBind.X11.Internal.Key (KeySymLike(fromKeySym,toKeySym))--newtype NumPadUnlocked' =- NumPadUnlocked' { unwrapNumPadUnlocked :: NumPadUnlocked }- deriving Show--instance Arbitrary NumPadUnlocked' where- arbitrary = fmap NumPadUnlocked' arbitraryBoundedEnum--newtype NumPadLocked' =- NumPadLocked' { unwrapNumPadLocked :: NumPadLocked }- deriving Show--instance Arbitrary NumPadLocked' where- arbitrary = fmap NumPadLocked' arbitraryBoundedEnum--spec :: Spec-spec = do- spec_keySymLike "NumPadUnlocked" unwrapNumPadUnlocked- spec_keySymLike "NumPadLocked" unwrapNumPadLocked--spec_keySymLike :: (Arbitrary k', Show k', KeySymLike k, Eq k) => String -> (k' -> k) -> Spec-spec_keySymLike label unwrapper = describe label $ do- it "is a KeySymLike" $ property $ (prop_keySymLike . unwrapper)--prop_keySymLike :: (KeySymLike k, Eq k) => k -> Bool-prop_keySymLike key = (fromKeySym . toKeySym) key == Just key-
test/WildBind/X11/TestUtil.hs view
@@ -5,14 +5,18 @@ -- -- module WildBind.X11.TestUtil- ( checkIfX11Available+ ( checkIfX11Available,+ withGrabs ) where +import Control.Exception (bracket) import Control.Monad (when) import Control.Applicative ((<$>)) import System.Environment (lookupEnv) import Test.Hspec +import WildBind (FrontEnd(..))+ isDisplaySet :: IO Bool isDisplaySet = exists <$> lookupEnv "DISPLAY" where exists (Just v) = v /= ""@@ -22,3 +26,10 @@ checkIfX11Available = before $ do available <- isDisplaySet when (not available) $ pendingWith "DISPLAY env is not set. Skipped."++withGrabs :: FrontEnd s i -> [i] -> IO a -> IO a+withGrabs front inputs action = bracket grabAll (const ungrabAll) (const action)+ where+ grabAll = mapM_ (frontSetGrab front) inputs+ ungrabAll = mapM_ (frontUnsetGrab front) inputs+
test/WildBind/X11Spec.hs view
@@ -1,54 +1,74 @@-{-# LANGUAGE FlexibleContexts, ScopedTypeVariables, CPP #-}+{-# LANGUAGE FlexibleContexts, CPP, OverloadedStrings #-} module WildBind.X11Spec (main, spec) where import Control.Applicative ((<$>))-import Control.Exception (finally)+import Control.Concurrent.Async (async, waitCatch)+import Control.Exception (finally, Exception(fromException), throwIO)+import Control.Monad (forM_)+import Control.Monad.IO.Class (liftIO)+import qualified Control.Monad.Trans.State as State+import Data.Monoid ((<>))+import Data.List (intercalate, reverse)+import Data.IORef (IORef, newIORef, readIORef, modifyIORef)+import Data.Text (unpack) import Data.Time.Clock (getCurrentTime, diffUTCTime)+import qualified Graphics.X11.Xlib as Xlib import System.IO (hPutStrLn,stderr) import Test.Hspec import WildBind ( FrontEnd(frontSetGrab, frontUnsetGrab, frontNextEvent),- FrontEvent(FEChange,FEInput)+ FrontEvent(FEChange,FEInput), Describable, ActionDescription,+ Option(..), defOption, wildBind',+ binds', startFrom, on, as, run, whenBack, revise, justBefore )+import qualified WildBind.Description as WBD import qualified WildBind.Input.NumPad as NumPad-import WildBind.X11 (withFrontEnd, ActiveWindow)+import WildBind.X11+ ( withFrontEnd, ActiveWindow, XKeyInput,+ XMod(..), XKeyEvent(..), KeyEventType(..),+ ctrl, alt, super, shift, release, press+ )+import qualified WildBind.X11.KeySym as WKS -import WildBind.X11.TestUtil (checkIfX11Available)+import WildBind.X11.TestUtil (checkIfX11Available, withGrabs) +newtype MyException = MyException String+ deriving (Show,Eq,Ord)++instance Exception MyException+ main :: IO () main = hspec spec maybeRun :: Expectation -> Expectation-#ifdef TEST_NUMPAD_INTERACTIVE+#ifdef TEST_INTERACTIVE maybeRun = id #else-maybeRun _ = pendingWith ("You need to set test_numpad_interactive flag to run the test.")+maybeRun _ = pendingWith ("You need to set test-interactive flag to run the test.") #endif - p :: String -> IO () p = hPutStrLn stderr . ("--- " ++) -grabExp :: forall i- . (Bounded i, Enum i, Show i, Eq i)- => FrontEnd ActiveWindow i -> i -> Expectation+withFrontEndForTest :: (XKeyInput i, Describable i, Ord i) => (FrontEnd ActiveWindow i -> IO a) -> IO a+withFrontEndForTest action = withFrontEnd $ \front -> do+ _ <- frontNextEvent front -- discard the first FEChange event.+ action front++grabExp :: (Bounded i, Enum i, Show i, Eq i)+ => FrontEnd ActiveWindow i -> i -> Expectation grabExp front grab_input = grabExpMain `finally` releaseAll where grabExpMain = do frontSetGrab front grab_input p ("Press some numpad keys (grab="++ show grab_input ++")..")- ev <- frontNextEvent front :: IO (FrontEvent ActiveWindow i)+ ev <- frontNextEvent front p ("Got event: " ++ show ev)- case ev of- FEChange _ -> expectationFailure "FEChange is caught. not expected"- FEInput got -> do- got `shouldBe` grab_input- releaseAll = mapM_ (frontUnsetGrab front) (enumFromTo minBound maxBound :: [i])+ ev `shouldBe` FEInput grab_input+ releaseAll = mapM_ (frontUnsetGrab front) (enumFromTo minBound maxBound) -grabCase :: forall i . (Bounded i, Enum i, Show i, Eq i) => FrontEnd ActiveWindow i -> Expectation-grabCase front = do- _ <- frontNextEvent front :: IO (FrontEvent ActiveWindow i) -- discard the first FEChange event.- mapM_ (grabExp front) (enumFromTo minBound maxBound :: [i])+grabCase :: (Bounded i, Enum i, Show i, Eq i) => FrontEnd ActiveWindow i -> Expectation+grabCase front = mapM_ (grabExp front) (enumFromTo minBound maxBound) stopWatchMsec :: IO a -> IO (a, Int) stopWatchMsec act = do@@ -57,6 +77,20 @@ end <- getCurrentTime return (ret, floor ((diffUTCTime end start) * 1000)) +describeStr :: Describable a => a -> String+describeStr = unpack . WBD.describe++unshiftNewBinding :: Eq i => IORef [[(i,ActionDescription)]] -> [(i,ActionDescription)] -> IO ()+unshiftNewBinding ref got = do+ cur <- readIORef ref+ case cur of+ [] -> update+ (latest : _) -> if latest /= got+ then update+ else return ()+ where+ update = modifyIORef ref (got :)+ spec :: Spec spec = checkIfX11Available $ do describe "X11Front" $ do@@ -71,10 +105,66 @@ withFrontEnd $ \f2 -> do frontSetGrab f1 NumPad.NumLeft `shouldReturn` () frontSetGrab f2 NumPad.NumLeft `shouldReturn` ()+ it "should control key grab based on ('release' || 'press')" $ maybeRun $ withFrontEnd $ \f -> do+ got_binds_rev <- newIORef []+ let opt = defOption { optBindingHook = unshiftNewBinding got_binds_rev,+ optCatch = (\_ _ e -> throwIO e)+ }+ b_base = binds' $ do+ on (press $ ctrl $ WKS.xK_g) `as` "P(C-g)" `run` liftIO (throwIO $ MyException "NG")+ on (release $ alt $ ctrl $ WKS.xK_x) `as` "R(M-C-x)" `run` liftIO (throwIO $ MyException "OK")+ b_0 = whenBack (== 0) $ binds' $ do+ on (press $ ctrl $ WKS.xK_i) `as` "P(C-i)" `run` State.put 1+ b_1 = whenBack (== 1) $ binds' $ do+ on (press $ ctrl $ WKS.xK_i) `as` "P(C-i)" `run` State.put 0+ on (press $ alt $ ctrl $ WKS.xK_x) `as` "P(M-C-x)" `run` return ()+ rev _ _ i = justBefore $ p ("Input: " ++ (unpack $ WBD.describe i))+ b = revise rev $ startFrom (0 :: Int) $ b_base <> b_0 <> b_1+ exp_descs = [ ["P(C-g)", "R(M-C-x)", "P(C-i)"],+ ["P(C-g)", "R(M-C-x)", "P(C-i)", "P(M-C-x)"],+ ["P(C-g)", "R(M-C-x)", "P(C-i)"]+ ]+ p "Input C-i C-i M-C-x. If wildBind is still blocked, then type C-g."+ result <- waitCatch =<< async (wildBind' opt b f)+ got_descs <- fmap ((map . map) snd . reverse) $ readIORef got_binds_rev+ length got_descs `shouldBe` length exp_descs+ forM_ (zip got_descs exp_descs) $ uncurry shouldMatchList+ case result of+ Right _ -> expectationFailure "expects an exception, but nothing happened."+ Left e -> fromException e `shouldBe` (Just $ MyException "OK")+ describe "X11Front - NumPadUnlocked" $ do- it "should grab/ungrab keys" $ maybeRun $ withFrontEnd $ \(f :: FrontEnd ActiveWindow NumPad.NumPadUnlocked) -> do- grabCase f+ it "should grab/ungrab keys" $ maybeRun $ withFrontEndForTest $ \f -> do+ let grabCase' :: FrontEnd ActiveWindow NumPad.NumPadUnlocked -> Expectation+ grabCase' = grabCase+ grabCase' f describe "X11Front - NumPadLocked" $ do- it "should grab/ungrab keys" $ maybeRun $ withFrontEnd $ \(f :: FrontEnd ActiveWindow NumPad.NumPadLocked) -> do- grabCase f-+ it "should grab/ungrab keys" $ maybeRun $ withFrontEndForTest $ \f -> do+ let grabCase' :: FrontEnd ActiveWindow NumPad.NumPadLocked -> Expectation+ grabCase' = grabCase+ grabCase' f+ describe "X11Front - normal modified keys (pressed)" $ do+ it "should distinguish modifiers" $ maybeRun $ withFrontEndForTest $ \f -> do+ let inputs = [ ctrl Xlib.xK_i,+ ctrl $ alt Xlib.xK_i,+ super Xlib.xK_i,+ shift $ super Xlib.xK_I+ ]+ withGrabs f inputs $ do+ p ("Grabbed " ++ (intercalate ", " $ map describeStr inputs))+ forM_ inputs $ \input -> do+ p ("Push " ++ describeStr input)+ press_ev <- frontNextEvent f+ p ("Got event: " ++ show press_ev)+ press_ev `shouldBe` FEInput input+ release_ev <- frontNextEvent f+ p ("Got event: " ++ show release_ev)+ release_ev `shouldBe` (FEInput $ input { xKeyEventType = KeyRelease })+ describe "X11Front - Either" $ do+ it "should combine input types" $ maybeRun $ withFrontEndForTest $ \f -> do+ let inputs = [Left NumPad.NumLPeriod, Right NumPad.NumDelete]+ withGrabs f inputs $ do+ forM_ inputs $ \input -> do+ p ("Push " ++ show input)+ ev <- frontNextEvent f+ ev `shouldBe` FEInput input
wild-bind-x11.cabal view
@@ -1,5 +1,5 @@ name: wild-bind-x11-version: 0.1.0.7+version: 0.2.0.0 author: Toshio Ito <debug.ito@gmail.com> maintainer: Toshio Ito <debug.ito@gmail.com> license: BSD3@@ -17,20 +17,27 @@ default-language: Haskell2010 hs-source-dirs: src ghc-options: -Wall -fno-warn-unused-imports- default-extensions: TypeSynonymInstances, OverloadedStrings, MultiParamTypeClasses, FlexibleInstances+ other-extensions: GeneralizedNewtypeDeriving, FlexibleContexts, TypeSynonymInstances,+ OverloadedStrings exposed-modules: WildBind.X11,+ WildBind.X11.Emulate,+ WildBind.X11.Emulate.Example,+ WildBind.X11.KeySym, WildBind.X11.Internal.Key, WildBind.X11.Internal.Window, WildBind.X11.Internal.NotificationDebouncer--- other-modules: + other-modules: WildBind.X11.Internal.FrontEnd,+ WildBind.X11.Internal.GrabMan build-depends: base >=4.6 && <5.0, wild-bind >=0.1.0 && <0.2, text >=1.2.0 && <1.3, X11 >=1.6.1 && <1.9, containers >=0.5.0 && <0.6, transformers >=0.3.0 && <0.6,+ mtl >=2.2.1 && <2.3, fold-debounce >=0.2.0 && <0.3,- stm >=2.4.2 && <2.5+ stm >=2.4.2 && <2.5,+ semigroups >=0.16.2.2 && <0.19 -- executable wild-bind-x11 -- default-language: Haskell2010@@ -41,8 +48,8 @@ -- -- other-extensions: -- build-depends: base >=4 && <5 -flag test-numpad-interactive- description: Run tests that require the user to interact with the test script via a numpad.+flag test-interactive+ description: Run tests that require the user to interact with the test script. default: False test-suite spec@@ -50,19 +57,19 @@ default-language: Haskell2010 hs-source-dirs: test ghc-options: -Wall -fno-warn-unused-imports -threaded "-with-rtsopts=-M512m -N"- if flag(test-numpad-interactive)- cpp-options: -DTEST_NUMPAD_INTERACTIVE+ if flag(test-interactive)+ cpp-options: -DTEST_INTERACTIVE main-is: Spec.hs- other-extensions: FlexibleContexts, ScopedTypeVariables, CPP+ other-extensions: FlexibleContexts, CPP, OverloadedStrings other-modules: WildBind.X11Spec,- WildBind.X11.Internal.KeySpec,+ WildBind.X11.EmulateSpec, WildBind.X11.Internal.NotificationDebouncerSpec, WildBind.X11.TestUtil build-depends: base, wild-bind-x11,- wild-bind, X11,+ wild-bind, X11, text, transformers, hspec >=2.1.7,- QuickCheck >=2.6 && <3.0,- time >=1.5.0 && <1.9+ time >=1.5.0 && <1.9,+ async >=2.0.2 && <2.2 source-repository head type: git