{-# LINE 1 "System/Linux/Input/Device.hsc" #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LINE 2 "System/Linux/Input/Device.hsc" #-}

module System.Linux.Input.Device (
      getDeviceName
    , getDeviceId
    , InputId (..)
    ) where

import Prelude hiding (product)
import qualified Data.ByteString.Char8 as BSC
import qualified Data.ByteString.Unsafe as BSC
import Foreign
import Foreign.C
import System.IO (Handle)
import System.Posix.Types
import System.Posix.IO (handleToFd)


{-# LINE 19 "System/Linux/Input/Device.hsc" #-}

foreign import ccall "ioctl" c_ioctl :: CInt -> CInt -> Ptr () -> IO CInt

c_ioctl' :: Fd -> CInt -> Ptr d -> IO ()
c_ioctl' f req p =
    throwErrnoIfMinus1_ "ioctl" $
        c_ioctl (fromIntegral f) req (castPtr p)

getDeviceName :: Handle -> IO BSC.ByteString
getDeviceName h = withFd h $ \f->do
    BSC.takeWhile (/='\0') `fmap` ioctlBS f 2147501318 (BSC.replicate 255 '\0')
{-# LINE 30 "System/Linux/Input/Device.hsc" #-}

ioctlBS :: Fd -> Int -> BSC.ByteString -> IO BSC.ByteString
ioctlBS f req buf = do
    BSC.unsafeUseAsCString buf $ \p -> do
        print $ (sizedIoctl req $ BSC.length buf, 2164212998)
{-# LINE 35 "System/Linux/Input/Device.hsc" #-}
        c_ioctl' f (sizedIoctl req $ BSC.length buf) p
    return buf

-- | Sets the size of an ioctl request number
sizedIoctl :: Int -> Int -> CInt
sizedIoctl req size =
    fromIntegral $ (req .&. complement (mask `shiftL` shift))
                   .|. ((mask .&. size) `shiftL` shift)
  where
    mask = 16383
{-# LINE 45 "System/Linux/Input/Device.hsc" #-}
    shift = 16
{-# LINE 46 "System/Linux/Input/Device.hsc" #-}

data InputId = InputId { busType :: Word16
                       , vendor  :: Word16
                       , product :: Word16
                       , version :: Word16
                       }
             deriving (Ord, Eq, Show)

instance Storable InputId where
    sizeOf _ = 8
    alignment _ = 8
    peek p = do
        busType <- peekElemOff p' 0
        vendor  <- peekElemOff p' 1
        product <- peekElemOff p' 2
        version <- peekElemOff p' 3
        return $ InputId busType vendor product version
      where p' = castPtr p :: Ptr Word16

    poke p i = do
        pokeElemOff p' 0 $ busType i
        pokeElemOff p' 1 $ vendor i
        pokeElemOff p' 2 $ product i
        pokeElemOff p' 3 $ version i
      where p' = castPtr p :: Ptr Word16

-- | Invoke ioctl with a Storable argument
ioctlStorable :: Storable a => Fd -> Int -> a -> IO a
ioctlStorable f req a = alloca $ \p -> do
    poke p a
    c_ioctl' f (fromIntegral req) p
    peek p

-- | Invoke ioctl with an uninitialized Storable argument
ioctlStorable' :: Storable a => Fd -> Int -> IO a
ioctlStorable' f req = alloca $ \p -> do
    c_ioctl' f (fromIntegral req) p
    peek p

withFd :: Handle -> (Fd -> IO a) -> IO a
withFd h action = handleToFd h >>= action

getDeviceId :: Handle -> IO InputId
getDeviceId h =
    withFd h $ \f->ioctlStorable' f 2148025602
{-# LINE 91 "System/Linux/Input/Device.hsc" #-}