evdev-2.2.0: src/Evdev/Uinput.hs
-- | Create virtual input devices.
module Evdev.Uinput (
Device,
newDevice,
writeEvent,
writeBatch,
defaultDeviceOpts,
DeviceOpts (..),
deviceSyspath,
deviceDevnode,
-- * Re-exports from 'Evdev'
AbsInfo (..),
Event(..),
EventData(..),
KeyEvent(..),
EventCode(..),
EventValue(..),
) where
import Control.Monad
import Data.Tuple.Extra
import Foreign
import Data.ByteString.Char8 (ByteString)
import Evdev hiding (Device, newDevice)
import Evdev.Codes
import qualified Evdev.LowLevel as LL
import Util
-- | A `uinput` device.
newtype Device = Device LL.UDevice
-- | Create a new `uinput` device.
newDevice ::
-- | Device name
ByteString ->
DeviceOpts ->
IO Device
newDevice name DeviceOpts{..} = do
dev <- LL.libevdev_new
LL.setDeviceName dev name
let maybeSet :: (LL.Device -> a -> IO ()) -> Maybe a -> IO ()
maybeSet setter x = maybe (pure ()) (setter dev) x
maybeSet LL.setDevicePhys phys
maybeSet LL.setDeviceUniq uniq
maybeSet LL.libevdev_set_id_product idProduct
maybeSet LL.libevdev_set_id_vendor idVendor
maybeSet LL.libevdev_set_id_bustype idBustype
maybeSet LL.libevdev_set_id_version idVersion
let enable :: Ptr () -> EventType -> [Word16] -> IO ()
enable ptr t cs = do
unless (null cs) $ cec $ LL.enableType dev t'
forM_ cs $ \c -> cec $ LL.enableCode dev t' c ptr
where
t' = fromEnum' t
mapM_
(uncurry $ enable nullPtr)
[ (EvKey, map fromEnum' keys)
, (EvRel, map fromEnum' relAxes)
, (EvMsc, map fromEnum' miscs)
, (EvSw, map fromEnum' switchs)
, (EvLed, map fromEnum' leds)
, (EvSnd, map fromEnum' sounds)
, (EvFf, map fromEnum' ffs)
, (EvPwr, map fromEnum' powers)
, (EvFfStatus, map fromEnum' ffStats)
]
forM_ reps $ \(rep, n) -> do
pf <- mallocForeignPtr
withForeignPtr pf \p -> do
poke p n
enable (castPtr p) EvRep [fromEnum' rep]
forM_ absAxes $ \(axis, absInfo) ->
LL.withAbsInfo absInfo $ \ptr ->
enable ptr EvAbs [fromEnum' axis]
fmap Device $ cec $ LL.createFromDevice dev $ fromEnum' LL.UOMManaged
where
cec :: CErrCall a => IO a -> IO (CErrCallRes a)
cec = cErrCall "newDevice" ()
data DeviceOpts = DeviceOpts
{ phys :: Maybe ByteString
, uniq :: Maybe ByteString
, idProduct :: Maybe Int
, idVendor :: Maybe Int
, idBustype :: Maybe Int
, idVersion :: Maybe Int
, keys :: [Key]
, relAxes :: [RelativeAxis]
, absAxes :: [(AbsoluteAxis, LL.AbsInfo)]
, miscs :: [MiscEvent]
, switchs :: [SwitchEvent]
, leds :: [LEDEvent]
, sounds :: [SoundEvent]
, reps :: [(RepeatEvent, Int)]
, ffs :: [EventCode]
, powers :: [EventCode]
, ffStats :: [EventCode]
}
defaultDeviceOpts :: DeviceOpts
defaultDeviceOpts =
DeviceOpts
{ uniq = Nothing
, phys = Nothing
, idProduct = Nothing
, idVendor = Nothing
, idBustype = Nothing
, idVersion = Nothing
, keys = []
, relAxes = []
, absAxes = []
, miscs = []
, switchs = []
, leds = []
, sounds = []
, reps = []
, ffs = []
, powers = []
, ffStats = []
}
-- | Write a single event. Doesn't issue a sync event, so: @writeEvent dev e /= writeBatch dev [e]@.
writeEvent :: Device -> EventData -> IO ()
writeEvent (Device dev) e = do
cErrCall "writeEvent" dev $ uncurry3 (LL.writeEvent dev) $ toCEventData e
-- | Write several events followed by a 'SynReport'.
writeBatch :: Foldable t => Device -> t EventData -> IO ()
writeBatch dev es = do
forM_ es $ writeEvent dev
writeEvent dev $ SyncEvent SynReport
deviceSyspath :: Device -> IO (Maybe ByteString)
deviceSyspath = LL.getSyspath . \(Device d) -> d
deviceDevnode :: Device -> IO (Maybe ByteString)
deviceDevnode = LL.getDevnode . \(Device d) -> d