File: GValue.hs

package info (click to toggle)
haskell-haskell-gi-base 0.26.8-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 408 kB
  • sloc: haskell: 1,604; ansic: 324; makefile: 5
file content (541 lines) | stat: -rw-r--r-- 17,809 bytes parent folder | download
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)