packages feed

macos-corelibs-0.0.1.0: src/MacSdk/Framework/CoreFoundation/RunLoop.hs

{-| Run loops for system-wide events. -}

module MacSdk.Framework.CoreFoundation.RunLoop
  ( CFRunLoopSourceRef
  , RunLoop
  , RunLoopSource
  , RunLoopMode(..)
  , runLoopContainsSource
  , runLoopGetCurrent
  , runLoopAddSource
  , mainRunLoop
  , runLoopRun
  , runLoopStop
  , runLoopSourceInvalidate
  , machPortRunLoopSource
  , addSourceToMainLoop
  -- * Mach ports
  , CFMachPortRef
  , MachPort
  , eventTapIsEnabled
  , enableEventTap
  , cgEventTapIsEnabled
  -- , releaseMachPort
  ) where

import Control.Monad.Managed
import MacSdk.Framework.CoreFoundation.Object
import MacSdk.Framework.CoreFoundation.String
import MacSdk.Framework.CoreFoundation.Allocator
import MacSdk.Framework.CoreFoundation.Array
import Foreign hiding (with)
import Foreign.C.Types (CBool(..), CInt(..))
import Control.Monad
import System.IO.Unsafe (unsafePerformIO)

data CFMachPort
type CFMachPortRef = Ptr CFMachPort
instance CFClass CFMachPort where
type MachPort = Object CFMachPort

foreign import ccall unsafe "CGEventTapIsEnabled"
  cgEventTapIsEnabled :: CFMachPortRef -> IO CBool

eventTapIsEnabled :: MachPort -> IO Bool
eventTapIsEnabled = fmap toBool . flip withCFPtr cgEventTapIsEnabled

foreign import ccall unsafe "CGEventTapEnable"
  cgEventTapEnable :: CFMachPortRef -> CBool -> IO ()

enableEventTap :: MachPort -> Bool -> IO ()
enableEventTap mp b = withCFPtr mp $ flip cgEventTapEnable (fromBool b)

-- releaseMachPort :: MachPort -> IO ()
-- releaseMachPort = flip withCFPtr cfRelease

data CFRunLoop
type CFRunLoopRef = Ptr CFRunLoop
instance CFClass CFRunLoop

-- | Type of run loops.
--
-- A run loop is an event loop in a (system) thread that looks for events, such
-- as mouse clicks, key presses, and timer firings. The run loop dispatches
-- these events to interested parties and then goes to sleep waiting for more
-- events. [Advanced Mac OS X Programming, 1st ed.]
type RunLoop = Object CFRunLoop

foreign import ccall unsafe kCFRunLoopCommonModes_ :: IO CFRunLoopMode
foreign import ccall unsafe kCFRunLoopDefaultMode_ :: IO CFRunLoopMode

type CFRunLoopMode = CFStringRef
data RunLoopMode
  = CommonModes
  -- ^ Objects added to a run loop using this value as the mode are monitored
  -- by all run loop modes that have been declared as a member of the set of
  -- “common” modes with 'runLoopAddCommonMode'.
  | DefaultMode
  -- ^ Run loop mode that should be used when a thread is in its default, or
  -- idle, state, waiting for an event. This mode is used when the run loop is
  -- started with 'runLoopRun'.

toCFRunLoopMode :: RunLoopMode -> CFRunLoopMode
toCFRunLoopMode = unsafePerformIO . \case
  CommonModes -> kCFRunLoopCommonModes_
  DefaultMode -> kCFRunLoopDefaultMode_

data CFRunLoopSource
type CFRunLoopSourceRef = Ptr CFRunLoopSource
instance CFClass CFRunLoopSource
type RunLoopSource = Object CFRunLoopSource

foreign import ccall unsafe "Carbon/Carbon.h CFRunLoopContainsSource"
  cfRunLoopContainsSource
  :: CFRunLoopRef -> CFRunLoopSourceRef -> CFStringRef -> IO CBool

runLoopContainsSource :: RunLoop -> RunLoopSource -> RunLoopMode -> IO Bool
runLoopContainsSource rl rls mode = flip with pure $ do
  rl' <- managed $ withCFPtr rl
  rls' <- managed $ withCFPtr rls
--   str' <- managed $ withCFPtr str
  liftIO . fmap toBool $ cfRunLoopContainsSource rl' rls' (toCFRunLoopMode mode) -- str'

foreign import ccall unsafe "Carbon/Carbon.h CFRunLoopAddSource"
  cfRunLoopAddSource
  :: CFRunLoopRef -> CFRunLoopSourceRef -> CFStringRef -> IO ()

runLoopAddSource :: RunLoop -> RunLoopSource -> RunLoopMode -> IO ()
runLoopAddSource rl rls mode = runManaged $ do
  rl' <- managed $ withCFPtr rl
  rls' <- managed $ withCFPtr rls
--  str' <- managed $ withCFPtr str
  liftIO $ cfRunLoopAddSource rl' rls' (toCFRunLoopMode mode) -- str'

foreign import ccall unsafe "Carbon/Carbon.h CFRunLoopGetMain"
  cfRunLoopGetMain :: IO CFRunLoopRef

foreign import ccall unsafe "Carbon/Carbon.h CFRunLoopGetCurrent"
  cfRunLoopGetCurrent :: IO CFRunLoopRef

-- | Returns the RunLoop object for the current thread.
runLoopGetCurrent :: IO RunLoop
runLoopGetCurrent = cfRunLoopGetCurrent >>= retainManageCFObj

-- | Runs the current thread’s CFRunLoop object in its default mode
-- indefinitely.
foreign import ccall "Carbon/Carbon.h CFRunLoopRun"
  runLoopRun :: IO ()

-- | Forces a CFRunLoop object to stop running.
foreign import ccall "Carbon/Carbon.h CFRunLoopStop"
  cfRunLoopStop :: CFRunLoopRef -> IO ()

runLoopStop :: RunLoop -> IO ()
runLoopStop l = withCFPtr l cfRunLoopStop

-- | Returns the main RunLoop object.
mainRunLoop :: IO RunLoop
mainRunLoop = cfRunLoopGetMain >>= retainManageCFObj

foreign import ccall unsafe "Carbon/Carbon.h CFRunLoopSourceInvalidate"
  cfRunLoopSourceInvalidate :: CFRunLoopSourceRef -> IO ()

runLoopSourceInvalidate :: RunLoopSource -> IO ()
runLoopSourceInvalidate = flip withCFPtr cfRunLoopSourceInvalidate

foreign import ccall "CFMachPortCreateRunLoopSource"
  cfMachPortCreateRunLoopSource
  :: CFAllocatorRef -> CFMachPortRef -> CFIndex -> IO CFRunLoopSourceRef

machPortRunLoopSource :: Allocator -> MachPort -> Int -> IO RunLoopSource
machPortRunLoopSource al mp i = flip with pure $ do
  al' <- managed $ withCFPtr al
  mp' <- managed $ withCFPtr mp
  liftIO $ cfMachPortCreateRunLoopSource al' mp' (fromIntegral i) >>= manageCFObj

-- | Creates a loop source from the given mach port, and adds it to the main run
-- loop with default parameters.
addSourceToMainLoop :: MachPort -> IO ()
addSourceToMainLoop port = nullAllocator >>= \alloc -> 
  join $ runLoopAddSource <$> mainRunLoop
                          <*> machPortRunLoopSource alloc port 0
                          <*> pure CommonModes