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