{-# LANGUAGE RecordWildCards #-}
{-| This module contains some higher-level functions,
 - wrapping XInput calls. #-}
module Graphics.X11.XInput.Functions
  (xinputInit,
   xinputCheckVersion,
   handleXCookie,
   eventButton,
   eventWindow,
   eventKeyMask,
   eventMousePos
  ) where

import Control.Applicative
import Control.Monad.Trans
import Foreign.C
import Foreign.Ptr
import Foreign.Storable
import Foreign.Marshal.Alloc
import Foreign.Marshal.Array
import qualified Graphics.X11 as X11
import qualified Graphics.X11.Xlib.Extras as E

import Graphics.X11.XInput.Types
import Graphics.X11.XInput.Foreign
import Graphics.X11.XInput.Parser

-- | Initialize XInput 2.0 extension.
xinputInit :: X11.Display -> IO XInputInitResult
xinputInit dpy = do
  withCString "XInputExtension" $ \xinput ->
    alloca $ \opcode -> 
      alloca $ \event ->
        alloca $ \error -> do
          result <- xQueryExtension dpy xinput opcode event error
          if result /= 0
            then do
                 xi_opcode <- peek opcode
                 mbVer <- xinputCheckVersion dpy
                 case mbVer of
                   Nothing  -> return $ InitOK xi_opcode
                   Just (major, minor) -> return $ VersionMismatch major minor
            else return NoXInput

xinputMajor, xinputMinor :: CInt
xinputMajor = 2
xinputMinor = 0

-- | Returns Nothing if XInput 2.0 is supported, or
-- Just (major, minor) if another version is supported
xinputCheckVersion :: X11.Display -> IO (Maybe (Int, Int))
xinputCheckVersion dpy = do
  alloca $ \majorPtr ->
    alloca $ \minorPtr -> do
      poke majorPtr xinputMajor
      poke minorPtr xinputMinor
      result <- xinputVersion dpy majorPtr minorPtr
      supportedMajor <- peek majorPtr
      supportedMinor <- peek minorPtr
      if result == 1
        then return $ Just (fromIntegral supportedMajor, fromIntegral supportedMinor)
        else return Nothing

-- | Handle usual X11 event or cookie event.
handleXCookie :: (MonadIO m) => X11.Display
                             -> Opcode               -- ^ Extension identifier (one got from xinputInit)
                             -> X11.XEventPtr        -- ^ Pointer to X11 event
                             -> (E.Event -> m a)     -- ^ Handler for usual X11 event
                             -> (EventCookie -> m a) -- ^ Handler for X11 cookie event
                             -> m a
handleXCookie dpy xi_opcode xev evHandler cookieHandler = do
--   liftIO $ putStrLn "handling XCookie"
  evtype <- liftIO $ get_event_type xev
  ext    <- liftIO $ get_event_extension xev
  hasCookie <- liftIO $ getEventData dpy (castPtr xev)
  result <- if (evtype == genericEvent) && (ext == xi_opcode) && hasCookie
              then do
--                    liftIO $ putStrLn "XInput event"
                   cookieHandler =<< (liftIO $ getXGenericEventCookie xev)
              else evHandler =<< (liftIO $ E.getEvent xev)
  liftIO $ freeEventData dpy (castPtr xev)
  return result

-- | Shortcut to get button number or keycode
-- from keyboard or mouse event. Returns Nothing
-- if this is not mouse or keyboard event.
eventButton :: Event -> Maybe Int
eventButton (Event {..})
  | (eType `elem` [XI_ButtonPress, XI_ButtonRelease,
                   XI_KeyPress, XI_KeyRelease]) =
        case eSpecific of
          GPointerEvent {peDetail = n} -> Just n
          _                            -> Nothing
  | otherwise = Nothing

-- | Shortcut to get pointer position from event
eventMousePos :: Event -> Maybe (X11.Position, X11.Position)
eventMousePos (Event {..})
  | (eType `elem` [XI_ButtonPress, XI_ButtonRelease,
                   XI_KeyPress, XI_KeyRelease,
                   XI_Enter, XI_Leave]) =
        let x = round (peRootX eSpecific)
            y = round (peRootY eSpecific)
        in  Just (x, y)
  | otherwise = Nothing

-- | Shortcut to get event window.
-- Returns Nothing if this is not pointer-related event
eventWindow :: Event -> Maybe X11.Window
eventWindow e =
  case eSpecific e of
    GPointerEvent {peEvent = w} -> Just w
    _                           -> Nothing

-- | Shortcut to get keymask from event
eventKeyMask :: Event -> Maybe X11.KeyMask
eventKeyMask (Event {eSpecific = GPointerEvent {peSpecific = e}}) =
  Just $ fromIntegral $ msBase $ peMods e
eventKeyMask _ = Nothing