{-# 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 :: X11Front i -> Window -> k -> m ()
sendKeyTo X11Front i
front Window
win k
key = IO () -> m ()
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 :: 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 :: 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 :: 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 :: 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 :: 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