packages feed

kmonad-0.4.1: src/KMonad/Keyboard/IO/Mac/IOKitSource.hs

module KMonad.Keyboard.IO.Mac.IOKitSource
  ( iokitSource
  )
where

import KMonad.Prelude

import Foreign.Marshal hiding (void)
import Foreign.Ptr
import Foreign.Storable
import Foreign.C.String

import KMonad.Keyboard
import KMonad.Keyboard.IO
import KMonad.Keyboard.IO.Mac.Types

--------------------------------------------------------------------------------

-- | Use the mac c-api to `grab` a keyboard
foreign import ccall "grab_kb"
  grab_kb :: CString -> IO Word8

-- | Release the keyboard hook
foreign import ccall "release_kb"
  release_kb :: IO Word8

-- | Pass a pointer to a buffer to wait_key, when it returns the buffer can be
-- read for the next key event.
foreign import ccall "wait_key"
  wait_key :: Ptr MacKeyEvent -> IO Word8


data EvBuf = EvBuf
  { _buffer :: !(Ptr MacKeyEvent)
  }
makeLenses ''EvBuf

-- | Return a KeySource using the Mac IOKit approach
iokitSource :: HasLogFunc e
  => (Maybe String)
  -> RIO e (Acquire KeySource)
iokitSource name = mkKeySource (iokitOpen name) iokitClose iokitRead


--------------------------------------------------------------------------------

-- | Ask IOKit to open keyboards matching the specified name
iokitOpen :: HasLogFunc e
  => (Maybe String)
  -> RIO e EvBuf
iokitOpen m = do
  logInfo "Opening IOKit devices"
  liftIO $ do

    case m of
      Nothing -> void $ grab_kb nullPtr
      Just s  -> do
        str <- newCString s
        void $ grab_kb str
        free str

    buf <- mallocBytes $ sizeOf (undefined :: MacKeyEvent)
    pure $ EvBuf buf

-- | Ask Mac to close the queue
iokitClose :: HasLogFunc e => EvBuf -> RIO e ()
iokitClose b = do
  logInfo "Closing IOKit devices"
  liftIO $ do
    _ <- release_kb
    free $ b^.buffer

-- | Get a new 'KeyEvent' from Mac
--
-- NOTE: This can throw an error if the event fails to convert.
iokitRead :: HasLogFunc e => EvBuf -> RIO e KeyEvent
iokitRead b = do
  we <- liftIO $ do
    _ <- wait_key $ b^.buffer
    peek $ b^.buffer
  either throwIO pure $ fromMacKeyEvent we