macos-corelibs-0.0.1.0: src/MacSdk/Framework/CoreFoundation/Object.hs
{-# LANGUAGE ScopedTypeVariables #-}
{- |
Core Foundation objects.
https://developer.apple.com/documentation/corefoundation/cftype?language=objc
-}
module MacSdk.Framework.CoreFoundation.Object
(
-- * Core Foundation types
CFTypeID
, CFClass(..)
, CFTypeable(..)
, Object(..)
, withCFPtr
, Ref(..)
, objRetain
, refRelease
, objEquals
, objShow
, getTypeID
, objCast
-- * Low level
, CFType_
, CFType
, CFTypeRef
, CFObject
, manageCFObj
, retainManageCFObj
, cfRetain
, cf_retain
, cfReleasePtr
, cf_release
, cf_equal
, cfEqual
, cf_show
, cfGetTypeID
) where
import Control.Monad.Managed
import Foreign hiding (with)
import Foreign.C.Types (CBool(..))
import Data.Proxy
data CFType_
type CFType = Object CFType_
type CFTypeRef = Ptr CFType_
type CFObject = Object CFType_
instance CFClass CFType_ where
type CFTypeID = Word64
-- | Typeclass of types corresponding to a CFType class, and that can
-- therefore by upcasted to a Core Foundation object.
class CFClass a where
asCFType :: Ptr a -> Ptr CFType_
asCFType = castPtr
-- | Typeclass of CFType classes whose CFType ID can be queried at runtime.
class CFClass a => CFTypeable a where
cfTypeID :: Proxy a -> IO CFTypeID
-- | A reference to a CoreFoundation object whose memory allocation is
-- automatically managed by the GHC runtime.
newtype Object a = Object (ForeignPtr a)
withCFPtr :: Object a -> (Ptr a -> IO b) -> IO b
withCFPtr (Object p) = withForeignPtr p
-- | A reference to a CoreFoundation object whose memory allocation is manually
-- managed. It can be used to keep an object in memory beyond its garbage
-- collection. Note that failing to release a manual memory reference will cause
-- a memory leak.
newtype Ref = Ref { unRef :: CFTypeRef }
-- | Retains an object in memory, returning a corresponding reference.
--
-- An object can be retained multiple times, each time creating a new, distinct
-- memory reference. Every reference that is created with this function must be
-- explicitly released with 'refRelease' to avoid memory leaks.
objRetain :: CFClass a => Object a -> IO Ref
objRetain = flip withCFPtr $ \p ->
let q = asCFType p in cf_retain q >> pure (Ref q)
-- | Releases a manual memory reference.
refRelease :: Ref -> IO ()
refRelease = cf_release . unRef
-- TODO: check that it is only used with references following the "copy rule"
manageCFObj :: Ptr a -> IO (Object a)
manageCFObj ptr =
if ptr == nullPtr
then error "manageCFObj: null pointer"
else Object <$> newForeignPtr cfReleasePtr ptr
-- TODO: check that it is only used with references following the "create rule"
retainManageCFObj :: CFClass a => Ptr a -> IO (Object a)
retainManageCFObj ref = cfRetain ref >> manageCFObj ref
cfRetain :: CFClass a => Ptr a -> IO ()
cfRetain p = cf_retain (asCFType p)
foreign import ccall "Carbon/Carbon.h CFRetain" cf_retain :: CFTypeRef -> IO ()
foreign import ccall "&CFRelease" cfReleasePtr :: FunPtr (Ptr a -> IO ())
foreign import ccall "CFRelease" cf_release :: CFTypeRef -> IO ()
foreign import ccall "Carbon/Carbon.h CFEqual"
cf_equal :: CFTypeRef -> CFTypeRef -> IO CBool
-- | Suubtype polymorphic equality testing between two Core Foundation objects.
objEquals :: (CFClass a, MonadIO m) => Object a -> Object a -> m Bool
objEquals x y = liftIO . flip with pure $ do
p <- asCFType <$> managed (withCFPtr x)
q <- asCFType <$> managed (withCFPtr y)
liftIO (fmap toBool (cf_equal p q))
cfEqual :: CFClass a => Ptr a -> Ptr a -> IO Bool
cfEqual x y = do
res <- cf_equal (asCFType x) (asCFType y)
pure (toBool res)
foreign import ccall "CFShow" cf_show :: CFTypeRef -> IO ()
-- | Sends a "show" message to the object.
objShow :: CFClass o => Object o -> IO ()
objShow = flip withCFPtr (cf_show . asCFType)
foreign import ccall "CFGetTypeID" cfGetTypeID :: CFTypeRef -> IO CFTypeID
-- | Returns the 'CFTypeID' of a given object.
getTypeID :: CFClass o => Object o -> IO CFTypeID
getTypeID = flip withCFPtr (cfGetTypeID . asCFType)
-- | Type safe cast from a CFType to another.
objCast :: forall o o'. (CFClass o, CFTypeable o')
=> Object o -> IO (Maybe (Object o'))
objCast o = do
ty <- getTypeID o
wanted <- cfTypeID (Proxy :: Proxy o')
if ty == wanted
then Just <$> withCFPtr o (retainManageCFObj . castPtr)
else pure Nothing