packages feed

wild-bind-x11-0.2.0.0: test/WildBind/X11/EmulateSpec.hs

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