{-# LANGUAGE DeriveAnyClass #-} {-| Module : KMonad.Keyboard.IO Description : The logic behind sending and receiving key events to the OS Copyright : (c) David Janssen, 2019 License : MIT Maintainer : janssen.dhj@gmail.com Stability : experimental Portability : non-portable (MPTC with FD, FFI to Linux-only c-code) -} module KMonad.Keyboard.IO ( -- * KeySink: send keyboard events to the OS -- $snk KeySink , mkKeySink , emitKey -- * KeySource: read keyboard events from the OS , KeySource , mkKeySource , awaitKey ) where import KMonad.Prelude import KMonad.Keyboard import KMonad.Util import qualified RIO.Text as T -------------------------------------------------------------------------------- -- $snk -- | A 'KeySink' sends key actions to the OS newtype KeySink = KeySink { emitKeyWith :: KeyEvent -> IO () } -- | Create a new 'KeySink' mkKeySink :: HasLogFunc e => RIO e snk -- ^ Action to acquire the keysink -> (snk -> RIO e ()) -- ^ Action to close the keysink -> (snk -> KeyEvent -> RIO e ()) -- ^ Action to write with the keysink -> RIO e (Acquire KeySink) mkKeySink o c w = do u <- askUnliftIO let open = unliftIO u $ logInfo "Opening KeySink" >> o let close snk = unliftIO u $ logInfo "Closing KeySink" >> c snk let write snk a = unliftIO u $ w snk a `catch` logRethrow "Encountered error in KeySink" pure $ KeySink . write <$> mkAcquire open close -- | Emit a key to the OS emitKey :: (HasLogFunc e) => KeySink -> KeyEvent -> RIO e () emitKey snk e = do logDebug $ "Emitting: " <> display e liftIO $ emitKeyWith snk e -------------------------------------------------------------------------------- -- $src -- | A 'KeySource' is an action that awaits 'KeyEvent's from the OS newtype KeySource = KeySource { awaitKeyWith :: IO KeyEvent} -- | Create a new KeySource mkKeySource :: HasLogFunc e => RIO e src -- ^ Action to acquire the keysink -> (src -> RIO e ()) -- ^ Action to close the keysink -> (src -> RIO e KeyEvent) -- ^ Action to write with the keysink -> RIO e (Acquire KeySource) mkKeySource o c r = do u <- askUnliftIO let open = unliftIO u $ logInfo "Opening KeySource" >> o let close src = unliftIO u $ logInfo "Closing KeySource" >> c src let read src = unliftIO u $ r src `catch` logRethrow "Encountered error in KeySource" pure $ KeySource . read <$> mkAcquire open close -- | Wait for the next key from the OS awaitKey :: (HasLogFunc e) => KeySource -> RIO e KeyEvent awaitKey src = do e <- liftIO . awaitKeyWith $ src logDebug $ "\n" <> display (T.replicate 80 "-") <> "\nReceived event: " <> display e pure e