{-| Module : $Header$ Copyright : (c) 2016-19 Brian W Bush License : MIT Maintainer : Brian W Bush Stability : Production Portability : Linux Interpret events from a SpaceNavigator \<\>. -} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE RecordWildCards #-} module System.Hardware.Linux.SpaceNav ( -- * Types and sizes SpaceNav(..) , byteLength -- * Event handling , interpretSpaceNav , readSpaceNav ) where import Data.Aeson.Types (FromJSON, ToJSON) import Data.Binary (Binary(..), decode) import Data.Bits ((.&.), complement, shift) import Data.ByteString.Lazy.Char8 as BS (ByteString, readFile, splitAt) import Data.Serialize (Serialize) import Data.Word (Word32) import GHC.Generics (Generic) import System.Hardware.Linux.Input (InputEvent(..), byteLength) -- | SpaceNavigator data. data SpaceNav = SpaceNavButton { timestamp :: Integer -- ^ The event timestamp, in POSIX picoseconds. , number :: Int -- ^ The button number. , pressed :: Bool -- ^ Whether the button is depressed. } | SpaceNavAnalog { timestamp :: Integer -- ^ The event timestamp, in POSIX picoseconds. , number :: Int -- ^ The axis number. , setting :: Double -- ^ The data value. } | SpaceNavNull deriving (Eq, Generic, Ord, Read, Show) instance FromJSON SpaceNav instance ToJSON SpaceNav instance Binary SpaceNav instance Serialize SpaceNav -- | Interpret SpaceNavigator event bytes on a Linux input deviceThis interpretation is based on \<\> and \<\>. interpretSpaceNav :: ByteString -- ^ The bytes from /dev/input. -> SpaceNav -- ^ The corresponding SpaceNavigator data. interpretSpaceNav x = let InputEvent{..} = decode x (seconds, microseconds) = timeval seconds' = fromIntegral seconds :: Integer microseconds' = fromIntegral microseconds :: Integer timestamp = (10^(6 :: Int) * seconds' + microseconds') * 10^(6 :: Int) in case typ of 0x01 -> let number = fromIntegral $ code .&. 0x00ff pressed = value /= 0 in SpaceNavButton{..} 0x02 -> let number = fromIntegral code setting = fromIntegral (twosComplement value) / 400 in SpaceNavAnalog{..} _ -> SpaceNavNull -- | Decode a two's complement. twosComplement :: Word32 -- ^ The two's complement. -> Int -- ^ The corresponding integer. twosComplement x = fromIntegral (x' .&. complement mask) - fromIntegral (x' .&. mask) where x' = fromIntegral x :: Int mask = 1 `shift` 31 -- | Read a stream of SpaceNavigator data. readSpaceNav :: FilePath -- ^ The SpaceNavigator device, e.g., "\/dev\/input\/spacenav0". -> IO [SpaceNav] -- ^ Action to read the SpaceNavigator data. readSpaceNav path = let chunks :: ByteString -> [ByteString] chunks x = let (y, ys) = BS.splitAt 8 x in y : chunks ys in map interpretSpaceNav . chunks <$> BS.readFile path