haskell-gi-base 0.20.8 → 0.26.9
raw patch · 36 files changed
Files
- ChangeLog.md +66/−0
- Data/GI/Base.hs +7/−6
- Data/GI/Base/Attributes.hs +413/−83
- Data/GI/Base/BasicConversions.hsc +10/−8
- Data/GI/Base/BasicTypes.hs +0/−199
- Data/GI/Base/BasicTypes.hsc +221/−0
- Data/GI/Base/Closure.hs +0/−41
- Data/GI/Base/Constructible.hs +4/−6
- Data/GI/Base/DynVal.hs +144/−0
- Data/GI/Base/GArray.hs +16/−0
- Data/GI/Base/GClosure.hs +96/−0
- Data/GI/Base/GError.hs +259/−0
- Data/GI/Base/GError.hsc +0/−254
- Data/GI/Base/GHashTable.hsc +14/−11
- Data/GI/Base/GObject.hsc +451/−24
- Data/GI/Base/GParamSpec.hsc +361/−12
- Data/GI/Base/GQuark.hsc +22/−0
- Data/GI/Base/GType.hsc +39/−30
- Data/GI/Base/GValue.hs +541/−0
- Data/GI/Base/GValue.hsc +0/−378
- Data/GI/Base/GVariant.hsc +53/−50
- Data/GI/Base/Internal/CTypes.hsc +40/−0
- Data/GI/Base/Internal/PathFieldAccess.hs +76/−0
- Data/GI/Base/ManagedPtr.hs +156/−63
- Data/GI/Base/Overloading.hs +121/−161
- Data/GI/Base/Overloading.hs-boot +3/−0
- Data/GI/Base/Properties.hsc +202/−115
- Data/GI/Base/ShortPrelude.hs +9/−5
- Data/GI/Base/Signals.hs +292/−0
- Data/GI/Base/Signals.hs-boot +53/−0
- Data/GI/Base/Signals.hsc +0/−174
- Data/GI/Base/Utils.hsc +32/−11
- LICENSE +14/−0
- c/hsgclosure.c +0/−202
- csrc/hsgclosure.c +477/−0
- haskell-gi-base.cabal +22/−20
ChangeLog.md view
@@ -1,3 +1,69 @@+### 0.24.8+++ Make the StablePtr IsGValue instance make a copy in fromGValue.++### 0.24.7+++ Add a mechanism for marshalling generic Haskell values into `GValue`s.++### 0.24.4+++ Add a workaround for old hsc2hs versions, so drop the constraint on hsc2hs.++### 0.24.3+++ Require hsc2hs version 0.68.6 or higher++### 0.24.2+++ Provide type init functions for GParamSpec types.++### 0.24.1+++ Support for allocating `GArray`s.++### 0.24.0+++ Support for non-GObject objects. As part of this work the GObject hierarchy has been slightly reworked. The main change is that 'gobjectType' has now become [glibType](https://hackage.haskell.org/package/haskell-gi-base-0.24.0/docs/Data-GI-Base-BasicTypes.html#v:glibType) (part of the [TypedObject](https://hackage.haskell.org/package/haskell-gi-base-0.24.0/docs/Data-GI-Base-BasicTypes.html#t:TypedObject) typeclass).++### 0.22.2+++ Reinstate the new' method.++### 0.22.1+++ Fix a memory allocation error in [GClosure](https://hackage.haskell.org/package/haskell-gi-base-0.22.0/docs/Data-GI-Base.html#t:GClosure) that could lead to crashes.++### 0.22.0+++ Require base >= 0.4.9 (GHC version >= 8.0), so that we can use TypeApplications.+++ Make [GClosure](https://hackage.haskell.org/package/haskell-gi-base-0.22.0/docs/Data-GI-Base.html#t:GClosure) a primitive type, and make it depend on a phantom parameter to increase type safety.++### 0.21.5+++ Add [releaseObject](https://hackage.haskell.org/package/haskell-gi-base-0.21.5/docs/Data-GI-Base-ManagedPtr.html#v:releaseObject), a function useful for manually releasing memory associated to GObjects.++### 0.21.4+++ Add support for callback-valued properties.++### 0.21.3+++ Fix a compilation error on Windows, see [issue 193](https://github.com/haskell-gi/haskell-gi/issues/193).++### 0.21.2+++ Export [newManagedPtr_](https://hackage.haskell.org/package/haskell-gi-base-0.21.2/docs/Data-GI-Base-ManagedPtr.html#v:newManagedPtr_).++### 0.21.1+++ Remove the `::=` and `::~` constructors in `AttrOp`, since they cannot really be used for anything, as they are pure functions.++### 0.21.0+++ New release to keep major version parity with the `haskell-gi` package, no changes otherwise.+ ### 0.20.8 + Fix a bug which could lead to crashes when releasing boxed objects, see [issue #130](https://github.com/haskell-gi/haskell-gi/issues/130).
Data/GI/Base.hs view
@@ -8,26 +8,27 @@ ( module Data.GI.Base.Attributes , module Data.GI.Base.BasicConversions , module Data.GI.Base.BasicTypes- , module Data.GI.Base.Closure+ , module Data.GI.Base.GClosure , module Data.GI.Base.Constructible , module Data.GI.Base.GError , module Data.GI.Base.GHashTable- , module Data.GI.Base.GObject , module Data.GI.Base.GValue , module Data.GI.Base.GVariant , module Data.GI.Base.ManagedPtr , module Data.GI.Base.Signals+ , module Data.GI.Base.Overloading ) where import Data.GI.Base.Attributes (get, set, AttrOp(..)) import Data.GI.Base.BasicConversions import Data.GI.Base.BasicTypes-import Data.GI.Base.Closure+import Data.GI.Base.GClosure (GClosure) import Data.GI.Base.Constructible (new) import Data.GI.Base.GError import Data.GI.Base.GHashTable-import Data.GI.Base.GObject (new')-import Data.GI.Base.GValue (GValue(..), IsGValue(..))+import Data.GI.Base.GValue (GValue(..), fromGValue, toGValue, IsGValue(..)) import Data.GI.Base.GVariant import Data.GI.Base.ManagedPtr-import Data.GI.Base.Signals (on, after, SignalProxy(PropertyNotify))+import Data.GI.Base.Signals (on, after,+ SignalProxy(PropertyNotify, PropertySet, (:::)))+import Data.GI.Base.Overloading (asA)
Data/GI/Base/Attributes.hs view
@@ -1,6 +1,19 @@-{-# LANGUAGE GADTs, ScopedTypeVariables, DataKinds, KindSignatures,- TypeFamilies, TypeOperators, MultiParamTypeClasses, ConstraintKinds,- UndecidableInstances, FlexibleInstances #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE ImplicitParams #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-} -- | --@@ -13,6 +26,14 @@ -- attribute, but we can also have attributes for types not descending -- from `Data.GI.Base.BasicTypes.GObject`). --+-- If you're wondering what the possible attributes of a GObject are,+-- look at the list of properties in the documentation, e.g. the+-- Properties heading of the docs for 'GI.Gtk.Objects.Button' lists+-- properties such as @image@ and @relief@. Parent classes may also+-- introduce properties, so since a Button is an instance of+-- @IsActionable@, it inherits properties like @actionName@ from+-- 'GI.Gtk.Interfaces.Actionable' too.+-- -- As an example consider a @button@ widget and a property (of the -- Button class, or any of its parent classes or implemented -- interfaces) called "label". The simplest way of getting the value@@ -84,11 +105,10 @@ -- -- There are other variants of these operators, see 'AttrOp' -- below. ':=>' and ':~>' are like ':=' and ':~' but operate in the--- 'IO' monad rather than being pure. There is also '::=' and '::~'--- which take the object as an extra parameter.+-- 'IO' monad rather than being pure. -- -- Attributes can also be set during construction of a--- `Data.GI.Base.BasicTypes.GObject` using `Data.GI.Base.Properties.new`+-- `Data.GI.Base.BasicTypes.GObject` using `Data.GI.Base.Constructible.new` -- -- > button <- new Button [_label := "Can't touch this!", _sensitive := False] --@@ -98,7 +118,7 @@ -- `set` afterwards. That these invariants hold is also checked during -- compile time. ----- == Nullable atributes+-- == Nullable attributes -- -- Whenever the attribute is represented as a pointer in the C side, -- it is often the case that the underlying C representation admits or@@ -107,7 +127,7 @@ -- representing the @NULL@ pointer value (notable exceptions are -- `Data.GI.Base.BasicTypes.GList` and -- `Data.GI.Base.BasicTypes.GSList`, for which @NULL@ is represented--- simply as he empty list). This can be overriden in the+-- simply as the empty list). This can be overridden in the -- introspection data, since sometimes attributes are non-nullable, -- even if the type would allow for @NULL@. --@@ -135,81 +155,170 @@ set, clear, - AttrLabelProxy(..)+ AttrLabelProxy(..),++ resolveAttr,+ bindPropToField,++ EqMaybe(..) ) where +import Control.Monad (void, when) import Control.Monad.IO.Class (MonadIO, liftIO) -import Data.Proxy (Proxy(..))-+import Data.GI.Base.BasicTypes (GObject)+import Data.GI.Base.DynVal (DynVal(..), ModelProxy, DVKey(..),+ modelProxyCurrentValue, modelProxyRegisterHandler,+ modelProxyUpdate, dvKeys, dvRead) import Data.GI.Base.GValue (GValueConstruct)-import Data.GI.Base.Overloading (HasAttributeList,- ResolveAttribute, IsLabelProxy(..))+import Data.GI.Base.Overloading (HasAttributeList, ResolveAttribute,+ ResolvedSymbolInfo)+import Data.GI.Base.Internal.PathFieldAccess (PathFieldAccess(..), Components) -import GHC.TypeLits+import {-# SOURCE #-} Data.GI.Base.Signals (SignalInfo(..),+ SignalProxy,+ on, after, connectGObjectNotify,+ SignalConnectMode(..))++import Data.Kind (Type)+import Data.Proxy (Proxy(..))+import qualified Data.Text as T++import GHC.TypeLits (Symbol, KnownSymbol, ErrorMessage(..), TypeError,+ symbolVal) import GHC.Exts (Constraint) -#if MIN_VERSION_base(4,9,0) import GHC.OverloadedLabels (IsLabel(..))-#endif+import qualified Optics.Core as O -infixr 0 :=,:~,:=>,:~>,::=,::~+infixr 0 :=,:~,:=>,:~>,:<~,:!<~ -- | A proxy for attribute labels. data AttrLabelProxy (a :: Symbol) = AttrLabelProxy --- | Support for overloaded labels.-instance a ~ x => IsLabelProxy x (AttrLabelProxy a) where- fromLabelProxy _ = AttrLabelProxy- #if MIN_VERSION_base(4,10,0) instance a ~ x => IsLabel x (AttrLabelProxy a) where fromLabel = AttrLabelProxy-#elif MIN_VERSION_base(4,9,0)+#else instance a ~ x => IsLabel x (AttrLabelProxy a) where fromLabel _ = AttrLabelProxy #endif -- | Info describing an attribute.-class AttrInfo (info :: *) where+class AttrInfo (info :: Type) where -- | The operations that are allowed on the attribute. type AttrAllowedOps info :: [AttrOpTag]- -- | Constraint on the value being set.- type AttrSetTypeConstraint info :: * -> Constraint+ -- | Constraint on the type for which we are allowed to -- create\/set\/get the attribute.- type AttrBaseTypeConstraint info :: * -> Constraint+ type AttrBaseTypeConstraint info :: Type -> Constraint+ -- | Type returned by `attrGet`. type AttrGetType info++ -- | Constraint on the value being set.+ type AttrSetTypeConstraint info :: Type -> Constraint+ type AttrSetTypeConstraint info = (~) (AttrGetType info)++ -- | Constraint on the value being set, with allocation allowed+ -- (see ':&=' below).+ type AttrTransferTypeConstraint info :: Type -> Constraint+ type AttrTransferTypeConstraint info = (~) (AttrTransferType info)++ -- | Type resulting from the allocation.+ type AttrTransferType info :: Type+ type AttrTransferType info = AttrGetType info+ -- | Name of the attribute. type AttrLabel info :: Symbol+ -- | Type which introduces the attribute. type AttrOrigin info+ -- | Get the value of the given attribute. attrGet :: AttrBaseTypeConstraint info o =>- Proxy info -> o -> IO (AttrGetType info)+ o -> IO (AttrGetType info)+ default attrGet :: -- Make sure that a non-default method+ -- implementation is provided if AttrGet+ -- is set.+ CheckNotElem 'AttrGet (AttrAllowedOps info)+ (GetNotProvidedError info) =>+ o -> IO (AttrGetType info)+ attrGet = undefined+ -- | Set the value of the given attribute, after the object having -- the attribute has already been created. attrSet :: (AttrBaseTypeConstraint info o, AttrSetTypeConstraint info b) =>- Proxy info -> o -> b -> IO ()+ o -> b -> IO ()+ default attrSet :: -- Make sure that a non-default method+ -- implementation is provided if AttrSet+ -- is set.+ CheckNotElem 'AttrSet (AttrAllowedOps info)+ (SetNotProvidedError info) =>+ o -> b -> IO ()+ attrSet = undefined+ -- | Set the value of the given attribute to @NULL@ (for nullable -- attributes). attrClear :: AttrBaseTypeConstraint info o =>- Proxy info -> o -> IO ()- -- | Build a `GValue` representing the attribute.+ o -> IO ()+ default attrClear :: -- Make sure that a non-default method+ -- implementation is provided if AttrClear+ -- is set.+ CheckNotElem 'AttrClear (AttrAllowedOps info)+ (ClearNotProvidedError info) =>+ o -> IO ()+ attrClear = undefined++ -- | Build a `Data.GI.Base.GValue.GValue` representing the attribute. attrConstruct :: (AttrBaseTypeConstraint info o, AttrSetTypeConstraint info b) =>- Proxy info -> b -> IO (GValueConstruct o)+ b -> IO (GValueConstruct o)+ default attrConstruct :: -- Make sure that a non-default method+ -- implementation is provided if AttrConstruct+ -- is set.+ CheckNotElem 'AttrConstruct (AttrAllowedOps info)+ (ConstructNotProvidedError info) =>+ b -> IO (GValueConstruct o)+ attrConstruct = undefined --- | Result of checking whether an op is allowed on an attribute.-data OpAllowed tag attrName definingType useType =- OpIsAllowed-#if !MIN_VERSION_base(4,9,0)- | AttrOpNotAllowed Symbol tag Symbol definingType Symbol attrName-#endif+ -- | Allocate memory as necessary to generate a settable type from+ -- the transfer type. This is useful for types which needs+ -- allocations for marshalling from Haskell to C, this makes the+ -- allocation explicit.+ attrTransfer :: forall o b. (AttrBaseTypeConstraint info o,+ AttrTransferTypeConstraint info b) =>+ Proxy o -> b -> IO (AttrTransferType info)+ default attrTransfer :: forall o b. (AttrBaseTypeConstraint info o,+ AttrTransferTypeConstraint info b,+ b ~ AttrGetType info,+ b ~ AttrTransferType info) =>+ Proxy o -> b -> IO (AttrTransferType info)+ attrTransfer _ = return -#if MIN_VERSION_base(4,9,0)+ -- | Like `attrSet`, but it uses the same type as the getter. This+ -- is useful for some nullable types, for which the getter returns+ -- @Maybe a@, while the setter takes an @a@. `attrPut` will+ -- instead accept an @Maybe a@.+ attrPut :: AttrBaseTypeConstraint info o =>+ o -> AttrGetType info -> IO ()+ default attrPut :: -- Make sure that a non-default method+ -- implementation is provided if AttrSet+ -- is set.+ CheckNotElem 'AttrPut (AttrAllowedOps info)+ (PutNotProvidedError info) =>+ o -> AttrGetType info -> IO ()+ attrPut = undefined++ -- | Return some information about the overloaded attribute,+ -- useful for debugging. See `resolveAttr` for how to access this+ -- conveniently.+ dbgAttrInfo :: Maybe ResolvedSymbolInfo+ dbgAttrInfo = Nothing++-- | Pretty print a type, indicating the parent type that introduced+-- the attribute, if different. type family TypeOriginInfo definingType useType :: ErrorMessage where TypeOriginInfo definingType definingType = 'Text "‘" ':<>: 'ShowType definingType ':<>: 'Text "’"@@ -217,34 +326,93 @@ 'Text "‘" ':<>: 'ShowType useType ':<>: 'Text "’ (inherited from parent type ‘" ':<>: 'ShowType definingType ':<>: 'Text "’)"-#endif -- | Look in the given list to see if the given `AttrOp` is a member, -- if not return an error type.-type family AttrOpIsAllowed (tag :: AttrOpTag) (ops :: [AttrOpTag]) (label :: Symbol) (definingType :: *) (useType :: *) :: OpAllowed AttrOpTag Symbol * * where+type family AttrOpIsAllowed (tag :: AttrOpTag) (ops :: [AttrOpTag]) (label :: Symbol) (definingType :: Type) (useType :: Type) :: Constraint where AttrOpIsAllowed tag '[] label definingType useType =-#if !MIN_VERSION_base(4,9,0)- 'AttrOpNotAllowed "Error: operation " tag " not allowed for attribute " definingType "." label-#else TypeError ('Text "Attribute ‘" ':<>: 'Text label ':<>: 'Text "’ for type " ':<>: TypeOriginInfo definingType useType ':<>: 'Text " is not " ':<>: 'Text (AttrOpText tag) ':<>: 'Text ".")-#endif- AttrOpIsAllowed tag (tag ': ops) label definingType useType = 'OpIsAllowed+ AttrOpIsAllowed tag (tag ': ops) label definingType useType = () AttrOpIsAllowed tag (other ': ops) label definingType useType = AttrOpIsAllowed tag ops label definingType useType -- | Whether a given `AttrOpTag` is allowed on an attribute, given the -- info type.-type family AttrOpAllowed (tag :: AttrOpTag) (info :: *) (useType :: *) :: Constraint where+type family AttrOpAllowed (tag :: AttrOpTag) (info :: Type) (useType :: Type) :: Constraint where AttrOpAllowed tag info useType =- AttrOpIsAllowed tag (AttrAllowedOps info) (AttrLabel info) (AttrOrigin info) useType ~ 'OpIsAllowed+ AttrOpIsAllowed tag (AttrAllowedOps info) (AttrLabel info) (AttrOrigin info) useType +-- | Error to be raised when an operation is allowed, but an+-- implementation has not been provided.+type family OpNotProvidedError (info :: o) (op :: AttrOpTag) (methodName :: Symbol) :: ErrorMessage where+ OpNotProvidedError info op methodName =+ 'Text "The attribute ‘" ':<>: 'Text (AttrLabel info) ':<>:+ 'Text "’ for type ‘" ':<>:+ 'ShowType (AttrOrigin info) ':<>:+ 'Text "’ is declared as " ':<>:+ 'Text (AttrOpText op) ':<>:+ 'Text ", but no implementation of ‘" ':<>:+ 'Text methodName ':<>:+ 'Text "’ has been provided."+ ':$$: 'Text "Either provide an implementation of ‘" ':<>:+ 'Text methodName ':<>:+ 'Text "’ or remove ‘" ':<>:+ 'ShowType op ':<>:+ 'Text "’ from ‘AttrAllowedOps’."++-- | Error to be raised when AttrClear is allowed, but an+-- implementation has not been provided.+type family ClearNotProvidedError (info :: o) :: ErrorMessage where+ ClearNotProvidedError info = OpNotProvidedError info 'AttrClear "attrClear"++-- | Error to be raised when AttrGet is allowed, but an+-- implementation has not been provided.+type family GetNotProvidedError (info :: o) :: ErrorMessage where+ GetNotProvidedError info = OpNotProvidedError info 'AttrGet "attrGet"++-- | Error to be raised when AttrSet is allowed, but an+-- implementation has not been provided.+type family SetNotProvidedError (info :: o) :: ErrorMessage where+ SetNotProvidedError info = OpNotProvidedError info 'AttrSet "attrSet"++-- | Error to be raised when AttrSet is allowed, but an `attrPut`+-- implementation has not been provided.+type family PutNotProvidedError (info :: o) :: ErrorMessage where+ PutNotProvidedError info = OpNotProvidedError info 'AttrSet "attrPut"++-- | Error to be raised when AttrConstruct is allowed, but an+-- implementation has not been provided.+type family ConstructNotProvidedError (info :: o) :: ErrorMessage where+ ConstructNotProvidedError info = OpNotProvidedError info 'AttrConstruct "attrConstruct"++-- | Check if the given element is a member, and if so raise the given+-- error.+type family CheckNotElem (a :: k) (as :: [k]) (msg :: ErrorMessage) :: Constraint where+ CheckNotElem a '[] msg = ()+ CheckNotElem a (a ': rest) msg = TypeError msg+ CheckNotElem a (other ': rest) msg = CheckNotElem a rest msg+ -- | Possible operations on an attribute.-data AttrOpTag = AttrGet | AttrSet | AttrConstruct | AttrClear+data AttrOpTag = AttrGet+ -- ^ It is possible to read the value of the attribute+ -- with `get`.+ | AttrSet+ -- ^ It is possible to write the value of the attribute+ -- with `set`.+ | AttrConstruct+ -- ^ It is possible to set the value of the attribute+ -- in `Data.GI.Base.Constructible.new`.+ | AttrClear+ -- ^ It is possible to clear the value of the+ -- (nullable) attribute with `clear`.+ | AttrPut+ -- ^ It is possible to set a value of the same type as+ -- the one returned by `get`.+ deriving (Eq, Ord, Enum, Bounded, Show) -#if MIN_VERSION_base(4,9,0) -- | A user friendly description of the `AttrOpTag`, useful when -- printing type errors. type family AttrOpText (tag :: AttrOpTag) :: Symbol where@@ -252,7 +420,7 @@ AttrOpText 'AttrSet = "settable" AttrOpText 'AttrConstruct = "constructible" AttrOpText 'AttrClear = "nullable"-#endif+ AttrOpText 'AttrPut = "puttable" -- | Constraint on a @obj@\/@attr@ pair so that `set` works on values -- of type @value@.@@ -263,8 +431,8 @@ AttrOpAllowed 'AttrSet info obj, (AttrSetTypeConstraint info) value) --- | Constraint on a @obj@\/@value@ pair so that `new` works on values--- of type @@value@.+-- | Constraint on a @obj@\/@value@ pair so that+-- `Data.GI.Base.Constructible.new` works on values of type @value@. type AttrConstructC info obj attr value = (HasAttributeList obj, info ~ ResolveAttribute attr obj, AttrInfo info,@@ -274,7 +442,7 @@ -- | Constructors for the different operations allowed on an attribute. data AttrOp obj (tag :: AttrOpTag) where- -- Assign a value to an attribute+ -- | Assign a value to an attribute (:=) :: (HasAttributeList obj, info ~ ResolveAttribute attr obj, AttrInfo info,@@ -282,7 +450,7 @@ AttrOpAllowed tag info obj, (AttrSetTypeConstraint info) b) => AttrLabelProxy (attr :: Symbol) -> b -> AttrOp obj tag- -- Assign the result of an IO action to an attribute+ -- | Assign the result of an IO action to an attribute (:=>) :: (HasAttributeList obj, info ~ ResolveAttribute attr obj, AttrInfo info,@@ -290,7 +458,7 @@ AttrOpAllowed tag info obj, (AttrSetTypeConstraint info) b) => AttrLabelProxy (attr :: Symbol) -> IO b -> AttrOp obj tag- -- Apply an update function to an attribute+ -- | Apply an update function to an attribute (:~) :: (HasAttributeList obj, info ~ ResolveAttribute attr obj, AttrInfo info,@@ -301,7 +469,7 @@ (AttrSetTypeConstraint info) b, a ~ (AttrGetType info)) => AttrLabelProxy (attr :: Symbol) -> (a -> b) -> AttrOp obj tag- -- Apply an IO update function to an attribute+ -- | Apply an IO update function to an attribute (:~>) :: (HasAttributeList obj, info ~ ResolveAttribute attr obj, AttrInfo info,@@ -312,46 +480,144 @@ (AttrSetTypeConstraint info) b, a ~ (AttrGetType info)) => AttrLabelProxy (attr :: Symbol) -> (a -> IO b) -> AttrOp obj tag- -- Assign a value to an attribute with the object as an argument- (::=) :: (HasAttributeList obj,+ -- | Assign a value to an attribute, allocating any necessary+ -- memory for representing the Haskell value as a C value. Note+ -- that it is the responsibility of the caller to make sure that+ -- the memory is freed when no longer used, otherwise there will+ -- be a memory leak. In the majority of cases you probably want to+ -- use ':=' instead, which has no potential memory leaks (at the+ -- cost of sometimes requiring some explicit Haskell -> C+ -- marshalling).+ (:&=) :: (HasAttributeList obj, info ~ ResolveAttribute attr obj, AttrInfo info, AttrBaseTypeConstraint info obj,- tag ~ 'AttrSet, AttrOpAllowed tag info obj,- (AttrSetTypeConstraint info) b) =>- AttrLabelProxy (attr :: Symbol) -> (obj -> b) -> AttrOp obj tag- -- Apply an update function to an attribute with the object as an- -- argument- (::~) :: (HasAttributeList obj,+ (AttrTransferTypeConstraint info) b,+ AttrSetTypeConstraint info (AttrTransferType info)) =>+ AttrLabelProxy (attr :: Symbol) -> b -> AttrOp obj tag+ -- | Bind a property to the given `DynVal`, so that the property+ -- is changed whenever the `DynVal` is. This requires the implicit+ -- param @?_haskell_gi_modelProxy@, of type @`ModelProxy` model@ to be set.+ (:!<~) :: (HasAttributeList obj, info ~ ResolveAttribute attr obj, AttrInfo info, AttrBaseTypeConstraint info obj,- tag ~ 'AttrSet,- AttrOpAllowed 'AttrSet info obj,- AttrOpAllowed 'AttrGet info obj,+ AttrOpAllowed tag info obj, (AttrSetTypeConstraint info) b,- a ~ (AttrGetType info)) =>- AttrLabelProxy (attr :: Symbol) -> (obj -> a -> b) -> AttrOp obj tag+ ?_haskell_gi_modelProxy :: ModelProxy model+ ) =>+ AttrLabelProxy (attr :: Symbol) -> DynVal model b -> AttrOp obj ta+ -- | Bind a property to the given `DynVal`, so that the property+ -- is changed whenever the `DynVal` is. This requires the implicit+ -- param @?_haskell_gi_modelProxy@, of type @`ModelProxy` model@+ -- to be set. This will only actually set the property whenever+ -- the `DynVal` changes if the new value of the `DynVal` is+ -- different from the actual value of the property. If you want to+ -- set the property without checking equality you can use `:!<~`+ -- instead.+ (:<~) :: (HasAttributeList obj,+ info ~ ResolveAttribute attr obj,+ AttrInfo info,+ AttrBaseTypeConstraint info obj,+ AttrOpAllowed tag info obj,+ (AttrSetTypeConstraint info) b,+ AttrOpAllowed 'AttrGet info obj,+ EqMaybe b (AttrGetType info),+ ?_haskell_gi_modelProxy :: ModelProxy model+ ) =>+ AttrLabelProxy (attr :: Symbol) -> DynVal model b -> AttrOp obj tag+ -- | Given an AttrLabelProxy, bind the given attribute to the+ -- corresponding field in the model proxy (if there's one), so+ -- that changes in the attribute are reflected back into changes+ -- of the model.+ Bind :: (HasAttributeList obj,+ GObject obj,+ info ~ ResolveAttribute propName obj,+ AttrInfo info,+ KnownSymbol (AttrLabel info),+ AttrBaseTypeConstraint info obj,+ AttrOpAllowed tag info obj,+ AttrOpAllowed 'AttrPut info obj,+ ?_haskell_gi_modelProxy :: ModelProxy model,+ outType ~ AttrGetType info,+ (AttrSetTypeConstraint info) outType,+ components ~ Components fieldName,+ PathFieldAccess components model outType,+ KnownSymbol fieldName,+ Eq outType+ ) =>+ AttrLabelProxy (propName :: Symbol) ->+ AttrLabelProxy (fieldName :: Symbol) ->+ AttrOp obj tag + -- | Connect the given signal to a signal handler.+ On :: (GObject obj, SignalInfo info) =>+ SignalProxy obj info+ -> ((?self :: obj) => HaskellCallbackType info)+ -> AttrOp obj tag+ -- | Like 'On', but connect after the default signal.+ After :: (GObject obj, SignalInfo info) =>+ SignalProxy obj info+ -> ((?self :: obj) => HaskellCallbackType info)+ -> AttrOp obj tag++class EqMaybe a b where+ eqMaybe :: a -> b -> Bool++instance Eq a => EqMaybe a a where+ eqMaybe x y = x == y++instance Eq a => EqMaybe a (Maybe a) where+ eqMaybe _ Nothing = False+ eqMaybe x (Just y) = x == y+ -- | Set a number of properties for some object. set :: forall o m. MonadIO m => o -> [AttrOp o 'AttrSet] -> m () set obj = liftIO . mapM_ app where- resolve :: AttrLabelProxy attr -> Proxy (ResolveAttribute attr o)- resolve _ = Proxy- app :: AttrOp o 'AttrSet -> IO ()- app (attr := x) = attrSet (resolve attr) obj x- app (attr :=> x) = x >>= attrSet (resolve attr) obj- app (attr :~ f) = attrGet (resolve attr) obj >>=- \v -> attrSet (resolve attr) obj (f v)- app (attr :~> f) = attrGet (resolve attr) obj >>= f >>=- attrSet (resolve attr) obj- app (attr ::= f) = attrSet (resolve attr) obj (f obj)- app (attr ::~ f) = attrGet (resolve attr) obj >>=- \v -> attrSet (resolve attr) obj (f obj v)+ app ((_attr :: AttrLabelProxy label) := x) =+ attrSet @(ResolveAttribute label o) obj x + app ((_attr :: AttrLabelProxy label) :=> x) =+ x >>= attrSet @(ResolveAttribute label o) obj++ app ((_attr :: AttrLabelProxy label) :~ f) =+ attrGet @(ResolveAttribute label o) obj >>=+ \v -> attrSet @(ResolveAttribute label o) obj (f v)++ app ((_attr :: AttrLabelProxy label) :~> f) =+ attrGet @(ResolveAttribute label o) obj >>= f >>=+ attrSet @(ResolveAttribute label o) obj++ app ((_attr :: AttrLabelProxy label) :&= x) =+ attrTransfer @(ResolveAttribute label o) (Proxy @o) x >>=+ attrSet @(ResolveAttribute label o) obj++ app ((_attr :: AttrLabelProxy label) :!<~ dv) = do+ model <- modelProxyCurrentValue ?_haskell_gi_modelProxy+ attrSet @(ResolveAttribute label o) obj (dvRead dv model)+ modelProxyRegisterHandler ?_haskell_gi_modelProxy (dvKeys dv) $ \modifiedModel ->+ attrSet @(ResolveAttribute label o) obj (dvRead dv modifiedModel)++ app ((_attr :: AttrLabelProxy label) :<~ dv) = do+ model <- modelProxyCurrentValue ?_haskell_gi_modelProxy+ currentValue <- attrGet @(ResolveAttribute label o) obj+ let newValue = dvRead dv model+ when (not $ newValue `eqMaybe` currentValue) $ do+ attrSet @(ResolveAttribute label o) obj newValue+ modelProxyRegisterHandler ?_haskell_gi_modelProxy (dvKeys dv) $ \modifiedModel -> do+ current <- attrGet @(ResolveAttribute label o) obj+ let modifiedValue = dvRead dv modifiedModel+ when (not $ modifiedValue `eqMaybe` current) $+ attrSet @(ResolveAttribute label o) obj modifiedValue++ app (Bind pattr fattr) = bindPropToField (Proxy @'AttrSet) obj pattr fattr++ app (On signal callback) = void $ on obj signal callback+ app (After signal callback) = void $ after obj signal callback+ -- | Constraints on a @obj@\/@attr@ pair so `get` is possible, -- producing a value of type @result@. type AttrGetC info obj attr result = (HasAttributeList obj,@@ -365,7 +631,7 @@ get :: forall info attr obj result m. (AttrGetC info obj attr result, MonadIO m) => obj -> AttrLabelProxy (attr :: Symbol) -> m result-get o _ = liftIO $ attrGet (Proxy :: Proxy info) o+get o _ = liftIO $ attrGet @info o -- | Constraint on a @obj@\/@attr@ pair so that `clear` is allowed. type AttrClearC info obj attr = (HasAttributeList obj,@@ -378,4 +644,68 @@ clear :: forall info attr obj m. (AttrClearC info obj attr, MonadIO m) => obj -> AttrLabelProxy (attr :: Symbol) -> m ()-clear o _ = liftIO $ attrClear (Proxy :: Proxy info) o+clear o _ = liftIO $ attrClear @info o++-- | Return the fully qualified attribute name that a given overloaded+-- attribute resolves to (mostly useful for debugging).+--+-- > resolveAttr #sensitive button+resolveAttr :: forall info attr obj.+ (HasAttributeList obj, info ~ ResolveAttribute attr obj,+ AttrInfo info) =>+ obj -> AttrLabelProxy (attr :: Symbol) -> Maybe ResolvedSymbolInfo+resolveAttr _o _p = dbgAttrInfo @info++-- | Create a binding between the given property of an object and a+-- field in the model.+bindPropToField :: forall o info prop field model outType tag components.+ (HasAttributeList o,+ GObject o,+ info ~ ResolveAttribute prop o,+ AttrInfo info,+ KnownSymbol (AttrLabel info),+ AttrBaseTypeConstraint info o,+ AttrOpAllowed tag info o,+ AttrOpAllowed 'AttrPut info o,+ ?_haskell_gi_modelProxy :: ModelProxy model,+ outType ~ AttrGetType info,+ (AttrSetTypeConstraint info) outType,+ components ~ Components field,+ PathFieldAccess components model outType,+ KnownSymbol field,+ Eq outType+ ) =>+ Proxy tag -> o -> AttrLabelProxy (prop :: Symbol) ->+ AttrLabelProxy (field :: Symbol) -> IO ()+bindPropToField _ obj _ _ = do+ model <- modelProxyCurrentValue ?_haskell_gi_modelProxy++ -- Set the property to the current value in the model.+ currentPropValue <- attrGet @(ResolveAttribute prop o) obj+ let (lens, components) = pathFieldAccess (Proxy @components)+ (Proxy @model)+ key = DVKeyDirect components+ currentModelValue = O.view lens model+ when (currentModelValue /= currentPropValue) $+ attrPut @(ResolveAttribute prop o) obj currentModelValue++ -- Set the property whenever the model changes.+ modelProxyRegisterHandler ?_haskell_gi_modelProxy key $ \modifiedModel -> do+ let newVal = O.view lens modifiedModel+ oldVal <- attrGet @(ResolveAttribute prop o) obj+ when (newVal /= oldVal) $+ attrPut @(ResolveAttribute prop o) obj newVal++ -- Change the model whenever the property changes.+ let handler = \_parent _psec -> do+ newVal <- attrGet @(ResolveAttribute prop o) obj+ let doUpdate curModel =+ let oldVal = O.view lens curModel+ in if newVal == oldVal+ then Nothing+ else Just $ O.set lens newVal curModel+ modelProxyUpdate ?_haskell_gi_modelProxy components doUpdate+ propName = T.pack $ symbolVal (Proxy @(AttrLabel (ResolveAttribute prop o)))++ void $ connectGObjectNotify obj handler SignalConnectBefore (Just propName)+
Data/GI/Base/BasicConversions.hsc view
@@ -89,7 +89,6 @@ import Data.GI.Base.BasicTypes import Data.GI.Base.CallStack (HasCallStack)-import Data.GI.Base.GHashTable (GEqualFunc, GHashFunc) import Data.GI.Base.ManagedPtr (copyBoxedPtr) import Data.GI.Base.Utils (allocBytes, callocBytes, memcpy, freeMem, checkUnexpectedReturnNULL)@@ -115,11 +114,11 @@ foreign import ccall "g_list_prepend" g_list_prepend :: Ptr (GList (Ptr a)) -> Ptr a -> IO (Ptr (GList (Ptr a))) --- Given a Haskell list of items, construct a GList with those values.+-- | Given a Haskell list of items, construct a GList with those values. packGList :: [Ptr a] -> IO (Ptr (GList (Ptr a))) packGList l = foldM g_list_prepend nullPtr $ reverse l --- Given a GSList construct the corresponding Haskell list.+-- | Given a GSList construct the corresponding Haskell list. unpackGList :: Ptr (GList (Ptr a)) -> IO [Ptr a] unpackGList gsl | gsl == nullPtr = return []@@ -134,11 +133,11 @@ foreign import ccall "g_slist_prepend" g_slist_prepend :: Ptr (GSList (Ptr a)) -> Ptr a -> IO (Ptr (GSList (Ptr a))) --- Given a Haskell list of items, construct a GSList with those values.+-- | Given a Haskell list of items, construct a GSList with those values. packGSList :: [Ptr a] -> IO (Ptr (GSList (Ptr a))) packGSList l = foldM g_slist_prepend nullPtr $ reverse l --- Given a GSList construct the corresponding Haskell list.+-- | Given a GSList construct the corresponding Haskell list. unpackGSList :: Ptr (GSList (Ptr a)) -> IO [Ptr a] unpackGSList gsl = unpackGList (castPtr gsl) @@ -169,7 +168,7 @@ dataPtr <- peek (castPtr array :: Ptr (Ptr a)) nitems <- peek (array `plusPtr` sizeOf dataPtr) go dataPtr nitems- where go :: Ptr a -> Int -> IO [a]+ where go :: Ptr a -> CUInt -> IO [a] go _ 0 = return [] go ptr n = do x <- peek ptr@@ -201,7 +200,7 @@ dataPtr <- peek (castPtr array :: Ptr (Ptr (Ptr a))) nitems <- peek (array `plusPtr` sizeOf dataPtr) go dataPtr nitems- where go :: Ptr (Ptr a) -> Int -> IO [Ptr a]+ where go :: Ptr (Ptr a) -> CUInt -> IO [Ptr a] go _ 0 = return [] go ptr n = do x <- peek ptr@@ -533,6 +532,9 @@ _ <- f ptr mapZeroTerminatedCArray f (dataPtr `plusPtr` sizeOf ptr) +-- | Given a set of pointers to blocks of memory of the specified+-- size, copy the contents of these blocks to a freshly-allocated+-- (with `allocBytes`) continuous area of memory. packBlockArray :: Int -> [Ptr a] -> IO (Ptr a) packBlockArray size items = do let nitems = length items@@ -555,7 +557,7 @@ buf <- g_memdup ptr (fromIntegral size) (buf :) <$> go size (n-1) (ptr `plusPtr` size) -unpackBoxedArrayWithLength :: forall a b. (Integral a, BoxedObject b) =>+unpackBoxedArrayWithLength :: forall a b. (Integral a, GBoxed b) => Int -> a -> Ptr b -> IO [Ptr b] unpackBoxedArrayWithLength size n ptr = go size (fromIntegral n) ptr where go :: Int -> Int -> Ptr b -> IO [Ptr b]
− Data/GI/Base/BasicTypes.hs
@@ -1,199 +0,0 @@-{-# LANGUAGE ConstraintKinds, FlexibleContexts, FlexibleInstances,- DeriveDataTypeable, TypeFamilies, ScopedTypeVariables #-}-#if !MIN_VERSION_base(4,8,0)-{-# LANGUAGE OverlappingInstances #-}-#endif-#if MIN_VERSION_base(4,9,0)-{-# LANGUAGE DataKinds, TypeOperators, UndecidableInstances #-}-#endif--- | Basic types used in the bindings.-module Data.GI.Base.BasicTypes- (- -- * GType related- module Data.GI.Base.GType -- reexported for convenience-- -- * Memory management- , ManagedPtr(..)- , ManagedPtrNewtype- , BoxedObject(..)- , BoxedEnum(..)- , BoxedFlags(..)- , GObject(..)- , WrappedPtr(..)- , UnexpectedNullPointerReturn(..)- , NullToNothing(..)-- -- * Basic GLib \/ GObject types- , GVariant(..)- , GParamSpec(..)-- , GArray(..)- , GPtrArray(..)- , GByteArray(..)- , GHashTable(..)- , GList(..)- , g_list_free- , GSList(..)- , g_slist_free-- , IsGFlag-- , PtrWrapped(..)- , GDestroyNotify- ) where--#if !MIN_VERSION_base(4,8,0)-import Control.Applicative ((<$>))-#endif-import Control.Exception (Exception, catch)-import Control.Monad.IO.Class (MonadIO(..))-import Data.Coerce (Coercible)-import Data.IORef (IORef)-import Data.Proxy (Proxy)-import qualified Data.Text as T-import Data.Typeable (Typeable)-import Foreign.Ptr (Ptr, FunPtr)-import Foreign.ForeignPtr (ForeignPtr)--import Data.GI.Base.CallStack (CallStack)-import Data.GI.Base.GType---- | Thin wrapper over `ForeignPtr`, supporting the extra notion of--- `disowning`, that is, not running the finalizers associated with--- the foreign ptr.-data ManagedPtr a = ManagedPtr {- managedForeignPtr :: ForeignPtr a- , managedPtrIsDisowned :: IORef (Maybe CallStack)- -- ^ When disowned, the `CallStack` for the disowning call.- }---- | A constraint ensuring that the given type is coercible to a--- ManagedPtr. It will hold for newtypes of the form------ > newtype Foo = Foo (ManagedPtr Foo)------ which is the typical shape of wrapped 'GObject's.-type ManagedPtrNewtype a = Coercible a (ManagedPtr ())--- Notice that the Coercible here is to ManagedPtr (), instead of--- "ManagedPtr a", which would be the most natural thing. Both are--- representationally equivalent, so this is not a big deal. This is--- to work around a problem in ghc 7.10:--- https://ghc.haskell.org/trac/ghc/ticket/10715---- | Wrapped boxed structures, identified by their `GType`.-class ManagedPtrNewtype a => BoxedObject a where- boxedType :: a -> IO GType -- This should not use the value of its- -- argument.---- | Enums with an associated `GType`.-class BoxedEnum a where- boxedEnumType :: a -> IO GType---- | Flags with an associated `GType`.-class BoxedFlags a where- boxedFlagsType :: Proxy a -> IO GType---- | Pointers to structs/unions without an associated `GType`.-class ManagedPtrNewtype a => WrappedPtr a where- -- | Allocate a zero-initialized block of memory for the given type.- wrappedPtrCalloc :: IO (Ptr a)- -- | Make a copy of the given `WrappedPtr`.- wrappedPtrCopy :: a -> IO a- -- | A pointer to a function for freeing the given pointer, or- -- `Nothing` is the memory associated to the pointer does not need- -- to be freed.- wrappedPtrFree :: Maybe (FunPtr (Ptr a -> IO ()))---- | A wrapped `GObject`.-class ManagedPtrNewtype a => GObject a where- -- | The `GType` for this object.- gobjectType :: a -> IO GType---- | A common omission in the introspection data is missing (nullable)--- annotations for return types, when they clearly are nullable. (A--- common idiom is "Returns: valid value, or %NULL if something went--- wrong.")------ Haskell wrappers will raise this exception if the return value is--- an unexpected `Foreign.Ptr.nullPtr`.-data UnexpectedNullPointerReturn =- UnexpectedNullPointerReturn { nullPtrErrorMsg :: T.Text }- deriving (Typeable)--instance Show UnexpectedNullPointerReturn where- show r = T.unpack (nullPtrErrorMsg r)--instance Exception UnexpectedNullPointerReturn--type family UnMaybe a :: * where- UnMaybe (Maybe a) = a- UnMaybe a = a--{-# DEPRECATED nullToNothing ["This will be removed in future versions of haskell-gi.", "If you know of wrong introspection data in a binding please report it as an issue at", "http://github.com/haskell-gi/haskell-gi", "so that it can be fixed."] #-}-class NullToNothing a where- -- | Some functions are not marked as having a nullable return type- -- in the introspection data. The result is that they currently do- -- not return a Maybe type. This functions lets you work around this- -- in a way that will not break when the introspection data is fixed.- --- -- When you want to call a `someHaskellGIFunction` that may return null- -- wrap the call like this.- --- -- > nullToNothing (someHaskellGIFunction x y)- --- -- The result will be a Maybe type even if the introspection data has- -- not been fixed for `someHaskellGIFunction` yet.- nullToNothing :: MonadIO m => IO a -> m (Maybe (UnMaybe a))--instance-#if MIN_VERSION_base(4,8,0)- {-# OVERLAPPABLE #-}-#endif- a ~ UnMaybe a => NullToNothing a where- nullToNothing f = liftIO $- (Just <$> f) `catch` (\(_::UnexpectedNullPointerReturn) -> return Nothing)--instance NullToNothing (Maybe a) where- nullToNothing = liftIO---- | A <https://developer.gnome.org/glib/stable/glib-GVariant.html GVariant>. See "Data.GI.Base.GVariant" for further methods.-newtype GVariant = GVariant (ManagedPtr GVariant)---- | A <https://developer.gnome.org/gobject/stable/gobject-GParamSpec.html GParamSpec>. See "Data.GI.Base.GParamSpec" for further methods.-newtype GParamSpec = GParamSpec (ManagedPtr GParamSpec)---- | An enum usable as a flag for a function.-class Enum a => IsGFlag a---- | A <https://developer.gnome.org/glib/stable/glib-Arrays.html GArray>. Marshalling for this type is done in "Data.GI.Base.BasicConversions", it is mapped to a list on the Haskell side.-data GArray a = GArray (Ptr (GArray a))---- | A <https://developer.gnome.org/glib/stable/glib-Pointer-Arrays.html GPtrArray>. Marshalling for this type is done in "Data.GI.Base.BasicConversions", it is mapped to a list on the Haskell side.-data GPtrArray a = GPtrArray (Ptr (GPtrArray a))---- | A <https://developer.gnome.org/glib/stable/glib-Byte-Arrays.html GByteArray>. Marshalling for this type is done in "Data.GI.Base.BasicConversions", it is packed to a 'Data.ByteString.ByteString' on the Haskell side.-data GByteArray = GByteArray (Ptr GByteArray)---- | A <https://developer.gnome.org/glib/stable/glib-Hash-Tables.html GHashTable>. It is mapped to a 'Data.Map.Map' on the Haskell side.-data GHashTable a b = GHashTable (Ptr (GHashTable a b))---- | A <https://developer.gnome.org/glib/stable/glib-Doubly-Linked-Lists.html GList>, mapped to a list on the Haskell side. Marshalling is done in "Data.GI.Base.BasicConversions".-data GList a = GList (Ptr (GList a))---- | A <https://developer.gnome.org/glib/stable/glib-Singly-Linked-Lists.html GSList>, mapped to a list on the Haskell side. Marshalling is done in "Data.GI.Base.BasicConversions".-data GSList a = GSList (Ptr (GSList a))---- | Some APIs, such as `GHashTable`, pass around scalar types--- wrapped into a pointer. We encode such a type as follows.-newtype PtrWrapped a = PtrWrapped {unwrapPtr :: Ptr a}---- | Destroy the memory associated with a given pointer.-type GDestroyNotify a = FunPtr (Ptr a -> IO ())---- | Free the given 'GList'.-foreign import ccall "g_list_free" g_list_free ::- Ptr (GList a) -> IO ()---- | Free the given 'GSList'.-foreign import ccall "g_slist_free" g_slist_free ::- Ptr (GSList a) -> IO ()
+ Data/GI/Base/BasicTypes.hsc view
@@ -0,0 +1,221 @@+{-# LANGUAGE ConstraintKinds, FlexibleContexts, FlexibleInstances,+ TypeFamilies, ScopedTypeVariables,+ MultiParamTypeClasses, DataKinds, TypeOperators, UndecidableInstances,+ AllowAmbiguousTypes #-}++-- | Basic types used in the bindings.+module Data.GI.Base.BasicTypes+ (+ -- * Memory management+ ManagedPtr(..)+ , ManagedPtrNewtype(..)+ , BoxedPtr(..)+ , CallocPtr(..)+ , UnexpectedNullPointerReturn(..)++ -- * Basic GLib \/ GObject types+ , TypedObject(..)+ , GObject+ , GType(..)+ , CGType+ , gtypeName+ , GVariant(..)+ , GBoxed+ , BoxedEnum+ , BoxedFlags+ , GParamSpec(..)+ , noGParamSpec++ , GArray(..)+ , GPtrArray(..)+ , GByteArray(..)+ , GHashTable(..)+ , GList(..)+ , g_list_free+ , GSList(..)+ , g_slist_free++ , IsGFlag++ , PtrWrapped(..)+ , GDestroyNotify++ , GHashFunc+ , GEqualFunc+ ) where++import Control.Exception (Exception)++import Data.Coerce (coerce, Coercible)+import Data.IORef (IORef)+import qualified Data.Text as T+import Data.Int+import Data.Word++import Foreign.C (CString, peekCString)+import Foreign.Ptr (Ptr, FunPtr)+import Foreign.ForeignPtr (ForeignPtr)++import {-# SOURCE #-} Data.GI.Base.Overloading (HasParentTypes)+import Data.GI.Base.CallStack (CallStack)++#include <glib-object.h>++-- | Thin wrapper over `ForeignPtr`, supporting the extra notion of+-- `disowning`, that is, not running the finalizers associated with+-- the foreign ptr.+data ManagedPtr a = ManagedPtr {+ managedForeignPtr :: ForeignPtr a+ , managedPtrAllocCallStack :: Maybe CallStack+ -- ^ `CallStack` for the call that created the pointer.+ , managedPtrIsDisowned :: IORef (Maybe CallStack)+ -- ^ When disowned, the `CallStack` for the disowning call.+ }++-- | Two 'ManagedPtr's are equal if they wrap the same underlying+-- C 'Ptr'.+instance Eq (ManagedPtr a) where+ a == b = managedForeignPtr a == managedForeignPtr b++-- | A constraint ensuring that the given type is a newtype over a+-- `ManagedPtr`.+class Coercible a (ManagedPtr ()) => ManagedPtrNewtype a where+ toManagedPtr :: a -> ManagedPtr a++-- | A default instance for `IsManagedPtr` for newtypes over `ManagedPtr`.+instance {-# OVERLAPPABLE #-} Coercible a (ManagedPtr ()) => ManagedPtrNewtype a where+ toManagedPtr = coerce+-- Notice that the Coercible here above to ManagedPtr (), instead of+-- "ManagedPtr a", which would be the most natural thing. Both are+-- representationally equivalent, so this is not a big deal. This is+-- to work around a problem in ghc 7.10:+-- https://ghc.haskell.org/trac/ghc/ticket/10715+--+-- Additionally, a simpler approach would be to simply do+--+-- > type IsManagedPtr a = Coercible a (ManagedPtr ())+--+-- but this requires the constructor of the datatype to be in scope,+-- which is cumbersome (for instance, one often wants to call `castTo`+-- on the results of `Gtk.builderGetObject`, which is a `GObject`,+-- whose constructor is not necessarily in scope when using `GI.Gtk`).+--+-- When we make the bindings we will always add explicit instances,+-- which cannot be hidden, avoiding the issue. We keep the default+-- instance for convenience when writing new object types.++-- | Pointers to chunks of memory which we know how to copy and+-- release.+class ManagedPtrNewtype a => BoxedPtr a where+ -- | Make a copy of the given `BoxedPtr`.+ boxedPtrCopy :: a -> IO a+ -- | A pointer to a function for freeing the given pointer.+ boxedPtrFree :: a -> IO ()++-- | A ptr to a memory block which we know how to allocate and fill+-- with zero.+class BoxedPtr a => CallocPtr a where+ -- | Allocate a zero-initialized block of memory for the given type.+ boxedPtrCalloc :: IO (Ptr a)++-- | A wrapped object that has an associated GLib type. This does not+-- necessarily descend from `GObject`, that constraint is implemented+-- by `GObject` below.+class HasParentTypes a => TypedObject a where+ -- | The `GType` for this object.+ glibType :: IO GType++-- | Chunks of memory whose allocation/deallocation info has been+-- registered with the GLib type system.+class (ManagedPtrNewtype a, TypedObject a) => GBoxed a++-- | A wrapped `GObject`, or any other type that descends from it.+class (ManagedPtrNewtype a, TypedObject a) => GObject a++-- | Enums with an associated `GType`.+class TypedObject a => BoxedEnum a++-- | Flags with an associated `GType`.+class TypedObject a => BoxedFlags a++-- | A type identifier in the GLib type system. This is the low-level+-- type associated with the representation in memory, when using this+-- on the Haskell side use `GType` below.+type CGType = #type GType++-- | A newtype for use on the Haskell side.+newtype GType = GType {gtypeToCGType :: CGType}+ deriving (Eq, Show)++foreign import ccall "g_type_name" g_type_name :: GType -> IO CString++-- | Get the name assigned to the given `GType`.+gtypeName :: GType -> IO String+gtypeName gtype = g_type_name gtype >>= peekCString++-- | A common omission in the introspection data is missing (nullable)+-- annotations for return types, when they clearly are nullable. (A+-- common idiom is "Returns: valid value, or %NULL if something went+-- wrong.")+--+-- Haskell wrappers will raise this exception if the return value is+-- an unexpected `Foreign.Ptr.nullPtr`.+data UnexpectedNullPointerReturn =+ UnexpectedNullPointerReturn { nullPtrErrorMsg :: T.Text }++instance Show UnexpectedNullPointerReturn where+ show r = T.unpack (nullPtrErrorMsg r)++instance Exception UnexpectedNullPointerReturn++-- | A <https://docs.gtk.org/glib/struct.Variant.html GVariant>. See "Data.GI.Base.GVariant" for further methods.+newtype GVariant = GVariant (ManagedPtr GVariant)++-- | A <https://docs.gtk.org/gobject/class.ParamSpec.html GParamSpec>. See "Data.GI.Base.GParamSpec" for further methods.+newtype GParamSpec = GParamSpec (ManagedPtr GParamSpec)++-- | A convenient synonym for @Nothing :: Maybe GParamSpec@.+noGParamSpec :: Maybe GParamSpec+noGParamSpec = Nothing++-- | An enum usable as a flag for a function.+class Enum a => IsGFlag a++-- | A <https://docs.gtk.org/glib/struct.Array.html GArray>. Marshalling for this type is done in "Data.GI.Base.BasicConversions", it is mapped to a list on the Haskell side.+data GArray a = GArray (Ptr (GArray a))++-- | A <https://docs.gtk.org/glib/struct.PtrArray.html GPtrArray>. Marshalling for this type is done in "Data.GI.Base.BasicConversions", it is mapped to a list on the Haskell side.+data GPtrArray a = GPtrArray (Ptr (GPtrArray a))++-- | A <https://docs.gtk.org/glib/struct.ByteArray.html GByteArray>. Marshalling for this type is done in "Data.GI.Base.BasicConversions", it is packed to a 'Data.ByteString.ByteString' on the Haskell side.+data GByteArray = GByteArray (Ptr GByteArray)++-- | A <https://docs.gtk.org/glib/struct.HashTable.html GHashTable>. It is mapped to a 'Data.Map.Map' on the Haskell side.+data GHashTable a b = GHashTable (Ptr (GHashTable a b))++-- | A <https://docs.gtk.org/glib/struct.List.html GList>, mapped to a list on the Haskell side. Marshalling is done in "Data.GI.Base.BasicConversions".+data GList a = GList (Ptr (GList a))++-- | A <https://docs.gtk.org/glib/struct.SList.html GSList>, mapped to a list on the Haskell side. Marshalling is done in "Data.GI.Base.BasicConversions".+data GSList a = GSList (Ptr (GSList a))++-- | Some APIs, such as `GHashTable`, pass around scalar types+-- wrapped into a pointer. We encode such a type as follows.+newtype PtrWrapped a = PtrWrapped {unwrapPtr :: Ptr a}++-- | Destroy the memory pointed to by a given pointer type.+type GDestroyNotify ptr = FunPtr (ptr -> IO ())++-- | Free the given 'GList'.+foreign import ccall "g_list_free" g_list_free ::+ Ptr (GList a) -> IO ()++-- | Free the given 'GSList'.+foreign import ccall "g_slist_free" g_slist_free ::+ Ptr (GSList a) -> IO ()++-- | A pointer to a hashing function on the C side.+type GHashFunc a = FunPtr (PtrWrapped a -> IO #{type guint})++-- | A pointer to an equality checking function on the C side.+type GEqualFunc a = FunPtr (PtrWrapped a -> PtrWrapped a -> IO #{type gboolean})
− Data/GI/Base/Closure.hs
@@ -1,41 +0,0 @@--- Some helper functions to create closures.-module Data.GI.Base.Closure- ( Closure(..)- , newCClosure- , noClosure- ) where--import Foreign--import Data.GI.Base.BasicTypes-import Data.GI.Base.ManagedPtr (wrapBoxed)-import Data.GI.Base.Utils (safeFreeFunPtrPtr)--newtype Closure = Closure (ManagedPtr Closure)--noClosure :: Maybe Closure-noClosure = Nothing--foreign import ccall "g_closure_get_type" c_g_closure_get_type ::- IO GType--instance BoxedObject Closure where- boxedType _ = c_g_closure_get_type---foreign import ccall "g_cclosure_new" g_cclosure_new- :: FunPtr a -> Ptr () -> FunPtr c -> IO (Ptr Closure)--foreign import ccall "g_closure_ref" g_closure_ref- :: Ptr Closure -> IO (Ptr Closure)--foreign import ccall "g_closure_sink" g_closure_sink- :: Ptr Closure -> IO ()--newCClosure :: FunPtr a -> IO Closure-newCClosure ptr = do- closure <- g_cclosure_new ptr nullPtr safeFreeFunPtrPtr- -- The Haskell runtime will manage the memory associated to the- -- closure, so ref and sink to let GLib know this.- g_closure_ref closure >>= g_closure_sink- wrapBoxed Closure closure
Data/GI/Base/Constructible.hs view
@@ -1,5 +1,5 @@ {-# LANGUAGE DataKinds, FlexibleInstances, MultiParamTypeClasses,- UndecidableInstances, KindSignatures, TypeFamilies #-}+ UndecidableInstances, KindSignatures, TypeFamilies, TypeOperators #-} #if !MIN_VERSION_base(4,8,0) {-# LANGUAGE OverlappingInstances #-} #endif@@ -20,12 +20,10 @@ -- | Constructible types, i.e. those which can be allocated by `new`. class Constructible a (tag :: AttrOpTag) where- new :: MonadIO m => (ManagedPtr a -> a) -> [AttrOp a tag] -> m a+ -- | Allocate a new instance of the given type, with the given attributes.+ new :: MonadIO m => (ManagedPtr a -> a) -> [AttrOp a tag] -> m a -- | Default instance, assuming we have a `GObject`.-instance-#if MIN_VERSION_base(4,8,0)- {-# OVERLAPPABLE #-}-#endif+instance {-# OVERLAPPABLE #-} (GObject a, tag ~ 'AttrConstruct) => Constructible a tag where new = constructGObject
+ Data/GI/Base/DynVal.hs view
@@ -0,0 +1,144 @@+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}++{-|++This is an __experimental__ module that introduces support for dynamic+values: these are functions from a record @model@ to some type @a@+which keep track of which selectors of @model@ does the result depend+on. For example, for a record of the form++> data Example = Example {+> first :: Int,+> second :: Bool,+> third :: Float+> }++a `DynVal Example String` could be constructed, assuming that you are+given a @record@ `DynVal` representing the full record, using:++> let format = \f s -> "First is " <> f <> " and second is " <> s+> formatted = format <$> record.first <*> record.second :: DynVal Example String++Here we are showcasing two properties of `DynVal`s: they can be+conveniently constructed using @OverloadedRecordDot@, and they provide+an `Applicative` instance. The resulting @formatted@ `DynVal` keeps+track of the fact that it depends on the @first@ and @second@ record+selectors.++-}++module Data.GI.Base.DynVal+ ( DynVal(..), DVKey(..), ModelProxy(..), dvKeys, dvRead,+ modelProxyCurrentValue, modelProxyRegisterHandler, modelProxyUpdate) where++import GHC.Records (HasField(..))+import qualified GHC.TypeLits as TL++import Data.Proxy (Proxy(..))+import qualified Data.Set as S+import Data.String (IsString(..))+import qualified Data.Text as T++data DVKey = DVKeyDirect [T.Text]+ -- ^ Direct access to subfields: for example writing+ -- @record.field.subfield@ (using the `HasField`+ -- instance) would lead to @`DVKeyDirect` ["field",+ -- "subfield"]@+ | DVKeyDerived (S.Set [T.Text])+ -- ^ Value derived from a direct key, by acting with the+ -- functor or applicative instances.+ deriving (Eq, Ord, Show)++-- | A `DynVal` is a way of extracting values of type @a@ from+-- @model@, which keeps track of which fields (parameterised by+-- `dvKeys`) in @model@ are needed for computing the `DynVal`.+data DynVal model a = DynVal DVKey (model -> a)++-- | Keys to fields in the model that this `DynVal` depends on.+dvKeys :: DynVal model a -> DVKey+dvKeys (DynVal s _) = s++-- | Compute the actual value given a model.+dvRead :: DynVal model a -> model -> a+dvRead (DynVal _ r) = r++-- | Turn a key into a derived one.+toDerived :: DVKey -> DVKey+toDerived (DVKeyDirect d) = DVKeyDerived (S.singleton d)+toDerived derived = derived++-- | Joining of keys always produces derived ones.+instance Semigroup DVKey where+ DVKeyDirect a <> DVKeyDirect b = DVKeyDerived $ S.fromList [a,b]+ (DVKeyDirect a) <> (DVKeyDerived b) =+ DVKeyDerived $ S.insert a b+ (DVKeyDerived a) <> (DVKeyDirect b) =+ DVKeyDerived $ S.insert b a+ (DVKeyDerived a) <> (DVKeyDerived b) =+ DVKeyDerived $ S.union a b++instance Functor (DynVal model) where+ fmap f dv = DynVal (toDerived $ dvKeys dv) (f . dvRead dv)++instance Applicative (DynVal model) where+ pure x = DynVal (DVKeyDerived S.empty) (const x)+ dF <*> dA = DynVal (dvKeys dF <> dvKeys dA)+ (\m -> let f = dvRead dF m+ in f (dvRead dA m))++instance IsString (DynVal model T.Text) where+ fromString s = pure (T.pack s)++{-+-- If we make dvKeys :: model -> S.Set DVKey we can also produce a+-- Monad instance, but the set of resulting keys might depend on the+-- specific model passed, which could lead to subtle bugs.++instance Monad (DynVal model) where+ dv >>= gen = let runGen = \m -> gen (dvRead dv m)+ in DynVal {dvKeys = \m -> S.union (dvKeys dv m)+ (dvKeys (runGen m) m)+ , dvRead = \m -> dvRead (runGen m) m+ }+-}++-- | A `ModelProxy` is a way of obtaining records of type `model`,+-- which allows for registering for notifications whenever certain+-- keys (typically associated to record fields) get modified, and+-- allows to modify fields of the model.+data ModelProxy model = ModelProxy (IO model) (DVKey -> (model -> IO ()) -> IO ()) ([T.Text] -> (model -> Maybe model) -> IO ())++-- The following would be most naturally field accessors, but because+-- we introduce HasField instances for proxies we need to make these+-- ordinary functions instead.++-- | Obtain the current value of the model.+modelProxyCurrentValue :: ModelProxy model -> IO model+modelProxyCurrentValue (ModelProxy m _ _) = m++-- | Register a handler that will be executed whenever any of the+-- fields in the model pointed to by the keys is modified.+modelProxyRegisterHandler :: ModelProxy model -> DVKey -> (model -> IO ()) -> IO ()+modelProxyRegisterHandler (ModelProxy _ r _) = r++-- | Modify the given keys in the proxy, using the given update+-- function, of type (model -> Maybe model). If this function returns+-- Nothing no modification will be performed, otherwise the modified+-- model will be stored in the ModelProxy, and any listeners will be+-- notified of a change.+modelProxyUpdate :: ModelProxy model -> [T.Text] -> (model -> Maybe model)+ -> IO ()+modelProxyUpdate (ModelProxy _ _ u) = u++instance (HasField fieldName field a,+ TL.KnownSymbol fieldName) =>+ HasField fieldName (DynVal model field) (DynVal model a) where+ getField dv = let fn = T.pack . TL.symbolVal $ (Proxy :: Proxy fieldName)+ key = case dvKeys dv of+ derived@(DVKeyDerived _) -> derived+ DVKeyDirect direct -> DVKeyDirect (direct <> [fn])+ in DynVal key (getField @fieldName . dvRead dv)
+ Data/GI/Base/GArray.hs view
@@ -0,0 +1,16 @@+-- | Utilities for dealing with `GArray` types.+module Data.GI.Base.GArray+ ( allocGArray+ ) where++import Foreign.C (CInt(..), CUInt(..))+import Foreign.Ptr (Ptr)++import Data.GI.Base.BasicTypes (GArray(..))++-- | Args are zero_terminated, clear_, element_size+foreign import ccall g_array_new :: CInt -> CInt -> CUInt -> IO (Ptr (GArray a))++-- | Allocate a `GArray` with elements of the given size.+allocGArray :: CUInt -> IO (Ptr (GArray a))+allocGArray size = g_array_new 0 1 size
+ Data/GI/Base/GClosure.hs view
@@ -0,0 +1,96 @@+{-# LANGUAGE TypeFamilies, DataKinds #-}+-- | Some helper functions for dealing with @GClosure@s.+module Data.GI.Base.GClosure+ ( GClosure(..)+ , newGClosure+ , wrapGClosurePtr+ , newGClosureFromPtr+ , noGClosure+ , unrefGClosure+ , disownGClosure+ ) where++import Foreign.Ptr (Ptr, FunPtr, nullPtr)+import Foreign.C (CInt(..))++import Control.Monad (when)+import Control.Monad.IO.Class (MonadIO, liftIO)++import Data.GI.Base.BasicTypes+import Data.GI.Base.CallStack (HasCallStack)+import Data.GI.Base.ManagedPtr (newBoxed, newManagedPtr',+ disownManagedPtr, withManagedPtr)+import Data.GI.Base.Overloading (ParentTypes, HasParentTypes)++-- | The basic type. This corresponds to a wrapped @GClosure@ on the C+-- side, which is a boxed object.+newtype GClosure a = GClosure (ManagedPtr (GClosure a))++-- | A convenience alias for @Nothing :: Maybe (GClosure a)@.+noGClosure :: Maybe (GClosure a)+noGClosure = Nothing++foreign import ccall "g_closure_get_type" c_g_closure_get_type ::+ IO GType++-- | There are no types in the bindings that a closure can be safely+-- cast to.+type instance ParentTypes (GClosure a) = '[]+instance HasParentTypes (GClosure a)++-- | Find the associated `GType` for the given closure.+instance TypedObject (GClosure a) where+ glibType = c_g_closure_get_type++-- | `GClosure`s are registered as boxed in the GLib type system.+instance GBoxed (GClosure a)++foreign import ccall "g_cclosure_new" g_cclosure_new+ :: FunPtr a -> Ptr () -> FunPtr c -> IO (Ptr (GClosure a))++-- Releasing the `FunPtr` for the signal handler.+foreign import ccall "& haskell_gi_release_signal_closure"+ ptr_to_release_closure :: FunPtr (Ptr () -> Ptr () -> IO ())++-- | Create a new `GClosure` holding the given `FunPtr`. Note that+-- after calling this the `FunPtr` will be freed whenever the+-- `GClosure` is garbage collected, so it is generally not safe to+-- refer to the generated `FunPtr` after this function returns.+newGClosure :: MonadIO m => FunPtr a -> m (GClosure a)+newGClosure ptr = liftIO $ do+ closure <- g_cclosure_new ptr nullPtr ptr_to_release_closure+ wrapGClosurePtr closure++foreign import ccall g_closure_ref :: Ptr (GClosure a) -> IO (Ptr (GClosure a))+foreign import ccall g_closure_sink :: Ptr (GClosure a) -> IO ()+foreign import ccall g_closure_unref :: Ptr (GClosure a) -> IO ()+foreign import ccall "&g_closure_unref" ptr_to_g_closure_unref ::+ FunPtr (Ptr (GClosure a) -> IO ())++foreign import ccall "haskell_gi_g_closure_is_floating" g_closure_is_floating ::+ Ptr (GClosure a) -> IO CInt++-- | Take ownership of a passed in 'Ptr' to a 'GClosure'.+wrapGClosurePtr :: Ptr (GClosure a) -> IO (GClosure a)+wrapGClosurePtr closurePtr = do+ floating <- g_closure_is_floating closurePtr+ when (floating /= 0) $ do+ _ <- g_closure_ref closurePtr+ g_closure_sink closurePtr+ fPtr <- newManagedPtr' ptr_to_g_closure_unref closurePtr+ return $! GClosure fPtr++-- | Construct a Haskell wrapper for the 'GClosure', without assuming+-- ownership.+newGClosureFromPtr :: Ptr (GClosure a) -> IO (GClosure a)+newGClosureFromPtr = newBoxed GClosure++-- | Decrease the reference count of the given 'GClosure'. If the+-- reference count reaches 0 the memory will be released.+unrefGClosure :: (HasCallStack, MonadIO m) => GClosure a -> m ()+unrefGClosure closure = liftIO $ withManagedPtr closure g_closure_unref++-- | Disown (that is, remove from te purview of the Haskell Garbage+-- Collector) the given 'GClosure'.+disownGClosure :: GClosure a -> IO (Ptr (GClosure a))+disownGClosure = disownManagedPtr
+ Data/GI/Base/GError.hs view
@@ -0,0 +1,259 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies, DataKinds #-}++-- | To catch GError exceptions use the+-- catchGError* or handleGError* functions. They work in a similar+-- way to the standard 'Control.Exception.catch' and+-- 'Control.Exception.handle' functions.+--+-- To catch just a single specific error use 'catchGErrorJust' \/+-- 'handleGErrorJust'. To catch any error in a particular error domain+-- use 'catchGErrorJustDomain' \/ 'handleGErrorJustDomain'+--+-- For convenience, generated code also includes specialized variants+-- of 'catchGErrorJust' \/ 'handleGErrorJust' for each error type. For+-- example, for errors of type <https://hackage.haskell.org/package/gi-gdkpixbuf/docs/GI-GdkPixbuf-Enums.html#t:PixbufError PixbufError> one could+-- invoke <https://hackage.haskell.org/package/gi-gdkpixbuf/docs/GI-GdkPixbuf-Enums.html#v:catchPixbufError catchPixbufError> \/+-- <https://hackage.haskell.org/package/gi-gdkpixbuf-2.0.14/docs/GI-GdkPixbuf-Enums.html#v:handlePixbufError handlePixbufError>. The definition is simply+--+-- > catchPixbufError :: IO a -> (PixbufError -> GErrorMessage -> IO a) -> IO a+-- > catchPixbufError = catchGErrorJustDomain+--+-- Notice that the type is suitably specialized, so only+-- errors of type <https://hackage.haskell.org/package/gi-gdkpixbuf/docs/GI-GdkPixbuf-Enums.html#t:PixbufError PixbufError> will be caught.+module Data.GI.Base.GError+ (+ -- * Unpacking GError+ --+ GError(..)+ , gerrorDomain+ , gerrorCode+ , gerrorMessage++ , GErrorDomain+ , GErrorCode+ , GErrorMessage++ -- * Catching GError exceptions+ , catchGErrorJust+ , catchGErrorJustDomain++ , handleGErrorJust+ , handleGErrorJustDomain++ -- * Creating new 'GError's+ , gerrorNew++ -- * Implementation specific details+ -- | The following are used in the implementation+ -- of the bindings, and are in general not necessary for using the+ -- API.+ , GErrorClass(..)++ , propagateGError+ , checkGError+ , maybePokeGError+ ) where++#if __GLASGOW_HASKELL__ < 710+import Control.Applicative ((<$>))+#endif++import Foreign (poke, peek)+import Foreign.Ptr (Ptr, plusPtr, nullPtr)+import Foreign.C+import Control.Exception+import Data.Text (Text)+import qualified Data.Text as T++import System.IO.Unsafe (unsafePerformIO)++import Data.GI.Base.BasicTypes (GType(..), ManagedPtr, TypedObject(..),+ GBoxed)+import Data.GI.Base.BasicConversions (withTextCString, cstringToText)+import Data.GI.Base.ManagedPtr (withManagedPtr, wrapBoxed, copyBoxed)+import Data.GI.Base.Overloading (ParentTypes, HasParentTypes)+import Data.GI.Base.Utils (allocMem, freeMem)++import Data.GI.Base.Internal.CTypes (GQuark, C_gint, gerror_domain_offset,+ gerror_code_offset, gerror_message_offset)++-- | A GError, consisting of a domain, code and a human readable+-- message. These can be accessed by 'gerrorDomain', 'gerrorCode' and+-- 'gerrorMessage' below.+newtype GError = GError (ManagedPtr GError)++instance Show GError where+ show gerror = unsafePerformIO $ do+ code <- gerrorCode gerror+ message <- gerrorMessage gerror+ return $ T.unpack message ++ " (" ++ show code ++ ")"++instance Exception GError++-- | There are no types in the bindings that a `GError` can be safely+-- cast to.+type instance ParentTypes GError = '[]+instance HasParentTypes GError++foreign import ccall "g_error_get_type" g_error_get_type :: IO GType++instance TypedObject GError where+ glibType = g_error_get_type++-- | `GError`s are registered as boxed in the GLib type system.+instance GBoxed GError++-- | A code used to identify the "namespace" of the error. Within each error+-- domain all the error codes are defined in an enumeration. Each gtk\/gnome+-- module that uses GErrors has its own error domain. The rationale behind+-- using error domains is so that each module can organise its own error codes+-- without having to coordinate on a global error code list.+type GErrorDomain = GQuark++-- | A code to identify a specific error within a given 'GErrorDomain'. Most of+-- time you will not need to deal with this raw code since there is an+-- enumeration type for each error domain. Of course which enumeration to use+-- depends on the error domain, but if you use 'catchGErrorJustDomain' or+-- 'handleGErrorJustDomain', this is worked out for you automatically.+type GErrorCode = C_gint++-- | A human readable error message.+type GErrorMessage = Text++foreign import ccall "g_error_new_literal" g_error_new_literal ::+ GQuark -> GErrorCode -> CString -> IO (Ptr GError)++-- | Create a new 'GError'.+gerrorNew :: GErrorDomain -> GErrorCode -> GErrorMessage -> IO GError+gerrorNew domain code message =+ withTextCString message $ \cstring ->+ g_error_new_literal domain code cstring >>= wrapBoxed GError++-- | Return the domain for the given `GError`. This is a GQuark, a+-- textual representation can be obtained with+-- `GI.GLib.quarkToString`.+gerrorDomain :: GError -> IO GQuark+gerrorDomain gerror =+ withManagedPtr gerror $ \ptr ->+ peek $ ptr `plusPtr` gerror_domain_offset++-- | The numeric code for the given `GError`.+gerrorCode :: GError -> IO GErrorCode+gerrorCode gerror =+ withManagedPtr gerror $ \ptr ->+ peek $ ptr `plusPtr` gerror_code_offset++-- | A text message describing the `GError`.+gerrorMessage :: GError -> IO GErrorMessage+gerrorMessage gerror =+ withManagedPtr gerror $ \ptr ->+ (peek $ ptr `plusPtr` gerror_message_offset) >>= cstringToText++-- | Each error domain's error enumeration type should be an instance of this+-- class. This class helps to hide the raw error and domain codes from the+-- user.+--+-- Example for <https://hackage.haskell.org/package/gi-gdkpixbuf/docs/GI-GdkPixbuf-Enums.html#t:PixbufError PixbufError>:+--+-- > instance GErrorClass PixbufError where+-- > gerrorClassDomain _ = "gdk-pixbuf-error-quark"+--+class Enum err => GErrorClass err where+ gerrorClassDomain :: err -> Text -- ^ This must not use the value of its+ -- parameter so that it is safe to pass+ -- 'undefined'.++foreign import ccall unsafe "g_quark_try_string" g_quark_try_string ::+ CString -> IO GQuark++-- | Given the string representation of an error domain returns the+-- corresponding error quark.+gErrorQuarkFromDomain :: Text -> IO GQuark+gErrorQuarkFromDomain domain = withTextCString domain g_quark_try_string++-- | This will catch just a specific GError exception. If you need to catch a+-- range of related errors, 'catchGErrorJustDomain' is probably more+-- appropriate. Example:+--+-- > do image <- catchGErrorJust PixbufErrorCorruptImage+-- > loadImage+-- > (\errorMessage -> do log errorMessage+-- > return mssingImagePlaceholder)+--+catchGErrorJust :: GErrorClass err => err -- ^ The error to catch+ -> IO a -- ^ The computation to run+ -> (GErrorMessage -> IO a) -- ^ Handler to invoke if+ -- an exception is raised+ -> IO a+catchGErrorJust code action handler = catch action handler'+ where handler' gerror = do+ quark <- gErrorQuarkFromDomain (gerrorClassDomain code)+ domain <- gerrorDomain gerror+ code' <- gerrorCode gerror+ if domain == quark && code' == (fromIntegral . fromEnum) code+ then gerrorMessage gerror >>= handler+ else throw gerror -- Pass it on++-- | Catch all GErrors from a particular error domain. The handler function+-- should just deal with one error enumeration type. If you need to catch+-- errors from more than one error domain, use this function twice with an+-- appropriate handler functions for each.+--+-- > catchGErrorJustDomain+-- > loadImage+-- > (\err message -> case err of+-- > PixbufErrorCorruptImage -> ...+-- > PixbufErrorInsufficientMemory -> ...+-- > PixbufErrorUnknownType -> ...+-- > _ -> ...)+--+catchGErrorJustDomain :: forall err a. GErrorClass err =>+ IO a -- ^ The computation to run+ -> (err -> GErrorMessage -> IO a) -- ^ Handler to invoke if an exception is raised+ -> IO a+catchGErrorJustDomain action handler = catch action handler'+ where handler' gerror = do+ quark <- gErrorQuarkFromDomain (gerrorClassDomain (undefined :: err))+ domain <- gerrorDomain gerror+ if domain == quark+ then do+ code <- (toEnum . fromIntegral) <$> gerrorCode gerror+ msg <- gerrorMessage gerror+ handler code msg+ else throw gerror++-- | A verson of 'handleGErrorJust' with the arguments swapped around.+handleGErrorJust :: GErrorClass err => err -> (GErrorMessage -> IO a) -> IO a -> IO a+handleGErrorJust code = flip (catchGErrorJust code)++-- | A verson of 'catchGErrorJustDomain' with the arguments swapped around.+handleGErrorJustDomain :: GErrorClass err => (err -> GErrorMessage -> IO a) -> IO a -> IO a+handleGErrorJustDomain = flip catchGErrorJustDomain++-- | Run the given function catching possible 'GError's in its+-- execution. If a 'GError' is emitted this throws the corresponding+-- exception.+propagateGError :: (Ptr (Ptr GError) -> IO a) -> IO a+propagateGError f = checkGError f throw++-- | Like 'propagateGError', but allows to specify a custom handler+-- instead of just throwing the exception.+checkGError :: (Ptr (Ptr GError) -> IO a) -> (GError -> IO a) -> IO a+checkGError f handler = do+ gerrorPtr <- allocMem+ poke gerrorPtr nullPtr+ result <- f gerrorPtr+ gerror <- peek gerrorPtr+ freeMem gerrorPtr+ if gerror /= nullPtr+ then wrapBoxed GError gerror >>= handler+ else return result++-- | If the passed in @`Maybe` `GError`@ is not `Nothing`, store a+-- copy in the passed in pointer, unless the pointer is `nullPtr`.+maybePokeGError :: Ptr (Ptr GError) -> Maybe GError -> IO ()+maybePokeGError _ Nothing = return ()+maybePokeGError ptrPtr (Just gerror)+ | ptrPtr == nullPtr = return ()+ | otherwise = copyBoxed gerror >>= poke ptrPtr
− Data/GI/Base/GError.hsc
@@ -1,254 +0,0 @@-{-# LANGUAGE DeriveDataTypeable, ScopedTypeVariables #-}---- | To catch GError exceptions use the--- catchGError* or handleGError* functions. They work in a similar--- way to the standard 'Control.Exception.catch' and--- 'Control.Exception.handle' functions.------ To catch just a single specific error use 'catchGErrorJust' \/--- 'handleGErrorJust'. To catch any error in a particular error domain--- use 'catchGErrorJustDomain' \/ 'handleGErrorJustDomain'------ For convenience, generated code also includes specialized variants--- of 'catchGErrorJust' \/ 'handleGErrorJust' for each error type. For--- example, for errors of type <https://hackage.haskell.org/package/gi-gdkpixbuf/docs/GI-GdkPixbuf-Enums.html#t:PixbufError PixbufError> one could--- invoke <https://hackage.haskell.org/package/gi-gdkpixbuf/docs/GI-GdkPixbuf-Enums.html#v:catchPixbufError catchPixbufError> \/--- <https://hackage.haskell.org/package/gi-gdkpixbuf-2.0.14/docs/GI-GdkPixbuf-Enums.html#v:handlePixbufError handlePixbufError>. The definition is simply------ > catchPixbufError :: IO a -> (PixbufError -> GErrorMessage -> IO a) -> IO a--- > catchPixbufError = catchGErrorJustDomain------ Notice that the type is suitably specialized, so only--- errors of type <https://hackage.haskell.org/package/gi-gdkpixbuf/docs/GI-GdkPixbuf-Enums.html#t:PixbufError PixbufError> will be caught.-module Data.GI.Base.GError- (- -- * Unpacking GError- --- GError(..)- , gerrorDomain- , gerrorCode- , gerrorMessage-- , GErrorDomain- , GErrorCode- , GErrorMessage-- -- * Catching GError exceptions- , catchGErrorJust- , catchGErrorJustDomain-- , handleGErrorJust- , handleGErrorJustDomain-- -- * Creating new 'GError's- , gerrorNew-- -- * Implementation specific details- -- | The following are used in the implementation- -- of the bindings, and are in general not necessary for using the- -- API.- , GErrorClass(..)-- , propagateGError- , checkGError- , maybePokeGError- ) where--#if __GLASGOW_HASKELL__ < 710-import Control.Applicative ((<$>))-#endif--import Foreign (poke, peek)-import Foreign.Ptr (Ptr, plusPtr, nullPtr)-import Foreign.C-import Control.Exception-import Data.Text (Text)-import qualified Data.Text as T-import Data.Typeable (Typeable)-import Data.Int-import Data.Word--import System.IO.Unsafe (unsafePerformIO)--import Data.GI.Base.BasicTypes (BoxedObject(..), GType(..), ManagedPtr)-import Data.GI.Base.BasicConversions (withTextCString, cstringToText)-import Data.GI.Base.ManagedPtr (wrapBoxed, withManagedPtr, copyBoxed)-import Data.GI.Base.Utils (allocMem, freeMem)--#include <glib.h>---- | A GError, consisting of a domain, code and a human readable--- message. These can be accessed by 'gerrorDomain', 'gerrorCode' and--- 'gerrorMessage' below.-newtype GError = GError (ManagedPtr GError)- deriving (Typeable)--instance Show GError where- show gerror = unsafePerformIO $ do- code <- gerrorCode gerror- message <- gerrorMessage gerror- return $ T.unpack message ++ " (" ++ show code ++ ")"--instance Exception GError--foreign import ccall "g_error_get_type" g_error_get_type :: IO GType--instance BoxedObject GError where- boxedType _ = g_error_get_type---- | A GQuark.-type GQuark = #type GQuark---- | A code used to identify the "namespace" of the error. Within each error--- domain all the error codes are defined in an enumeration. Each gtk\/gnome--- module that uses GErrors has its own error domain. The rationale behind--- using error domains is so that each module can organise its own error codes--- without having to coordinate on a global error code list.-type GErrorDomain = GQuark---- | A code to identify a specific error within a given 'GErrorDomain'. Most of--- time you will not need to deal with this raw code since there is an--- enumeration type for each error domain. Of course which enumeration to use--- depends on the error domain, but if you use 'catchGErrorJustDomain' or--- 'handleGErrorJustDomain', this is worked out for you automatically.-type GErrorCode = #type gint---- | A human readable error message.-type GErrorMessage = Text--foreign import ccall "g_error_new_literal" g_error_new_literal ::- GQuark -> GErrorCode -> CString -> IO (Ptr GError)---- | Create a new 'GError'.-gerrorNew :: GErrorDomain -> GErrorCode -> GErrorMessage -> IO GError-gerrorNew domain code message =- withTextCString message $ \cstring ->- g_error_new_literal domain code cstring >>= wrapBoxed GError---- | Return the domain for the given `GError`. This is a GQuark, a--- textual representation can be obtained with--- `GI.GLib.quarkToString`.-gerrorDomain :: GError -> IO GQuark-gerrorDomain gerror =- withManagedPtr gerror $ \ptr ->- peek $ ptr `plusPtr` #{offset GError, domain}---- | The numeric code for the given `GError`.-gerrorCode :: GError -> IO GErrorCode-gerrorCode gerror =- withManagedPtr gerror $ \ptr ->- peek $ ptr `plusPtr` #{offset GError, code}---- | A text message describing the `GError`.-gerrorMessage :: GError -> IO GErrorMessage-gerrorMessage gerror =- withManagedPtr gerror $ \ptr ->- (peek $ ptr `plusPtr` #{offset GError, message}) >>= cstringToText---- | Each error domain's error enumeration type should be an instance of this--- class. This class helps to hide the raw error and domain codes from the--- user.------ Example for <https://hackage.haskell.org/package/gi-gdkpixbuf/docs/GI-GdkPixbuf-Enums.html#t:PixbufError PixbufError>:------ > instance GErrorClass PixbufError where--- > gerrorClassDomain _ = "gdk-pixbuf-error-quark"----class Enum err => GErrorClass err where- gerrorClassDomain :: err -> Text -- ^ This must not use the value of its- -- parameter so that it is safe to pass- -- 'undefined'.--foreign import ccall unsafe "g_quark_try_string" g_quark_try_string ::- CString -> IO GQuark---- | Given the string representation of an error domain returns the--- corresponding error quark.-gErrorQuarkFromDomain :: Text -> IO GQuark-gErrorQuarkFromDomain domain = withTextCString domain g_quark_try_string---- | This will catch just a specific GError exception. If you need to catch a--- range of related errors, 'catchGErrorJustDomain' is probably more--- appropriate. Example:------ > do image <- catchGErrorJust PixbufErrorCorruptImage--- > loadImage--- > (\errorMessage -> do log errorMessage--- > return mssingImagePlaceholder)----catchGErrorJust :: GErrorClass err => err -- ^ The error to catch- -> IO a -- ^ The computation to run- -> (GErrorMessage -> IO a) -- ^ Handler to invoke if- -- an exception is raised- -> IO a-catchGErrorJust code action handler = catch action handler'- where handler' gerror = do- quark <- gErrorQuarkFromDomain (gerrorClassDomain code)- domain <- gerrorDomain gerror- code' <- gerrorCode gerror- if domain == quark && code' == (fromIntegral . fromEnum) code- then gerrorMessage gerror >>= handler- else throw gerror -- Pass it on---- | Catch all GErrors from a particular error domain. The handler function--- should just deal with one error enumeration type. If you need to catch--- errors from more than one error domain, use this function twice with an--- appropriate handler functions for each.------ > catchGErrorJustDomain--- > loadImage--- > (\err message -> case err of--- > PixbufErrorCorruptImage -> ...--- > PixbufErrorInsufficientMemory -> ...--- > PixbufErrorUnknownType -> ...--- > _ -> ...)----catchGErrorJustDomain :: forall err a. GErrorClass err =>- IO a -- ^ The computation to run- -> (err -> GErrorMessage -> IO a) -- ^ Handler to invoke if an exception is raised- -> IO a-catchGErrorJustDomain action handler = catch action handler'- where handler' gerror = do- quark <- gErrorQuarkFromDomain (gerrorClassDomain (undefined :: err))- domain <- gerrorDomain gerror- if domain == quark- then do- code <- (toEnum . fromIntegral) <$> gerrorCode gerror- msg <- gerrorMessage gerror- handler code msg- else throw gerror---- | A verson of 'handleGErrorJust' with the arguments swapped around.-handleGErrorJust :: GErrorClass err => err -> (GErrorMessage -> IO a) -> IO a -> IO a-handleGErrorJust code = flip (catchGErrorJust code)---- | A verson of 'catchGErrorJustDomain' with the arguments swapped around.-handleGErrorJustDomain :: GErrorClass err => (err -> GErrorMessage -> IO a) -> IO a -> IO a-handleGErrorJustDomain = flip catchGErrorJustDomain---- | Run the given function catching possible 'GError's in its--- execution. If a 'GError' is emitted this throws the corresponding--- exception.-propagateGError :: (Ptr (Ptr GError) -> IO a) -> IO a-propagateGError f = checkGError f throw---- | Like 'propagateGError', but allows to specify a custom handler--- instead of just throwing the exception.-checkGError :: (Ptr (Ptr GError) -> IO a) -> (GError -> IO a) -> IO a-checkGError f handler = do- gerrorPtr <- allocMem- poke gerrorPtr nullPtr- result <- f gerrorPtr- gerror <- peek gerrorPtr- freeMem gerrorPtr- if gerror /= nullPtr- then wrapBoxed GError gerror >>= handler- else return result---- | If the passed in @`Maybe` `GError`@ is not `Nothing`, store a--- copy in the passed in pointer, unless the pointer is `nullPtr`.-maybePokeGError :: Ptr (Ptr GError) -> Maybe GError -> IO ()-maybePokeGError _ Nothing = return ()-maybePokeGError ptrPtr (Just gerror)- | ptrPtr == nullPtr = return ()- | otherwise = copyBoxed gerror >>= poke ptrPtr
Data/GI/Base/GHashTable.hsc view
@@ -21,24 +21,18 @@ , gStrEqual , cstringPackPtr , cstringUnpackPtr+ , gvaluePackPtr+ , gvalueUnpackPtr ) where -import Data.Int-import Data.Word- import Foreign.C-import Foreign.Ptr (Ptr, castPtr, FunPtr)+import Foreign.Ptr (Ptr, castPtr) -import Data.GI.Base.BasicTypes (PtrWrapped(..))+import Data.GI.Base.BasicTypes (PtrWrapped(..), GHashFunc, GEqualFunc)+import Data.GI.Base.GValue (GValue) #include <glib-object.h> --- | A pointer to a hashing function on the C side.-type GHashFunc a = FunPtr (PtrWrapped a -> IO #{type guint})---- | A pointer to an equality checking function on the C side.-type GEqualFunc a = FunPtr (PtrWrapped a -> PtrWrapped a -> IO #{type gboolean})- -- | Compute the hash for a `Ptr`. foreign import ccall "&g_direct_hash" gDirectHash :: GHashFunc (Ptr a) @@ -66,3 +60,12 @@ -- | Extract a `CString` wrapped into a `Ptr` coming from a `GHashTable`. cstringUnpackPtr :: PtrWrapped CString -> CString cstringUnpackPtr = ptrUnpackPtr++-- | Pack a `Ptr` to `GValue` into a `Ptr` than can go into a `GHashTable`.+gvaluePackPtr :: Ptr GValue -> PtrWrapped (Ptr GValue)+gvaluePackPtr = ptrPackPtr++-- | Extract a `Ptr` to `GValue` wrapped into a `Ptr` coming from a+-- `GHashTable`.+gvalueUnpackPtr :: PtrWrapped (Ptr GValue) -> Ptr GValue+gvalueUnpackPtr = ptrUnpackPtr
Data/GI/Base/GObject.hsc view
@@ -1,22 +1,94 @@-{-# LANGUAGE ScopedTypeVariables, DataKinds, TypeFamilies #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE ImplicitParams #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-} +-- | This module constains helpers for dealing with `GObject`-derived+-- types.+ module Data.GI.Base.GObject- ( constructGObject+ ( -- * Constructing new `GObject`s+ constructGObject , new'++ -- * User data+ , gobjectGetUserData+ , gobjectSetUserData+ , gobjectModifyUserData++ -- * Deriving new object types+ , DerivedGObject(..)+ , registerGType+ , gobjectGetPrivateData+ , gobjectSetPrivateData+ , gobjectModifyPrivateData++ , GObjectClass(..)+ , gtypeFromClass+ , gtypeFromInstance++ -- * Installing properties for derived objects+ , gobjectInstallProperty+ , gobjectInstallCIntProperty+ , gobjectInstallCStringProperty+ , gobjectInstallGBooleanProperty ) where +import Data.Maybe (catMaybes)+import Control.Monad (void, when) import Control.Monad.IO.Class (MonadIO, liftIO)+ import Data.Proxy (Proxy(..))+import Data.Coerce (coerce) import Foreign.C (CUInt(..), CString, newCString)-import Foreign+import Foreign.Ptr (Ptr, FunPtr, nullPtr, castPtr, plusPtr, nullFunPtr)+import Foreign.StablePtr (newStablePtr, deRefStablePtr,+ castStablePtrToPtr, castPtrToStablePtr)+import Foreign.Storable (Storable(peek, poke, pokeByteOff, sizeOf))+import Foreign (mallocBytes, copyBytes, free) +#if !MIN_VERSION_base(4,11,0)+import Data.Monoid ((<>))+#endif+import Data.Text (Text)+import qualified Data.Text as T++import qualified Optics.Core as O+ import Data.GI.Base.Attributes (AttrOp(..), AttrOpTag(..), AttrLabelProxy,- attrConstruct)-import Data.GI.Base.BasicTypes (GType(..), GObject(..), ManagedPtr)+ attrConstruct, attrTransfer,+ AttrInfo(..), EqMaybe(..), bindPropToField)+import Data.GI.Base.BasicTypes (CGType, GType(..), GObject, GSList,+ GDestroyNotify, ManagedPtr(..), GParamSpec(..),+ TypedObject(glibType),+ gtypeName, g_slist_free)+import Data.GI.Base.BasicConversions (withTextCString, cstringToText,+ packGSList, mapGSList)+import Data.GI.Base.CallStack (HasCallStack, prettyCallStack)+import Data.GI.Base.DynVal (modelProxyCurrentValue, modelProxyRegisterHandler,+ dvKeys, dvRead)+import Data.GI.Base.GParamSpec (PropertyInfo(..),+ gParamSpecValue,+ CIntPropertyInfo(..), CStringPropertyInfo(..),+ GBooleanPropertyInfo(..),+ gParamSpecCInt, gParamSpecCString, gParamSpecGBoolean,+ getGParamSpecGetterSetter,+ PropGetSetter(..))+import Data.GI.Base.GQuark (GQuark(..), gQuarkFromString) import Data.GI.Base.GValue (GValue(..), GValueConstruct(..))-import Data.GI.Base.ManagedPtr (withManagedPtr, touchManagedPtr, wrapObject)+import Data.GI.Base.ManagedPtr (withManagedPtr, touchManagedPtr, wrapObject,+ newObject) import Data.GI.Base.Overloading (ResolveAttribute)+import Data.GI.Base.Signals (on, after)+import Data.GI.Base.Utils (dbgLog, callocBytes, freeMem)+import Data.GI.Base.Internal.PathFieldAccess (Components, PathFieldAccess(..)) #include <glib-object.h> @@ -24,32 +96,97 @@ GType -> CUInt -> Ptr CString -> Ptr a -> IO (Ptr b) -- | Construct a GObject given the constructor and a list of settable--- attributes.+-- attributes. See `Data.GI.Base.Constructible.new` for a more general+-- version. constructGObject :: forall o m. (GObject o, MonadIO m) => (ManagedPtr o -> o) -> [AttrOp o 'AttrConstruct] -> m o constructGObject constructor attrs = liftIO $ do- props <- mapM construct attrs- doConstructGObject constructor props+ props <- catMaybes <$> mapM construct attrs+ obj <- doConstructGObject constructor props+ mapM_ (setSignal obj) attrs+ mapM_ (registerHandlers obj) attrs+ return obj where- resolve :: AttrLabelProxy attr -> Proxy (ResolveAttribute attr o)- resolve _ = Proxy- construct :: AttrOp o 'AttrConstruct ->- IO (GValueConstruct o)- construct (attr := x) = attrConstruct (resolve attr) x- construct (attr :=> x) = x >>= attrConstruct (resolve attr)+ IO (Maybe (GValueConstruct o))+ construct ((_attr :: AttrLabelProxy label) := x) =+ Just <$> attrConstruct @(ResolveAttribute label o) x + construct ((_attr :: AttrLabelProxy label) :=> x) =+ Just <$> (x >>= attrConstruct @(ResolveAttribute label o))++ construct ((_attr :: AttrLabelProxy label) :&= x) = Just <$>+ (attrTransfer @(ResolveAttribute label o) (Proxy @o) x >>=+ attrConstruct @(ResolveAttribute label o))++ construct ((_attr :: AttrLabelProxy label) :!<~ dv) = do+ model <- modelProxyCurrentValue ?_haskell_gi_modelProxy+ Just <$> attrConstruct @(ResolveAttribute label o) (dvRead dv model)++ construct (Bind (_pattr :: AttrLabelProxy prop)+ (_fattr :: AttrLabelProxy field)) = do+ model <- modelProxyCurrentValue ?_haskell_gi_modelProxy+ let lens = getLens (Proxy @field) model+ value = O.view lens model+ Just <$> attrConstruct @(ResolveAttribute prop o) value+ where getLens :: forall path components model value.+ (components ~ Components path,+ PathFieldAccess components model value) =>+ Proxy path -> model -> O.Lens' model value+ getLens _ _ = let+ (lens, _) = pathFieldAccess (Proxy @components) (Proxy @model)+ in lens++ -- Since we are constructing the object there's nothing to compare+ -- to, so we just set the value.+ construct ((_attr :: AttrLabelProxy label) :<~ dv) = do+ model <- modelProxyCurrentValue ?_haskell_gi_modelProxy+ Just <$> attrConstruct @(ResolveAttribute label o) (dvRead dv model)++ construct (On _ _) = return Nothing+ construct (After _ _) = return Nothing++ setSignal :: GObject o => o -> AttrOp o 'AttrConstruct -> IO ()+ setSignal obj (On signal callback) = void $ on obj signal callback+ setSignal obj (After signal callback) = void $ after obj signal callback+ setSignal _ _ = return ()++ registerHandlers :: GObject o => o -> AttrOp o 'AttrConstruct -> IO ()+ registerHandlers obj ((_attr :: AttrLabelProxy label) :!<~ dv) =+ modelProxyRegisterHandler ?_haskell_gi_modelProxy (dvKeys dv) $ \modifiedModel ->+ attrSet @(ResolveAttribute label o) obj (dvRead dv modifiedModel)++ registerHandlers obj ((_attr :: AttrLabelProxy label) :<~ dv) =+ modelProxyRegisterHandler ?_haskell_gi_modelProxy (dvKeys dv) $ \modifiedModel -> do+ current <- attrGet @(ResolveAttribute label o) obj+ let modifiedValue = dvRead dv modifiedModel+ when (not $ eqMaybe modifiedValue current) $+ attrSet @(ResolveAttribute label o) obj modifiedValue++ registerHandlers obj (Bind pattr fattr) =+ bindPropToField (Proxy @'AttrConstruct) obj pattr fattr++ registerHandlers _ _ = pure ()++-- | Construct the given `GObject`, given a set of actions+-- constructing desired `GValue`s to set at construction time.+new' :: (HasCallStack, MonadIO m, GObject o) =>+ (ManagedPtr o -> o) -> [m (GValueConstruct o)] -> m o+new' constructor actions = do+ props <- sequence actions+ doConstructGObject constructor props+ -- | Construct the `GObject` given the list of `GValueConstruct`s.-doConstructGObject :: forall o m. (GObject o, MonadIO m)+doConstructGObject :: forall o m. (HasCallStack, GObject o, MonadIO m) => (ManagedPtr o -> o) -> [GValueConstruct o] -> m o doConstructGObject constructor props = liftIO $ do let nprops = length props names <- mallocBytes (nprops * sizeOf nullPtr) values <- mallocBytes (nprops * gvalueSize) fill names values props- gtype <- gobjectType (undefined :: o)+ gtype <- glibType @o result <- g_object_new gtype (fromIntegral nprops) names values freeStrings nprops names free values@@ -89,10 +226,300 @@ do peek namePtr >>= free freeStrings (n-1) (namePtr `plusPtr` sizeOf nullPtr) --- | Construct the given `GObject`, given a set of actions--- constructing desired `GValue`s to set at construction time.-new' :: (MonadIO m, GObject o) =>- (ManagedPtr o -> o) -> [IO (GValueConstruct o)] -> m o-new' constructor actions = do- props <- liftIO $ sequence (actions)- doConstructGObject constructor props+-- | Wrapper around @GObjectClass@ on the C-side.+newtype GObjectClass = GObjectClass (Ptr GObjectClass)++-- | This typeclass contains the data necessary for defining a new+-- `GObject` type from Haskell.+class GObject a => DerivedGObject a where+ -- | The parent type+ type GObjectParentType a+ -- | Type of the private data for each instance.+ type GObjectPrivateData a++ -- | Name of the type, it should be unique.+ objectTypeName :: Text++ -- | Code to run when the class is inited. This is a good place to+ -- register signals and properties for the type.+ objectClassInit :: GObjectClass -> IO ()++ -- | Code to run when each instance of the type is+ -- constructed. Returns the private data to be associated with the+ -- new instance (use `gobjectGetPrivateData` and+ -- `gobjectSetPrivateData` to manipulate this further).+ objectInstanceInit :: GObjectClass -> a -> IO (GObjectPrivateData a)++ -- | List of interfaces implemented by the type. Each element is a+ -- triplet (@gtype@, @interfaceInit@, @interfaceFinalize@), where+ -- @gtype :: IO GType@ is a constructor for the type of the+ -- interface, @interfaceInit :: Ptr () -> IO ()@ is a function that+ -- registers the callbacks in the interface, and @interfaceFinalize+ -- :: Maybe (Ptr () -> IO ())@ is the (optional) finalizer.+ objectInterfaces :: [(IO GType, Ptr () -> IO (), Maybe (Ptr () -> IO ()))]+ objectInterfaces = []++type CGTypeClassInit = GObjectClass -> IO ()+foreign import ccall "wrapper"+ mkClassInit :: CGTypeClassInit -> IO (FunPtr CGTypeClassInit)++type CGTypeInstanceInit o = Ptr o -> GObjectClass -> IO ()+foreign import ccall "wrapper"+ mkInstanceInit :: CGTypeInstanceInit o -> IO (FunPtr (CGTypeInstanceInit o))++type CGTypeInterfaceInit = Ptr () -> Ptr () -> IO ()+foreign import ccall "wrapper"+ mkInterfaceInit :: CGTypeInterfaceInit -> IO (FunPtr CGTypeInterfaceInit)++type CGTypeInterfaceFinalize = Ptr () -> Ptr () -> IO ()+foreign import ccall "wrapper"+ mkInterfaceFinalize :: CGTypeInterfaceFinalize -> IO (FunPtr CGTypeInterfaceFinalize)++foreign import ccall g_type_from_name :: CString -> IO CGType++foreign import ccall "haskell_gi_register_gtype" register_gtype ::+ CGType -> CString -> FunPtr CGTypeClassInit ->+ FunPtr (CGTypeInstanceInit o) -> Ptr (GSList a) -> IO CGType++foreign import ccall "haskell_gi_gtype_from_class" gtype_from_class ::+ GObjectClass -> IO CGType++-- | Find the `GType` associated to a given `GObjectClass`.+gtypeFromClass :: GObjectClass -> IO GType+gtypeFromClass klass = GType <$> gtype_from_class klass++foreign import ccall "haskell_gi_gtype_from_instance" gtype_from_instance ::+ Ptr o -> IO CGType++-- | Find the `GType` for a given `GObject`.+gtypeFromInstance :: GObject o => o -> IO GType+gtypeFromInstance obj = withManagedPtr obj $ \objPtr ->+ (GType <$> gtype_from_instance objPtr)++foreign import ccall g_param_spec_get_name ::+ Ptr GParamSpec -> IO CString++type CPropertyGetter o = Ptr o -> CUInt -> Ptr GValue -> Ptr GParamSpec -> IO ()++foreign import ccall "wrapper"+ mkPropertyGetter :: CPropertyGetter o -> IO (FunPtr (CPropertyGetter o))++type CPropertySetter o = Ptr o -> CUInt -> Ptr GValue -> Ptr GParamSpec -> IO ()++foreign import ccall "wrapper"+ mkPropertySetter :: CPropertySetter o -> IO (FunPtr (CPropertySetter o))++-- | Register the given type into the @GObject@ type system and return+-- the resulting `GType`, if it has not been registered already. If+-- the type has been registered already the existing `GType` will be+-- returned instead.+--+-- Note that for this function to work the type must be an instance of+-- `DerivedGObject`.+registerGType :: forall o. (HasCallStack, DerivedGObject o,+ GObject (GObjectParentType o),+ GObject o) =>+ (ManagedPtr o -> o) -> IO GType+registerGType construct = withTextCString (objectTypeName @o) $ \cTypeName -> do+ cgtype <- g_type_from_name cTypeName+ if cgtype /= 0+ then return (GType cgtype) -- Already registered+ else do+ classInit <- mkClassInit (unwrapClassInit $ objectClassInit @o)+ instanceInit <- mkInstanceInit (unwrapInstanceInit $ objectInstanceInit @o)+ (GType parentCGType) <- glibType @(GObjectParentType o)+ interfaces <- mapM packInterface (objectInterfaces @o) >>= packGSList+ gtype <- GType <$> register_gtype parentCGType cTypeName classInit instanceInit interfaces+ mapGSList freeInterfaceInfo interfaces+ g_slist_free interfaces+ return gtype++ where+ unwrapInstanceInit :: (GObjectClass -> o -> IO (GObjectPrivateData o)) ->+ CGTypeInstanceInit o+ unwrapInstanceInit instanceInit objPtr klass = do+ privateData <- do+ obj <- newObject construct (castPtr objPtr :: Ptr o)+ instanceInit klass obj+ instanceSetPrivateData objPtr privateData++ unwrapClassInit :: (GObjectClass -> IO ()) -> CGTypeClassInit+ unwrapClassInit classInit klass@(GObjectClass klassPtr) = do+ getFunPtr <- mkPropertyGetter marshallGetter+ (#poke GObjectClass, get_property) klassPtr getFunPtr+ setFunPtr <- mkPropertySetter marshallSetter+ (#poke GObjectClass, set_property) klassPtr setFunPtr+ classInit klass++ marshallSetter :: CPropertySetter o+ marshallSetter objPtr _ gvPtr pspecPtr = do+ maybeGetSet <- getGParamSpecGetterSetter pspecPtr+ case maybeGetSet of+ Nothing -> do+ pspecName <- g_param_spec_get_name pspecPtr >>= cstringToText+ typeName <- glibType @o >>= gtypeName+ dbgLog $ "WARNING: Attempting to set unknown property \""+ <> pspecName <> "\" of type \"" <> T.pack typeName <> "\"."+ Just pgs -> (propSetter pgs) objPtr gvPtr++ marshallGetter :: CPropertyGetter o+ marshallGetter objPtr _ destGValuePtr pspecPtr = do+ maybeGetSet <- getGParamSpecGetterSetter pspecPtr+ case maybeGetSet of+ Nothing -> do+ pspecName <- g_param_spec_get_name pspecPtr >>= cstringToText+ typeName <- glibType @o >>= gtypeName+ dbgLog $ "WARNING: Attempting to get unknown property \""+ <> pspecName <> "\" of type \"" <> T.pack typeName <> "\"."+ Just pgs -> (propGetter pgs) objPtr destGValuePtr++ packInterface :: (IO GType, Ptr () -> IO (), Maybe (Ptr () -> IO ()))+ -> IO (Ptr CGType)+ packInterface (ifaceGTypeConstruct, initHs, maybeFinalize) = do+ gtype <- ifaceGTypeConstruct+ info <- callocBytes #{size GInterfaceInfo}+ initFn <- mkInterfaceInit (unwrapInit initHs)+ finalizeFn <- case maybeFinalize of+ Just finalizeHs -> mkInterfaceFinalize (unwrapFinalize finalizeHs)+ Nothing -> pure nullFunPtr+ #{poke GInterfaceInfo, interface_init} info initFn+ #{poke GInterfaceInfo, interface_finalize} info finalizeFn++ combined <- callocBytes (#{size GType} + #{size gpointer})+ poke combined (gtypeToCGType gtype)+ poke (combined `plusPtr` #{size GType}) info+ return combined++ unwrapInit :: (Ptr () -> IO ()) -> CGTypeInterfaceInit+ unwrapInit f ptr _data = f ptr++ unwrapFinalize :: (Ptr () -> IO ()) -> CGTypeInterfaceFinalize+ unwrapFinalize = unwrapInit++ freeInterfaceInfo :: Ptr CGType -> IO ()+ freeInterfaceInfo combinedPtr = do+ info <- peek (combinedPtr `plusPtr` #{size GType})+ freeMem info+ freeMem combinedPtr++-- | Quark with the key to the private data for this object type.+privateKey :: forall o. DerivedGObject o => IO (GQuark (GObjectPrivateData o))+privateKey = gQuarkFromString $ objectTypeName @o <> "::haskell-gi-private-data"++-- | Get the private data associated with the given object.+gobjectGetPrivateData :: forall o. (HasCallStack, DerivedGObject o) =>+ o -> IO (GObjectPrivateData o)+gobjectGetPrivateData obj = do+ key <- privateKey @o+ maybePriv <- gobjectGetUserData obj key+ case maybePriv of+ Just priv -> return priv+ Nothing -> do+ case managedPtrAllocCallStack (coerce obj) of+ Nothing -> error ("Failed to get private data pointer!\n"+ <> "Set the env var HASKELL_GI_DEBUG_MEM=1 to get more info.")+ Just cs -> withManagedPtr obj $ \objPtr -> do+ let errMsg = "Failed to get private data pointer for" <> show objPtr <> "!\n"+ <> "Callstack for allocation was:\n"+ <> prettyCallStack cs <> "\n\n"+ error errMsg++foreign import ccall g_object_get_qdata ::+ Ptr a -> GQuark b -> IO (Ptr c)++-- | Get the value of a given key for the object.+gobjectGetUserData :: (HasCallStack, GObject o) => o -> GQuark a -> IO (Maybe a)+gobjectGetUserData obj key = do+ dataPtr <- withManagedPtr obj $ \objPtr ->+ g_object_get_qdata objPtr key+ if dataPtr /= nullPtr+ then Just <$> deRefStablePtr (castPtrToStablePtr dataPtr)+ else return Nothing++foreign import ccall "&hs_free_stable_ptr" ptr_to_hs_free_stable_ptr ::+ GDestroyNotify (Ptr ())++foreign import ccall g_object_set_qdata_full ::+ Ptr a -> GQuark b -> Ptr () -> GDestroyNotify (Ptr ()) -> IO ()++-- | Set the value of the user data for the given `GObject` to a+-- `StablePtr` to the given Haskell object. The `StablePtr` will be+-- freed when the object is destroyed, or the value is replaced.+gobjectSetUserData :: (HasCallStack, GObject o) =>+ o -> GQuark a -> a -> IO ()+gobjectSetUserData obj key value = withManagedPtr obj $ \objPtr ->+ instanceSetUserData objPtr key value++-- | A combination of `gobjectGetUserData` and `gobjectSetUserData`,+-- for convenience.+gobjectModifyUserData :: (HasCallStack, GObject o) =>+ o -> GQuark a -> (Maybe a -> a) -> IO ()+gobjectModifyUserData obj key transform = do+ userData <- gobjectGetUserData obj key+ gobjectSetUserData obj key (transform userData)++-- | Like `gobjectSetUserData`, but it works on the raw object pointer.+-- Note that this is unsafe, unless used in a context where we are sure that+-- the GC will not release the object while we run.+instanceSetUserData :: (HasCallStack, GObject o) =>+ Ptr o -> GQuark a -> a -> IO ()+instanceSetUserData objPtr key value = do+ stablePtr <- newStablePtr value+ g_object_set_qdata_full objPtr key (castStablePtrToPtr stablePtr)+ ptr_to_hs_free_stable_ptr++-- | Set the private data associated with the given object.+gobjectSetPrivateData :: forall o. (HasCallStack, DerivedGObject o) =>+ o -> GObjectPrivateData o -> IO ()+gobjectSetPrivateData obj value = withManagedPtr obj $ \objPtr ->+ instanceSetPrivateData objPtr value++-- | Set the private data for a given instance.+instanceSetPrivateData :: forall o. (HasCallStack, DerivedGObject o) =>+ Ptr o -> GObjectPrivateData o -> IO ()+instanceSetPrivateData objPtr priv = do+ key <- privateKey @o+ instanceSetUserData objPtr key priv++foreign import ccall g_object_class_install_property ::+ GObjectClass -> CUInt -> Ptr GParamSpec -> IO ()++-- | Modify the private data for the given object.+gobjectModifyPrivateData :: forall o. (HasCallStack, DerivedGObject o) =>+ o -> (GObjectPrivateData o -> GObjectPrivateData o)+ -> IO ()+gobjectModifyPrivateData obj transform = do+ private <- gobjectGetPrivateData obj+ gobjectSetPrivateData obj (transform private)++-- | Add a Haskell object-valued property to the given object class.+gobjectInstallProperty :: DerivedGObject o =>+ GObjectClass -> PropertyInfo o a -> IO ()+gobjectInstallProperty klass propInfo = do+ pspec <- gParamSpecValue propInfo+ withManagedPtr pspec $ \pspecPtr ->+ g_object_class_install_property klass 1 pspecPtr++-- | Add a `Foreign.C.CInt`-valued property to the given object class.+gobjectInstallCIntProperty :: DerivedGObject o =>+ GObjectClass -> CIntPropertyInfo o -> IO ()+gobjectInstallCIntProperty klass propInfo = do+ pspec <- gParamSpecCInt propInfo+ withManagedPtr pspec $ \pspecPtr ->+ g_object_class_install_property klass 1 pspecPtr++-- | Add a `CString`-valued property to the given object class.+gobjectInstallCStringProperty :: DerivedGObject o =>+ GObjectClass -> CStringPropertyInfo o -> IO ()+gobjectInstallCStringProperty klass propInfo = do+ pspec <- gParamSpecCString propInfo+ withManagedPtr pspec $ \pspecPtr ->+ g_object_class_install_property klass 1 pspecPtr++-- | Add a `${type gboolean}`-valued property to the given object class.+gobjectInstallGBooleanProperty :: DerivedGObject o =>+ GObjectClass -> GBooleanPropertyInfo o -> IO ()+gobjectInstallGBooleanProperty klass propInfo = do+ pspec <- gParamSpecGBoolean propInfo+ withManagedPtr pspec $ \pspecPtr ->+ g_object_class_install_property klass 1 pspecPtr
Data/GI/Base/GParamSpec.hsc view
@@ -1,23 +1,57 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}++-- | Management of `GParamSpec`s. module Data.GI.Base.GParamSpec- ( noGParamSpec+ ( -- * Memory management+ wrapGParamSpecPtr+ , newGParamSpecFromPtr+ , unrefGParamSpec+ , disownGParamSpec - , wrapGParamSpecPtr- , newGParamSpecFromPtr- , unrefGParamSpec- , disownGParamSpec- ) where+ -- * GParamSpec building+ , PropertyInfo(..)+ , gParamSpecValue+ , CStringPropertyInfo(..)+ , gParamSpecCString+ , CIntPropertyInfo(..)+ , gParamSpecCInt+ , GBooleanPropertyInfo(..)+ , gParamSpecGBoolean+ , GParamFlag(..) -import Foreign.Ptr+ -- * Get\/Set+ , PropGetSetter(..)+ , getGParamSpecGetterSetter+ ) where++import Foreign.C (CInt(..), CString)+import Foreign.Ptr (Ptr, FunPtr, castPtr, nullPtr)+import Foreign.StablePtr (newStablePtr, deRefStablePtr,+ castStablePtrToPtr, castPtrToStablePtr) import Control.Monad (void)+import Data.Coerce (coerce)+import Data.Int+import Data.Maybe (fromMaybe)+import Data.Text (Text) -import Data.GI.Base.ManagedPtr (newManagedPtr', withManagedPtr, disownManagedPtr)-import Data.GI.Base.BasicTypes (GParamSpec(..))+import Data.GI.Base.ManagedPtr (newManagedPtr', withManagedPtr,+ disownManagedPtr,+ newObject, withTransient)+import Data.GI.Base.BasicConversions (gflagsToWord, withTextCString)+import Data.GI.Base.BasicTypes (GObject, GParamSpec(..),+ GType(..), IsGFlag, ManagedPtr)+import Data.GI.Base.GQuark (GQuark(..), gQuarkFromString)+import Data.GI.Base.GType (gtypeStablePtr)+import qualified Data.GI.Base.GValue as GV+import Data.GI.Base.GValue (GValue(..), IsGValue(..), take_stablePtr) #include <glib-object.h> -noGParamSpec :: Maybe GParamSpec-noGParamSpec = Nothing- foreign import ccall "g_param_spec_ref_sink" g_param_spec_ref_sink :: Ptr GParamSpec -> IO (Ptr GParamSpec) foreign import ccall "g_param_spec_ref" g_param_spec_ref ::@@ -50,3 +84,318 @@ -- collected. disownGParamSpec :: GParamSpec -> IO (Ptr GParamSpec) disownGParamSpec = disownManagedPtr++{- | Flags controlling the behaviour of the the parameters. -}+data GParamFlag = GParamReadable+ {- ^ the parameter is readable -}+ | GParamWritable+ {- ^ the parameter is writable -}+ | GParamConstruct+ {- ^ the parameter will be set upon object construction -}+ | GParamConstructOnly+ {- ^ the parameter can only be set upon object construction -}+ | GParamExplicitNotify+ {- ^ calls to 'GI.GObject.Objects.Object.objectSetProperty' for this+property will not automatically result in a \"notify\" signal being+emitted: the implementation must call+'GI.GObject.Objects.Object.objectNotify' themselves in case the+property actually changes. -}+ | AnotherGParamFlag Int+ -- ^ Catch-all for unknown values+ deriving (Show, Eq)++instance Enum GParamFlag where+ fromEnum GParamReadable = #const G_PARAM_READABLE+ fromEnum GParamWritable = #const G_PARAM_WRITABLE+ fromEnum GParamConstruct = #const G_PARAM_CONSTRUCT+ fromEnum GParamConstructOnly = #const G_PARAM_CONSTRUCT_ONLY+ fromEnum GParamExplicitNotify = #const G_PARAM_EXPLICIT_NOTIFY+ fromEnum (AnotherGParamFlag k) = k++ toEnum (#const G_PARAM_READABLE) = GParamReadable+ toEnum (#const G_PARAM_WRITABLE) = GParamWritable+ toEnum (#const G_PARAM_CONSTRUCT) = GParamConstruct+ toEnum (#const G_PARAM_CONSTRUCT_ONLY) = GParamConstructOnly+ toEnum (#const G_PARAM_EXPLICIT_NOTIFY) = GParamExplicitNotify+ toEnum k = AnotherGParamFlag k++instance Ord GParamFlag where+ compare a b = compare (fromEnum a) (fromEnum b)++instance IsGFlag GParamFlag++-- | Default set of flags when constructing properties.+defaultFlags :: Num a => a+defaultFlags = gflagsToWord [GParamReadable, GParamWritable,+ GParamExplicitNotify]++-- | Low-level getter and setter for the property.+data PropGetSetter o = PropGetSetter+ { propGetter :: Ptr o -> Ptr GValue -> IO ()+ , propSetter :: Ptr o -> Ptr GValue -> IO ()+ }++-- | The `GQuark` pointing to the setter and getter of the property.+pspecQuark :: IO (GQuark (PropGetSetter o))+pspecQuark = gQuarkFromString "haskell-gi-get-set"++-- | The basic constructor for a GObject. They are all isomorphic.+newtype GObjectConstructor = GObjectConstructor (ManagedPtr GObjectConstructor)++-- | Construct a copy of the object from the given pointer.+objectFromPtr :: forall a o. GObject o => Ptr a -> IO o+objectFromPtr objPtr = newObject @o @o (coerce @_ @(ManagedPtr o -> o) GObjectConstructor) (castPtr objPtr)++-- | Wrap a Haskell getter/setter into a lower level one.+wrapGetSet :: forall o a. (GObject o, IsGValue a) =>+ (o -> IO a) -- ^ Haskell side getter+ -> (o -> a -> IO ()) -- ^ Haskell side setter+ -> (Ptr GValue -> a -> IO ()) -- ^ Setter for the `GValue`+ -> PropGetSetter o+wrapGetSet getter setter gvalueSetter = PropGetSetter {+ propGetter = \objPtr destPtr -> do+ value <- objectFromPtr objPtr >>= getter+ gvalueSetter destPtr value+ , propSetter = \objPtr newGValuePtr ->+ withTransient newGValuePtr $ \newGValue -> do+ obj <- objectFromPtr objPtr+ value <- GV.fromGValue newGValue+ setter obj value+ }++-- | Information on a property encoding a Haskell value. Note that+-- from the C side this property will appear as an opaque pointer. Use+-- the specialized constructors below for creating properties+-- meaningful from the C side.+--+-- A property name consists of segments consisting of ASCII letters+-- and digits, separated by either the \'-\' or \'_\' character. The+-- first character of a property name must be a letter. Names which+-- violate these rules lead to undefined behaviour.+--+-- When creating and looking up a property, either separator can be+-- used, but they cannot be mixed. Using \'-\' is considerably more+-- efficient and in fact required when using property names as detail+-- strings for signals.+--+-- Beyond the name, properties have two more descriptive strings+-- associated with them, the @nick@, which should be suitable for use+-- as a label for the property in a property editor, and the @blurb@,+-- which should be a somewhat longer description, suitable for e.g. a+-- tooltip. The @nick@ and @blurb@ should ideally be localized.+data PropertyInfo o a = PropertyInfo+ { name :: Text -- ^ Identifier for the property.+ , nick :: Text -- ^ Identifier for display to the user.+ , blurb :: Text -- ^ Description of the property.+ , setter :: o -> a -> IO () -- ^ Handler invoked when the+ -- property is being set.+ , getter :: o -> IO a -- ^ Handler that returns the current+ -- value of the property.+ , flags :: Maybe [GParamFlag] -- ^ Set of flags, or `Nothing` for+ -- the default set of flags.+ }++foreign import ccall g_param_spec_boxed ::+ CString -> CString -> CString -> GType -> CInt -> IO (Ptr GParamSpec)++-- | Create a `GParamSpec` for a Haskell value.+gParamSpecValue :: forall o a. GObject o => PropertyInfo o a -> IO GParamSpec+gParamSpecValue (PropertyInfo {..}) =+ withTextCString name $ \cname ->+ withTextCString nick $ \cnick ->+ withTextCString blurb $ \cblurb -> do+ pspecPtr <- g_param_spec_boxed cname cnick cblurb+ gtypeStablePtr+ (maybe defaultFlags gflagsToWord flags)+ quark <- pspecQuark @o+ gParamSpecSetQData pspecPtr quark+ (PropGetSetter { propGetter = getter', propSetter = setter'})+ wrapGParamSpecPtr pspecPtr+ where+ getter' :: Ptr o -> Ptr GValue -> IO ()+ getter' objPtr destPtr = do+ stablePtr <- objectFromPtr objPtr >>= getter >>= newStablePtr+ take_stablePtr destPtr stablePtr++ setter' :: Ptr o -> (Ptr GValue) -> IO ()+ setter' objPtr gvPtr = withTransient gvPtr $ \gv -> do+ obj <- objectFromPtr objPtr+ val <- GV.fromGValue gv >>= deRefStablePtr+ setter obj val++-- | Information on a property of type `CInt` to be registered. A+-- property name consists of segments consisting of ASCII letters and+-- digits, separated by either the \'-\' or \'_\' character. The first+-- character of a property name must be a letter. Names which violate+-- these rules lead to undefined behaviour.+--+-- When creating and looking up a property, either separator can be+-- used, but they cannot be mixed. Using \'-\' is considerably more+-- efficient and in fact required when using property names as detail+-- strings for signals.+--+-- Beyond the name, properties have two more descriptive strings+-- associated with them, the @nick@, which should be suitable for use+-- as a label for the property in a property editor, and the @blurb@,+-- which should be a somewhat longer description, suitable for e.g. a+-- tooltip. The @nick@ and @blurb@ should ideally be localized.+data CIntPropertyInfo o = CIntPropertyInfo+ { name :: Text -- ^ Identifier for the property.+ , nick :: Text -- ^ Identifier for display to the user.+ , blurb :: Text -- ^ Description of the property.+ , defaultValue :: CInt -- ^ Default value.+ , setter :: o -> CInt -> IO () -- ^ Handler invoked when the+ -- property is being set.+ , getter :: o -> IO CInt -- ^ Handler that returns the current+ -- value of the property.+ , flags :: Maybe [GParamFlag] -- ^ Set of flags, or `Nothing` for+ -- the default set of flags.+ , minValue :: Maybe CInt -- ^ Minimum value, or `Nothing`,+ -- which would be replaced by+ -- @MININT@.+ , maxValue :: Maybe CInt -- ^ Maximum value, or `Nothing`,+ -- which would be replaced by+ -- @MAXINT@.+ }++foreign import ccall g_param_spec_int ::+ CString -> CString -> CString -> CInt -> CInt -> CInt -> CInt+ -> IO (Ptr GParamSpec)++-- | Create a `GParamSpec` for an integer param.+gParamSpecCInt :: GObject o => CIntPropertyInfo o -> IO GParamSpec+gParamSpecCInt (CIntPropertyInfo {..}) =+ withTextCString name $ \cname ->+ withTextCString nick $ \cnick ->+ withTextCString blurb $ \cblurb -> do+ pspecPtr <- g_param_spec_int cname cnick cblurb+ (fromMaybe minBound minValue)+ (fromMaybe maxBound maxValue)+ defaultValue+ (maybe defaultFlags gflagsToWord flags)+ quark <- pspecQuark+ gParamSpecSetQData pspecPtr quark (wrapGetSet getter setter gvalueSet_)+ wrapGParamSpecPtr pspecPtr++-- | Information on a property of type `Text` to be registered. A+-- property name consists of segments consisting of ASCII letters and+-- digits, separated by either the \'-\' or \'_\' character. The first+-- character of a property name must be a letter. Names which violate+-- these rules lead to undefined behaviour.+--+-- When creating and looking up a property, either separator can be+-- used, but they cannot be mixed. Using \'-\' is considerably more+-- efficient and in fact required when using property names as detail+-- strings for signals.+--+-- Beyond the name, properties have two more descriptive strings+-- associated with them, the @nick@, which should be suitable for use+-- as a label for the property in a property editor, and the @blurb@,+-- which should be a somewhat longer description, suitable for e.g. a+-- tooltip. The @nick@ and @blurb@ should ideally be localized.+data CStringPropertyInfo o = CStringPropertyInfo+ { name :: Text+ , nick :: Text+ , blurb :: Text+ , defaultValue :: Maybe Text+ , flags :: Maybe [GParamFlag]+ , setter :: o -> Maybe Text -> IO ()+ , getter :: o -> IO (Maybe Text)+ }++foreign import ccall g_param_spec_string ::+ CString -> CString -> CString -> CString -> CInt -> IO (Ptr GParamSpec)++-- | Create a `GParamSpec` for a string param.+gParamSpecCString :: GObject o => CStringPropertyInfo o -> IO GParamSpec+gParamSpecCString (CStringPropertyInfo {..}) =+ withTextCString name $ \cname ->+ withTextCString nick $ \cnick ->+ withTextCString blurb $ \cblurb -> do+ pspecPtr <- case defaultValue of+ Nothing -> g_param_spec_string cname cnick cblurb nullPtr+ (maybe defaultFlags gflagsToWord flags)+ Just value ->+ withTextCString value $ \cdefault ->+ g_param_spec_string cname cnick cblurb cdefault+ (maybe defaultFlags gflagsToWord flags)+ quark <- pspecQuark+ gParamSpecSetQData pspecPtr quark (wrapGetSet getter setter gvalueSet_)+ wrapGParamSpecPtr pspecPtr++-- | Information on a property of type `type gboolean` to be registered. A+-- property name consists of segments consisting of ASCII letters and+-- digits, separated by either the \'-\' or \'_\' character. The first+-- character of a property name must be a letter. Names which violate+-- these rules lead to undefined behaviour.+--+-- When creating and looking up a property, either separator can be+-- used, but they cannot be mixed. Using \'-\' is considerably more+-- efficient and in fact required when using property names as detail+-- strings for signals.+--+-- Beyond the name, properties have two more descriptive strings+-- associated with them, the @nick@, which should be suitable for use+-- as a label for the property in a property editor, and the @blurb@,+-- which should be a somewhat longer description, suitable for e.g. a+-- tooltip. The @nick@ and @blurb@ should ideally be localized.+data GBooleanPropertyInfo o = GBooleanPropertyInfo+ { name :: Text+ , nick :: Text+ , blurb :: Text+ , defaultValue :: Bool+ , flags :: Maybe [GParamFlag]+ , setter :: o -> Bool -> IO ()+ , getter :: o -> IO (Bool)+ }++foreign import ccall g_param_spec_boolean ::+ CString -> CString -> CString -> #{type gboolean} -> CInt -> IO (Ptr GParamSpec)++-- | Create a `GParamSpec` for a bool param.+gParamSpecGBoolean :: GObject o => GBooleanPropertyInfo o -> IO GParamSpec+gParamSpecGBoolean (GBooleanPropertyInfo {..}) =+ withTextCString name $ \cname ->+ withTextCString nick $ \cnick ->+ withTextCString blurb $ \cblurb -> do+ pspecPtr <- g_param_spec_boolean cname cnick cblurb+ ((fromIntegral . fromEnum) defaultValue)+ (maybe defaultFlags gflagsToWord flags)+ quark <- pspecQuark+ gParamSpecSetQData pspecPtr quark (wrapGetSet getter setter gvalueSet_)+ wrapGParamSpecPtr pspecPtr++foreign import ccall g_param_spec_set_qdata_full ::+ Ptr GParamSpec -> GQuark a -> Ptr b -> FunPtr (Ptr c -> IO ()) -> IO ()++foreign import ccall "&hs_free_stable_ptr" ptr_to_hs_free_stable_ptr ::+ FunPtr (Ptr a -> IO ())++-- | Set the given user data on the `GParamSpec`.+gParamSpecSetQData :: Ptr GParamSpec -> GQuark a -> a -> IO ()+gParamSpecSetQData pspecPtr quark d = do+ ptr <- newStablePtr d+ g_param_spec_set_qdata_full pspecPtr quark+ (castStablePtrToPtr ptr)+ ptr_to_hs_free_stable_ptr++foreign import ccall g_param_spec_get_qdata ::+ Ptr GParamSpec -> GQuark a -> IO (Ptr b)++-- | Get the user data for the given `GQuark` on the `GParamSpec`.+gParamSpecGetQData :: Ptr GParamSpec -> GQuark a -> IO (Maybe a)+gParamSpecGetQData pspecPtr quark = do+ ptr <- g_param_spec_get_qdata pspecPtr quark+ if ptr /= nullPtr+ then Just <$> deRefStablePtr (castPtrToStablePtr ptr)+ else return Nothing++-- | Attempt to get the Haskell setter and getter for the given+-- `GParamSpec`. This will only be possible if the `GParamSpec` was+-- created with one of the functions above, if this is not the case+-- the function will return `Nothing`.+getGParamSpecGetterSetter :: forall o. Ptr GParamSpec ->+ IO (Maybe (PropGetSetter o))+getGParamSpecGetterSetter pspecPtr = do+ quark <- pspecQuark @o+ gParamSpecGetQData pspecPtr quark
+ Data/GI/Base/GQuark.hsc view
@@ -0,0 +1,22 @@+-- | Basic support for `GQuark`s.+module Data.GI.Base.GQuark+ ( GQuark(..)+ , gQuarkFromString+ ) where++import Data.Text (Text)+import Data.Word+import Foreign.C (CString)++import Data.GI.Base.BasicConversions (withTextCString)++#include <glib-object.h>++-- | A `GQuark`, which is simply an integer.+newtype GQuark a = GQuark (#type GQuark)++foreign import ccall g_quark_from_string :: CString -> IO (GQuark a)++-- | Construct a GQuark from the given string.+gQuarkFromString :: Text -> IO (GQuark a)+gQuarkFromString text = withTextCString text g_quark_from_string
Data/GI/Base/GType.hsc view
@@ -1,11 +1,8 @@+{-# LANGUAGE OverloadedStrings #-}+ -- | Basic `GType`s. module Data.GI.Base.GType- ( GType(..)- , CGType-- , gtypeName-- , gtypeString+ ( gtypeString , gtypePointer , gtypeInt , gtypeUInt@@ -16,6 +13,7 @@ , gtypeFloat , gtypeDouble , gtypeBoolean+ , gtypeError , gtypeGType , gtypeStrv , gtypeBoxed@@ -23,27 +21,16 @@ , gtypeVariant , gtypeByteArray , gtypeInvalid+ , gtypeParam++ , gtypeStablePtr+ , gtypeHValue ) where -import Data.Word-import Foreign.C.String (CString, peekCString)+import Data.GI.Base.BasicTypes (GType(..), CGType) #include <glib-object.h> --- | A type identifier in the GLib type system. This is the low-level--- type associated with the representation in memory, when using this--- on the Haskell side use `GType` below.-type CGType = #type GType---- | A newtype for use on the haskell side.-newtype GType = GType {gtypeToCGType :: CGType}--foreign import ccall "g_type_name" g_type_name :: GType -> IO CString---- | Get the name assigned to the given `GType`.-gtypeName :: GType -> IO String-gtypeName gtype = g_type_name gtype >>= peekCString- {-| [Note: compile-time vs run-time GTypes] Notice that there are two types of GType's: the fundamental ones,@@ -68,19 +55,19 @@ gtypePointer :: GType gtypePointer = GType #const G_TYPE_POINTER --- | `GType` for signed integers (`gint` or `gint32`).+-- | `GType` for signed integers (@gint@ or @gint32@). gtypeInt :: GType gtypeInt = GType #const G_TYPE_INT --- | `GType` for unsigned integers (`guint` or `guint32`).+-- | `GType` for unsigned integers (@guint@ or @guint32@). gtypeUInt :: GType gtypeUInt = GType #const G_TYPE_UINT --- | `GType` for `glong`.+-- | `GType` for @glong@. gtypeLong :: GType gtypeLong = GType #const G_TYPE_LONG --- | `GType` for `gulong`.+-- | `GType` for @gulong@. gtypeULong :: GType gtypeULong = GType #const G_TYPE_ULONG @@ -104,11 +91,11 @@ gtypeBoolean :: GType gtypeBoolean = GType #const G_TYPE_BOOLEAN --- | `GType` corresponding to a `BoxedObject`.+-- | `GType` corresponding to a boxed object. gtypeBoxed :: GType gtypeBoxed = GType #const G_TYPE_BOXED --- | `GType` corresponding to a `GObject`.+-- | `GType` corresponding to a @GObject@. gtypeObject :: GType gtypeObject = GType #const G_TYPE_OBJECT @@ -117,10 +104,14 @@ gtypeInvalid :: GType gtypeInvalid = GType #const G_TYPE_INVALID --- | The `GType` corresponding to a `GVariant`.+-- | The `GType` corresponding to a @GVariant@. gtypeVariant :: GType gtypeVariant = GType #const G_TYPE_VARIANT +-- | The `GType` corresponding to 'Data.GI.Base.BasicTypes.GParamSpec'.+gtypeParam :: GType+gtypeParam = GType #const G_TYPE_PARAM+ {- Run-time types -} foreign import ccall "g_gtype_get_type" g_gtype_get_type :: CGType@@ -137,6 +128,24 @@ foreign import ccall "g_byte_array_get_type" g_byte_array_get_type :: CGType --- | `GType` for a boxed type holding a `GByteArray`.+-- | `GType` for a boxed type holding a @GByteArray@. gtypeByteArray :: GType gtypeByteArray = GType g_byte_array_get_type++foreign import ccall haskell_gi_StablePtr_get_type :: CGType++-- | The `GType` for boxed `StablePtr`s.+gtypeStablePtr :: GType+gtypeStablePtr = GType haskell_gi_StablePtr_get_type++foreign import ccall haskell_gi_HaskellValue_get_type :: CGType++-- | The `GType` for a generic Haskell value.+gtypeHValue :: GType+gtypeHValue = GType haskell_gi_HaskellValue_get_type++foreign import ccall "g_error_get_type" g_error_get_type :: CGType++-- | The `GType` corresponding to 'Data.GI.Base.GError.GError'.+gtypeError :: GType+gtypeError = GType g_error_get_type
+ Data/GI/Base/GValue.hs view
@@ -0,0 +1,541 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE DataKinds, TypeFamilies #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE AllowAmbiguousTypes #-}++module Data.GI.Base.GValue+ (+ -- * Constructing GValues+ GValue(..)+ , IsGValue(..)+ , toGValue+ , fromGValue+ , GValueConstruct(..)+ , ptr_to_gvalue_free++ , newGValue+ , buildGValue+ , disownGValue+ , noGValue+ , newGValueFromPtr+ , wrapGValuePtr+ , unsetGValue+ , gvalueType++ -- * Packing GValues into arrays+ , packGValueArray+ , unpackGValueArrayWithLength+ , mapGValueArrayWithLength++ -- * Packing Haskell values into GValues+ , HValue(..)++ -- * Setters and getters+ , set_object+ , get_object+ , set_boxed+ , get_boxed+ , set_variant+ , get_variant+ , set_enum+ , get_enum+ , set_flags+ , get_flags+ , set_stablePtr+ , get_stablePtr+ , take_stablePtr+ , set_param+ , get_param+ , set_hvalue+ , get_hvalue+ ) where++import Control.Monad.IO.Class (MonadIO, liftIO)++import Data.Coerce (coerce)+import Data.Word+import Data.Int+import Data.Text (Text, pack, unpack)++import Foreign.C.Types (CInt(..), CUInt(..), CFloat(..), CDouble(..),+ CLong(..), CULong(..))+import Foreign.C.String (CString)+import Foreign.Ptr (Ptr, nullPtr, plusPtr, FunPtr)+import Foreign.StablePtr (StablePtr, castStablePtrToPtr, castPtrToStablePtr,+ newStablePtr, deRefStablePtr)+import Type.Reflection (typeRep, TypeRep)+import System.IO (hPutStrLn, stderr)++import Data.Dynamic (toDyn, fromDynamic, Typeable)+import Data.GI.Base.BasicTypes+import Data.GI.Base.BasicConversions (cstringToText, textToCString)+import Data.GI.Base.GType+import Data.GI.Base.ManagedPtr+import Data.GI.Base.Utils (callocBytes, freeMem)+import Data.GI.Base.Internal.CTypes (cgvalueSize)+import Data.GI.Base.Overloading (ParentTypes, HasParentTypes)++-- | Haskell-side representation of a @GValue@.+newtype GValue = GValue (ManagedPtr GValue)++-- | A pointer to a function freeing GValues.+foreign import ccall "&haskell_gi_gvalue_free" ptr_to_gvalue_free ::+ FunPtr (Ptr GValue -> IO ())++-- | There are no types in the bindings that a `GValue` can be safely+-- cast to.+type instance ParentTypes GValue = '[]+instance HasParentTypes GValue++foreign import ccall unsafe "g_value_get_type" c_g_value_get_type ::+ IO GType++-- | Find the associated `GType` for `GValue`.+instance TypedObject GValue where+ glibType = c_g_value_get_type++-- | `GValue`s are registered as boxed in the GLib type system.+instance GBoxed GValue++foreign import ccall "g_value_init" g_value_init ::+ Ptr GValue -> CGType -> IO (Ptr GValue)++-- | A type holding a `GValue` with an associated label. It is+-- parameterized by a phantom type encoding the target type for the+-- `GValue` (useful when constructing properties).+data GValueConstruct o = GValueConstruct String GValue++-- | Build a new, empty, `GValue` of the given type.+newGValue :: GType -> IO GValue+newGValue (GType gtype) = do+ gvptr <- callocBytes cgvalueSize+ _ <- g_value_init gvptr gtype+ gv <- wrapBoxed GValue gvptr+ return $! gv++-- | Take ownership of a passed in 'Ptr'.+wrapGValuePtr :: Ptr GValue -> IO GValue+wrapGValuePtr ptr = wrapBoxed GValue ptr++-- | Construct a Haskell wrapper for the given 'GValue', making a+-- copy.+newGValueFromPtr :: Ptr GValue -> IO GValue+newGValueFromPtr ptr = newBoxed GValue ptr++-- | A convenience function for building a new GValue and setting the+-- initial value.+buildGValue :: GType -> (Ptr GValue -> a -> IO ()) -> a -> IO GValue+buildGValue gtype setter val = do+ gv <- newGValue gtype+ withManagedPtr gv $ \gvPtr -> setter gvPtr val+ return gv++-- | Disown a `GValue`, i.e. do not unref the underlying object when+-- the Haskell object is garbage collected.+disownGValue :: GValue -> IO (Ptr GValue)+disownGValue = disownManagedPtr++foreign import ccall "_haskell_gi_g_value_get_type" g_value_get_type :: Ptr GValue -> IO CGType++-- | Return the `GType` contained by a `GValue`.+gvalueType :: GValue -> IO GType+gvalueType gv = withManagedPtr gv $ \gvptr -> do+ cgtype <- g_value_get_type gvptr+ return (GType cgtype)++foreign import ccall "g_value_unset" g_value_unset :: Ptr GValue -> IO ()++-- | Unset the `GValue`, freeing all resources associated to it.+unsetGValue :: Ptr GValue -> IO ()+unsetGValue = g_value_unset++-- | A convenient alias for @Nothing :: Maybe GValue@.+noGValue :: Maybe GValue+noGValue = Nothing++-- | Class for types that can be marshaled back and forth between+-- Haskell values and `GValue`s. These are low-level methods, you+-- might want to use `toGValue` and `fromGValue` instead for a higher+-- level interface.+class IsGValue a where+ gvalueGType_ :: IO GType -- ^ `GType` for the `GValue`+ -- containing values of this type.+ gvalueSet_ :: Ptr GValue -> a -> IO () -- ^ Set the `GValue` to+ -- the given Haskell+ -- value.+ gvalueGet_ :: Ptr GValue -> IO a -- ^ Get the Haskel value inside+ -- the `GValue`.++-- | Create a `GValue` from the given Haskell value.+toGValue :: forall a m. (IsGValue a, MonadIO m) => a -> m GValue+toGValue val = liftIO $ do+ gvptr <- callocBytes cgvalueSize+ GType gtype <- gvalueGType_ @a+ _ <- g_value_init gvptr gtype+ gvalueSet_ gvptr val+ gv <- wrapBoxed GValue gvptr+ return $! gv++-- | Create a Haskell object out of the given `GValue`.+fromGValue :: (IsGValue a, MonadIO m) => GValue -> m a+fromGValue gv = liftIO $ withManagedPtr gv gvalueGet_++instance IsGValue (Maybe String) where+ gvalueGType_ = return gtypeString+ gvalueSet_ gvPtr mstr = set_string gvPtr (pack <$> mstr)+ gvalueGet_ v = (fmap unpack) <$> get_string v++instance IsGValue (Maybe Text) where+ gvalueGType_ = return gtypeString+ gvalueSet_ = set_string+ gvalueGet_ = get_string++instance IsGValue (Ptr a) where+ gvalueGType_ = return gtypePointer+ gvalueSet_ = set_pointer+ gvalueGet_ = get_pointer++instance IsGValue Int32 where+ gvalueGType_ = return gtypeInt+ gvalueSet_ = set_int32+ gvalueGet_ = get_int32++instance IsGValue Word32 where+ gvalueGType_ = return gtypeUInt+ gvalueSet_ = set_uint32+ gvalueGet_ = get_uint32++instance IsGValue CInt where+ gvalueGType_ = return gtypeInt+ gvalueSet_ = set_int+ gvalueGet_ = get_int++instance IsGValue CUInt where+ gvalueGType_ = return gtypeUInt+ gvalueSet_ = set_uint+ gvalueGet_ = get_uint++instance IsGValue CLong where+ gvalueGType_ = return gtypeLong+ gvalueSet_ = set_long+ gvalueGet_ = get_long++instance IsGValue CULong where+ gvalueGType_ = return gtypeULong+ gvalueSet_ = set_ulong+ gvalueGet_ = get_ulong++instance IsGValue Int64 where+ gvalueGType_ = return gtypeInt64+ gvalueSet_ = set_int64+ gvalueGet_ = get_int64++instance IsGValue Word64 where+ gvalueGType_ = return gtypeUInt64+ gvalueSet_ = set_uint64+ gvalueGet_ = get_uint64++instance IsGValue Float where+ gvalueGType_ = return gtypeFloat+ gvalueSet_ = set_float+ gvalueGet_ = get_float++instance IsGValue Double where+ gvalueGType_ = return gtypeDouble+ gvalueSet_ = set_double+ gvalueGet_ = get_double++instance IsGValue Bool where+ gvalueGType_ = return gtypeBoolean+ gvalueSet_ = set_boolean+ gvalueGet_ = get_boolean++instance IsGValue GType where+ gvalueGType_ = return gtypeGType+ gvalueSet_ = set_gtype+ gvalueGet_ = get_gtype++instance IsGValue (StablePtr a) where+ gvalueGType_ = return gtypeStablePtr+ gvalueSet_ = set_stablePtr+ gvalueGet_ = get_stablePtr++instance IsGValue (Maybe GParamSpec) where+ gvalueGType_ = return gtypeParam+ gvalueSet_ = set_param+ gvalueGet_ = get_param++instance Typeable a => IsGValue (HValue a) where+ gvalueGType_ = return gtypeHValue+ gvalueSet_ = set_hvalue+ gvalueGet_ = get_hvalue++foreign import ccall "g_value_set_string" _set_string ::+ Ptr GValue -> CString -> IO ()+foreign import ccall "g_value_get_string" _get_string ::+ Ptr GValue -> IO CString++set_string :: Ptr GValue -> Maybe Text -> IO ()+set_string ptr maybeStr = do+ cstr <- case maybeStr of+ Just str -> textToCString str+ Nothing -> return nullPtr+ _set_string ptr cstr+ freeMem cstr++get_string :: Ptr GValue -> IO (Maybe Text)+get_string gvptr = do+ cstr <- _get_string gvptr+ if cstr /= nullPtr+ then Just <$> cstringToText cstr+ else return Nothing++foreign import ccall unsafe "g_value_set_pointer" set_pointer ::+ Ptr GValue -> Ptr a -> IO ()+foreign import ccall unsafe "g_value_get_pointer" get_pointer ::+ Ptr GValue -> IO (Ptr b)++foreign import ccall unsafe "g_value_set_int" set_int ::+ Ptr GValue -> CInt -> IO ()+foreign import ccall unsafe "g_value_get_int" get_int ::+ Ptr GValue -> IO CInt++set_int32 :: Ptr GValue -> Int32 -> IO ()+set_int32 gv n = set_int gv (coerce n)++get_int32 :: Ptr GValue -> IO Int32+get_int32 gv = coerce <$> get_int gv++foreign import ccall unsafe "g_value_set_uint" set_uint ::+ Ptr GValue -> CUInt -> IO ()+foreign import ccall unsafe "g_value_get_uint" get_uint ::+ Ptr GValue -> IO CUInt++set_uint32 :: Ptr GValue -> Word32 -> IO ()+set_uint32 gv n = set_uint gv (coerce n)++get_uint32 :: Ptr GValue -> IO Word32+get_uint32 gv = coerce <$> get_uint gv++foreign import ccall unsafe "g_value_set_long" set_long ::+ Ptr GValue -> CLong -> IO ()+foreign import ccall unsafe "g_value_get_long" get_long ::+ Ptr GValue -> IO CLong++foreign import ccall unsafe "g_value_set_ulong" set_ulong ::+ Ptr GValue -> CULong -> IO ()+foreign import ccall unsafe "g_value_get_ulong" get_ulong ::+ Ptr GValue -> IO CULong++foreign import ccall unsafe "g_value_set_int64" set_int64 ::+ Ptr GValue -> Int64 -> IO ()+foreign import ccall unsafe "g_value_get_int64" get_int64 ::+ Ptr GValue -> IO Int64++foreign import ccall unsafe "g_value_set_uint64" set_uint64 ::+ Ptr GValue -> Word64 -> IO ()+foreign import ccall unsafe "g_value_get_uint64" get_uint64 ::+ Ptr GValue -> IO Word64++foreign import ccall unsafe "g_value_set_float" _set_float ::+ Ptr GValue -> CFloat -> IO ()+foreign import ccall unsafe "g_value_get_float" _get_float ::+ Ptr GValue -> IO CFloat++set_float :: Ptr GValue -> Float -> IO ()+set_float gv f = _set_float gv (realToFrac f)++get_float :: Ptr GValue -> IO Float+get_float gv = realToFrac <$> _get_float gv++foreign import ccall unsafe "g_value_set_double" _set_double ::+ Ptr GValue -> CDouble -> IO ()+foreign import ccall unsafe "g_value_get_double" _get_double ::+ Ptr GValue -> IO CDouble++set_double :: Ptr GValue -> Double -> IO ()+set_double gv d = _set_double gv (realToFrac d)++get_double :: Ptr GValue -> IO Double+get_double gv = realToFrac <$> _get_double gv++foreign import ccall unsafe "g_value_set_boolean" _set_boolean ::+ Ptr GValue -> CInt -> IO ()+foreign import ccall unsafe "g_value_get_boolean" _get_boolean ::+ Ptr GValue -> IO CInt++set_boolean :: Ptr GValue -> Bool -> IO ()+set_boolean gv b = _set_boolean gv (fromIntegral $ fromEnum b)++get_boolean :: Ptr GValue -> IO Bool+get_boolean gv = (/= 0) <$> _get_boolean gv++foreign import ccall unsafe "g_value_set_gtype" _set_gtype ::+ Ptr GValue -> CGType -> IO ()+foreign import ccall unsafe "g_value_get_gtype" _get_gtype ::+ Ptr GValue -> IO CGType++set_gtype :: Ptr GValue -> GType -> IO ()+set_gtype gv (GType g) = _set_gtype gv g++get_gtype :: Ptr GValue -> IO GType+get_gtype gv = GType <$> _get_gtype gv++foreign import ccall "g_value_set_object" _set_object ::+ Ptr GValue -> Ptr a -> IO ()+foreign import ccall "g_value_get_object" _get_object ::+ Ptr GValue -> IO (Ptr a)++set_object :: GObject a => Ptr GValue -> Ptr a -> IO ()+set_object = _set_object++get_object :: GObject a => Ptr GValue -> IO (Ptr a)+get_object = _get_object++foreign import ccall "g_value_set_boxed" set_boxed ::+ Ptr GValue -> Ptr a -> IO ()+foreign import ccall "g_value_get_boxed" get_boxed ::+ Ptr GValue -> IO (Ptr b)+foreign import ccall "g_value_dup_boxed" dup_boxed ::+ Ptr GValue -> IO (Ptr b)++foreign import ccall "g_value_set_variant" set_variant ::+ Ptr GValue -> Ptr GVariant -> IO ()+foreign import ccall "g_value_get_variant" get_variant ::+ Ptr GValue -> IO (Ptr GVariant)++foreign import ccall unsafe "g_value_set_enum" set_enum ::+ Ptr GValue -> CUInt -> IO ()+foreign import ccall unsafe "g_value_get_enum" get_enum ::+ Ptr GValue -> IO CUInt++foreign import ccall unsafe "g_value_set_flags" set_flags ::+ Ptr GValue -> CUInt -> IO ()+foreign import ccall unsafe "g_value_get_flags" get_flags ::+ Ptr GValue -> IO CUInt++-- | Set the value of `GValue` containing a `StablePtr`+set_stablePtr :: Ptr GValue -> StablePtr a -> IO ()+set_stablePtr gv ptr = set_boxed gv (castStablePtrToPtr ptr)++foreign import ccall g_value_take_boxed :: Ptr GValue -> Ptr a -> IO ()++-- | Like `set_stablePtr`, but the `GValue` takes ownership of the `StablePtr`+take_stablePtr :: Ptr GValue -> StablePtr a -> IO ()+take_stablePtr gvPtr stablePtr =+ g_value_take_boxed gvPtr (castStablePtrToPtr stablePtr)++-- | Get (a freshly allocated copy of) the value of a `GValue`+-- containing a `StablePtr`+get_stablePtr :: Ptr GValue -> IO (StablePtr a)+get_stablePtr gv = castPtrToStablePtr <$> dup_boxed gv++foreign import ccall g_value_copy :: Ptr GValue -> Ptr GValue -> IO ()++-- | Pack the given list of GValues contiguously into a C array+packGValueArray :: [GValue] -> IO (Ptr GValue)+packGValueArray gvalues = withManagedPtrList gvalues $ \ptrs -> do+ let nitems = length ptrs+ mem <- callocBytes $ cgvalueSize * nitems+ fill mem ptrs+ return mem+ where fill :: Ptr GValue -> [Ptr GValue] -> IO ()+ fill _ [] = return ()+ fill ptr (x:xs) = do+ gtype <- g_value_get_type x+ _ <- g_value_init ptr gtype+ g_value_copy x ptr+ fill (ptr `plusPtr` cgvalueSize) xs++-- | Unpack an array of contiguous GValues into a list of GValues.+unpackGValueArrayWithLength :: Integral a =>+ a -> Ptr GValue -> IO [GValue]+unpackGValueArrayWithLength nitems gvalues = go (fromIntegral nitems) gvalues+ where go :: Int -> Ptr GValue -> IO [GValue]+ go 0 _ = return []+ go n ptr = do+ gv <- callocBytes cgvalueSize+ gtype <- g_value_get_type ptr+ _ <- g_value_init gv gtype+ g_value_copy ptr gv+ wrapped <- wrapGValuePtr gv+ (wrapped :) <$> go (n-1) (ptr `plusPtr` cgvalueSize)++-- | Map over the `GValue`s inside a C array.+mapGValueArrayWithLength :: Integral a =>+ a -> (Ptr GValue -> IO c) -> Ptr GValue -> IO ()+mapGValueArrayWithLength nvalues f arrayPtr+ | (arrayPtr == nullPtr) = return ()+ | (nvalues <= 0) = return ()+ | otherwise = go (fromIntegral nvalues) arrayPtr+ where go :: Int -> Ptr GValue -> IO ()+ go 0 _ = return ()+ go n ptr = do+ _ <- f ptr+ go (n-1) (ptr `plusPtr` cgvalueSize)++foreign import ccall unsafe "g_value_set_param" _set_param ::+ Ptr GValue -> Ptr GParamSpec -> IO ()+foreign import ccall unsafe "g_value_get_param" _get_param ::+ Ptr GValue -> IO (Ptr GParamSpec)++-- | Set the value of `GValue` containing a `GParamSpec`+set_param :: Ptr GValue -> Maybe GParamSpec -> IO ()+set_param gv (Just ps) = withManagedPtr ps (_set_param gv)+set_param gv Nothing = _set_param gv nullPtr++foreign import ccall "g_param_spec_ref" g_param_spec_ref ::+ Ptr GParamSpec -> IO (Ptr GParamSpec)+foreign import ccall "&g_param_spec_unref" ptr_to_g_param_spec_unref ::+ FunPtr (Ptr GParamSpec -> IO ())++-- | Get the value of a `GValue` containing a `GParamSpec`+get_param :: Ptr GValue -> IO (Maybe GParamSpec)+get_param gv = do+ ptr <- _get_param gv+ if ptr == nullPtr+ then return Nothing+ else do+ fPtr <- g_param_spec_ref ptr >>= newManagedPtr' ptr_to_g_param_spec_unref+ return . Just $! GParamSpec fPtr++-- | A type isomorphic to `Maybe a`, used to indicate to+-- `fromGValue`/`toGValue` that we are packing a native Haskell value,+-- without attempting to marshall it to the corresponding C type.+data HValue a = HValue a -- ^ A packed value of type `a`+ | NoHValue -- ^ An empty `HValue`+ deriving (Show, Eq)++-- | Set the `GValue` to the given Haskell value.+set_hvalue :: Typeable a => Ptr GValue -> HValue a -> IO ()+set_hvalue gvPtr NoHValue = set_boxed gvPtr nullPtr+set_hvalue gvPtr (HValue v) = do+ sPtr <- newStablePtr (toDyn v)+ g_value_take_boxed gvPtr (castStablePtrToPtr sPtr)++-- | Get the value in the GValue, checking that the type is+-- `gtypeHValue`. Will return NULL and print a warning if the GValue+-- is of the wrong type.+foreign import ccall "haskell_gi_safe_get_boxed_haskell_value" safe_get_boxed_hvalue ::+ Ptr GValue -> IO (Ptr b)++-- | Read the Haskell value of the given type from the `GValue`. If+-- the `GValue` contains no value of the expected type, `NoHValue`+-- will be returned instead, and an error will be printed to stderr.+get_hvalue :: forall a. Typeable a => Ptr GValue -> IO (HValue a)+get_hvalue gvPtr = do+ hvaluePtr <- safe_get_boxed_hvalue gvPtr+ if hvaluePtr == nullPtr+ then return NoHValue+ else do+ dyn <- deRefStablePtr (castPtrToStablePtr hvaluePtr)+ case fromDynamic dyn of+ Nothing -> do+ hPutStrLn stderr ("HASKELL-GI: Unexpected type ‘" <> show dyn+ <> "’ inside the GValue at ‘" <> show gvPtr+ <> "’.\n\tExpected ‘" <> show (typeRep :: TypeRep a)+ <> "’.")+ return NoHValue+ Just val -> return (HValue val)
− Data/GI/Base/GValue.hsc
@@ -1,378 +0,0 @@-{-# LANGUAGE FlexibleInstances #-}-module Data.GI.Base.GValue- ( GValue(..)- , IsGValue(..)-- , newGValue -- Build a new, empty, GValue of the given type- , buildGValue -- Build a new GValue and initialize to the given value- , noGValue-- , GValueConstruct(..)-- , set_string- , get_string- , set_pointer- , get_pointer- , set_int- , get_int- , set_uint- , get_uint- , set_long- , get_long- , set_ulong- , get_ulong- , set_int32- , get_int32- , set_uint32- , get_uint32- , set_int64- , get_int64- , set_uint64- , get_uint64- , set_float- , get_float- , set_double- , get_double- , set_boolean- , get_boolean- , set_gtype- , get_gtype- , set_object- , get_object- , set_boxed- , get_boxed- , set_variant- , get_variant- , set_enum- , get_enum- , set_flags- , get_flags- ) where--#include <glib-object.h>--#if !MIN_VERSION_base(4,8,0)-import Control.Applicative ((<$>))-#endif--import Data.Coerce (coerce)-import Data.Word-import Data.Int-import Data.Text (Text, pack, unpack)--import Foreign.C.Types (CInt(..), CUInt(..), CFloat(..), CDouble(..),- CLong(..), CULong(..))-import Foreign.C.String (CString)-import Foreign.Ptr (Ptr, nullPtr)--import Data.GI.Base.BasicTypes-import Data.GI.Base.BasicConversions (cstringToText, textToCString)-import Data.GI.Base.ManagedPtr-import Data.GI.Base.Utils (callocBytes, freeMem)--newtype GValue = GValue (ManagedPtr GValue)--noGValue :: Maybe GValue-noGValue = Nothing--foreign import ccall unsafe "g_value_get_type" c_g_value_get_type ::- IO CGType--instance BoxedObject GValue where- boxedType _ = GType <$> c_g_value_get_type--foreign import ccall "g_value_init" g_value_init ::- Ptr GValue -> CGType -> IO (Ptr GValue)---- | A type holding a `GValue` with an associated label. It is--- parameterized by a phantom type encoding the target type for the--- `GValue` (useful when constructing properties).-data GValueConstruct o = GValueConstruct String GValue--newGValue :: GType -> IO GValue-newGValue (GType gtype) = do- gvptr <- callocBytes #size GValue- _ <- g_value_init gvptr gtype- gv <- wrapBoxed GValue gvptr- return $! gv---- Build a new GValue and set the initial value, just for convenience-buildGValue :: GType -> (GValue -> a -> IO ()) -> a -> IO GValue-buildGValue gtype setter val = do- gv <- newGValue gtype- setter gv val- return gv--class IsGValue a where- toGValue :: a -> IO GValue- fromGValue :: GValue -> IO a--instance IsGValue (Maybe String) where- toGValue = buildGValue gtypeString set_string . fmap pack- fromGValue v = (fmap unpack) <$> get_string v--instance IsGValue (Maybe Text) where- toGValue = buildGValue gtypeString set_string- fromGValue = get_string--instance IsGValue (Ptr a) where- toGValue = buildGValue gtypePointer set_pointer- fromGValue = get_pointer--instance IsGValue Int32 where- toGValue = buildGValue gtypeInt set_int32- fromGValue = get_int32--instance IsGValue Word32 where- toGValue = buildGValue gtypeUInt set_uint32- fromGValue = get_uint32--instance IsGValue CInt where- toGValue = buildGValue gtypeInt set_int- fromGValue = get_int--instance IsGValue CUInt where- toGValue = buildGValue gtypeUInt set_uint- fromGValue = get_uint--instance IsGValue CLong where- toGValue = buildGValue gtypeLong set_long- fromGValue = get_long--instance IsGValue CULong where- toGValue = buildGValue gtypeULong set_ulong- fromGValue = get_ulong--instance IsGValue Int64 where- toGValue = buildGValue gtypeInt64 set_int64- fromGValue = get_int64--instance IsGValue Word64 where- toGValue = buildGValue gtypeUInt64 set_uint64- fromGValue = get_uint64--instance IsGValue Float where- toGValue = buildGValue gtypeFloat set_float- fromGValue = get_float--instance IsGValue Double where- toGValue = buildGValue gtypeDouble set_double- fromGValue = get_double--instance IsGValue Bool where- toGValue = buildGValue gtypeBoolean set_boolean- fromGValue = get_boolean--instance IsGValue GType where- toGValue = buildGValue gtypeGType set_gtype- fromGValue = get_gtype--foreign import ccall "g_value_set_string" _set_string ::- Ptr GValue -> CString -> IO ()-foreign import ccall "g_value_get_string" _get_string ::- Ptr GValue -> IO CString--set_string :: GValue -> Maybe Text -> IO ()-set_string gv maybeStr =- withManagedPtr gv $ \ptr -> do- cstr <- case maybeStr of- Just str -> textToCString str- Nothing -> return nullPtr- _set_string ptr cstr- freeMem cstr--get_string :: GValue -> IO (Maybe Text)-get_string gv = withManagedPtr gv $ \gvptr -> do- cstr <- _get_string gvptr- if cstr /= nullPtr- then Just <$> cstringToText cstr- else return Nothing--foreign import ccall unsafe "g_value_set_pointer" _set_pointer ::- Ptr GValue -> Ptr a -> IO ()-foreign import ccall unsafe "g_value_get_pointer" _get_pointer ::- Ptr GValue -> IO (Ptr b)--set_pointer :: GValue -> Ptr a -> IO ()-set_pointer gv ptr = withManagedPtr gv $ flip _set_pointer ptr--get_pointer :: GValue -> IO (Ptr b)-get_pointer gv = withManagedPtr gv _get_pointer--foreign import ccall unsafe "g_value_set_int" _set_int ::- Ptr GValue -> CInt -> IO ()-foreign import ccall unsafe "g_value_get_int" _get_int ::- Ptr GValue -> IO CInt--set_int32 :: GValue -> Int32 -> IO ()-set_int32 gv n = withManagedPtr gv $ flip _set_int (coerce n)--get_int32 :: GValue -> IO Int32-get_int32 gv = coerce <$> withManagedPtr gv _get_int--set_int :: GValue -> CInt -> IO ()-set_int gv n = withManagedPtr gv $ flip _set_int n--get_int :: GValue -> IO CInt-get_int gv = withManagedPtr gv _get_int--foreign import ccall unsafe "g_value_set_uint" _set_uint ::- Ptr GValue -> CUInt -> IO ()-foreign import ccall unsafe "g_value_get_uint" _get_uint ::- Ptr GValue -> IO CUInt--set_uint32 :: GValue -> Word32 -> IO ()-set_uint32 gv n = withManagedPtr gv $ flip _set_uint (coerce n)--get_uint32 :: GValue -> IO Word32-get_uint32 gv = coerce <$> withManagedPtr gv _get_uint--set_uint :: GValue -> CUInt -> IO ()-set_uint gv n = withManagedPtr gv $ flip _set_uint n--get_uint :: GValue -> IO CUInt-get_uint gv = withManagedPtr gv _get_uint--foreign import ccall unsafe "g_value_set_long" _set_long ::- Ptr GValue -> CLong -> IO ()-foreign import ccall unsafe "g_value_get_long" _get_long ::- Ptr GValue -> IO CLong--set_long :: GValue -> CLong -> IO ()-set_long gv n = withManagedPtr gv $ flip _set_long n--get_long :: GValue -> IO CLong-get_long gv = withManagedPtr gv _get_long--foreign import ccall unsafe "g_value_set_ulong" _set_ulong ::- Ptr GValue -> CULong -> IO ()-foreign import ccall unsafe "g_value_get_ulong" _get_ulong ::- Ptr GValue -> IO CULong--set_ulong :: GValue -> CULong -> IO ()-set_ulong gv n = withManagedPtr gv $ flip _set_ulong n--get_ulong :: GValue -> IO CULong-get_ulong gv = withManagedPtr gv _get_ulong--foreign import ccall unsafe "g_value_set_int64" _set_int64 ::- Ptr GValue -> Int64 -> IO ()-foreign import ccall unsafe "g_value_get_int64" _get_int64 ::- Ptr GValue -> IO Int64--set_int64 :: GValue -> Int64 -> IO ()-set_int64 gv n = withManagedPtr gv $ flip _set_int64 n--get_int64 :: GValue -> IO Int64-get_int64 gv = withManagedPtr gv _get_int64--foreign import ccall unsafe "g_value_set_uint64" _set_uint64 ::- Ptr GValue -> Word64 -> IO ()-foreign import ccall unsafe "g_value_get_uint64" _get_uint64 ::- Ptr GValue -> IO Word64--set_uint64 :: GValue -> Word64 -> IO ()-set_uint64 gv n = withManagedPtr gv $ flip _set_uint64 n--get_uint64 :: GValue -> IO Word64-get_uint64 gv = withManagedPtr gv _get_uint64--foreign import ccall unsafe "g_value_set_float" _set_float ::- Ptr GValue -> CFloat -> IO ()-foreign import ccall unsafe "g_value_get_float" _get_float ::- Ptr GValue -> IO CFloat--set_float :: GValue -> Float -> IO ()-set_float gv f = withManagedPtr gv $ flip _set_float (realToFrac f)--get_float :: GValue -> IO Float-get_float gv = realToFrac <$> withManagedPtr gv _get_float--foreign import ccall unsafe "g_value_set_double" _set_double ::- Ptr GValue -> CDouble -> IO ()-foreign import ccall unsafe "g_value_get_double" _get_double ::- Ptr GValue -> IO CDouble--set_double :: GValue -> Double -> IO ()-set_double gv d = withManagedPtr gv $ flip _set_double (realToFrac d)--get_double :: GValue -> IO Double-get_double gv = realToFrac <$> withManagedPtr gv _get_double--foreign import ccall unsafe "g_value_set_boolean" _set_boolean ::- Ptr GValue -> CInt -> IO ()-foreign import ccall unsafe "g_value_get_boolean" _get_boolean ::- Ptr GValue -> IO CInt--set_boolean :: GValue -> Bool -> IO ()-set_boolean gv b = withManagedPtr gv $ \ptr ->- _set_boolean ptr (fromIntegral $ fromEnum b)--get_boolean :: GValue -> IO Bool-get_boolean gv = withManagedPtr gv $ \ptr -> (/= 0) <$> _get_boolean ptr--foreign import ccall unsafe "g_value_set_gtype" _set_gtype ::- Ptr GValue -> CGType -> IO ()-foreign import ccall unsafe "g_value_get_gtype" _get_gtype ::- Ptr GValue -> IO CGType--set_gtype :: GValue -> GType -> IO ()-set_gtype gv (GType g) = withManagedPtr gv $ \ptr -> _set_gtype ptr g--get_gtype :: GValue -> IO GType-get_gtype gv = GType <$> withManagedPtr gv _get_gtype--foreign import ccall "g_value_set_object" _set_object ::- Ptr GValue -> Ptr a -> IO ()-foreign import ccall "g_value_get_object" _get_object ::- Ptr GValue -> IO (Ptr a)--set_object :: GObject a => GValue -> Ptr a -> IO ()-set_object gv o = withManagedPtr gv $ flip _set_object o--get_object :: GObject b => GValue -> IO (Ptr b)-get_object gv = withManagedPtr gv _get_object--foreign import ccall "g_value_set_boxed" _set_boxed ::- Ptr GValue -> Ptr a -> IO ()-foreign import ccall "g_value_get_boxed" _get_boxed ::- Ptr GValue -> IO (Ptr b)--set_boxed :: GValue -> Ptr a -> IO ()-set_boxed gv b = withManagedPtr gv $ flip _set_boxed b--get_boxed :: GValue -> IO (Ptr b)-get_boxed gv = withManagedPtr gv _get_boxed--foreign import ccall "g_value_set_variant" _set_variant ::- Ptr GValue -> Ptr GVariant -> IO ()-foreign import ccall "g_value_get_variant" _get_variant ::- Ptr GValue -> IO (Ptr GVariant)--set_variant :: GValue -> Ptr GVariant -> IO ()-set_variant gv v = withManagedPtr gv $ flip _set_variant v--get_variant :: GValue -> IO (Ptr GVariant)-get_variant gv = withManagedPtr gv _get_variant--foreign import ccall unsafe "g_value_set_enum" _set_enum ::- Ptr GValue -> CUInt -> IO ()-foreign import ccall unsafe "g_value_get_enum" _get_enum ::- Ptr GValue -> IO CUInt--set_enum :: GValue -> CUInt -> IO ()-set_enum gv e = withManagedPtr gv $ flip _set_enum e--get_enum :: GValue -> IO CUInt-get_enum gv = withManagedPtr gv _get_enum--foreign import ccall unsafe "g_value_set_flags" _set_flags ::- Ptr GValue -> CUInt -> IO ()-foreign import ccall unsafe "g_value_get_flags" _get_flags ::- Ptr GValue -> IO CUInt--set_flags :: GValue -> CUInt -> IO ()-set_flags gv f = withManagedPtr gv $ flip _set_flags f--get_flags :: GValue -> IO CUInt-get_flags gv = withManagedPtr gv _get_flags
Data/GI/Base/GVariant.hsc view
@@ -133,9 +133,6 @@ #include <glib-object.h> -#if !MIN_VERSION_base(4,8,0)-import Control.Applicative ((<$>), (<*>))-#endif import Control.Monad (when, void, (>=>)) import Control.Exception.Base (bracket) @@ -144,7 +141,9 @@ import qualified Data.ByteString as B import Data.Word import Data.Int+#if !MIN_VERSION_base(4,11,0) import Data.Monoid ((<>))+#endif import Data.Maybe (isJust, fromJust) import qualified Data.Map as M @@ -239,7 +238,7 @@ -- | Get the expected type of a 'GVariant', in 'GVariant' -- notation. See--- <https://developer.gnome.org/glib/stable/glib-GVariantType.html>+-- <https://docs.gtk.org/glib/struct.VariantType.html> -- for the meaning of the resulting format string. gvariantGetTypeString :: GVariant -> IO Text gvariantGetTypeString variant =@@ -644,6 +643,12 @@ [0..(n_children-1)] else return [] +-- No type checking is done here, it is assumed that the caller knows+-- that the passed variant is indeed of a container type with at least+-- one child.+gvariant_get_child :: (Ptr GVariant) -> IO GVariant+gvariant_get_child vptr = g_variant_get_child_value vptr 0 >>= wrapGVariantPtr+ instance IsGVariant a => IsGVariant (Maybe a) where toGVariant = gvariantFromMaybe fromGVariant = gvariantToMaybe@@ -830,9 +835,7 @@ gvariantToSinglet :: forall a. IsGVariant a => GVariant -> IO (Maybe a) gvariantToSinglet = withExplicitType fmt- (gvariant_get_children- >=> return . head- >=> unsafeFromGVariant)+ (gvariant_get_child >=> unsafeFromGVariant) where fmt = toGVariantFormatString (undefined :: GVariantSinglet a) instance (IsGVariant a, IsGVariant b) => IsGVariant (a,b) where@@ -855,14 +858,14 @@ gvariantToTwoTuple variant = do let expectedType = toGVariantFormatString (undefined :: (a,b)) maybeChildren <- withExplicitType expectedType gvariant_get_children variant- if isJust maybeChildren- then do- let (Just [a1,a2]) = maybeChildren- (ma1, ma2) <- (,) <$> fromGVariant a1 <*> fromGVariant a2- return $ if isJust ma1 && isJust ma2- then Just (fromJust ma1, fromJust ma2)- else Nothing- else return Nothing+ case maybeChildren of+ Just [a1,a2] -> do+ (ma1, ma2) <- (,) <$> fromGVariant a1 <*> fromGVariant a2+ return $ if isJust ma1 && isJust ma2+ then Just (fromJust ma1, fromJust ma2)+ else Nothing+ Just _ -> error "gvariantToTwoTuple :: the impossible happened, this is a bug."+ Nothing -> return Nothing instance (IsGVariant a, IsGVariant b, IsGVariant c) => IsGVariant (a,b,c) where toGVariant = gvariantFromThreeTuple@@ -887,16 +890,16 @@ gvariantToThreeTuple variant = do let expectedType = toGVariantFormatString (undefined :: (a,b,c)) maybeChildren <- withExplicitType expectedType gvariant_get_children variant- if isJust maybeChildren- then do- let (Just [a1,a2,a3]) = maybeChildren- (ma1, ma2, ma3) <- (,,) <$> fromGVariant a1- <*> fromGVariant a2- <*> fromGVariant a3- return $ if isJust ma1 && isJust ma2 && isJust ma3- then Just (fromJust ma1, fromJust ma2, fromJust ma3)- else Nothing- else return Nothing+ case maybeChildren of+ Just [a1,a2,a3] -> do+ (ma1, ma2, ma3) <- (,,) <$> fromGVariant a1+ <*> fromGVariant a2+ <*> fromGVariant a3+ return $ if isJust ma1 && isJust ma2 && isJust ma3+ then Just (fromJust ma1, fromJust ma2, fromJust ma3)+ else Nothing+ Just _ -> error "gvariantToThreeTuple :: the impossible happened, this is a bug."+ Nothing -> return Nothing instance (IsGVariant a, IsGVariant b, IsGVariant c, IsGVariant d) => IsGVariant (a,b,c,d) where@@ -924,17 +927,17 @@ gvariantToFourTuple variant = do let expectedType = toGVariantFormatString (undefined :: (a,b,c,d)) maybeChildren <- withExplicitType expectedType gvariant_get_children variant- if isJust maybeChildren- then do- let (Just [a1,a2,a3,a4]) = maybeChildren- (ma1, ma2, ma3,ma4) <- (,,,) <$> fromGVariant a1- <*> fromGVariant a2- <*> fromGVariant a3- <*> fromGVariant a4- return $ if isJust ma1 && isJust ma2 && isJust ma3 && isJust ma4- then Just (fromJust ma1, fromJust ma2, fromJust ma3, fromJust ma4)- else Nothing- else return Nothing+ case maybeChildren of+ Just [a1,a2,a3,a4] -> do+ (ma1, ma2, ma3,ma4) <- (,,,) <$> fromGVariant a1+ <*> fromGVariant a2+ <*> fromGVariant a3+ <*> fromGVariant a4+ return $ if isJust ma1 && isJust ma2 && isJust ma3 && isJust ma4+ then Just (fromJust ma1, fromJust ma2, fromJust ma3, fromJust ma4)+ else Nothing+ Just _ -> error "gvariantToFourTuple :: the impossible happened, this is a bug."+ Nothing -> return Nothing instance (IsGVariant a, IsGVariant b, IsGVariant c, IsGVariant d, IsGVariant e) => IsGVariant (a,b,c,d,e) where@@ -966,17 +969,17 @@ gvariantToFiveTuple variant = do let expectedType = toGVariantFormatString (undefined :: (a,b,c,d,e)) maybeChildren <- withExplicitType expectedType gvariant_get_children variant- if isJust maybeChildren- then do- let (Just [a1,a2,a3,a4,a5]) = maybeChildren- (ma1, ma2, ma3, ma4, ma5) <- (,,,,) <$> fromGVariant a1- <*> fromGVariant a2- <*> fromGVariant a3- <*> fromGVariant a4- <*> fromGVariant a5- return $ if isJust ma1 && isJust ma2 && isJust ma3 &&- isJust ma4 && isJust ma5- then Just (fromJust ma1, fromJust ma2, fromJust ma3,- fromJust ma4, fromJust ma5)- else Nothing- else return Nothing+ case maybeChildren of+ Just [a1,a2,a3,a4,a5] -> do+ (ma1, ma2, ma3, ma4, ma5) <- (,,,,) <$> fromGVariant a1+ <*> fromGVariant a2+ <*> fromGVariant a3+ <*> fromGVariant a4+ <*> fromGVariant a5+ return $ if isJust ma1 && isJust ma2 && isJust ma3 &&+ isJust ma4 && isJust ma5+ then Just (fromJust ma1, fromJust ma2, fromJust ma3,+ fromJust ma4, fromJust ma5)+ else Nothing+ Just _ -> error "gvariantToFiveTuple :: the impossible happened, this is a bug."+ Nothing -> return Nothing
+ Data/GI/Base/Internal/CTypes.hsc view
@@ -0,0 +1,40 @@+-- | Versions of hsc2hs older than 0.68.6 cannot deal with Haskell+-- code including promoted constructors, so isolate the required types+-- in here.+--+-- /Warning/: This module is internal, and might disappear in the future.+module Data.GI.Base.Internal.CTypes+ ( GQuark+ , C_gint+ , cgvalueSize+ , gerror_domain_offset+ , gerror_code_offset+ , gerror_message_offset+ ) where++#include <glib-object.h>++import Data.Int+import Data.Word++-- | The size in bytes of a GValue struct in C.+cgvalueSize :: Int+cgvalueSize = #size GValue++-- | The Haskell type corresponding to a GQuark on the C side.+type GQuark = #{type GQuark}++-- | The Haskell type corresponding to a gint on the C side.+type C_gint = #{type gint}++-- | The offset in bytes inside a `GError` of its @domain@ field.+gerror_domain_offset :: Int+gerror_domain_offset = #{offset GError, domain}++-- | The offset in bytes inside a `GError` of its @code@ field.+gerror_code_offset :: Int+gerror_code_offset = #{offset GError, code}++-- | The offset in bytes inside a `GError` of its @emssage@ field.+gerror_message_offset :: Int+gerror_message_offset = #{offset GError, message}
+ Data/GI/Base/Internal/PathFieldAccess.hs view
@@ -0,0 +1,76 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}++-- | Support for creating lenses from overloaded labels of the type+-- @#fieldName@, or @#"fieldName.subfield"@.++module Data.GI.Base.Internal.PathFieldAccess+ ( Components, PathFieldAccess(..)) where++import qualified GHC.TypeLits as TL+import Data.Kind (Constraint, Type)+import Data.Proxy (Proxy(..))+import qualified Data.Text as T+import qualified Optics.Core as O+import qualified Optics.Internal.Generic as OG++-- The Char class was introduced in GHC 9.2 (base 4.16), if this is+-- not available we fall back to an implementation that does not allow+-- for nested access. Note that overloaded labels of the form+-- #"record.subrecord" only became valid syntax in GHC 9.6, so this is+-- only really useful from that version.+#if MIN_VERSION_base(4,16,0)+type family AppendChar (s :: TL.Symbol) (c :: Char) where+ AppendChar s c = TL.AppendSymbol s (TL.ConsSymbol c "")++type family DoSplit (c :: Char) (acc :: TL.Symbol) (uncons :: Maybe (Char, TL.Symbol)) where+ DoSplit _ acc Nothing = '[acc]+ DoSplit c acc (Just '(c, rest)) = acc : DoSplit c "" (TL.UnconsSymbol rest)+ DoSplit c acc (Just '(k, rest)) = DoSplit c (AppendChar acc k) (TL.UnconsSymbol rest)++type family SplitByChar (c :: Char) (s :: TL.Symbol) :: [TL.Symbol] where+ SplitByChar c s = DoSplit c "" (TL.UnconsSymbol s)++type family Components (s :: TL.Symbol) :: [TL.Symbol] where+ Components s = SplitByChar '.' s+#else+type family Components (s :: TL.Symbol) :: [TL.Symbol] where+ Components s = '[s]+#endif++-- | Check that the given symbol is not the empty string. If it is,+-- raise a TypeError with the given msg.+type family NonEmpty (s :: TL.Symbol) (msg :: TL.ErrorMessage) :: Constraint where+ NonEmpty "" msg = TL.TypeError msg+ NonEmpty _ _ = ()++-- | Create a lens for the given path, and return it together with the+-- path split into components.+class PathFieldAccess (path :: [TL.Symbol]) (model :: Type) (val :: Type) | path model -> val where+ pathFieldAccess :: Proxy path -> Proxy model -> (O.Lens' model val, [T.Text])++instance {-# OVERLAPPING #-}+ (OG.GFieldImpl fieldName model model val val,+ NonEmpty fieldName (TL.Text "Field names cannot be empty"),+ TL.KnownSymbol fieldName) =>+ PathFieldAccess (fieldName : '[]) model val where+ pathFieldAccess _ _ = (OG.gfieldImpl @fieldName @model @model @val @val,+ [T.pack $ TL.symbolVal (Proxy @fieldName)])++instance (OG.GFieldImpl fieldName model model val val,+ TL.KnownSymbol fieldName,+ PathFieldAccess rest val inner) =>+ PathFieldAccess (fieldName : rest) model inner where+ pathFieldAccess _ _ =+ let (innerLens, innerPath) = pathFieldAccess (Proxy @rest) (Proxy @val)+ outerLens = OG.gfieldImpl @fieldName @model @model @val @val+ outerName = T.pack $ TL.symbolVal (Proxy @fieldName)+ in (outerLens O.% innerLens, outerName : innerPath)+
Data/GI/Base/ManagedPtr.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE FlexibleContexts, ScopedTypeVariables #-} -- For HasCallStack compatibility {-# LANGUAGE ImplicitParams, KindSignatures, ConstraintKinds #-}+{-# LANGUAGE TypeApplications #-} -- | We wrap most objects in a "managed pointer", which is basically a -- 'ForeignPtr' of the appropriate type together with a notion of@@ -14,6 +15,7 @@ -- * Managed pointers newManagedPtr , newManagedPtr'+ , newManagedPtr_ , withManagedPtr , maybeWithManagedPtr , withManagedPtrList@@ -26,10 +28,13 @@ -- * Safe casting , castTo , unsafeCastTo+ , checkInstanceType -- * Wrappers , newObject+ , withNewObject , wrapObject+ , releaseObject , unrefObject , disownObject , newBoxed@@ -47,10 +52,14 @@ import Control.Applicative ((<$>)) #endif import Control.Monad (when, void)+import Control.Monad.Fix (mfix) import Data.Coerce (coerce) import Data.IORef (newIORef, readIORef, writeIORef, IORef)-import Data.Maybe (isNothing)+import Data.Maybe (isNothing, isJust)+#if !MIN_VERSION_base(4,11,0)+import Data.Monoid ((<>))+#endif import Foreign.C (CInt(..)) import Foreign.Ptr (Ptr, FunPtr, castPtr, nullPtr)@@ -63,28 +72,53 @@ prettyCallStack, callStack) import Data.GI.Base.Utils +import qualified Data.Text as T+ import System.IO (hPutStrLn, stderr)+import System.Environment (lookupEnv) -- | Thin wrapper over `Foreign.Concurrent.newForeignPtr`.-newManagedPtr :: Ptr a -> IO () -> IO (ManagedPtr a)+newManagedPtr :: HasCallStack => Ptr a -> IO () -> IO (ManagedPtr a) newManagedPtr ptr finalizer = do- let ownedFinalizer :: IORef (Maybe CallStack) -> IO ()- ownedFinalizer callStackRef = do- cs <- readIORef callStackRef- when (isNothing cs) finalizer isDisownedRef <- newIORef Nothing- fPtr <- FC.newForeignPtr ptr (ownedFinalizer isDisownedRef)+ dbgMode <- isJust <$> lookupEnv "HASKELL_GI_DEBUG_MEM"+ let dbgCallStack = if dbgMode+ then Just callStack+ else Nothing+ fPtr <- FC.newForeignPtr ptr (ownedFinalizer finalizer ptr dbgCallStack isDisownedRef) return $ ManagedPtr { managedForeignPtr = fPtr+ , managedPtrAllocCallStack = dbgCallStack , managedPtrIsDisowned = isDisownedRef } +-- | Run the finalizer for an owned pointer, assuming it has now been+-- disowned.+ownedFinalizer :: IO () -> Ptr a -> Maybe CallStack -> IORef (Maybe CallStack)+ -> IO ()+ownedFinalizer finalizer ptr allocCallStack callStackRef = do+ cs <- readIORef callStackRef+ -- cs will be @Just cs@ whenever the pointer has been disowned.+ when (isNothing cs) $ case allocCallStack of+ Just acs -> do+ printAllocDebug ptr acs+ finalizer+ dbgLog (T.pack "Released successfully.\n")+ Nothing -> finalizer++-- | Print some debug diagnostics for an allocation.+printAllocDebug :: Ptr a -> CallStack -> IO ()+printAllocDebug ptr allocCS =+ (dbgLog . T.pack) ("Releasing <" <> show ptr <> ">. "+ <> "Callstack for allocation was:\n"+ <> prettyCallStack allocCS <> "\n\n")+ foreign import ccall "dynamic" mkFinalizer :: FinalizerPtr a -> Ptr a -> IO () -- | Version of `newManagedPtr` taking a `FinalizerPtr` and a -- corresponding `Ptr`, as in `Foreign.ForeignPtr.newForeignPtr`.-newManagedPtr' :: FinalizerPtr a -> Ptr a -> IO (ManagedPtr a)+newManagedPtr' :: HasCallStack => FinalizerPtr a -> Ptr a -> IO (ManagedPtr a) newManagedPtr' finalizer ptr = newManagedPtr ptr (mkFinalizer finalizer ptr) -- | Thin wrapper over `Foreign.Concurrent.newForeignPtr_`.@@ -94,17 +128,18 @@ fPtr <- newForeignPtr_ ptr return $ ManagedPtr { managedForeignPtr = fPtr+ , managedPtrAllocCallStack = Nothing , managedPtrIsDisowned = isDisownedRef } -- | Do not run the finalizers upon garbage collection of the -- `ManagedPtr`.-disownManagedPtr :: forall a. (HasCallStack, ManagedPtrNewtype a) => a -> IO (Ptr a)+disownManagedPtr :: forall a b. (HasCallStack, ManagedPtrNewtype a) => a -> IO (Ptr b) disownManagedPtr managed = do ptr <- unsafeManagedPtrGetPtr managed writeIORef (managedPtrIsDisowned c) (Just callStack)- return ptr- where c = coerce managed :: ManagedPtr ()+ return (castPtr ptr)+ where c = toManagedPtr managed -- | Perform an IO action on the 'Ptr' inside a managed pointer. withManagedPtr :: (HasCallStack, ManagedPtrNewtype a) => a -> (Ptr a -> IO c) -> IO c@@ -132,11 +167,11 @@ -- | Perform the IO action with a transient managed pointer. The -- managed pointer will be valid while calling the action, but will be--- disowned as soon as the action finished.+-- disowned as soon as the action finishes. withTransient :: (HasCallStack, ManagedPtrNewtype a)- => (ManagedPtr a -> a) -> Ptr a -> (a -> IO b) -> IO b-withTransient constructor ptr action = do- managed <- constructor <$> newManagedPtr_ ptr+ => Ptr a -> (a -> IO b) -> IO b+withTransient ptr action = do+ managed <- coerce <$> newManagedPtr_ ptr r <- action managed _ <- disownManagedPtr managed return r@@ -154,7 +189,7 @@ unsafeManagedPtrCastPtr :: forall a b. (HasCallStack, ManagedPtrNewtype a) => a -> IO (Ptr b) unsafeManagedPtrCastPtr m = do- let c = coerce m :: ManagedPtr ()+ let c = toManagedPtr m ptr = (castPtr . unsafeForeignPtrToPtr . managedForeignPtr) c disowned <- readIORef (managedPtrIsDisowned c) maybe (return ptr) (notOwnedWarning ptr) disowned@@ -174,40 +209,53 @@ -- (i.e. it has not been garbage collected by the runtime) at the -- point that this is called. touchManagedPtr :: forall a. ManagedPtrNewtype a => a -> IO ()-touchManagedPtr m = let c = coerce m :: ManagedPtr ()+touchManagedPtr m = let c = toManagedPtr m in (touchForeignPtr . managedForeignPtr) c -- Safe casting machinery foreign import ccall unsafe "check_object_type"- c_check_object_type :: Ptr o -> CGType -> CInt+ c_check_object_type :: Ptr o -> CGType -> IO CInt --- | Cast to the given type, checking that the cast is valid. If it is--- not, we return `Nothing`. Usage:+-- | Check whether the given object is an instance of the given type.+checkInstanceType :: (ManagedPtrNewtype o, TypedObject o) =>+ o -> GType -> IO Bool+checkInstanceType obj (GType cgtype) = withManagedPtr obj $ \objPtr -> do+ check <- c_check_object_type objPtr cgtype+ return $ check /= 0++-- | Cast from one object type to another, checking that the cast is+-- valid. If it is not, we return `Nothing`. Usage: -- -- > maybeWidget <- castTo Widget label-castTo :: forall o o'. (GObject o, GObject o') =>+castTo :: forall o o'. (HasCallStack,+ ManagedPtrNewtype o, TypedObject o,+ ManagedPtrNewtype o', TypedObject o',+ GObject o') => (ManagedPtr o' -> o') -> o -> IO (Maybe o')-castTo constructor obj =- withManagedPtr obj $ \objPtr -> do- GType t <- gobjectType (undefined :: o')- if c_check_object_type objPtr t /= 1- then return Nothing- else Just <$> newObject constructor objPtr+castTo constructor obj = do+ gtype <- glibType @o'+ isInstance <- checkInstanceType obj gtype+ if isInstance+ then return . Just . constructor . coerce $ toManagedPtr obj+ else return Nothing --- | Cast to the given type, assuming that the cast will succeed. This--- function will call `error` if the cast is illegal.-unsafeCastTo :: forall o o'. (HasCallStack, GObject o, GObject o') =>+-- | Cast a typed object to a new type (without any assumption that+-- both types descend from `GObject`), assuming that the cast will+-- succeed. This function will call `error` if the cast is illegal.+unsafeCastTo :: forall o o'. (HasCallStack,+ ManagedPtrNewtype o, TypedObject o,+ ManagedPtrNewtype o', TypedObject o') => (ManagedPtr o' -> o') -> o -> IO o'-unsafeCastTo constructor obj =- withManagedPtr obj $ \objPtr -> do- GType t <- gobjectType (undefined :: o')- if c_check_object_type objPtr t /= 1+unsafeCastTo constructor obj = do+ gtype <- glibType @o'+ isInstance <- checkInstanceType obj gtype+ if not isInstance then do- srcType <- gobjectType obj >>= gtypeName- destType <- gobjectType (undefined :: o') >>= gtypeName+ srcType <- glibType @o >>= gtypeName+ destType <- glibType @o' >>= gtypeName error $ "unsafeCastTo :: invalid conversion from " ++ srcType ++ " to " ++ destType ++ " requested."- else newObject constructor objPtr+ else return (constructor $ coerce $ toManagedPtr obj) -- Reference counting for constructors foreign import ccall "&dbg_g_object_unref"@@ -240,6 +288,16 @@ fPtr <- newManagedPtr' ptr_to_g_object_unref $ castPtr ptr return $! constructor fPtr +-- | Perform the given IO action with a wrapped copy of the given ptr+-- to a GObject. Note that this increases the reference count of the+-- wrapped GObject, similarly to 'newObject'.+withNewObject :: (HasCallStack, GObject o)+ => Ptr o -> (o -> IO b) -> IO b+withNewObject ptr action = do+ void $ g_object_ref_sink ptr+ managed <- newManagedPtr' ptr_to_g_object_unref $ castPtr ptr+ action (coerce managed)+ -- | Same as 'newObject', but we steal ownership of the object. wrapObject :: forall a b. (HasCallStack, GObject a, GObject b) => (ManagedPtr a -> a) -> Ptr b -> IO a@@ -248,6 +306,17 @@ fPtr <- newManagedPtr' ptr_to_g_object_unref $ castPtr ptr return $! constructor fPtr +-- | Unref the given `GObject` and disown it. Use this if you want to+-- manually release the memory associated to a given `GObject`+-- (assuming that no other reference to the underlying C object exists)+-- before the garbage collector does it. It is typically not safe to+-- access the `GObject` after calling this function.+releaseObject :: (HasCallStack, GObject a) => a -> IO ()+releaseObject obj = do+ ptr <- disownObject obj+ dbgDealloc obj+ dbg_g_object_unref ptr+ -- It is fine to use unsafe here, since all this does is schedule an -- idle callback. The scheduling itself will never block for a long -- time, or call back into Haskell.@@ -257,8 +326,10 @@ -- | Decrease the reference count of the given 'GObject'. The memory -- associated with the object may be released if the reference count -- reaches 0.-unrefObject :: GObject a => a -> IO ()-unrefObject obj = withManagedPtr obj dbg_g_object_unref+unrefObject :: (HasCallStack, GObject a) => a -> IO ()+unrefObject obj = withManagedPtr obj $ \ptr -> do+ dbgDealloc obj+ dbg_g_object_unref ptr -- | Print some debug info (if the right environment valiable is set) -- about the object being disowned.@@ -268,10 +339,11 @@ -- | Disown a GObject, that is, do not unref the associated foreign -- GObject when the Haskell object gets garbage collected. Returns the -- pointer to the underlying GObject.-disownObject :: GObject a => a -> IO (Ptr b)+disownObject :: (HasCallStack, GObject a) => a -> IO (Ptr b) disownObject obj = withManagedPtr obj $ \ptr -> do- dbg_g_object_disown ptr- castPtr <$> disownManagedPtr obj+ dbgDealloc obj+ dbg_g_object_disown ptr+ castPtr <$> disownManagedPtr obj -- It is fine to use unsafe here, since all this does is schedule an -- idle callback. The scheduling itself will never block for a long@@ -284,32 +356,32 @@ -- | Construct a Haskell wrapper for the given boxed object. We make a -- copy of the object.-newBoxed :: forall a. BoxedObject a => (ManagedPtr a -> a) -> Ptr a -> IO a+newBoxed :: forall a. (HasCallStack, GBoxed a) => (ManagedPtr a -> a) -> Ptr a -> IO a newBoxed constructor ptr = do- GType gtype <- boxedType (undefined :: a)+ GType gtype <- glibType @a ptr' <- g_boxed_copy gtype ptr fPtr <- newManagedPtr ptr' (boxed_free_helper gtype ptr') return $! constructor fPtr -- | Like 'newBoxed', but we do not make a copy (we "steal" the passed -- object, so now it is managed by the Haskell runtime).-wrapBoxed :: forall a. BoxedObject a => (ManagedPtr a -> a) -> Ptr a -> IO a+wrapBoxed :: forall a. (HasCallStack, GBoxed a) => (ManagedPtr a -> a) -> Ptr a -> IO a wrapBoxed constructor ptr = do- GType gtype <- boxedType (undefined :: a)+ GType gtype <- glibType @a fPtr <- newManagedPtr ptr (boxed_free_helper gtype ptr) return $! constructor fPtr -- | Make a copy of the given boxed object.-copyBoxed :: forall a. BoxedObject a => a -> IO (Ptr a)+copyBoxed :: forall a. (HasCallStack, GBoxed a) => a -> IO (Ptr a) copyBoxed b = do- GType gtype <- boxedType b+ GType gtype <- glibType @a withManagedPtr b (g_boxed_copy gtype) -- | Like 'copyBoxed', but acting directly on a pointer, instead of a -- managed pointer.-copyBoxedPtr :: forall a. BoxedObject a => Ptr a -> IO (Ptr a)+copyBoxedPtr :: forall a. GBoxed a => Ptr a -> IO (Ptr a) copyBoxedPtr ptr = do- GType gtype <- boxedType (undefined :: a)+ GType gtype <- glibType @a g_boxed_copy gtype ptr foreign import ccall "g_boxed_free" g_boxed_free ::@@ -317,37 +389,58 @@ -- | Free the memory associated with a boxed object. Note that this -- disowns the associated `ManagedPtr` via `disownManagedPtr`.-freeBoxed :: forall a. (HasCallStack, BoxedObject a) => a -> IO ()+freeBoxed :: forall a. (HasCallStack, GBoxed a) => a -> IO () freeBoxed boxed = do- GType gtype <- boxedType (undefined :: a)+ GType gtype <- glibType @a ptr <- disownManagedPtr boxed+ dbgDealloc boxed g_boxed_free gtype ptr -- | Disown a boxed object, that is, do not free the associated -- foreign GBoxed when the Haskell object gets garbage--- collected. Returns the pointer to the underlying `BoxedObject`.-disownBoxed :: (HasCallStack, BoxedObject a) => a -> IO (Ptr a)+-- collected. Returns the pointer to the underlying `GBoxed`.+disownBoxed :: (HasCallStack, GBoxed a) => a -> IO (Ptr a) disownBoxed = disownManagedPtr -- | Wrap a pointer, taking ownership of it.-wrapPtr :: WrappedPtr a => (ManagedPtr a -> a) -> Ptr a -> IO a-wrapPtr constructor ptr = do- fPtr <- case wrappedPtrFree of- Nothing -> newManagedPtr_ ptr- Just finalizer -> newManagedPtr' finalizer ptr+wrapPtr :: (HasCallStack, BoxedPtr a) => (ManagedPtr a -> a) -> Ptr a -> IO a+wrapPtr constructor ptr = mfix $ \wrapped -> do+ fPtr <- newManagedPtr ptr (boxedPtrFree wrapped) return $! constructor fPtr -- | Wrap a pointer, making a copy of the data.-newPtr :: WrappedPtr a => (ManagedPtr a -> a) -> Ptr a -> IO a+newPtr :: (HasCallStack, BoxedPtr a) => (ManagedPtr a -> a) -> Ptr a -> IO a newPtr constructor ptr = do tmpWrap <- newManagedPtr_ ptr- ptr' <- wrappedPtrCopy (constructor tmpWrap)+ ptr' <- boxedPtrCopy (constructor tmpWrap) return $! ptr' -- | Make a copy of a wrapped pointer using @memcpy@ into a freshly -- allocated memory region of the given size.-copyBytes :: WrappedPtr a => Int -> Ptr a -> IO (Ptr a)+copyBytes :: (HasCallStack, CallocPtr a) => Int -> Ptr a -> IO (Ptr a) copyBytes size ptr = do- ptr' <- wrappedPtrCalloc+ ptr' <- boxedPtrCalloc memcpy ptr' ptr size return ptr'++foreign import ccall unsafe "g_thread_self" g_thread_self :: IO (Ptr ())++-- | Same as `dbgDeallocPtr`, but for `ManagedPtr`s, and no callstack+-- needs to be provided.+dbgDealloc :: (HasCallStack, ManagedPtrNewtype a) => a -> IO ()+dbgDealloc m = do+ env <- lookupEnv "HASKELL_GI_DEBUG_MEM"+ case env of+ Nothing -> return ()+ Just _ -> do+ let mPtr = toManagedPtr m+ ptr = (unsafeForeignPtrToPtr . managedForeignPtr) mPtr+ threadPtr <- g_thread_self+ hPutStrLn stderr ("Releasing <" ++ show ptr ++ "> from thread ["+ ++ show threadPtr ++ "].\n"+ ++ (case managedPtrAllocCallStack mPtr of+ Just allocCS -> "• Callstack for allocation:\n"+ ++ prettyCallStack allocCS ++ "\n\n"+ Nothing -> "")+ ++ "• CallStack for deallocation:\n"+ ++ prettyCallStack callStack ++ "\n")
Data/GI/Base/Overloading.hs view
@@ -1,17 +1,20 @@ {-# LANGUAGE TypeOperators, KindSignatures, DataKinds, PolyKinds, TypeFamilies, UndecidableInstances, EmptyDataDecls,- MultiParamTypeClasses, FlexibleInstances, ConstraintKinds #-}+ MultiParamTypeClasses, FlexibleInstances, ConstraintKinds,+ AllowAmbiguousTypes, FlexibleContexts, ScopedTypeVariables,+ TypeApplications, OverloadedStrings #-} --- | Helpers for dealing with `GObject`s.+-- | Helpers for dealing with overladed properties, signals and+-- methods. module Data.GI.Base.Overloading ( -- * Type level inheritance ParentTypes+ , HasParentTypes , IsDescendantOf-#if MIN_VERSION_base(4,9,0)- , UnknownAncestorError-#endif + , asA+ -- * Looking up attributes in parent types , AttributeList , HasAttributeList@@ -25,239 +28,196 @@ , HasSignal -- * Looking up methods in parent types- , MethodInfo(..)- , MethodProxy(..) , MethodResolutionFailed-- -- * Overloaded labels- , IsLabelProxy(..)+ , UnsupportedMethodError+ , OverloadedMethodInfo(..)+ , OverloadedMethod(..)+ , MethodProxy(..) -#if MIN_VERSION_base(4,9,0)- , module GHC.OverloadedLabels -- Reexported for convenience-#endif+ , ResolvedSymbolInfo(..)+ , resolveMethod ) where +import Data.Coerce (coerce)+import Data.Kind (Type)+ import GHC.Exts (Constraint) import GHC.TypeLits-import Data.Proxy (Proxy) -#if MIN_VERSION_base(4,9,0)-import GHC.OverloadedLabels (IsLabel(..))-#endif---- | Support for overloaded labels in ghc < 8.0. This is like the--- `IsLabel` class introduced in ghc 8.0 (for use with the--- OverloadedLabels extension) with the difference that the `Proxy`--- argument is lifted. (Using the unlifted Proxy# type in user code is--- a bit of a pain, hence the choice.)-class IsLabelProxy (x :: Symbol) a where- fromLabelProxy :: Proxy x -> a+import Data.GI.Base.BasicTypes (ManagedPtrNewtype, ManagedPtr(..)) --- | Join two lists.-type family JoinLists (as :: [a]) (bs :: [a]) :: [a] where- JoinLists '[] bs = bs- JoinLists (a ': as) bs = a ': JoinLists as bs+import Data.Text (Text)+import qualified Data.Text as T -- | Look in the given list of (symbol, tag) tuples for the tag -- corresponding to the given symbol. If not found raise the given -- type error.-type family FindElement (m :: Symbol) (ms :: [(Symbol, *)])-#if !MIN_VERSION_base(4,9,0)- (typeError :: *)-#else- (typeError :: ErrorMessage)-#endif- :: * where- FindElement m '[] typeError =-#if !MIN_VERSION_base(4,9,0)- typeError-#else- TypeError typeError-#endif+type family FindElement (m :: Symbol) (ms :: [(Symbol, Type)])+ (typeError :: ErrorMessage) :: Type where+ FindElement m '[] typeError = TypeError typeError FindElement m ('(m, o) ': ms) typeError = o FindElement m ('(m', o) ': ms) typeError = FindElement m ms typeError --- | Result of a ancestor check. Basically a Bool type with a bit of--- extra info in order to improve typechecker error messages.-data AncestorCheck t a = HasAncestor a t-#if !MIN_VERSION_base(4,9,0)- | DoesNotHaveRequiredAncestor Symbol t Symbol a-#endif+-- | All the types that are ascendants of this type, including+-- interfaces that the type implements.+type family ParentTypes a :: [Type] -#if MIN_VERSION_base(4,9,0)--- | Type error to be generated when an ancestor check fails.-type family UnknownAncestorError (a :: *) (t :: *) where- UnknownAncestorError a t =- TypeError ('Text "Required ancestor ‘" ':<>: 'ShowType a- ':<>: 'Text "’ not found for type ‘"- ':<>: 'ShowType t ':<>: 'Text "’.")-#endif+-- | A constraint on a type, to be fulfilled whenever it has a type+-- instance for `ParentTypes`. This leads to nicer errors, thanks to+-- the overlappable instance below.+class HasParentTypes (o :: Type) +-- | Default instance, which will give rise to an error for types+-- without an associated `ParentTypes` instance.+instance {-# OVERLAPPABLE #-}+ TypeError ('Text "Type ‘" ':<>: 'ShowType a ':<>:+ 'Text "’ does not have any known parent types.")+ => HasParentTypes a+ -- | Check whether a type appears in a list. We specialize the -- names/types a bit so the error messages are more informative.-type family CheckForAncestorType t (a :: *) (as :: [*]) :: AncestorCheck * * where- CheckForAncestorType t a '[] =-#if !MIN_VERSION_base(4,9,0)- 'DoesNotHaveRequiredAncestor "Error: Required ancestor" a "not found for type" t-#else- UnknownAncestorError a t-#endif- CheckForAncestorType t a (a ': as) = 'HasAncestor a t- CheckForAncestorType t a (b ': as) = CheckForAncestorType t a as+type family CheckForAncestorType t (a :: Type) (as :: [Type]) :: Constraint where+ CheckForAncestorType t a '[] = TypeError ('Text "Required ancestor ‘"+ ':<>: 'ShowType a+ ':<>: 'Text "’ not found for type ‘"+ ':<>: 'ShowType t ':<>: 'Text "’.")+ CheckForAncestorType t a (a ': as) = ()+ CheckForAncestorType t a (b ': as) = CheckForAncestorType t a as --- | Check that a type is in the list of `GObjectParents` of another--- `GObject`-derived type.-type family IsDescendantOf (parent :: *) (descendant :: *) :: Constraint where+-- | Check that a type is in the list of `ParentTypes` of another+-- type.+type family IsDescendantOf (parent :: Type) (descendant :: Type) :: Constraint where -- Every object is defined to be a descendant of itself.- IsDescendantOf d d = () ~ ()- IsDescendantOf p d = CheckForAncestorType d p (ParentTypes d) ~ 'HasAncestor p d+ IsDescendantOf d d = ()+ IsDescendantOf p d = CheckForAncestorType d p (ParentTypes d) --- | The direct parents of this object: its direct parent type, if any,--- and the interfaces it implements. The interfaces inherited from--- parent types can be omitted.-type family ParentTypes a :: [*]+-- | Safe coercions to a parent class. For instance:+--+-- > #show $ label `asA` Gtk.Widget+--+asA :: (ManagedPtrNewtype a, ManagedPtrNewtype b,+ HasParentTypes b, IsDescendantOf a b)+ => b -> (ManagedPtr a -> a) -> a+asA obj _constructor = coerce obj -- | The list of attributes defined for a given type. Each element of -- the list is a tuple, with the first element of the tuple the name -- of the attribute, and the second the type encoding the information--- of the attribute. This type will be an instance of `AttrInfo`.-type family AttributeList a :: [(Symbol, *)]+-- of the attribute. This type will be an instance of+-- `Data.GI.Base.Attributes.AttrInfo`.+type family AttributeList a :: [(Symbol, Type)] -- | A constraint on a type, to be fulfilled whenever it has a type -- instance for `AttributeList`. This is here for nicer error -- reporting. class HasAttributeList a -#if MIN_VERSION_base(4,9,0)--- Default instance, which will give rise to an error for types+-- | Default instance, which will give rise to an error for types -- without an associated `AttributeList`. instance {-# OVERLAPPABLE #-} TypeError ('Text "Type ‘" ':<>: 'ShowType a ':<>: 'Text "’ does not have any known attributes.") => HasAttributeList a-#endif -#if !MIN_VERSION_base(4,9,0)--- | Datatype returned when the attribute is not found, hopefully making--- the resulting error messages somewhat clearer.-data UnknownAttribute (msg1 :: Symbol) (s :: Symbol) (msg2 :: Symbol) (o :: *)-#endif- -- | Return the type encoding the attribute information for a given -- type and attribute.-type family ResolveAttribute (s :: Symbol) (o :: *) :: * where+type family ResolveAttribute (s :: Symbol) (o :: Type) :: Type where ResolveAttribute s o = FindElement s (AttributeList o)-#if !MIN_VERSION_base(4,9,0)- (UnknownAttribute "Error: could not find attribute" s "for object" o)-#else ('Text "Unknown attribute ‘" ':<>: 'Text s ':<>: 'Text "’ for object ‘" ':<>: 'ShowType o ':<>: 'Text "’.")-#endif -- | Whether a given type is in the given list. If found, return -- @success@, otherwise return @failure@.-type family IsElem (e :: Symbol) (es :: [(Symbol, *)]) (success :: k)-#if !MIN_VERSION_base(4,9,0)- (failure :: k)-#else- (failure :: ErrorMessage)-#endif- :: k where- IsElem e '[] success failure =-#if !MIN_VERSION_base(4,9,0)- failure-#else- TypeError failure-#endif+type family IsElem (e :: Symbol) (es :: [(Symbol, Type)]) (success :: k)+ (failure :: ErrorMessage) :: k where+ IsElem e '[] success failure = TypeError failure IsElem e ( '(e, t) ': es) success failure = success IsElem e ( '(other, t) ': es) s f = IsElem e es s f --- | Isomorphic to Bool, but having some extra debug information.-data AttributeCheck a t = HasAttribute-#if !MIN_VERSION_base(4,9,0)- | DoesNotHaveAttribute Symbol a Symbol t-#endif- -- | A constraint imposing that the given object has the given attribute.-type family HasAttribute (attr :: Symbol) (o :: *) where+type family HasAttribute (attr :: Symbol) (o :: Type) :: Constraint where HasAttribute attr o = IsElem attr (AttributeList o)- 'HasAttribute-#if !MIN_VERSION_base(4,9,0)- ('DoesNotHaveAttribute "Error: attribute" attr "not found for type" o)-#else+ (() :: Constraint) -- success ('Text "Attribute ‘" ':<>: 'Text attr ':<>: 'Text "’ not found for type ‘" ':<>: 'ShowType o ':<>: 'Text "’.")-#endif- ~ 'HasAttribute -- | A constraint that enforces that the given type has a given attribute.-class HasAttr (attr :: Symbol) (o :: *)+class HasAttr (attr :: Symbol) (o :: Type) instance HasAttribute attr o => HasAttr attr o --- | The list of signals defined for a given type. Each element of--- the list is a tuple, with the first element of the tuple the name--- of the signal, and the second the type encoding the information of--- the signal. This type will be an instance of `SignalInfo`.-type family SignalList a :: [(Symbol, *)]--#if !MIN_VERSION_base(4,9,0)--- | Datatype returned when the signal is not found, hopefully making--- the resulting error messages somewhat clearer.-data UnknownSignal (msg1 :: Symbol) (s :: Symbol) (msg2 :: Symbol) (o :: *)-#endif+-- | The list of signals defined for a given type. Each element of the+-- list is a tuple, with the first element of the tuple the name of+-- the signal, and the second the type encoding the information of the+-- signal. This type will be an instance of+-- `Data.GI.Base.Signals.SignalInfo`.+type family SignalList a :: [(Symbol, Type)] -- | Return the type encoding the signal information for a given -- type and signal.-type family ResolveSignal (s :: Symbol) (o :: *) :: * where+type family ResolveSignal (s :: Symbol) (o :: Type) :: Type where ResolveSignal s o = FindElement s (SignalList o)-#if !MIN_VERSION_base(4,9,0)- (UnknownSignal "Error: could not find signal" s "for object" o)-#else ('Text "Unknown signal ‘" ':<>: 'Text s ':<>: 'Text "’ for object ‘" ':<>: 'ShowType o ':<>: 'Text "’.")-#endif --- | Isomorphic to Bool, but having some extra debug information.-data SignalCheck s t = HasSignal-#if !MIN_VERSION_base(4,9,0)- | DoesNotHaveSignal Symbol s Symbol t-#endif- -- | A constraint enforcing that the signal exists for the given -- object, or one of its ancestors.-type family HasSignal (s :: Symbol) (o :: *) where+type family HasSignal (s :: Symbol) (o :: Type) :: Constraint where HasSignal s o = IsElem s (SignalList o)- 'HasSignal-#if !MIN_VERSION_base(4,9,0)- ('DoesNotHaveSignal "Error: signal" s "not found for type" o)-#else+ (() :: Constraint) -- success ('Text "Signal ‘" ':<>: 'Text s ':<>: 'Text "’ not found for type ‘" ':<>: 'ShowType o ':<>: 'Text "’.")-#endif- ~ 'HasSignal --- | Class for types containing the information about an overloaded--- method of type `o -> s`.-class MethodInfo i o s where- overloadedMethod :: MethodProxy i -> o -> s---- | Proxy for passing a type to `overloadedMethod`. We do not use--- `Data.Proxy.Proxy` directly since it clashes with types defined in--- the autogenerated bindings.-data MethodProxy a = MethodProxy+-- | A constraint that always fails with a type error, for+-- documentation purposes.+type family UnsupportedMethodError (s :: Symbol) (o :: Type) :: Type where+ UnsupportedMethodError s o =+ TypeError ('Text "Unsupported method ‘" ':<>:+ 'Text s ':<>: 'Text "’ for object ‘" ':<>:+ 'ShowType o ':<>: 'Text "’.") -#if !MIN_VERSION_base(4,9,0)--- | Datatype returned when the method is not found, hopefully making+-- | Returned when the method is not found, hopefully making -- the resulting error messages somewhat clearer.-data MethodResolutionFailed (label :: Symbol) (o :: *)-#else-type family MethodResolutionFailed (method :: Symbol) (o :: *) where+type family MethodResolutionFailed (method :: Symbol) (o :: Type) where MethodResolutionFailed m o = TypeError ('Text "Unknown method ‘" ':<>: 'Text m ':<>: 'Text "’ for type ‘" ':<>: 'ShowType o ':<>: 'Text "’.")-#endif++-- | Class for types containing the information about an overloaded+-- method of type @o -> s@.+class OverloadedMethod i o s where+ overloadedMethod :: o -> s -- ^ The actual method being invoked.++-- | Information about a fully resolved symbol, for debugging+-- purposes.+data ResolvedSymbolInfo = ResolvedSymbolInfo {+ resolvedSymbolName :: Text+ , resolvedSymbolURL :: Text+ }++instance Show ResolvedSymbolInfo where+ -- Format as a hyperlink on modern terminals (older+ -- terminals should ignore the hyperlink part).+ show info = T.unpack ("\ESC]8;;" <> resolvedSymbolURL info+ <> "\ESC\\" <> resolvedSymbolName info+ <> "\ESC]8;;\ESC\\")++-- | This is for debugging purposes, see `resolveMethod` below.+class OverloadedMethodInfo i o where+ overloadedMethodInfo :: Maybe ResolvedSymbolInfo++-- | A proxy for carrying the types `MethodInfoName` needs (this is used+-- for `resolveMethod`, see below).+data MethodProxy (info :: Type) (obj :: Type) = MethodProxy++-- | Return the fully qualified method name that a given overloaded+-- method call resolves to (mostly useful for debugging).+--+-- > resolveMethod widget #show+resolveMethod :: forall info obj. (OverloadedMethodInfo info obj) =>+ obj -> MethodProxy info obj -> Maybe ResolvedSymbolInfo+resolveMethod _o _p = overloadedMethodInfo @info @obj
+ Data/GI/Base/Overloading.hs-boot view
@@ -0,0 +1,3 @@+module Data.GI.Base.Overloading (HasParentTypes) where++class HasParentTypes a
Data/GI/Base/Properties.hsc view
@@ -1,7 +1,8 @@-{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE ScopedTypeVariables, TypeApplications #-} module Data.GI.Base.Properties- ( setObjectPropertyString+ ( setObjectPropertyIsGValueInstance+ , setObjectPropertyString , setObjectPropertyStringArray , setObjectPropertyPtr , setObjectPropertyInt@@ -20,11 +21,17 @@ , setObjectPropertyBoxed , setObjectPropertyEnum , setObjectPropertyFlags+ , setObjectPropertyClosure , setObjectPropertyVariant , setObjectPropertyByteArray , setObjectPropertyPtrGList , setObjectPropertyHash+ , setObjectPropertyCallback+ , setObjectPropertyGError+ , setObjectPropertyGValue+ , setObjectPropertyParamSpec + , getObjectPropertyIsGValueInstance , getObjectPropertyString , getObjectPropertyStringArray , getObjectPropertyPtr@@ -44,11 +51,17 @@ , getObjectPropertyBoxed , getObjectPropertyEnum , getObjectPropertyFlags+ , getObjectPropertyClosure , getObjectPropertyVariant , getObjectPropertyByteArray , getObjectPropertyPtrGList , getObjectPropertyHash+ , getObjectPropertyCallback+ , getObjectPropertyGError+ , getObjectPropertyGValue+ , getObjectPropertyParamSpec + , constructObjectPropertyIsGValueInstance , constructObjectPropertyString , constructObjectPropertyStringArray , constructObjectPropertyPtr@@ -68,10 +81,15 @@ , constructObjectPropertyBoxed , constructObjectPropertyEnum , constructObjectPropertyFlags+ , constructObjectPropertyClosure , constructObjectPropertyVariant , constructObjectPropertyByteArray , constructObjectPropertyPtrGList , constructObjectPropertyHash+ , constructObjectPropertyCallback+ , constructObjectPropertyGError+ , constructObjectPropertyGValue+ , constructObjectPropertyParamSpec ) where #if !MIN_VERSION_base(4,8,0)@@ -81,16 +99,19 @@ import qualified Data.ByteString.Char8 as B import Data.Text (Text)-import Data.Proxy (Proxy(..)) import Data.GI.Base.BasicTypes import Data.GI.Base.BasicConversions import Data.GI.Base.ManagedPtr+import Data.GI.Base.GError (GError(..)) import Data.GI.Base.GValue+import Data.GI.Base.GType+import Data.GI.Base.GClosure (GClosure(..)) import Data.GI.Base.GVariant (newGVariantFromPtr) import Data.GI.Base.Utils (freeMem, convertIfNonNull) -import Foreign (Ptr, Int32, Word32, Int64, Word64, nullPtr)+import Foreign (Ptr, FunPtr, Int32, Word32, Int64, Word64, nullPtr,+ castFunPtrToPtr, castPtrToFunPtr) import Foreign.C (CString, withCString) import Foreign.C.Types (CInt, CUInt, CLong, CULong) @@ -99,273 +120,267 @@ foreign import ccall "g_object_set_property" g_object_set_property :: Ptr a -> CString -> Ptr GValue -> IO () -setObjectProperty :: GObject a => a -> String -> b ->- (GValue -> b -> IO ()) -> GType -> IO ()-setObjectProperty obj propName propValue setter (GType gtype) = do- gvalue <- buildGValue (GType gtype) setter propValue+-- | Set a property on an object to the given `GValue`.+gobjectSetProperty :: GObject a => a -> String -> GValue -> IO ()+gobjectSetProperty obj propName gvalue = withManagedPtr obj $ \objPtr -> withCString propName $ \cPropName -> withManagedPtr gvalue $ \gvalueptr -> g_object_set_property objPtr cPropName gvalueptr +-- | A convenience wrapper over `gobjectSetProperty` that does the+-- wrapping of a value into a `GValue`.+setObjectProperty :: GObject a => a -> String -> b ->+ (Ptr GValue -> b -> IO ()) -> GType -> IO ()+setObjectProperty obj propName propValue setter (GType gtype) = do+ gvalue <- buildGValue (GType gtype) setter propValue+ gobjectSetProperty obj propName gvalue+ foreign import ccall "g_object_get_property" g_object_get_property :: Ptr a -> CString -> Ptr GValue -> IO () -getObjectProperty :: GObject a => a -> String ->- (GValue -> IO b) -> GType -> IO b-getObjectProperty obj propName getter gtype = do+-- | Get the `GValue` for the given property.+gobjectGetProperty :: GObject a => a -> String -> GType -> IO GValue+gobjectGetProperty obj propName gtype = do gvalue <- newGValue gtype withManagedPtr obj $ \objPtr -> withCString propName $ \cPropName -> withManagedPtr gvalue $ \gvalueptr -> g_object_get_property objPtr cPropName gvalueptr- getter gvalue+ return gvalue -constructObjectProperty :: String -> b -> (GValue -> b -> IO ()) ->+-- | A convenience wrapper over `gobjectGetProperty` that unwraps the+-- `GValue` into a Haskell value.+getObjectProperty :: GObject a => a -> String ->+ (Ptr GValue -> IO b) -> GType -> IO b+getObjectProperty obj propName getter gtype = do+ gv <- gobjectGetProperty obj propName gtype+ withManagedPtr gv getter++constructObjectProperty :: String -> b -> (Ptr GValue -> b -> IO ()) -> GType -> IO (GValueConstruct o) constructObjectProperty propName propValue setter gtype = do gvalue <- buildGValue gtype setter propValue return (GValueConstruct propName gvalue) +-- | Set a property for a type with a `IsGValue` instance.+setObjectPropertyIsGValueInstance :: (GObject a, IsGValue b) =>+ a -> String -> b -> IO ()+setObjectPropertyIsGValueInstance obj propName maybeVal = do+ gvalue <- toGValue maybeVal+ gobjectSetProperty obj propName gvalue++-- | Construct a property for a type with a `IsGValue` instance.+constructObjectPropertyIsGValueInstance :: IsGValue b => String -> b -> IO (GValueConstruct o)+constructObjectPropertyIsGValueInstance propName maybeVal = do+ gvalue <- toGValue maybeVal+ return (GValueConstruct propName gvalue)++-- | Get a nullable property for a type with a `IsGValue` instance.+getObjectPropertyIsGValueInstance :: forall a b. (GObject a, IsGValue b) =>+ a -> String -> IO b+getObjectPropertyIsGValueInstance obj propName = do+ gtype <- gvalueGType_ @b+ gv <- gobjectGetProperty obj propName gtype+ fromGValue gv+ setObjectPropertyString :: GObject a => a -> String -> Maybe Text -> IO ()-setObjectPropertyString obj propName str =- setObjectProperty obj propName str set_string gtypeString+setObjectPropertyString = setObjectPropertyIsGValueInstance constructObjectPropertyString :: String -> Maybe Text -> IO (GValueConstruct o)-constructObjectPropertyString propName str =- constructObjectProperty propName str set_string gtypeString+constructObjectPropertyString = constructObjectPropertyIsGValueInstance getObjectPropertyString :: GObject a => a -> String -> IO (Maybe Text)-getObjectPropertyString obj propName =- getObjectProperty obj propName get_string gtypeString+getObjectPropertyString = getObjectPropertyIsGValueInstance setObjectPropertyPtr :: GObject a => a -> String -> Ptr b -> IO ()-setObjectPropertyPtr obj propName ptr =- setObjectProperty obj propName ptr set_pointer gtypePointer+setObjectPropertyPtr = setObjectPropertyIsGValueInstance constructObjectPropertyPtr :: String -> Ptr b -> IO (GValueConstruct o)-constructObjectPropertyPtr propName ptr =- constructObjectProperty propName ptr set_pointer gtypePointer+constructObjectPropertyPtr = constructObjectPropertyIsGValueInstance getObjectPropertyPtr :: GObject a => a -> String -> IO (Ptr b)-getObjectPropertyPtr obj propName =- getObjectProperty obj propName get_pointer gtypePointer+getObjectPropertyPtr = getObjectPropertyIsGValueInstance setObjectPropertyInt :: GObject a => a -> String -> CInt -> IO ()-setObjectPropertyInt obj propName int =- setObjectProperty obj propName int set_int gtypeInt+setObjectPropertyInt = setObjectPropertyIsGValueInstance constructObjectPropertyInt :: String -> CInt -> IO (GValueConstruct o)-constructObjectPropertyInt propName int =- constructObjectProperty propName int set_int gtypeInt+constructObjectPropertyInt = constructObjectPropertyIsGValueInstance getObjectPropertyInt :: GObject a => a -> String -> IO CInt-getObjectPropertyInt obj propName =- getObjectProperty obj propName get_int gtypeInt+getObjectPropertyInt = getObjectPropertyIsGValueInstance setObjectPropertyUInt :: GObject a => a -> String -> CUInt -> IO ()-setObjectPropertyUInt obj propName uint =- setObjectProperty obj propName uint set_uint gtypeUInt+setObjectPropertyUInt = setObjectPropertyIsGValueInstance constructObjectPropertyUInt :: String -> CUInt -> IO (GValueConstruct o)-constructObjectPropertyUInt propName uint =- constructObjectProperty propName uint set_uint gtypeUInt+constructObjectPropertyUInt = constructObjectPropertyIsGValueInstance getObjectPropertyUInt :: GObject a => a -> String -> IO CUInt-getObjectPropertyUInt obj propName =- getObjectProperty obj propName get_uint gtypeUInt+getObjectPropertyUInt = getObjectPropertyIsGValueInstance setObjectPropertyLong :: GObject a => a -> String -> CLong -> IO ()-setObjectPropertyLong obj propName int =- setObjectProperty obj propName int set_long gtypeLong+setObjectPropertyLong = setObjectPropertyIsGValueInstance constructObjectPropertyLong :: String -> CLong -> IO (GValueConstruct o)-constructObjectPropertyLong propName int =- constructObjectProperty propName int set_long gtypeLong+constructObjectPropertyLong = constructObjectPropertyIsGValueInstance getObjectPropertyLong :: GObject a => a -> String -> IO CLong-getObjectPropertyLong obj propName =- getObjectProperty obj propName get_long gtypeLong+getObjectPropertyLong = getObjectPropertyIsGValueInstance setObjectPropertyULong :: GObject a => a -> String -> CULong -> IO ()-setObjectPropertyULong obj propName uint =- setObjectProperty obj propName uint set_ulong gtypeULong+setObjectPropertyULong = setObjectPropertyIsGValueInstance constructObjectPropertyULong :: String -> CULong -> IO (GValueConstruct o)-constructObjectPropertyULong propName uint =- constructObjectProperty propName uint set_ulong gtypeULong+constructObjectPropertyULong = constructObjectPropertyIsGValueInstance getObjectPropertyULong :: GObject a => a -> String -> IO CULong-getObjectPropertyULong obj propName =- getObjectProperty obj propName get_ulong gtypeULong+getObjectPropertyULong = getObjectPropertyIsGValueInstance setObjectPropertyInt32 :: GObject a => a -> String -> Int32 -> IO ()-setObjectPropertyInt32 obj propName int32 =- setObjectProperty obj propName int32 set_int32 gtypeInt+setObjectPropertyInt32 = setObjectPropertyIsGValueInstance constructObjectPropertyInt32 :: String -> Int32 -> IO (GValueConstruct o)-constructObjectPropertyInt32 propName int32 =- constructObjectProperty propName int32 set_int32 gtypeInt+constructObjectPropertyInt32 = constructObjectPropertyIsGValueInstance getObjectPropertyInt32 :: GObject a => a -> String -> IO Int32-getObjectPropertyInt32 obj propName =- getObjectProperty obj propName get_int32 gtypeInt+getObjectPropertyInt32 = getObjectPropertyIsGValueInstance setObjectPropertyUInt32 :: GObject a => a -> String -> Word32 -> IO ()-setObjectPropertyUInt32 obj propName uint32 =- setObjectProperty obj propName uint32 set_uint32 gtypeUInt+setObjectPropertyUInt32 = setObjectPropertyIsGValueInstance constructObjectPropertyUInt32 :: String -> Word32 -> IO (GValueConstruct o)-constructObjectPropertyUInt32 propName uint32 =- constructObjectProperty propName uint32 set_uint32 gtypeUInt+constructObjectPropertyUInt32 = constructObjectPropertyIsGValueInstance getObjectPropertyUInt32 :: GObject a => a -> String -> IO Word32-getObjectPropertyUInt32 obj propName =- getObjectProperty obj propName get_uint32 gtypeUInt+getObjectPropertyUInt32 = getObjectPropertyIsGValueInstance setObjectPropertyInt64 :: GObject a => a -> String -> Int64 -> IO ()-setObjectPropertyInt64 obj propName int64 =- setObjectProperty obj propName int64 set_int64 gtypeInt64+setObjectPropertyInt64 = setObjectPropertyIsGValueInstance constructObjectPropertyInt64 :: String -> Int64 -> IO (GValueConstruct o)-constructObjectPropertyInt64 propName int64 =- constructObjectProperty propName int64 set_int64 gtypeInt64+constructObjectPropertyInt64 = constructObjectPropertyIsGValueInstance getObjectPropertyInt64 :: GObject a => a -> String -> IO Int64-getObjectPropertyInt64 obj propName =- getObjectProperty obj propName get_int64 gtypeInt64+getObjectPropertyInt64 = getObjectPropertyIsGValueInstance setObjectPropertyUInt64 :: GObject a => a -> String -> Word64 -> IO ()-setObjectPropertyUInt64 obj propName uint64 =- setObjectProperty obj propName uint64 set_uint64 gtypeUInt64+setObjectPropertyUInt64 = setObjectPropertyIsGValueInstance constructObjectPropertyUInt64 :: String -> Word64 -> IO (GValueConstruct o)-constructObjectPropertyUInt64 propName uint64 =- constructObjectProperty propName uint64 set_uint64 gtypeUInt64+constructObjectPropertyUInt64 = constructObjectPropertyIsGValueInstance getObjectPropertyUInt64 :: GObject a => a -> String -> IO Word64-getObjectPropertyUInt64 obj propName =- getObjectProperty obj propName get_uint64 gtypeUInt64+getObjectPropertyUInt64 = getObjectPropertyIsGValueInstance setObjectPropertyFloat :: GObject a => a -> String -> Float -> IO ()-setObjectPropertyFloat obj propName float =- setObjectProperty obj propName float set_float gtypeFloat+setObjectPropertyFloat = setObjectPropertyIsGValueInstance constructObjectPropertyFloat :: String -> Float -> IO (GValueConstruct o)-constructObjectPropertyFloat propName float =- constructObjectProperty propName float set_float gtypeFloat+constructObjectPropertyFloat = constructObjectPropertyIsGValueInstance getObjectPropertyFloat :: GObject a => a -> String -> IO Float-getObjectPropertyFloat obj propName =- getObjectProperty obj propName get_float gtypeFloat+getObjectPropertyFloat = getObjectPropertyIsGValueInstance setObjectPropertyDouble :: GObject a => a -> String -> Double -> IO ()-setObjectPropertyDouble obj propName double =- setObjectProperty obj propName double set_double gtypeDouble+setObjectPropertyDouble = setObjectPropertyIsGValueInstance constructObjectPropertyDouble :: String -> Double -> IO (GValueConstruct o)-constructObjectPropertyDouble propName double =- constructObjectProperty propName double set_double gtypeDouble+constructObjectPropertyDouble = constructObjectPropertyIsGValueInstance getObjectPropertyDouble :: GObject a => a -> String -> IO Double-getObjectPropertyDouble obj propName =- getObjectProperty obj propName get_double gtypeDouble+getObjectPropertyDouble = getObjectPropertyIsGValueInstance setObjectPropertyBool :: GObject a => a -> String -> Bool -> IO ()-setObjectPropertyBool obj propName bool =- setObjectProperty obj propName bool set_boolean gtypeBoolean+setObjectPropertyBool = setObjectPropertyIsGValueInstance constructObjectPropertyBool :: String -> Bool -> IO (GValueConstruct o)-constructObjectPropertyBool propName bool =- constructObjectProperty propName bool set_boolean gtypeBoolean+constructObjectPropertyBool = constructObjectPropertyIsGValueInstance getObjectPropertyBool :: GObject a => a -> String -> IO Bool-getObjectPropertyBool obj propName =- getObjectProperty obj propName get_boolean gtypeBoolean+getObjectPropertyBool = getObjectPropertyIsGValueInstance setObjectPropertyGType :: GObject a => a -> String -> GType -> IO ()-setObjectPropertyGType obj propName gtype =- setObjectProperty obj propName gtype set_gtype gtypeGType+setObjectPropertyGType = setObjectPropertyIsGValueInstance constructObjectPropertyGType :: String -> GType -> IO (GValueConstruct o)-constructObjectPropertyGType propName bool =- constructObjectProperty propName bool set_gtype gtypeGType+constructObjectPropertyGType = constructObjectPropertyIsGValueInstance getObjectPropertyGType :: GObject a => a -> String -> IO GType-getObjectPropertyGType obj propName =- getObjectProperty obj propName get_gtype gtypeGType+getObjectPropertyGType = getObjectPropertyIsGValueInstance setObjectPropertyObject :: forall a b. (GObject a, GObject b) => a -> String -> Maybe b -> IO () setObjectPropertyObject obj propName maybeObject = do- gtype <- gobjectType (undefined :: b)+ gtype <- glibType @b maybeWithManagedPtr maybeObject $ \objectPtr -> setObjectProperty obj propName objectPtr set_object gtype constructObjectPropertyObject :: forall a o. GObject a => String -> Maybe a -> IO (GValueConstruct o) constructObjectPropertyObject propName maybeObject = do- gtype <- gobjectType (undefined :: a)+ gtype <- glibType @a maybeWithManagedPtr maybeObject $ \objectPtr -> constructObjectProperty propName objectPtr set_object gtype getObjectPropertyObject :: forall a b. (GObject a, GObject b) => a -> String -> (ManagedPtr b -> b) -> IO (Maybe b) getObjectPropertyObject obj propName constructor = do- gtype <- gobjectType (undefined :: b)+ gtype <- glibType @b getObjectProperty obj propName (\val -> (get_object val :: IO (Ptr b)) >>= flip convertIfNonNull (newObject constructor)) gtype -setObjectPropertyBoxed :: forall a b. (GObject a, BoxedObject b) =>+setObjectPropertyBoxed :: forall a b. (GObject a, GBoxed b) => a -> String -> Maybe b -> IO () setObjectPropertyBoxed obj propName maybeBoxed = do- gtype <- boxedType (undefined :: b)+ gtype <- glibType @b maybeWithManagedPtr maybeBoxed $ \boxedPtr -> setObjectProperty obj propName boxedPtr set_boxed gtype -constructObjectPropertyBoxed :: forall a o. (BoxedObject a) =>+constructObjectPropertyBoxed :: forall a o. (GBoxed a) => String -> Maybe a -> IO (GValueConstruct o) constructObjectPropertyBoxed propName maybeBoxed = do- gtype <- boxedType (undefined :: a)+ gtype <- glibType @a maybeWithManagedPtr maybeBoxed $ \boxedPtr -> constructObjectProperty propName boxedPtr set_boxed gtype -getObjectPropertyBoxed :: forall a b. (GObject a, BoxedObject b) =>+getObjectPropertyBoxed :: forall a b. (GObject a, GBoxed b) => a -> String -> (ManagedPtr b -> b) -> IO (Maybe b) getObjectPropertyBoxed obj propName constructor = do- gtype <- boxedType (undefined :: b)+ gtype <- glibType @b getObjectProperty obj propName (get_boxed >=> flip convertIfNonNull (newBoxed constructor)) gtype@@ -398,17 +413,17 @@ flip convertIfNonNull unpackZeroTerminatedUTF8CArray) gtypeStrv -setObjectPropertyEnum :: (GObject a, Enum b, BoxedEnum b) =>+setObjectPropertyEnum :: forall a b. (GObject a, Enum b, BoxedEnum b) => a -> String -> b -> IO () setObjectPropertyEnum obj propName enum = do- gtype <- boxedEnumType enum+ gtype <- glibType @b let cEnum = (fromIntegral . fromEnum) enum setObjectProperty obj propName cEnum set_enum gtype -constructObjectPropertyEnum :: (Enum a, BoxedEnum a) =>+constructObjectPropertyEnum :: forall a o. (Enum a, BoxedEnum a) => String -> a -> IO (GValueConstruct o) constructObjectPropertyEnum propName enum = do- gtype <- boxedEnumType enum+ gtype <- glibType @a let cEnum = (fromIntegral . fromEnum) enum constructObjectProperty propName cEnum set_enum gtype @@ -416,7 +431,7 @@ Enum b, BoxedEnum b) => a -> String -> IO b getObjectPropertyEnum obj propName = do- gtype <- boxedEnumType (undefined :: b)+ gtype <- glibType @b getObjectProperty obj propName (\val -> toEnum . fromIntegral <$> get_enum val) gtype@@ -425,24 +440,36 @@ a -> String -> [b] -> IO () setObjectPropertyFlags obj propName flags = do let cFlags = gflagsToWord flags- gtype <- boxedFlagsType (Proxy :: Proxy b)+ gtype <- glibType @b setObjectProperty obj propName cFlags set_flags gtype constructObjectPropertyFlags :: forall a o. (IsGFlag a, BoxedFlags a) => String -> [a] -> IO (GValueConstruct o) constructObjectPropertyFlags propName flags = do let cFlags = gflagsToWord flags- gtype <- boxedFlagsType (Proxy :: Proxy a)+ gtype <- glibType @a constructObjectProperty propName cFlags set_flags gtype getObjectPropertyFlags :: forall a b. (GObject a, IsGFlag b, BoxedFlags b) => a -> String -> IO [b] getObjectPropertyFlags obj propName = do- gtype <- boxedFlagsType (Proxy :: Proxy b)+ gtype <- glibType @b getObjectProperty obj propName (\val -> wordToGFlags <$> get_flags val) gtype +setObjectPropertyClosure :: forall a b. GObject a =>+ a -> String -> Maybe (GClosure b) -> IO ()+setObjectPropertyClosure = setObjectPropertyBoxed++constructObjectPropertyClosure :: String -> Maybe (GClosure a) -> IO (GValueConstruct o)+constructObjectPropertyClosure = constructObjectPropertyBoxed++getObjectPropertyClosure :: forall a b. GObject a =>+ a -> String -> IO (Maybe (GClosure b))+getObjectPropertyClosure obj propName =+ getObjectPropertyBoxed obj propName GClosure+ setObjectPropertyVariant :: GObject a => a -> String -> Maybe GVariant -> IO () setObjectPropertyVariant obj propName maybeVariant =@@ -506,7 +533,7 @@ getObjectPropertyPtrGList :: GObject a => a -> String -> IO [Ptr b] getObjectPropertyPtrGList obj propName =- getObjectProperty obj propName (get_pointer >=> unpackGList) gtypePointer+ getObjectProperty obj propName (gvalueGet_ >=> unpackGList) gtypePointer setObjectPropertyHash :: GObject a => a -> String -> b -> IO () setObjectPropertyHash =@@ -519,3 +546,63 @@ getObjectPropertyHash :: GObject a => a -> String -> IO b getObjectPropertyHash = error $ "Getting GHashTable properties not supported yet."++setObjectPropertyCallback :: GObject a => a -> String -> FunPtr b -> IO ()+setObjectPropertyCallback obj propName funPtr =+ setObjectProperty obj propName (castFunPtrToPtr funPtr) gvalueSet_ gtypePointer++constructObjectPropertyCallback :: String -> FunPtr b -> IO (GValueConstruct o)+constructObjectPropertyCallback propName funPtr =+ constructObjectProperty propName (castFunPtrToPtr funPtr) gvalueSet_ gtypePointer++getObjectPropertyCallback :: GObject a => a -> String ->+ (FunPtr b -> c) -> IO (Maybe c)+getObjectPropertyCallback obj propName wrapper = do+ ptr <- getObjectProperty obj propName gvalueGet_ gtypePointer+ if ptr /= nullPtr+ then return . Just . wrapper $ castPtrToFunPtr ptr+ else return Nothing++-- | Set a property of type `GError`.+setObjectPropertyGError :: forall a. GObject a =>+ a -> String -> Maybe GError -> IO ()+setObjectPropertyGError = setObjectPropertyBoxed++-- | Construct a property of type `GError`.+constructObjectPropertyGError :: String -> Maybe GError -> IO (GValueConstruct o)+constructObjectPropertyGError = constructObjectPropertyBoxed++-- | Get the value of a property of type `GError`.+getObjectPropertyGError :: forall a. GObject a =>+ a -> String -> IO (Maybe GError)+getObjectPropertyGError obj propName =+ getObjectPropertyBoxed obj propName GError++-- | Set a property of type `GValue`.+setObjectPropertyGValue :: forall a. GObject a =>+ a -> String -> Maybe GValue -> IO ()+setObjectPropertyGValue = setObjectPropertyBoxed++-- | Construct a property of type `GValue`.+constructObjectPropertyGValue :: String -> Maybe GValue -> IO (GValueConstruct o)+constructObjectPropertyGValue = constructObjectPropertyBoxed++-- | Get the value of a property of type `GValue`.+getObjectPropertyGValue :: forall a. GObject a =>+ a -> String -> IO (Maybe GValue)+getObjectPropertyGValue obj propName =+ getObjectPropertyBoxed obj propName GValue++-- | Construct a property of type `GParamSpec`.+constructObjectPropertyParamSpec :: String -> Maybe GParamSpec ->+ IO (GValueConstruct o)+constructObjectPropertyParamSpec = constructObjectPropertyIsGValueInstance++-- | Get a property of type `GParamSpec`.+getObjectPropertyParamSpec :: GObject a => a -> String -> IO (Maybe GParamSpec)+getObjectPropertyParamSpec = getObjectPropertyIsGValueInstance++-- | Set a property of type `GParamSpec`.+setObjectPropertyParamSpec :: GObject a =>+ a -> String -> Maybe GParamSpec -> IO ()+setObjectPropertyParamSpec = setObjectPropertyIsGValueInstance
Data/GI/Base/ShortPrelude.hs view
@@ -1,3 +1,6 @@+#if MIN_VERSION_base(4,20,0)+{-# LANGUAGE ExplicitNamespaces #-}+#endif -- | The Haskell Prelude exports a number of symbols that can easily -- collide with functions appearing in bindings. The generated code -- requires just a small subset of the functions in the Prelude,@@ -20,7 +23,7 @@ , module Data.GI.Base.Attributes , module Data.GI.Base.BasicTypes , module Data.GI.Base.BasicConversions- , module Data.GI.Base.Closure+ , module Data.GI.Base.GClosure , module Data.GI.Base.Constructible , module Data.GI.Base.GError , module Data.GI.Base.GHashTable@@ -29,7 +32,6 @@ , module Data.GI.Base.GVariant , module Data.GI.Base.GValue , module Data.GI.Base.ManagedPtr- , module Data.GI.Base.Properties , module Data.GI.Base.Signals , module Data.GI.Base.Utils @@ -46,7 +48,7 @@ , (++) , (=<<) , (>=>)- , Bool(..)+ , Bool() , Float , Double , undefined@@ -58,6 +60,9 @@ , when , fromIntegral , realToFrac+#if MIN_VERSION_base(4,20,0)+ , type (~)+#endif ) where import Control.Monad (when, (>=>))@@ -78,8 +83,8 @@ import Data.GI.Base.Attributes hiding (get, set) import Data.GI.Base.BasicTypes import Data.GI.Base.BasicConversions-import Data.GI.Base.Closure import Data.GI.Base.Constructible+import Data.GI.Base.GClosure (GClosure) import Data.GI.Base.GError import Data.GI.Base.GHashTable import Data.GI.Base.GObject@@ -87,7 +92,6 @@ import Data.GI.Base.GVariant import Data.GI.Base.GValue import Data.GI.Base.ManagedPtr-import Data.GI.Base.Properties import Data.GI.Base.Signals (SignalConnectMode(..), connectSignalFunPtr, SignalHandlerId, SignalInfo(..), GObjectNotifySignalInfo) import Data.GI.Base.Utils
+ Data/GI/Base/Signals.hs view
@@ -0,0 +1,292 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE EmptyDataDecls #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE ImplicitParams #-}+{-# LANGUAGE RankNTypes #-}++-- | Routines for connecting `GObject`s to signals. There are two+-- basic variants, 'on' and 'after', which correspond to+-- <https://docs.gtk.org/gobject/func.signal_connect.html g_signal_connect> and <https://docs.gtk.org/gobject/func.signal_connect_after.html g_signal_connect_after>, respectively.+--+-- Basic usage is+--+-- @ 'on' widget #signalName $ do ... @+--+-- or+--+-- @ 'after' widget #signalName $ do ... @+--+-- Note that in the Haskell bindings we represent the signal name in+-- camelCase, so a signal like <https://webkitgtk.org/reference/webkit2gtk/stable/WebKitUserContentManager.html#WebKitUserContentManager-script-message-received script-message-received> in the original API becomes <https://hackage.haskell.org/package/gi-webkit2-4.0.24/docs/GI-WebKit2-Objects-UserContentManager.html#g:16 scriptMessageReceived> in the bindings.+--+-- There are two variants of note. If you want to provide a detail+-- when connecting the signal you can use ':::', as follows:+--+-- @ 'on' widget (#scriptMessageReceived ':::' "handlerName") $ do ... @+--+-- On the other hand, if you want to connect to the "<https://hackage.haskell.org/package/gi-gobject-2.0.21/docs/GI-GObject-Objects-Object.html#g:30 notify>" signal for a property of a widget, it is recommended to use instead 'PropertyNotify', as follows:+--+-- @ 'on' widget ('PropertyNotify' #propertyName) $ do ... @+--+-- which has the advantage that it will be checked at compile time+-- that the widget does indeed have the property "@propertyName@".+module Data.GI.Base.Signals+ ( on+ , after+ , SignalProxy(..)+ , SignalConnectMode(..)+ , connectSignalFunPtr+ , disconnectSignalHandler+ , SignalHandlerId+ , SignalInfo(..)+ , GObjectNotifySignalInfo+ , SignalCodeGenError+ , resolveSignal+ , connectGObjectNotify+ ) where++import Control.Monad.IO.Class (MonadIO, liftIO)+import Data.Proxy (Proxy(..))+#if !MIN_VERSION_base(4,11,0)+import Data.Monoid ((<>))+#endif++import Foreign+import Foreign.C+#if !MIN_VERSION_base(4,13,0)+import Foreign.Ptr (nullPtr)+#endif++import GHC.TypeLits+import Data.Kind (Type)++import qualified Data.Text as T+import Data.Text (Text)++import Data.GI.Base.Attributes (AttrLabelProxy(..), AttrInfo(AttrLabel),+ AttrGetType, attrGet,+ AttrBaseTypeConstraint)+import Data.GI.Base.BasicConversions (withTextCString)+import Data.GI.Base.BasicTypes+import Data.GI.Base.GParamSpec (newGParamSpecFromPtr)+import Data.GI.Base.ManagedPtr (withManagedPtr, withTransient)+import Data.GI.Base.Overloading (ResolveSignal, ResolveAttribute,+ ResolvedSymbolInfo)++import GHC.OverloadedLabels (IsLabel(..))++-- | Type of a `GObject` signal handler id.+type SignalHandlerId = CULong++-- | Support for overloaded signal connectors.+data SignalProxy (object :: Type) (info :: Type) where+ -- | A basic signal name connector.+ SignalProxy :: SignalProxy o info+ -- | A signal connector annotated with a detail.+ (:::) :: forall o info. SignalProxy o info -> Text -> SignalProxy o info+ -- | A signal connector for the @notify@ signal on the given property.+ PropertyNotify :: (info ~ ResolveAttribute propName o,+ AttrInfo info,+ pl ~ AttrLabel info, KnownSymbol pl) =>+ AttrLabelProxy propName ->+ SignalProxy o GObjectNotifySignalInfo+ -- | A signal connector for the @notify@ signal on the given+ -- property, similar to `PropertyNotify`, but it passes the new+ -- value of the property to the callback for convenience.+ PropertySet :: (info ~ ResolveAttribute propName o,+ AttrInfo info,+ AttrBaseTypeConstraint info o,+ b ~ AttrGetType info,+ pl ~ AttrLabel info, KnownSymbol pl) =>+ AttrLabelProxy propName ->+ SignalProxy o (GObjectPropertySetSignalInfo b)++-- | Support for overloaded labels.+instance (info ~ ResolveSignal slot object) =>+ IsLabel slot (SignalProxy object info) where+#if MIN_VERSION_base(4,10,0)+ fromLabel = SignalProxy+#else+ fromLabel _ = SignalProxy+#endif++-- | Information about an overloaded signal.+class SignalInfo (info :: Type) where+ -- | The type for the signal handler.+ type HaskellCallbackType info :: Type+ -- | Connect a Haskell function to a signal of the given `GObject`,+ -- specifying whether the handler will be called before or after the+ -- default handler. Note that the callback being passed here admits+ -- an extra initial parameter with respect to the usual Haskell+ -- callback type. This will be passed as an /implicit/ @?self@+ -- argument to the Haskell callback.+ connectSignal :: GObject o =>+ o ->+ (o -> HaskellCallbackType info) ->+ SignalConnectMode ->+ Maybe Text ->+ IO SignalHandlerId++ -- | Optional extra debug information, for `resolveSignal` below.+ dbgSignalInfo :: Maybe ResolvedSymbolInfo+ dbgSignalInfo = Nothing++-- | Whether to connect a handler to a signal with `connectSignal` so+-- that it runs before/after the default handler for the given signal.+data SignalConnectMode = SignalConnectBefore -- ^ Run before the default handler.+ | SignalConnectAfter -- ^ Run after the default handler.++-- | Connect a signal to a signal handler.+on :: forall object info m.+ (GObject object, MonadIO m, SignalInfo info) =>+ object -> SignalProxy object info+ -> ((?self :: object) => HaskellCallbackType info)+ -> m SignalHandlerId+on o p@(PropertySet (_ :: AttrLabelProxy propName)) cb = liftIO $ do+ let wrapped = wrapPropertySet (Proxy @propName) (Proxy @object) cb+ cb' <- mkGObjectNotifyCallback wrapped+ connectSignalFunPtr o "notify" cb' SignalConnectBefore (proxyDetail p)+on o p c =+ liftIO $ connectSignal @info o w SignalConnectBefore (proxyDetail p)+ where w :: object -> HaskellCallbackType info+ w parent = let ?self = parent in c++-- | Wrap a @b -> IO ()@ callback into a property notify callback on+-- the C side, by adding some code that reads the current value of the+-- property before invoking the callback.+wrapPropertySet :: forall info prop obj.+ (info ~ ResolveAttribute prop obj,+ AttrBaseTypeConstraint info obj,+ AttrInfo info,+ GObject obj) =>+ Proxy (prop :: Symbol) -> Proxy obj ->+ ((?self :: obj) => AttrGetType info -> IO ()) ->+ Ptr obj -> Ptr GParamSpec -> Ptr () -> IO ()+wrapPropertySet _ _ cb objPtr _pspec _data =+ withTransient objPtr $ \self -> do+ val <- attrGet @(ResolveAttribute prop obj) self+ let ?self = self in cb val++-- | Connect a signal to a handler, running the handler after the default one.+after :: forall object info m.+ (GObject object, MonadIO m, SignalInfo info) =>+ object -> SignalProxy object info+ -> ((?self :: object) => HaskellCallbackType info)+ -> m SignalHandlerId+after o p c =+ liftIO $ connectSignal @info o w SignalConnectAfter (proxyDetail p)+ where w :: object -> HaskellCallbackType info+ w parent = let ?self = parent in c++-- | Given a signal proxy, determine the corresponding detail.+proxyDetail :: forall object info. SignalProxy object info -> Maybe Text+proxyDetail p = case p of+ SignalProxy -> Nothing+ (_ ::: detail) -> Just detail+ PropertyNotify (AttrLabelProxy :: AttrLabelProxy propName) ->+ Just . T.pack $ symbolVal (Proxy @(AttrLabel (ResolveAttribute propName object)))+ PropertySet (AttrLabelProxy :: AttrLabelProxy propName) ->+ Just . T.pack $ symbolVal (Proxy @(AttrLabel (ResolveAttribute propName object)))++-- Connecting GObjects to signals+foreign import ccall g_signal_connect_data ::+ Ptr a -> -- instance+ CString -> -- detailed_signal+ FunPtr b -> -- c_handler+ Ptr () -> -- data+ FunPtr c -> -- destroy_data+ CUInt -> -- connect_flags+ IO SignalHandlerId++-- Releasing the `FunPtr` for the signal handler.+foreign import ccall "& haskell_gi_release_signal_closure"+ ptr_to_release_closure :: FunPtr (Ptr () -> Ptr () -> IO ())++-- | Connect a signal to a handler, given as a `FunPtr`.+connectSignalFunPtr :: GObject o =>+ o -> Text -> FunPtr a -> SignalConnectMode ->+ Maybe Text -> IO SignalHandlerId+connectSignalFunPtr object signal fn mode maybeDetail = do+ let flags = case mode of+ SignalConnectAfter -> 1+ SignalConnectBefore -> 0+ signalSpec = case maybeDetail of+ Nothing -> signal+ Just detail -> signal <> "::" <> detail+ withTextCString signalSpec $ \csignal ->+ withManagedPtr object $ \objPtr ->+ g_signal_connect_data objPtr csignal fn nullPtr ptr_to_release_closure flags++foreign import ccall g_signal_handler_disconnect :: Ptr o -> SignalHandlerId -> IO ()++-- | Disconnect a previously connected signal.+disconnectSignalHandler :: GObject o => o -> SignalHandlerId -> IO ()+disconnectSignalHandler obj handlerId =+ withManagedPtr obj $ \objPtr ->+ g_signal_handler_disconnect objPtr handlerId++-- | Connection information for a "notify" signal indicating that a+-- specific property changed (see `PropertyNotify` for the relevant+-- constructor).+data GObjectNotifySignalInfo+instance SignalInfo GObjectNotifySignalInfo where+ type HaskellCallbackType GObjectNotifySignalInfo = GObjectNotifyCallback+ connectSignal = connectGObjectNotify++-- | Type for a `GObject` "notify" callback.+type GObjectNotifyCallback = GParamSpec -> IO ()++gobjectNotifyCallbackWrapper :: GObject o =>+ (o -> GObjectNotifyCallback) -> GObjectNotifyCallbackC o+gobjectNotifyCallbackWrapper cb selfPtr pspec _ = do+ pspec' <- newGParamSpecFromPtr pspec+ withTransient (castPtr selfPtr) $ \self -> cb self pspec'++type GObjectNotifyCallbackC o = Ptr o -> Ptr GParamSpec -> Ptr () -> IO ()++foreign import ccall "wrapper"+ mkGObjectNotifyCallback :: GObjectNotifyCallbackC o -> IO (FunPtr (GObjectNotifyCallbackC o))++-- | Connect the given notify callback for a GObject.+connectGObjectNotify :: GObject o =>+ o -> (o -> GObjectNotifyCallback) ->+ SignalConnectMode ->+ Maybe Text ->+ IO SignalHandlerId+connectGObjectNotify obj cb mode detail = do+ cb' <- mkGObjectNotifyCallback (gobjectNotifyCallbackWrapper cb)+ connectSignalFunPtr obj "notify" cb' mode detail++data GObjectPropertySetSignalInfo (b :: Type)+instance SignalInfo (GObjectPropertySetSignalInfo b) where+ type HaskellCallbackType (GObjectPropertySetSignalInfo b) = b -> IO ()+ connectSignal = undefined -- We connect these separately++-- | Generate an informative type error whenever one tries to use a+-- signal for which code generation has failed.+type family SignalCodeGenError (signalName :: Symbol) :: Type where+ SignalCodeGenError signalName = TypeError+ ('Text "The signal ‘"+ ':<>: 'Text signalName+ ':<>: 'Text "’ is not supported, because haskell-gi failed to generate appropriate bindings."+ ':$$: 'Text "Please file an issue at https://github.com/haskell-gi/haskell-gi/issues.")++-- | Return the fully qualified signal name that a given overloaded+-- signal resolves to (mostly useful for debugging).+--+-- > resolveSignal #childNotify button+resolveSignal :: forall object info. (GObject object, SignalInfo info) =>+ object -> SignalProxy object info -> Maybe ResolvedSymbolInfo+resolveSignal _o _p = dbgSignalInfo @info
+ Data/GI/Base/Signals.hs-boot view
@@ -0,0 +1,53 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE RoleAnnotations #-}+{-# LANGUAGE ImplicitParams #-}+{-# LANGUAGE RankNTypes #-}++module Data.GI.Base.Signals (SignalInfo(..), SignalProxy, on, after,+ connectGObjectNotify, SignalConnectMode(..)) where++import Data.GI.Base.Overloading (ResolvedSymbolInfo)+import Data.GI.Base.BasicTypes (GObject, GParamSpec)+import Control.Monad.IO.Class (MonadIO)+import Foreign.C (CULong)+import Data.Text (Text)++data SignalConnectMode = SignalConnectBefore+ | SignalConnectAfter++class SignalInfo info where+ type HaskellCallbackType info+ connectSignal :: GObject o =>+ o ->+ (o -> HaskellCallbackType info) ->+ SignalConnectMode ->+ Maybe Text ->+ IO SignalHandlerId+ dbgSignalInfo :: Maybe ResolvedSymbolInfo+ dbgSignalInfo = Nothing++type role SignalProxy nominal nominal+data SignalProxy object info where++type SignalHandlerId = CULong++on :: forall object info m.+ (GObject object, MonadIO m, SignalInfo info) =>+ object -> SignalProxy object info+ -> ((?self :: object) => HaskellCallbackType info) -> m SignalHandlerId++after :: forall object info m.+ (GObject object, MonadIO m, SignalInfo info) =>+ object -> SignalProxy object info+ -> ((?self :: object) => HaskellCallbackType info) -> m SignalHandlerId++type GObjectNotifyCallback = GParamSpec -> IO ()++connectGObjectNotify :: GObject o =>+ o -> (o -> GObjectNotifyCallback) ->+ SignalConnectMode ->+ Maybe Text ->+ IO SignalHandlerId
− Data/GI/Base/Signals.hsc
@@ -1,174 +0,0 @@-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE PolyKinds #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE EmptyDataDecls #-}-{-# LANGUAGE UndecidableInstances #-}---- | Routines for connecting `GObject`s to signals.-module Data.GI.Base.Signals- ( on- , after- , SignalProxy(..)- , SignalConnectMode(..)- , connectSignalFunPtr- , SignalHandlerId- , SignalInfo(..)- , GObjectNotifySignalInfo- ) where--import Control.Monad.IO.Class (MonadIO, liftIO)-import Data.Proxy (Proxy(..))--import Foreign-import Foreign.C--import GHC.TypeLits--import Data.GI.Base.Attributes (AttrLabelProxy, AttrInfo(AttrLabel))-import Data.GI.Base.BasicTypes-import Data.GI.Base.GParamSpec (newGParamSpecFromPtr)-import Data.GI.Base.ManagedPtr (withManagedPtr)-import Data.GI.Base.Overloading (ResolveSignal,- IsLabelProxy(..), ResolveAttribute)-import Data.GI.Base.Utils (safeFreeFunPtrPtr)--#if MIN_VERSION_base(4,9,0)-import GHC.OverloadedLabels (IsLabel(..))-#else-import Data.GI.Base.Overloading (HasSignal)-#endif---- | Type of a `GObject` signal handler id.-type SignalHandlerId = CULong---- | A class that provides a constraint satisfied by every type.-class NoConstraint a-instance NoConstraint a---- | Support for overloaded signal connectors.-data SignalProxy (object :: *) (info :: *) where- SignalProxy :: SignalProxy o info- PropertyNotify :: (info ~ ResolveAttribute propName o,- AttrInfo info,- pl ~ AttrLabel info) =>- AttrLabelProxy propName ->- SignalProxy o (GObjectNotifySignalInfo pl)---- | Support for overloaded labels.-instance-#if !MIN_VERSION_base(4,9,0)- -- This gives better error reporting in ghc < 8.0.- (HasSignal slot object, info ~ ResolveSignal slot object)-#else- info ~ ResolveSignal slot object-#endif- => IsLabelProxy slot (SignalProxy object info) where- fromLabelProxy _ = SignalProxy--#if MIN_VERSION_base(4,10,0)-instance info ~ ResolveSignal slot object =>- IsLabel slot (SignalProxy object info) where- fromLabel = SignalProxy-#elif MIN_VERSION_base(4,9,0)-instance info ~ ResolveSignal slot object =>- IsLabel slot (SignalProxy object info) where- fromLabel _ = SignalProxy-#endif---- | Information about an overloaded signal.-class SignalInfo (info :: *) where- type HaskellCallbackType info- -- | Connect a Haskell function to a signal of the given `GObject`,- -- specifying whether the handler will be called before or after- -- the default handler.- connectSignal :: GObject o =>- SignalProxy o info ->- o ->- HaskellCallbackType info ->- SignalConnectMode ->- IO SignalHandlerId---- | Whether to connect a handler to a signal with `connectSignal` so--- that it runs before/after the default handler for the given signal.-data SignalConnectMode = SignalConnectBefore -- ^ Run before the default handler.- | SignalConnectAfter -- ^ Run after the default handler.---- | Same as `connectSignal`, specifying from the beginning that the--- handler is to be run before the default handler.------ > on object signal handler = liftIO $ connectSignal signal object handler SignalConnectBefore-on :: forall object info m.- (GObject object, MonadIO m, SignalInfo info) =>- object -> SignalProxy object info- -> HaskellCallbackType info -> m SignalHandlerId-on o p c = liftIO $ connectSignal p o c SignalConnectBefore---- | Connect a signal to a handler, running the handler after the default one.------ > after object signal handler = liftIO $ connectSignal signal object handler SignalConnectAfter-after :: forall object info m.- (GObject object, MonadIO m, SignalInfo info) =>- object -> SignalProxy object info- -> HaskellCallbackType info -> m SignalHandlerId-after o p c = liftIO $ connectSignal p o c SignalConnectAfter---- Connecting GObjects to signals-foreign import ccall "g_signal_connect_data" g_signal_connect_data ::- Ptr a -> -- instance- CString -> -- detailed_signal- FunPtr b -> -- c_handler- Ptr () -> -- data- FunPtr c -> -- destroy_data- CUInt -> -- connect_flags- IO SignalHandlerId---- | Connect a signal to a handler, given as a `FunPtr`.-connectSignalFunPtr :: GObject o =>- o -> String -> FunPtr a -> SignalConnectMode -> IO SignalHandlerId-connectSignalFunPtr object signal fn mode = do- let flags = case mode of- SignalConnectAfter -> 1- SignalConnectBefore -> 0- withCString signal $ \csignal ->- withManagedPtr object $ \objPtr ->- g_signal_connect_data objPtr csignal fn (castFunPtrToPtr fn) safeFreeFunPtrPtr flags---- | Connection information for a "notify" signal indicating that a--- specific property changed (see `PropertyNotify` for the relevant--- constructor).-data GObjectNotifySignalInfo (propName :: Symbol)-instance KnownSymbol propName =>- SignalInfo (GObjectNotifySignalInfo propName) where- type HaskellCallbackType (GObjectNotifySignalInfo propName) = GObjectNotifyCallback- connectSignal = connectGObjectNotify (symbolVal (Proxy :: Proxy propName))---- | Type for a `GObject` `notify` callback.-type GObjectNotifyCallback = GParamSpec -> IO ()--gobjectNotifyCallbackWrapper ::- GObjectNotifyCallback -> Ptr () -> Ptr GParamSpec -> Ptr () -> IO ()-gobjectNotifyCallbackWrapper _cb _ pspec _ = do- pspec' <- newGParamSpecFromPtr pspec- _cb pspec'--type GObjectNotifyCallbackC = Ptr () -> Ptr GParamSpec -> Ptr () -> IO ()--foreign import ccall "wrapper"- mkGObjectNotifyCallback :: GObjectNotifyCallbackC -> IO (FunPtr GObjectNotifyCallbackC)---- | Connect the given notify callback for a GObject.-connectGObjectNotify :: forall o i. GObject o =>- String ->- SignalProxy o (i :: *) ->- o -> GObjectNotifyCallback ->- SignalConnectMode -> IO SignalHandlerId-connectGObjectNotify propName _ obj cb mode = do- cb' <- mkGObjectNotifyCallback (gobjectNotifyCallbackWrapper cb)- let signalName = "notify::" ++ propName- connectSignalFunPtr obj signalName cb' mode
Data/GI/Base/Utils.hsc view
@@ -1,5 +1,5 @@ {-# LANGUAGE ScopedTypeVariables, TupleSections, OverloadedStrings,- FlexibleContexts, ConstraintKinds #-}+ FlexibleContexts, ConstraintKinds, TypeApplications #-} {- | Assorted utility functions for bindings. -} module Data.GI.Base.Utils ( whenJust@@ -21,29 +21,34 @@ , memcpy , safeFreeFunPtr , safeFreeFunPtrPtr+ , safeFreeFunPtrPtr' , maybeReleaseFunPtr , checkUnexpectedReturnNULL , checkUnexpectedNothing+ , dbgLog ) where #include <glib-object.h> -#if !MIN_VERSION_base(4,8,0)-import Control.Applicative (Applicative, pure, (<$>), (<*>))-#endif import Control.Exception (throwIO) import Control.Monad (void) import qualified Data.Text as T+import qualified Data.Text.Foreign as TF+#if !MIN_VERSION_base(4,11,0) import Data.Monoid ((<>))+#endif import Data.Word +#if !MIN_VERSION_base(4,13,0) import Foreign (peek)-import Foreign.C.Types (CSize(..))+#endif+import Foreign.C.Types (CSize(..), CChar) import Foreign.Ptr (Ptr, nullPtr, FunPtr, nullFunPtr, freeHaskellFunPtr) import Foreign.Storable (Storable(..)) -import Data.GI.Base.BasicTypes (GType(..), CGType, BoxedObject(..),+import Data.GI.Base.BasicTypes (GType(..), CGType, GBoxed,+ TypedObject(glibType), UnexpectedNullPointerReturn(..)) import Data.GI.Base.CallStack (HasCallStack, callStack, prettyCallStack) @@ -124,10 +129,10 @@ -- in particular may well be different from a plain g_malloc. In -- particular g_slice_alloc is often used for allocating boxed -- objects, which are then freed using g_slice_free.-callocBoxedBytes :: forall a. BoxedObject a => Int -> IO (Ptr a)+callocBoxedBytes :: forall a. GBoxed a => Int -> IO (Ptr a) callocBoxedBytes n = do ptr <- callocBytes n- GType cgtype <- boxedType (undefined :: a)+ GType cgtype <- glibType @a result <- g_boxed_copy cgtype ptr freeMem ptr return result@@ -168,6 +173,12 @@ foreign import ccall "& safeFreeFunPtr" safeFreeFunPtrPtr :: FunPtr (Ptr a -> IO ()) +-- | Similar to 'safeFreeFunPtrPtr', but accepts an additional+-- (ignored) argument. The first argument is interpreted as a+-- 'FunPtr', and released.+foreign import ccall "& safeFreeFunPtr2" safeFreeFunPtrPtr' ::+ FunPtr (Ptr a -> Ptr b -> IO ())+ -- | If given a pointer to the memory location, free the `FunPtr` at -- that location, and then the pointer itself. Useful for freeing the -- memory associated to callbacks which are called just once, with no@@ -186,7 +197,8 @@ throwIO (UnexpectedNullPointerReturn { nullPtrErrorMsg = "Received unexpected nullPtr in \"" <> fnName <> "\".\n" <>- "This is a bug in the introspection data, please report it at\nhttps://github.com/haskell-gi/haskell-gi/issues\n" <>+ "This might be a bug in the introspection data, or perhaps a use-after-free bug.\n" <>+ "If in doubt, please report it at\n\thttps://github.com/haskell-gi/haskell-gi/issues\n" <> T.pack (prettyCallStack callStack) }) | otherwise = return ()@@ -199,8 +211,17 @@ case result of Just r -> return r Nothing -> throwIO (UnexpectedNullPointerReturn {- nullPtrErrorMsg = "Received unexpected nullPtr in \""+ nullPtrErrorMsg = "Received unexpected Nothing in \"" <> fnName <> "\".\n" <>- "This is a bug in the introspection data, please report it at\nhttps://github.com/haskell-gi/haskell-gi/issues\n" <>+ "This might be a bug in the introspection data, or perhaps a use-after-free bug.\n" <>+ "If in doubt, please report it at\n\thttps://github.com/haskell-gi/haskell-gi/issues\n" <> T.pack (prettyCallStack callStack) })++foreign import ccall unsafe "dbg_log_with_len" dbg_log_with_len ::+ Ptr CChar -> Int -> IO ()++-- | Print a string to the debug log in an atomic way (so the output+-- of different threads does not get intermingled).+dbgLog :: T.Text -> IO ()+dbgLog msg = TF.withCStringLen msg $ \(ptr, len) -> dbg_log_with_len ptr len
LICENSE view
@@ -1,3 +1,17 @@+The haskell-gi-base library and included works are provided under the terms of+the GNU Library General Public License (LGPL) version 2.1 with the following+exception:++Static linking of applications or any other source to the haskell-gi-base+library does not constitute a modified or derivative work and does not require+the author(s) to provide source code for said work, to link against the shared+haskell-gi-base libraries, or to link their applications against a+user-supplied version of haskell-gi-base. If you link applications to a+modified version of haskell-gi-base, then the changes to haskell-gi-base must+be provided under the terms of the LGPL.++----------------------------------------------------------------------------+ GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999
− c/hsgclosure.c
@@ -1,202 +0,0 @@-#define _GNU_SOURCE--/* GHC's semi-public Rts API */-#include <Rts.h>--#include <stdlib.h>-#include <string.h>--#include <glib-object.h>-#include <glib.h>--int check_object_type(void *instance, GType type)-{- int result;-- if (instance != NULL) {- result = !!G_TYPE_CHECK_INSTANCE_TYPE(instance, type);- } else {- result = 0;- fprintf(stderr, "Check failed: got a null pointer\n");- }-- return result;-}--static int print_debug_info ()-{- static int __print_debug_info = -1;-- if (__print_debug_info == -1) {- __print_debug_info = getenv ("HASKELL_GI_DEBUG_MEM") != NULL;- }-- return __print_debug_info;-}--/* Information about a boxed type to free */-typedef struct {- GType gtype;- gpointer boxed;-} BoxedFreeInfo;--/* Auxiliary function for freeing boxed types in the main loop. See- the annotation in g_object_unref_in_main_loop() below. */-static gboolean main_loop_boxed_free_helper (gpointer _info)-{- BoxedFreeInfo *info = (BoxedFreeInfo*)_info;-- if (print_debug_info()) {- GThread *self = g_thread_self ();-- fprintf(stderr, "Freeing a boxed object at %p from idle callback [thread: %p]\n",- info->boxed, self);- fprintf(stderr, "\tIt is of type %s\n", g_type_name(info->gtype));- }-- g_boxed_free (info->gtype, info->boxed);-- if (print_debug_info()) {- fprintf(stderr, "\tdone\n");- }-- g_free(info);-- return FALSE; /* Do not invoke again */-}--void boxed_free_helper (GType gtype, void *boxed)-{- BoxedFreeInfo *info = g_malloc(sizeof(BoxedFreeInfo));-- info->gtype = gtype;- info->boxed = boxed;-- g_idle_add (main_loop_boxed_free_helper, info);-}--void dbg_g_object_disown (GObject *obj)-{- GType gtype;-- if (print_debug_info()) {- GThread *self = g_thread_self();- fprintf(stderr, "Disowning a GObject at %p [thread: %p]\n", obj, self);- gtype = G_TYPE_FROM_INSTANCE (obj);- fprintf(stderr, "\tIt is of type %s\n", g_type_name(gtype));- fprintf(stderr, "\tIts refcount before disowning is %d\n",- (int)obj->ref_count);- }-}--static void print_object_dbg_info (GObject *obj)-{- GThread *self = g_thread_self();- GType gtype;-- fprintf(stderr, "Unref of %p from idle callback [thread: %p]\n", obj, self);- gtype = G_TYPE_FROM_INSTANCE (obj);- fprintf(stderr, "\tIt is of type %s\n", g_type_name(gtype));- fprintf(stderr, "\tIts refcount before unref is %d\n",- (int)obj->ref_count);-}--/*- We schedule all GObject deletions to happen in the main loop. The- reason is that for some types the destructor is not thread safe, and- assumes that it is being run from the same thread as the main loop- that created the object.- */-static gboolean-g_object_unref_in_main_loop (gpointer obj)-{- if (print_debug_info()) {- print_object_dbg_info ((GObject*)obj);- }-- g_object_unref (obj);-- if (print_debug_info()) {- fprintf(stderr, "\tUnref done\n");- }-- return FALSE; /* Do not invoke again */-}--void dbg_g_object_unref (GObject *obj)-{- g_idle_add(g_object_unref_in_main_loop, obj);-}--/**- * dbg_g_object_new:- * @gtype: #GType for the object to construct.- * @n_props: Number of parameters for g_object_new_with_properties().- * @names: Names of the properties to be set.- * @values: Parameters for g_object_new_with_properties().- *- * Allocate a #GObject of #GType @gtype, with the given @params. The- * returned object is never floating, and we always own a reference to- * it. (It might not be the only existing to the object, but it is in- * any case safe to call g_object_unref() when we are not wrapping the- * object ourselves anymore.)- *- * Returns: A new #GObject.- */-gpointer dbg_g_object_new (GType gtype, guint n_props,- const char *names[], const GValue values[])-{- gpointer result;-- if (print_debug_info()) {- GThread *self = g_thread_self();-- fprintf(stderr, "Creating a new GObject of type %s [thread: %p]\n",- g_type_name(gtype), self);- }--#if GLIB_CHECK_VERSION(2,54,0)- result = g_object_new_with_properties (gtype, n_props, names, values);-#else- { GParameter params[n_props];- int i;-- for (i=0; i<n_props; i++) {- memcpy (¶ms[i].value, &values[i], sizeof(GValue));- params[i].name = names[i];- }-- result = g_object_newv (gtype, n_props, params);- }-#endif-- /*- Initially unowned GObjects can be either floating or not after- construction. They are generally floating, but GtkWindow for- instance is not floating after construction.-- In either case we want to call g_object_ref_sink(): if the object- is floating to take ownership of the reference, and otherwise to- add a reference that we own.-- If the object is not initially unowned we simply take control of- the initial reference (implicitly).- */- if (G_IS_INITIALLY_UNOWNED (result)) {- g_object_ref_sink (result);- }-- if (print_debug_info()) {- fprintf(stderr, "\tdone, got a pointer at %p\n", result);- }-- return result;-}--/* Same as freeHaskellFunctionPtr, but it does nothing when given a- null pointer, instead of crashing */-void safeFreeFunPtr(void *ptr)-{- if (ptr != NULL)- freeHaskellFunctionPtr(ptr);-}
+ csrc/hsgclosure.c view
@@ -0,0 +1,477 @@+#define _GNU_SOURCE++/* GHC's semi-public Rts API */+#include <Rts.h>++#include <stdarg.h>+#include <stdlib.h>+#include <string.h>+#include <pthread.h>++#include <glib-object.h>+#include <glib.h>++static int print_debug_info ()+{+ static int __print_debug_info = -1;++ if (__print_debug_info == -1) {+ __print_debug_info = getenv ("HASKELL_GI_DEBUG_MEM") != NULL;+ }++ return __print_debug_info;+}++/*+ A mutex protecting the log file handle. We make it recursive,+ i.e. refcounted, so it is OK to lock repeatedly in the same thread.+*/+static pthread_mutex_t log_mutex+#if defined(PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP)+ = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;+#elif defined(PTHREAD_RECURSIVE_MUTEX_INITIALIZER)+ = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;+#else+ ;+__attribute__ ((constructor)) static void init_log_mutex()+{+ pthread_mutexattr_t attr;+ pthread_mutexattr_init(&attr);+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);+ pthread_mutex_init(&log_mutex, &attr);+ pthread_mutexattr_destroy(&attr);+}+#endif++/* Give the current thread exclusive access to the log */+static void lock_log()+{+ pthread_mutex_lock(&log_mutex);+}++/* Decrease the refcount of the mutex protecting access to the log+ from other threads */+static void unlock_log()+{+ pthread_mutex_unlock(&log_mutex);+}++/* Print the given message to the log. The passed in string does not+ need to be zero-terminated. The message is only printed if the+ HASKELL_GI_DEBUG_MEM variable is set. */+void dbg_log_with_len (const char *msg, int len)+{+ if (print_debug_info()) {+ lock_log();+ fwrite(msg, len, 1, stderr);+ unlock_log();+ }+}++/* Print the given printf-style message to the log. The message is+ only printed if the HASKELL_GI_DEBUG_MEM variable is set. */+__attribute__ ((format (printf, 1, 2)))+static void dbg_log (const char *msg, ...)+{+ va_list args;++ va_start(args, msg);++ if (print_debug_info()) {+ lock_log();+ vfprintf(stderr, msg, args);+ unlock_log();+ }++ va_end(args);+}++int check_object_type (void *instance, GType type)+{+ int result;++ if (instance != NULL) {+ result = !!G_TYPE_CHECK_INSTANCE_TYPE(instance, type);+ } else {+ result = 0;+ dbg_log("Check failed: got a null pointer\n");+ }++ return result;+}++GType _haskell_gi_g_value_get_type (GValue *gvalue)+{+ return G_VALUE_TYPE (gvalue);+}++/* Information about a boxed type to free */+typedef struct {+ GType gtype;+ gpointer boxed;+} BoxedFreeInfo;++/* Auxiliary function for freeing boxed types in the main loop. See+ the annotation in g_object_unref_in_main_loop() below. */+static gboolean main_loop_boxed_free_helper (gpointer _info)+{+ BoxedFreeInfo *info = (BoxedFreeInfo*)_info;++ if (print_debug_info()) {+ GThread *self = g_thread_self ();+ lock_log();+ dbg_log("Freeing a boxed object at %p from idle callback [thread: %p]\n",+ info->boxed, self);+ dbg_log("\tIt is of type %s\n", g_type_name(info->gtype));+ }++ g_boxed_free (info->gtype, info->boxed);++ if (print_debug_info()) {+ dbg_log("\tdone freeing %p.\n", info->boxed);+ unlock_log();+ }++ g_free(info);++ return FALSE; /* Do not invoke again */+}++void boxed_free_helper (GType gtype, void *boxed)+{+ BoxedFreeInfo *info = g_malloc(sizeof(BoxedFreeInfo));++ info->gtype = gtype;+ info->boxed = boxed;++ g_idle_add (main_loop_boxed_free_helper, info);+}++void dbg_g_object_disown (GObject *obj)+{+ GType gtype;++ if (print_debug_info()) {+ lock_log();+ GThread *self = g_thread_self();+ dbg_log("Disowning a GObject at %p [thread: %p]\n", obj, self);+ gtype = G_TYPE_FROM_INSTANCE (obj);+ dbg_log("\tIt is of type %s\n", g_type_name(gtype));+ dbg_log("\tIts refcount before disowning is %d\n", (int)obj->ref_count);+ unlock_log();+ }+}++static void print_object_dbg_info (GObject *obj)+{+ GThread *self = g_thread_self();+ GType gtype;++ dbg_log("Unref of %p from idle callback [thread: %p]\n", obj, self);+ gtype = G_TYPE_FROM_INSTANCE (obj);+ dbg_log("\tIt is of type %s\n", g_type_name(gtype));+ dbg_log("\tIts refcount before unref is %d\n", (int)obj->ref_count);+}++/*+ We schedule all GObject deletions to happen in the main loop. The+ reason is that for some types the destructor is not thread safe, and+ assumes that it is being run from the same thread as the main loop+ that created the object.+ */+static gboolean+g_object_unref_in_main_loop (gpointer obj)+{+ if (print_debug_info()) {+ lock_log();+ print_object_dbg_info ((GObject*)obj);+ }++ g_object_unref (obj);++ if (print_debug_info()) {+ fprintf(stderr, "\tUnref done\n");+ unlock_log();+ }++ return FALSE; /* Do not invoke again */+}++void dbg_g_object_unref (GObject *obj)+{+ g_idle_add(g_object_unref_in_main_loop, obj);+}++static gboolean gvalue_unref_in_main_loop(void *gv)+{+ g_boxed_free(G_TYPE_VALUE, gv);++ return FALSE; /* Do not invoke again */+}++void haskell_gi_gvalue_free(GValue *gv)+{+ g_idle_add(gvalue_unref_in_main_loop, gv);+}++/**+ * dbg_g_object_new:+ * @gtype: #GType for the object to construct.+ * @n_props: Number of parameters for g_object_new_with_properties().+ * @names: Names of the properties to be set.+ * @values: Parameters for g_object_new_with_properties().+ *+ * Allocate a #GObject of #GType @gtype, with the given @params. The+ * returned object is never floating, and we always own a reference to+ * it. (It might not be the only existing to the object, but it is in+ * any case safe to call g_object_unref() when we are not wrapping the+ * object ourselves anymore.)+ *+ * Returns: A new #GObject.+ */+gpointer dbg_g_object_new (GType gtype, guint n_props,+ const char *names[], const GValue values[])+{+ gpointer result;++ if (print_debug_info()) {+ GThread *self = g_thread_self();++ lock_log();+ dbg_log("Creating a new GObject of type %s [thread: %p]\n",+ g_type_name(gtype), self);+ }++#if GLIB_CHECK_VERSION(2,54,0)+ result = g_object_new_with_properties (gtype, n_props, names, values);+#else+ { GParameter params[n_props];+ int i;++ for (i=0; i<n_props; i++) {+ memcpy (¶ms[i].value, &values[i], sizeof(GValue));+ params[i].name = names[i];+ }++ result = g_object_newv (gtype, n_props, params);+ }+#endif++ /*+ Initially unowned GObjects can be either floating or not after+ construction. They are generally floating, but GtkWindow for+ instance is not floating after construction.++ In either case we want to call g_object_ref_sink(): if the object+ is floating to take ownership of the reference, and otherwise to+ add a reference that we own.++ If the object is not initially unowned we simply take control of+ the initial reference (implicitly).+ */+ if (G_IS_INITIALLY_UNOWNED (result)) {+ g_object_ref_sink (result);+ }++ if (print_debug_info()) {+ dbg_log("\tdone, got a pointer at %p\n", result);+ unlock_log();+ }++ return result;+}++/* Same as freeHaskellFunctionPtr, but it does nothing when given a+ null pointer, instead of crashing */+void safeFreeFunPtr(void *ptr)+{+ if (ptr != NULL)+ freeHaskellFunctionPtr(ptr);+}++/* Same as safeFreeFunPtr, but it accepts (but ignores) an extra argument */+void safeFreeFunPtr2(void *ptr, void *unused)+{+ safeFreeFunPtr(ptr);+}++/* Returns the GType associated to a class instance */+GType haskell_gi_gtype_from_class (gpointer klass)+{+ return G_TYPE_FROM_CLASS (klass);+}++/* Returns the GType associated to a given instance */+GType haskell_gi_gtype_from_instance (gpointer instance)+{+ return G_TYPE_FROM_INSTANCE (instance);+}++static pthread_mutex_t gtypes_mutex = PTHREAD_MUTEX_INITIALIZER;++typedef struct {+ GType gtype;+ GInterfaceInfo *info;+} CombinedInterfaceInfo;++/* Register a new type into the GObject class hierarchy, if it has not+ been registered already */+GType haskell_gi_register_gtype (GType parent, const char *name,+ GClassInitFunc class_init,+ GInstanceInitFunc instance_init,+ GSList* interfaces)+{+ GType result;++ /* We lock here in order to make sure that we don't try to register+ the same type twice. */+ pthread_mutex_lock(>ypes_mutex);+ result = g_type_from_name (name);++ if (result == 0) {+ /* Note that class_init and instance_init are HsFunPtrs, which we+ keep alive for the duration of the program. */+ GTypeQuery query;+ g_type_query (parent, &query);+ result = g_type_register_static_simple (parent, name,+ query.class_size, class_init,+ query.instance_size, instance_init,+ 0);+ while (interfaces != NULL) {+ CombinedInterfaceInfo *info = (CombinedInterfaceInfo*) interfaces->data;+ g_type_add_interface_static (result, info->gtype, info->info);+ interfaces = interfaces -> next;+ }+ } else {+ /* Free the memory associated with the HsFunPtrs that we are+ given, to avoid a (small) memory leak. */+ hs_free_fun_ptr ((HsFunPtr)class_init);+ hs_free_fun_ptr ((HsFunPtr)instance_init);++ while (interfaces != NULL) {+ CombinedInterfaceInfo *info = (CombinedInterfaceInfo*) interfaces->data;+ hs_free_fun_ptr ((HsFunPtr) info -> info -> interface_init);+ if (info -> info -> interface_finalize)+ hs_free_fun_ptr ((HsFunPtr) info -> info -> interface_finalize);+ interfaces = interfaces -> next;+ }+ }+ pthread_mutex_unlock(>ypes_mutex);++ return result;+}++static HsStablePtr duplicateStablePtr(HsStablePtr stable_ptr)+{+ return getStablePtr(deRefStablePtr(stable_ptr));+}++GType haskell_gi_StablePtr_get_type (void)+{+ static gsize g_define_type_id = 0;++ if (g_once_init_enter (&g_define_type_id))+ {+ GType type_id =+ g_boxed_type_register_static (g_intern_static_string ("HaskellGIStablePtr"),+ duplicateStablePtr,+ hs_free_stable_ptr);++ g_once_init_leave (&g_define_type_id, type_id);+ }++ return g_define_type_id;+}++/* This is identical to haskell_gi_StablePtr_get_type, other than the+ type name. The reason for this is that we want two different types,+ to distinguish between GValues wrapping generic StablePtrs, and+ those wrapping specifically wrapping StablePtrs to Dynamic+ values. */+GType haskell_gi_HaskellValue_get_type (void)+{+ static gsize g_define_type_id = 0;++ if (g_once_init_enter (&g_define_type_id))+ {+ GType type_id =+ g_boxed_type_register_static (g_intern_static_string ("HaskellGIHaskellValue"),+ duplicateStablePtr,+ hs_free_stable_ptr);++ g_once_init_leave (&g_define_type_id, type_id);+ }++ return g_define_type_id;+}++/* A safer version of get_boxed, that checks that the GValue contains+ the right boxed type. */+gpointer haskell_gi_safe_get_boxed_haskell_value(const GValue *gv)+{+ if (G_VALUE_TYPE(gv) != haskell_gi_HaskellValue_get_type()) {+ fprintf(stderr, "Unexpected type inside the GValue: ‘%s’\n.",+ G_VALUE_TYPE_NAME(gv));++ return NULL;+ }++ return g_value_get_boxed(gv);+}++/* Release the FunPtr allocated for a Haskell signal handler */+void+haskell_gi_release_signal_closure (gpointer unused,+ GCClosure *closure)+{+ lock_log();+ dbg_log("Releasing a signal closure %p\n", closure->callback);++ hs_free_fun_ptr (closure->callback);++ dbg_log("\tDone.\n");+ unlock_log();+}++/* Check whether the given closure is floating */+gboolean+haskell_gi_g_closure_is_floating (GClosure *closure)+{+ return !!(closure->floating);+}++/* GParamSpec* types are registered as GObjects, but they do not have+ an exported type_init function. They only export CPP macros, so+ we have to provide our own. */+#define PARAM_TYPE(CamelCase, UPPERCASE) \+ GType haskell_gi_pspec_type_init_##CamelCase (void) { \+ return G_TYPE_##UPPERCASE; \+ }++PARAM_TYPE(ParamSpec, PARAM);+PARAM_TYPE(ParamSpecBoolean, PARAM_BOOLEAN);+PARAM_TYPE(ParamSpecBoxed, PARAM_BOXED);+PARAM_TYPE(ParamSpecChar, PARAM_CHAR);+PARAM_TYPE(ParamSpecDouble, PARAM_DOUBLE);+PARAM_TYPE(ParamSpecEnum, PARAM_ENUM);+PARAM_TYPE(ParamSpecFlags, PARAM_FLAGS);+PARAM_TYPE(ParamSpecFloat, PARAM_FLOAT);+PARAM_TYPE(ParamSpecGType, PARAM_GTYPE);+PARAM_TYPE(ParamSpecInt, PARAM_INT);+PARAM_TYPE(ParamSpecInt64, PARAM_INT64);+PARAM_TYPE(ParamSpecLong, PARAM_LONG);+PARAM_TYPE(ParamSpecObject, PARAM_OBJECT);+PARAM_TYPE(ParamSpecOverride, PARAM_OVERRIDE);+PARAM_TYPE(ParamSpecParam, PARAM_PARAM);+PARAM_TYPE(ParamSpecPointer, PARAM_POINTER);+PARAM_TYPE(ParamSpecString, PARAM_STRING);+PARAM_TYPE(ParamSpecUChar, PARAM_UCHAR);+PARAM_TYPE(ParamSpecUInt, PARAM_UINT);+PARAM_TYPE(ParamSpecUInt64, PARAM_UINT64);+PARAM_TYPE(ParamSpecULong, PARAM_ULONG);+PARAM_TYPE(ParamSpecUnichar, PARAM_UNICHAR);+PARAM_TYPE(ParamSpecVariant, PARAM_VARIANT);+/* The following is deprecated, ignore the warning that GLib raises. */+#undef GLIB_DEPRECATED_MACRO+#define GLIB_DEPRECATED_MACRO+PARAM_TYPE(ParamSpecValueArray, PARAM_VALUE_ARRAY);
haskell-gi-base.cabal view
@@ -1,25 +1,25 @@ name: haskell-gi-base-version: 0.20.8+version: 0.26.9 synopsis: Foundation for libraries generated by haskell-gi description: Foundation for libraries generated by haskell-gi-homepage: https://github.com/haskell-gi/haskell-gi-base+homepage: https://github.com/haskell-gi/haskell-gi license: LGPL-2.1 -- or above license-file: LICENSE author: Will Thompson, Iñaki García Etxebarria, Jonas Platte-maintainer: Iñaki García Etxebarria (garetxe@gmail.com)+maintainer: Iñaki García Etxebarria (inaki@blueleaf.cc) stability: Experimental category: Development build-type: Simple-cabal-version: >=1.8+cabal-version: 2.0 extra-source-files: ChangeLog.md source-repository head type: git- location: git://github.com/haskell-gi/haskell-gi-base.git+ location: https://github.com/haskell-gi/haskell-gi.git library exposed-modules: Data.GI.Base,@@ -27,11 +27,13 @@ Data.GI.Base.BasicConversions, Data.GI.Base.BasicTypes, Data.GI.Base.CallStack,- Data.GI.Base.Closure, Data.GI.Base.Constructible,+ Data.GI.Base.GArray, Data.GI.Base.GError,+ Data.GI.Base.GClosure, Data.GI.Base.GHashTable, Data.GI.Base.GObject,+ Data.GI.Base.GQuark, Data.GI.Base.GType, Data.GI.Base.GValue, Data.GI.Base.GVariant,@@ -41,23 +43,23 @@ Data.GI.Base.Properties, Data.GI.Base.ShortPrelude, Data.GI.Base.Signals,- Data.GI.Base.Utils+ Data.GI.Base.Utils,+ Data.GI.Base.DynVal,+ Data.GI.Base.Internal.CTypes+ Data.GI.Base.Internal.PathFieldAccess - pkgconfig-depends: gobject-2.0 >= 2.32, glib-2.0- build-depends: base >= 4.7 && < 5,+ pkgconfig-depends: gobject-2.0 >= 2.42, glib-2.0+ build-depends: base >= 4.11 && < 5, bytestring, containers,- text >= 1.0-- if !impl(ghc >= 8.0)- build-depends: transformers+ text >= 1.0,+ optics-core >= 0.4 - if impl(ghc >= 8.0)- ghc-options: -Wall -Wno-redundant-constraints -fwarn-incomplete-patterns- else- ghc-options: -Wall -fwarn-incomplete-patterns+ ghc-options: -Wall -Wno-redundant-constraints -fwarn-incomplete-patterns -Wcompat -Wno-unticked-promoted-constructors - build-tools: hsc2hs+ build-tool-depends: hsc2hs:hsc2hs cc-options: -fPIC- extensions: CPP, ForeignFunctionInterface, DoAndIfThenElse, MonoLocalBinds- c-sources: c/hsgclosure.c+ default-language: Haskell2010+ default-extensions: CPP, ForeignFunctionInterface, DoAndIfThenElse, MonoLocalBinds+ other-extensions: TypeApplications, ScopedTypeVariables+ c-sources: csrc/hsgclosure.c