{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE LiberalTypeSynonyms #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE ViewPatterns #-}

-- | A conduit interface to XInput events.

module Data.Conduit.XInput
  (xinputSource
  ,Device(..)
  ,Event(..)
  ,KeyCode(..))
  where

import           Control.Monad.IO.Class
import           Control.Monad.Trans.Class
import           Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as S8
import           Data.Conduit
import qualified Data.Conduit.Binary as CB
import qualified Data.Conduit.List as CL
import           Data.Conduit.Process
import           Data.Monoid
import           System.Exit

-- | Device identifier.
newtype Device = Device Int
  deriving (Num)

-- | An event.
data Event
  = Press
  | Release
  deriving (Enum,Bounded,Eq,Show)

-- | Key code.
newtype KeyCode =
  KeyCode Int
  deriving (Eq,Show,Num,Ord)

-- | Source of xinput keys.
xinputSource :: MonadIO m
             => Device -> ConduitM i (Event,KeyCode) m ExitCode
xinputSource (Device device) =
  do (exitCode,()) <- sourceCmdWithConsumer
                        ("unbuffer xinput test " <> show device)
                        (CB.lines $= CL.mapMaybe parse $=
                         awaitForever (lift . yield))
     return exitCode

-- | Parse an xinput test line.
parse :: ByteString -> Maybe (Event,KeyCode)
parse line =
  case S8.words (S8.drop 4 line) of
    [mode,S8.readInt -> Just (code,_)] ->
      return (if mode == "release"
                 then Release
                 else Press
             ,KeyCode code)
    _ -> Nothing