{-# 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      (KeyEventType (..), ToXKeyEvent (..), XKeyEvent,
                                                 press, release, xSendKeyEvent)
import           WildBind.X11.Internal.Window   (ActiveWindow, Window, 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 :: forall k (m :: * -> *) i.
(ToXKeyEvent k, MonadIO m) =>
X11Front i -> Window -> k -> m ()
sendKeyTo X11Front i
front Window
win k
key = forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ KeyMaskMap -> Display -> Window -> XKeyEvent -> IO ()
xSendKeyEvent KeyMaskMap
kmmap Display
disp Window
win_id XKeyEvent
key_event
  where
    kmmap :: KeyMaskMap
kmmap = forall k. X11Front k -> KeyMaskMap
x11KeyMaskMap X11Front i
front
    disp :: Display
disp = forall k. X11Front k -> Display
x11Display X11Front i
front
    win_id :: Window
win_id = Window -> Window
winID Window
win
    key_event :: XKeyEvent
key_event = forall k. ToXKeyEvent k => k -> XKeyEvent
toXKeyEvent k
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 :: forall k (m :: * -> *) i.
(ToXKeyEvent k, MonadIO m, MonadReader Window m) =>
X11Front i -> k -> m ()
sendKey X11Front i
front k
key = do
  Window
win <- forall r (m :: * -> *). MonadReader r m => m r
ask
  forall k (m :: * -> *) i.
(ToXKeyEvent k, MonadIO m) =>
X11Front i -> Window -> k -> m ()
sendKeyTo X11Front i
front Window
win k
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 :: forall k (m :: * -> *) i.
(ToXKeyEvent k, MonadIO m) =>
X11Front i -> Window -> k -> m ()
pushTo X11Front i
front Window
win k
key = do
  XKeyEvent -> m ()
send forall a b. (a -> b) -> a -> b
$ forall k. ToXKeyEvent k => k -> XKeyEvent
press k
key
  XKeyEvent -> m ()
send forall a b. (a -> b) -> a -> b
$ forall k. ToXKeyEvent k => k -> XKeyEvent
release k
key
  where
    send :: XKeyEvent -> m ()
send = forall k (m :: * -> *) i.
(ToXKeyEvent k, MonadIO m) =>
X11Front i -> Window -> k -> m ()
sendKeyTo X11Front i
front Window
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 :: forall k (m :: * -> *) i.
(ToXKeyEvent k, MonadIO m, MonadReader Window m) =>
X11Front i -> k -> m ()
push X11Front i
front k
key = do
  Window
win <- forall r (m :: * -> *). MonadReader r m => m r
ask
  forall k (m :: * -> *) i.
(ToXKeyEvent k, MonadIO m) =>
X11Front i -> Window -> k -> m ()
pushTo X11Front i
front Window
win k
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 :: forall from to i bs.
(ToXKeyEvent from, ToXKeyEvent to) =>
X11Front i -> from -> to -> Binding' bs Window XKeyEvent
remap X11Front i
front from
from to
to = forall i fs r a bs.
Ord i =>
Binder i (Action (ReaderT fs IO) r) a -> Binding' bs fs i
bindsF forall a b. (a -> b) -> a -> b
$ do
  forall i v. i -> v -> Binder i v ()
on (forall k. ToXKeyEvent k => k -> XKeyEvent
press from
from) forall (m :: * -> *) b a.
Functor m =>
(Action m () -> b) -> m a -> b
`run` XKeyEvent -> ReaderT Window IO ()
sendKey' (forall k. ToXKeyEvent k => k -> XKeyEvent
press to
to)
  forall i v. i -> v -> Binder i v ()
on (forall k. ToXKeyEvent k => k -> XKeyEvent
release from
from) forall (m :: * -> *) b a.
Functor m =>
(Action m () -> b) -> m a -> b
`run` XKeyEvent -> ReaderT Window IO ()
sendKey' (forall k. ToXKeyEvent k => k -> XKeyEvent
release to
to)
  where
    sendKey' :: XKeyEvent -> ReaderT Window IO ()
sendKey' = forall k (m :: * -> *) i.
(ToXKeyEvent k, MonadIO m, MonadReader Window m) =>
X11Front i -> k -> m ()
sendKey X11Front i
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 :: forall from to i bs.
(ToXKeyEvent from, ToXKeyEvent to) =>
X11Front i -> from -> to -> Binding' bs Window XKeyEvent
remapR X11Front i
front from
from to
to = forall i fs r a bs.
Ord i =>
Binder i (Action (ReaderT fs IO) r) a -> Binding' bs fs i
bindsF forall a b. (a -> b) -> a -> b
$ do
  forall i v. i -> v -> Binder i v ()
on (forall k. ToXKeyEvent k => k -> XKeyEvent
release from
from) forall (m :: * -> *) b a.
Functor m =>
(Action m () -> b) -> m a -> b
`run` forall k (m :: * -> *) i.
(ToXKeyEvent k, MonadIO m, MonadReader Window m) =>
X11Front i -> k -> m ()
push X11Front i
front to
to