1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 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)
|