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 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985
|
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE EmptyDataDecls #-}
{-|
This module contains some helper functions for dealing with GVariant
values. The simplest way of dealing with them is by using the
'IsGVariant' typeclass:
> str <- fromGVariant variant :: IO (Maybe Text)
assuming that the variant is expected to contain a
string in UTF8 encoding. The code becomes even shorter if the type
checker can determine the return type for you:
> readStringVariant :: GVariant -> IO Text
> readStringVariant variant =
> fromGVariant variant >>= \case
> Nothing -> error "Variant was not a string"
> Just str -> return str
Alternatively, you can use manually the gvariantFrom* and
gvariantTo* family of functions.
-}
module Data.GI.Base.GVariant
( IsGVariant(..)
, IsGVariantBasicType
, noGVariant
, gvariantGetTypeString
-- * Type wrappers
-- | Some 'GVariant' types are isomorphic to Haskell types, but they
-- carry some extra information. For example, there is a tuple
-- singlet type, which is isomorphic to a single Haskell value
-- with the added bit of information that it is wrapped in a tuple
-- container. In order to use these values you can use the
-- following wrappers, which allow the 'IsGVariant' instance to
-- disambiguate the requested type properly.
, GVariantSinglet(GVariantSinglet)
, GVariantDictEntry(GVariantDictEntry)
, GVariantHandle(GVariantHandle)
, GVariantObjectPath
, newGVariantObjectPath
, gvariantObjectPathToText
, GVariantSignature
, newGVariantSignature
, gvariantSignatureToText
-- * Manual memory management
, wrapGVariantPtr
, newGVariantFromPtr
, unrefGVariant
, disownGVariant
-- * Manual conversions
-- ** Basic types
--
-- | The use of these should be fairly self-explanatory. If you
-- want to convert a Haskell type into a 'GVariant', use
-- gvariantTo*. If you want to convert a 'GVariant' into a Haskell
-- type, use gvariantFrom*. The conversion can fail if the
-- 'GVariant' is not of the expected type (if you want to convert
-- a 'GVariant' containing a 'Int16' into a 'Text' value, say), in
-- which case 'Nothing' will be returned.
, gvariantToBool
, gvariantFromBool
, gvariantToWord8
, gvariantFromWord8
, gvariantToInt16
, gvariantFromInt16
, gvariantToWord16
, gvariantFromWord16
, gvariantToInt32
, gvariantFromInt32
, gvariantToWord32
, gvariantFromWord32
, gvariantToInt64
, gvariantFromInt64
, gvariantToWord64
, gvariantFromWord64
, gvariantToHandle
, gvariantFromHandle
, gvariantToDouble
, gvariantFromDouble
, gvariantToText
, gvariantFromText
, gvariantToObjectPath
, gvariantFromObjectPath
, gvariantToSignature
, gvariantFromSignature
-- ** Container type conversions
, gvariantToGVariant
, gvariantFromGVariant
, gvariantToBytestring
, gvariantFromBytestring
, gvariantFromMaybe
, gvariantToMaybe
, gvariantFromDictEntry
, gvariantToDictEntry
, gvariantFromMap
, gvariantToMap
, gvariantFromList
, gvariantToList
, gvariantFromTuple
, gvariantToTuple
) where
#include <glib-object.h>
import Control.Monad (when, void, (>=>))
import Control.Exception.Base (bracket)
import Data.Text (Text)
import Data.ByteString (ByteString)
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
import System.IO.Unsafe (unsafePerformIO)
import Foreign.C
import Foreign.Ptr
import Data.GI.Base.BasicTypes (GVariant(..))
import Data.GI.Base.BasicConversions
import Data.GI.Base.ManagedPtr (withManagedPtr, withManagedPtrList,
newManagedPtr', disownManagedPtr)
import Data.GI.Base.Utils (freeMem)
-- | An alias for @Nothing :: Maybe GVariant@ to save some typing.
noGVariant :: Maybe GVariant
noGVariant = Nothing
-- | The typeclass for types that can be automatically marshalled into
-- 'GVariant' using 'toGVariant' and 'fromGVariant'.
class IsGVariant a where
-- | Convert a value of the given type into a GVariant.
toGVariant :: a -> IO GVariant
-- | Try to decode a 'GVariant' into a target type. If the
-- conversion fails we return 'Nothing'. The type that was
-- expected can be obtained by calling 'toGVariantFormatString',
-- and the actual type as understood by the 'GVariant' code can be
-- obtained by calling 'gvariantToTypeString'.
fromGVariant :: GVariant -> IO (Maybe a)
-- | The expected format string for this type (the argument is
-- ignored).
toGVariantFormatString :: a -> Text
-- Same as fromGVariant, for cases where we have checked that things
-- have the right type in advance.
unsafeFromGVariant :: IsGVariant a => GVariant -> IO a
unsafeFromGVariant gv =
fromGVariant gv >>= \case
Nothing -> error "Error decoding GVariant. This is a bug in haskell-gi, please report it."
Just value -> return value
-- | The typeclass for basic type 'GVariant' types, i.e. those that
-- are not containers.
class Ord a => IsGVariantBasicType a
-- | Haskell has no notion of one element tuples, but GVariants do, so
-- the following allows for marshalling one element tuples properly
-- using 'fromGVariant' and 'toGVariant'. For instance, to construct a
-- single element tuple containing a string, you could do
--
-- > toGVariant (GVariantSinglet "Test")
newtype GVariantSinglet a = GVariantSinglet a
deriving (Eq, Show)
data GVariantType
foreign import ccall "g_variant_type_new" g_variant_type_new ::
CString -> IO (Ptr GVariantType)
foreign import ccall "g_variant_type_free" g_variant_type_free ::
Ptr GVariantType -> IO ()
foreign import ccall "g_variant_is_of_type" g_variant_is_of_type ::
Ptr GVariant -> Ptr GVariantType -> IO #{type gboolean}
withGVariantType :: Text -> (Ptr GVariantType -> IO a) -> IO a
withGVariantType text action = withTextCString text $ \textPtr ->
bracket (g_variant_type_new textPtr)
g_variant_type_free
action
gvariantIsOfType :: Text -> GVariant -> IO Bool
gvariantIsOfType typeString variant =
withGVariantType typeString $
\typePtr ->
(toEnum . fromIntegral) <$> withManagedPtr variant
(\vptr -> g_variant_is_of_type
vptr typePtr)
withExplicitType :: Text -> (Ptr GVariant -> IO a) -> GVariant -> IO (Maybe a)
withExplicitType format action variant = do
check <- gvariantIsOfType format variant
if check
then Just <$> withManagedPtr variant action
else return Nothing
withTypeCheck :: forall a. (IsGVariant a) =>
(Ptr GVariant -> IO a) -> GVariant -> IO (Maybe a)
withTypeCheck = withExplicitType $ toGVariantFormatString (undefined :: a)
foreign import ccall "g_variant_get_type_string" g_variant_get_type_string
:: Ptr GVariant -> IO CString
-- | Get the expected type of a 'GVariant', in 'GVariant'
-- notation. See
-- <https://docs.gtk.org/glib/struct.VariantType.html>
-- for the meaning of the resulting format string.
gvariantGetTypeString :: GVariant -> IO Text
gvariantGetTypeString variant =
withManagedPtr variant (g_variant_get_type_string >=> cstringToText)
foreign import ccall "g_variant_is_floating" g_variant_is_floating ::
Ptr GVariant -> IO CInt
foreign import ccall "g_variant_ref_sink" g_variant_ref_sink ::
Ptr GVariant -> IO (Ptr GVariant)
foreign import ccall "g_variant_ref" g_variant_ref ::
Ptr GVariant -> IO (Ptr GVariant)
foreign import ccall "g_variant_unref" g_variant_unref ::
Ptr GVariant -> IO ()
foreign import ccall "&g_variant_unref" ptr_to_g_variant_unref ::
FunPtr (Ptr GVariant -> IO ())
-- | Take ownership of a passed in 'Ptr' (typically created just for
-- us, so if it is floating we sink it).
wrapGVariantPtr :: Ptr GVariant -> IO GVariant
wrapGVariantPtr ptr = do
floating <- g_variant_is_floating ptr
when (floating /= 0) $ void $ g_variant_ref_sink ptr
fPtr <- newManagedPtr' ptr_to_g_variant_unref ptr
return $! GVariant fPtr
-- | Construct a Haskell wrapper for the given 'GVariant', without
-- assuming ownership.
newGVariantFromPtr :: Ptr GVariant -> IO GVariant
newGVariantFromPtr ptr = do
fPtr <- g_variant_ref ptr >>= newManagedPtr' ptr_to_g_variant_unref
return $! GVariant fPtr
-- | Remove a reference to the given 'GVariant'.
unrefGVariant :: GVariant -> IO ()
unrefGVariant gv = withManagedPtr gv g_variant_unref
-- | Disown a `GVariant`, i.e. do not unref the underlying object when
-- the Haskell object is garbage collected.
disownGVariant :: GVariant -> IO (Ptr GVariant)
disownGVariant = disownManagedPtr
instance IsGVariant Bool where
toGVariant = gvariantFromBool
fromGVariant = gvariantToBool
toGVariantFormatString _ = "b"
instance IsGVariantBasicType Bool
foreign import ccall "g_variant_new_boolean" new_bool
:: #{type gboolean} -> IO (Ptr GVariant)
gvariantFromBool :: Bool -> IO GVariant
gvariantFromBool = (new_bool . fromIntegral . fromEnum) >=> wrapGVariantPtr
foreign import ccall "g_variant_get_boolean" get_bool
:: Ptr GVariant -> IO #{type gboolean}
gvariantToBool :: GVariant -> IO (Maybe Bool)
gvariantToBool = withTypeCheck $ get_bool >=> (return . toEnum . fromIntegral)
instance IsGVariant Word8 where
toGVariant = gvariantFromWord8
fromGVariant = gvariantToWord8
toGVariantFormatString _ = "y"
instance IsGVariantBasicType Word8
foreign import ccall "g_variant_new_byte" new_byte
:: #{type guchar} -> IO (Ptr GVariant)
gvariantFromWord8 :: Word8 -> IO GVariant
gvariantFromWord8 = (new_byte . fromIntegral) >=> wrapGVariantPtr
foreign import ccall "g_variant_get_byte" get_byte
:: Ptr GVariant -> IO #{type guchar}
gvariantToWord8 :: GVariant -> IO (Maybe Word8)
gvariantToWord8 = withTypeCheck $ get_byte >=> (return . fromIntegral)
instance IsGVariant Int16 where
toGVariant = gvariantFromInt16
fromGVariant = gvariantToInt16
toGVariantFormatString _ = "n"
instance IsGVariantBasicType Int16
foreign import ccall "g_variant_new_int16" new_int16
:: #{type gint16} -> IO (Ptr GVariant)
gvariantFromInt16 :: Int16 -> IO GVariant
gvariantFromInt16 = (new_int16 . fromIntegral) >=> wrapGVariantPtr
foreign import ccall "g_variant_get_int16" get_int16
:: Ptr GVariant -> IO #{type gint16}
gvariantToInt16 :: GVariant -> IO (Maybe Int16)
gvariantToInt16 = withTypeCheck $ get_int16 >=> (return . fromIntegral)
instance IsGVariant Word16 where
toGVariant = gvariantFromWord16
fromGVariant = gvariantToWord16
toGVariantFormatString _ = "q"
instance IsGVariantBasicType Word16
foreign import ccall "g_variant_new_uint16" new_uint16
:: #{type guint16} -> IO (Ptr GVariant)
gvariantFromWord16 :: Word16 -> IO GVariant
gvariantFromWord16 = new_uint16 . fromIntegral >=> wrapGVariantPtr
foreign import ccall "g_variant_get_uint16" get_uint16
:: Ptr GVariant -> IO #{type guint16}
gvariantToWord16 :: GVariant -> IO (Maybe Word16)
gvariantToWord16 = withTypeCheck $ get_uint16 >=> (return . fromIntegral)
instance IsGVariant Int32 where
toGVariant = gvariantFromInt32
fromGVariant = gvariantToInt32
toGVariantFormatString _ = "i"
instance IsGVariantBasicType Int32
foreign import ccall "g_variant_new_int32" new_int32
:: #{type gint16} -> IO (Ptr GVariant)
gvariantFromInt32 :: Int32 -> IO GVariant
gvariantFromInt32 = (new_int32 . fromIntegral) >=> wrapGVariantPtr
foreign import ccall "g_variant_get_int32" get_int32
:: Ptr GVariant -> IO #{type gint32}
gvariantToInt32 :: GVariant -> IO (Maybe Int32)
gvariantToInt32 = withTypeCheck $ get_int32 >=> (return . fromIntegral)
instance IsGVariant Word32 where
toGVariant = gvariantFromWord32
fromGVariant = gvariantToWord32
toGVariantFormatString _ = "u"
instance IsGVariantBasicType Word32
foreign import ccall "g_variant_new_uint32" new_uint32
:: #{type guint32} -> IO (Ptr GVariant)
gvariantFromWord32 :: Word32 -> IO GVariant
gvariantFromWord32 = (new_uint32 . fromIntegral) >=> wrapGVariantPtr
foreign import ccall "g_variant_get_uint32" get_uint32
:: Ptr GVariant -> IO #{type guint32}
gvariantToWord32 :: GVariant -> IO (Maybe Word32)
gvariantToWord32 = withTypeCheck $ get_uint32 >=> (return . fromIntegral)
instance IsGVariant Int64 where
toGVariant = gvariantFromInt64
fromGVariant = gvariantToInt64
toGVariantFormatString _ = "x"
instance IsGVariantBasicType Int64
foreign import ccall "g_variant_new_int64" new_int64
:: #{type gint64} -> IO (Ptr GVariant)
gvariantFromInt64 :: Int64 -> IO GVariant
gvariantFromInt64 = (new_int64 . fromIntegral) >=> wrapGVariantPtr
foreign import ccall "g_variant_get_int64" get_int64
:: Ptr GVariant -> IO #{type gint64}
gvariantToInt64 :: GVariant -> IO (Maybe Int64)
gvariantToInt64 = withTypeCheck $ get_int64 >=> (return . fromIntegral)
instance IsGVariant Word64 where
toGVariant = gvariantFromWord64
fromGVariant = gvariantToWord64
toGVariantFormatString _ = "t"
instance IsGVariantBasicType Word64
foreign import ccall "g_variant_new_uint64" new_uint64
:: #{type guint64} -> IO (Ptr GVariant)
gvariantFromWord64 :: Word64 -> IO GVariant
gvariantFromWord64 = (new_uint64 . fromIntegral) >=> wrapGVariantPtr
foreign import ccall "g_variant_get_uint64" get_uint64
:: Ptr GVariant -> IO #{type guint64}
gvariantToWord64 :: GVariant -> IO (Maybe Word64)
gvariantToWord64 = withTypeCheck $ get_uint64 >=> (return . fromIntegral)
newtype GVariantHandle = GVariantHandle Int32
deriving (Eq, Ord, Show)
instance IsGVariant GVariantHandle where
toGVariant (GVariantHandle h) = gvariantFromHandle h
fromGVariant = gvariantToHandle >=> (return . (GVariantHandle <$>))
toGVariantFormatString _ = "h"
instance IsGVariantBasicType GVariantHandle
foreign import ccall "g_variant_new_handle" new_handle
:: #{type gint32} -> IO (Ptr GVariant)
-- | Convert a DBus handle (an 'Int32') into a 'GVariant'.
gvariantFromHandle :: Int32 -> IO GVariant
gvariantFromHandle h = (new_handle . fromIntegral) h >>= wrapGVariantPtr
foreign import ccall "g_variant_get_handle" get_handle
:: Ptr GVariant -> IO #{type gint32}
-- | Extract the DBus handle (an 'Int32') inside a 'GVariant'.
gvariantToHandle :: GVariant -> IO (Maybe Int32)
gvariantToHandle =
withExplicitType (toGVariantFormatString (undefined :: GVariantHandle)) $
get_handle >=> (return . fromIntegral)
instance IsGVariant Double where
toGVariant = gvariantFromDouble
fromGVariant = gvariantToDouble
toGVariantFormatString _ = "d"
instance IsGVariantBasicType Double
foreign import ccall "g_variant_new_double" new_double
:: #{type gdouble} -> IO (Ptr GVariant)
gvariantFromDouble :: Double -> IO GVariant
gvariantFromDouble = (new_double . realToFrac) >=> wrapGVariantPtr
foreign import ccall "g_variant_get_double" get_double
:: Ptr GVariant -> IO #{type gdouble}
gvariantToDouble :: GVariant -> IO (Maybe Double)
gvariantToDouble = withTypeCheck $ get_double >=> (return . realToFrac)
instance IsGVariant Text where
toGVariant = gvariantFromText
fromGVariant = gvariantToText
toGVariantFormatString _ = "s"
instance IsGVariantBasicType Text
foreign import ccall "g_variant_get_string" _get_string
:: Ptr GVariant -> Ptr #{type gsize} -> IO CString
get_string :: Ptr GVariant -> IO CString
get_string v = _get_string v nullPtr
-- | Decode an UTF-8 encoded string 'GVariant' into 'Text'.
gvariantToText :: GVariant -> IO (Maybe Text)
gvariantToText = withTypeCheck $ get_string >=> cstringToText
foreign import ccall "g_variant_new_take_string" take_string
:: CString -> IO (Ptr GVariant)
-- | Encode a 'Text' into an UTF-8 encoded string 'GVariant'.
gvariantFromText :: Text -> IO GVariant
gvariantFromText = textToCString >=> take_string >=> wrapGVariantPtr
foreign import ccall "g_variant_is_object_path" g_variant_is_object_path ::
CString -> IO #{type gboolean}
-- | An object representing a DBus object path, which is a particular
-- type of 'GVariant' too. (Just a string with some specific
-- requirements.) In order to construct/deconstruct a
-- 'GVariantObjectPath' one can use 'newGVariantObjectPath'
-- and 'gvariantObjectPathToText'.
newtype GVariantObjectPath = GVariantObjectPath Text
deriving (Ord, Eq, Show)
-- | Try to construct a DBus object path. If the passed string is not
-- a valid object path 'Nothing' will be returned.
newGVariantObjectPath :: Text -> Maybe GVariantObjectPath
newGVariantObjectPath p = unsafePerformIO $
withTextCString p $ \cstr -> do
isObjectPath <- toEnum . fromIntegral <$> g_variant_is_object_path cstr
if isObjectPath
then return $ Just (GVariantObjectPath p)
else return Nothing
-- | Return the 'Text' representation of a 'GVariantObjectPath'.
gvariantObjectPathToText :: GVariantObjectPath -> Text
gvariantObjectPathToText (GVariantObjectPath p) = p
instance IsGVariant GVariantObjectPath where
toGVariant = gvariantFromObjectPath
fromGVariant = gvariantToObjectPath >=> return . (GVariantObjectPath <$>)
toGVariantFormatString _ = "o"
instance IsGVariantBasicType GVariantObjectPath
foreign import ccall "g_variant_new_object_path" new_object_path
:: CString -> IO (Ptr GVariant)
-- | Construct a 'GVariant' containing an object path. In order to
-- build a 'GVariantObjectPath' value see 'newGVariantObjectPath'.
gvariantFromObjectPath :: GVariantObjectPath -> IO GVariant
gvariantFromObjectPath (GVariantObjectPath p) =
withTextCString p $ new_object_path >=> wrapGVariantPtr
-- | Extract a 'GVariantObjectPath' from a 'GVariant', represented as
-- its underlying 'Text' representation.
gvariantToObjectPath :: GVariant -> IO (Maybe Text)
gvariantToObjectPath =
withExplicitType (toGVariantFormatString (undefined :: GVariantObjectPath))
(get_string >=> cstringToText)
foreign import ccall "g_variant_is_signature" g_variant_is_signature ::
CString -> IO #{type gboolean}
-- | An object representing a DBus signature, which is a particular
-- type of 'GVariant' too. (Just a string with some specific
-- requirements.) In order to construct/deconstruct a
-- 'GVariantSignature' one can use 'newGVariantSignature' and
-- 'gvariantSignatureToText'.
newtype GVariantSignature = GVariantSignature Text
deriving (Ord, Eq, Show)
-- | Try to construct a DBus object path. If the passed string is not
-- a valid DBus signature 'Nothing' will be returned.
newGVariantSignature :: Text -> Maybe GVariantSignature
newGVariantSignature p = unsafePerformIO $
withTextCString p $ \cstr -> do
isSignature <- toEnum . fromIntegral <$> g_variant_is_signature cstr
if isSignature
then return $ Just (GVariantSignature p)
else return Nothing
-- | Return the 'Text' representation of a 'GVariantSignature'.
gvariantSignatureToText :: GVariantSignature -> Text
gvariantSignatureToText (GVariantSignature p) = p
instance IsGVariant GVariantSignature where
toGVariant = gvariantFromSignature
fromGVariant = gvariantToSignature >=> return . (GVariantSignature <$>)
toGVariantFormatString _ = "g"
instance IsGVariantBasicType GVariantSignature
foreign import ccall "g_variant_new_signature" new_signature
:: CString -> IO (Ptr GVariant)
-- | Construct a 'GVariant' containing an DBus signature. In order to
-- build a 'GVariantSignature' value see 'newGVariantSignature'.
gvariantFromSignature :: GVariantSignature -> IO GVariant
gvariantFromSignature (GVariantSignature p) =
withTextCString p $ new_signature >=> wrapGVariantPtr
-- | Extract a 'GVariantSignature' from a 'GVariant', represented as
-- 'Text'.
gvariantToSignature :: GVariant -> IO (Maybe Text)
gvariantToSignature =
withExplicitType (toGVariantFormatString (undefined :: GVariantSignature))
$ get_string >=> cstringToText
instance IsGVariant GVariant where
toGVariant = gvariantFromGVariant
fromGVariant = gvariantToGVariant
toGVariantFormatString _ = "v"
foreign import ccall "g_variant_new_variant" new_variant
:: Ptr GVariant -> IO (Ptr GVariant)
-- | Box a 'GVariant' inside another 'GVariant'.
gvariantFromGVariant :: GVariant -> IO GVariant
gvariantFromGVariant v = withManagedPtr v $ new_variant >=> wrapGVariantPtr
foreign import ccall "g_variant_get_variant" get_variant
:: Ptr GVariant -> IO (Ptr GVariant)
-- | Unbox a 'GVariant' contained inside another 'GVariant'.
gvariantToGVariant :: GVariant -> IO (Maybe GVariant)
gvariantToGVariant = withTypeCheck $ get_variant >=> wrapGVariantPtr
instance IsGVariant ByteString where
toGVariant = gvariantFromBytestring
fromGVariant = gvariantToBytestring
toGVariantFormatString _ = "ay"
foreign import ccall "g_variant_get_bytestring" get_bytestring
:: Ptr GVariant -> IO CString
-- | Extract a zero terminated list of bytes into a 'ByteString'.
gvariantToBytestring :: GVariant -> IO (Maybe ByteString)
gvariantToBytestring = withTypeCheck (get_bytestring >=> cstringToByteString)
foreign import ccall "g_variant_new_bytestring" new_bytestring
:: CString -> IO (Ptr GVariant)
-- | Encode a 'ByteString' into a list of bytes 'GVariant'.
gvariantFromBytestring :: ByteString -> IO GVariant
gvariantFromBytestring bs = wrapGVariantPtr =<<
B.useAsCString bs new_bytestring
foreign import ccall "g_variant_n_children" g_variant_n_children
:: Ptr GVariant -> IO #{type gsize}
foreign import ccall "g_variant_get_child_value" g_variant_get_child_value
:: Ptr GVariant -> #{type gsize} -> IO (Ptr GVariant)
-- No type checking is done here, it is assumed that the caller knows
-- that the passed variant is indeed of a container type.
gvariant_get_children :: (Ptr GVariant) -> IO [GVariant]
gvariant_get_children vptr = do
n_children <- g_variant_n_children vptr
-- n_children is an unsigned type (Word64 in 64 bit
-- architectures), so if it is 0 and we substract one we would
-- wrap around to 2^64-1.
if n_children /= 0
then mapM ((g_variant_get_child_value vptr) >=> wrapGVariantPtr)
[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
toGVariantFormatString _ = "m" <> toGVariantFormatString (undefined :: a)
foreign import ccall "g_variant_new_maybe" g_variant_new_maybe ::
Ptr GVariantType -> Ptr GVariant -> IO (Ptr GVariant)
-- | Convert a 'Maybe' value into a corresponding 'GVariant' of maybe
-- type.
gvariantFromMaybe :: forall a. IsGVariant a => Maybe a -> IO GVariant
gvariantFromMaybe m = do
let fmt = toGVariantFormatString (undefined :: a)
withGVariantType fmt $ \tPtr ->
case m of
Just child -> do
childVariant <- toGVariant child
withManagedPtr childVariant
(g_variant_new_maybe tPtr >=> wrapGVariantPtr)
Nothing -> g_variant_new_maybe tPtr nullPtr >>= wrapGVariantPtr
-- | Try to decode a maybe 'GVariant' into the corresponding 'Maybe'
-- type. If the conversion is successful this returns @Just x@, where
-- @x@ itself is of 'Maybe' type. So, in particular, @Just Nothing@
-- indicates a successful call, and means that the GVariant of maybe
-- type was empty.
gvariantToMaybe :: forall a. IsGVariant a => GVariant -> IO (Maybe (Maybe a))
gvariantToMaybe v = do
let fmt = toGVariantFormatString (undefined :: Maybe a)
withExplicitType fmt gvariant_get_children v >>=
\case
Just [] -> return (Just Nothing)
Just [child] -> fromGVariant child >>=
\case
Nothing -> return Nothing
Just result -> return (Just (Just result))
Just _ -> error "gvariantToMaybe :: the impossible happened, this is a bug."
Nothing -> return Nothing
-- | A DictEntry 'GVariant' is isomorphic to a two-tuple. Wrapping the
-- values into a 'GVariantDictentry' allows the 'IsGVariant' instance
-- to do the right thing.
data GVariantDictEntry key value = GVariantDictEntry key value
deriving (Eq, Show)
instance (IsGVariant a, IsGVariantBasicType a, IsGVariant b) =>
IsGVariant (GVariantDictEntry a b) where
toGVariant (GVariantDictEntry key value) =
gvariantFromDictEntry key value
fromGVariant gv =
((uncurry GVariantDictEntry) <$>) <$> gvariantToDictEntry gv
toGVariantFormatString _ = "{"
<> toGVariantFormatString (undefined :: a)
<> toGVariantFormatString (undefined :: b)
<> "}"
foreign import ccall "g_variant_new_dict_entry" g_variant_new_dict_entry ::
Ptr GVariant -> Ptr GVariant -> IO (Ptr GVariant)
-- | Construct a 'GVariant' of type DictEntry from the given 'key' and
-- 'value'. The key must be a basic 'GVariant' type, i.e. not a
-- container. This is determined by whether it belongs to the
-- 'IsGVariantBasicType' typeclass. On the other hand 'value' is an
-- arbitrary 'GVariant', and in particular it can be a container type.
gvariantFromDictEntry :: (IsGVariant key, IsGVariantBasicType key,
IsGVariant value) =>
key -> value -> IO GVariant
gvariantFromDictEntry key value = do
keyVar <- toGVariant key
valueVar <- toGVariant value
withManagedPtr keyVar $ \keyPtr ->
withManagedPtr valueVar $ \valuePtr ->
g_variant_new_dict_entry keyPtr valuePtr >>= wrapGVariantPtr
-- | Unpack a DictEntry variant into 'key' and 'value', which are
-- returned as a two element tuple in case of success.
gvariantToDictEntry :: forall key value.
(IsGVariant key, IsGVariantBasicType key,
IsGVariant value) =>
GVariant -> IO (Maybe (key, value))
gvariantToDictEntry =
withExplicitType fmt $ \varPtr -> do
[key, value] <- gvariant_get_children varPtr
(,) <$> unsafeFromGVariant key <*> unsafeFromGVariant value
where
fmt = toGVariantFormatString (undefined :: GVariantDictEntry key value)
instance (IsGVariant a, IsGVariantBasicType a, IsGVariant b) =>
IsGVariant (M.Map a b) where
toGVariant = gvariantFromMap
fromGVariant = gvariantToMap
toGVariantFormatString _ = "a{"
<> toGVariantFormatString (undefined :: a)
<> toGVariantFormatString (undefined :: b)
<> "}"
-- | Pack a 'Map' into a 'GVariant' for dictionary type, which is just
-- an array of 'GVariantDictEntry'.
gvariantFromMap :: (IsGVariant key, IsGVariantBasicType key,
IsGVariant value) =>
M.Map key value -> IO GVariant
gvariantFromMap m = gvariantFromList $
map (uncurry GVariantDictEntry) (M.toList m)
-- | Unpack a 'GVariant' into a 'M.Map'. Notice that this assumes that
-- all the elements in the 'GVariant' array of 'GVariantDictEntry' are
-- of the same type, which is not necessary for a generic 'GVariant',
-- so this is somewhat restrictive. For the general case it is
-- necessary to use 'gvariantToList' plus 'gvariantToDictEntry'
-- directly.
gvariantToMap :: forall key value.
(IsGVariant key, IsGVariantBasicType key,
IsGVariant value) =>
GVariant -> IO (Maybe (M.Map key value))
gvariantToMap = gvariantToList >=> (return . (fromDictEntryList <$>))
where fromDictEntryList :: [GVariantDictEntry key value] ->
M.Map key value
fromDictEntryList = M.fromList . (map tuplefy)
tuplefy :: GVariantDictEntry key value -> (key, value)
tuplefy (GVariantDictEntry key value) = (key, value)
instance IsGVariant a => IsGVariant [a] where
toGVariant = gvariantFromList
fromGVariant = gvariantToList
toGVariantFormatString _ = "a" <> toGVariantFormatString (undefined :: a)
foreign import ccall "g_variant_new_array" g_variant_new_array ::
Ptr GVariantType -> Ptr (Ptr GVariant) -> #{type gsize} -> IO (Ptr GVariant)
-- | Given a list of elements construct a 'GVariant' array containing
-- them.
gvariantFromList :: forall a. IsGVariant a => [a] -> IO GVariant
gvariantFromList children = do
let fmt = toGVariantFormatString (undefined :: a)
mapM toGVariant children >>= \childVariants ->
withManagedPtrList childVariants $ \childrenPtrs -> do
withGVariantType fmt $ \childType -> do
packed <- packPtrArray childrenPtrs
result <- g_variant_new_array childType packed
(fromIntegral $ length children)
freeMem packed
wrapGVariantPtr result
-- | Unpack a 'GVariant' array into its elements.
gvariantToList :: forall a. IsGVariant a => GVariant -> IO (Maybe [a])
gvariantToList = withExplicitType (toGVariantFormatString (undefined :: [a]))
(gvariant_get_children >=> mapM unsafeFromGVariant)
foreign import ccall "g_variant_new_tuple" g_variant_new_tuple
:: Ptr (Ptr GVariant) -> #{type gsize} -> IO (Ptr GVariant)
-- | Given a list of 'GVariant', construct a 'GVariant' tuple
-- containing the elements in the list.
gvariantFromTuple :: [GVariant] -> IO GVariant
gvariantFromTuple children =
withManagedPtrList children $ \childrenPtrs -> do
packed <- packPtrArray childrenPtrs
result <- g_variant_new_tuple packed (fromIntegral $ length children)
freeMem packed
wrapGVariantPtr result
-- | Extract the children of a 'GVariant' tuple into a list.
gvariantToTuple :: GVariant -> IO (Maybe [GVariant])
gvariantToTuple = withExplicitType "r" gvariant_get_children
-- | The empty tuple GVariant, mostly useful for type checking.
instance IsGVariant () where
toGVariant _ = gvariantFromTuple []
fromGVariant = withTypeCheck (const $ return ())
toGVariantFormatString _ = "()"
-- | One element tuples.
instance IsGVariant a => IsGVariant (GVariantSinglet a) where
toGVariant (GVariantSinglet s) = gvariantFromSinglet s
fromGVariant = gvariantToSinglet >=> return . (GVariantSinglet <$>)
toGVariantFormatString _ = "("
<> toGVariantFormatString (undefined :: a)
<> ")"
gvariantFromSinglet :: IsGVariant a => a -> IO GVariant
gvariantFromSinglet s = do
sv <- toGVariant s
gvariantFromTuple [sv]
gvariantToSinglet :: forall a. IsGVariant a => GVariant -> IO (Maybe a)
gvariantToSinglet = withExplicitType fmt
(gvariant_get_child >=> unsafeFromGVariant)
where fmt = toGVariantFormatString (undefined :: GVariantSinglet a)
instance (IsGVariant a, IsGVariant b) => IsGVariant (a,b) where
toGVariant = gvariantFromTwoTuple
fromGVariant = gvariantToTwoTuple
toGVariantFormatString _ = "("
<> toGVariantFormatString (undefined :: a)
<> toGVariantFormatString (undefined :: b)
<> ")"
gvariantFromTwoTuple :: (IsGVariant a, IsGVariant b) =>
(a,b) -> IO GVariant
gvariantFromTwoTuple (a, b) = do
va <- toGVariant a
vb <- toGVariant b
gvariantFromTuple [va, vb]
gvariantToTwoTuple :: forall a b. (IsGVariant a, IsGVariant b) =>
GVariant -> IO (Maybe (a,b))
gvariantToTwoTuple variant = do
let expectedType = toGVariantFormatString (undefined :: (a,b))
maybeChildren <- withExplicitType expectedType gvariant_get_children variant
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
fromGVariant = gvariantToThreeTuple
toGVariantFormatString _ = "("
<> toGVariantFormatString (undefined :: a)
<> toGVariantFormatString (undefined :: b)
<> toGVariantFormatString (undefined :: c)
<> ")"
gvariantFromThreeTuple :: (IsGVariant a, IsGVariant b, IsGVariant c) =>
(a,b,c) -> IO GVariant
gvariantFromThreeTuple (a, b, c) = do
va <- toGVariant a
vb <- toGVariant b
vc <- toGVariant c
gvariantFromTuple [va, vb, vc]
gvariantToThreeTuple :: forall a b c. (IsGVariant a, IsGVariant b,
IsGVariant c) =>
GVariant -> IO (Maybe (a,b,c))
gvariantToThreeTuple variant = do
let expectedType = toGVariantFormatString (undefined :: (a,b,c))
maybeChildren <- withExplicitType expectedType gvariant_get_children variant
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
toGVariant = gvariantFromFourTuple
fromGVariant = gvariantToFourTuple
toGVariantFormatString _ = "("
<> toGVariantFormatString (undefined :: a)
<> toGVariantFormatString (undefined :: b)
<> toGVariantFormatString (undefined :: c)
<> toGVariantFormatString (undefined :: d)
<> ")"
gvariantFromFourTuple :: (IsGVariant a, IsGVariant b, IsGVariant c,
IsGVariant d) => (a,b,c,d) -> IO GVariant
gvariantFromFourTuple (a, b, c, d) = do
va <- toGVariant a
vb <- toGVariant b
vc <- toGVariant c
vd <- toGVariant d
gvariantFromTuple [va, vb, vc, vd]
gvariantToFourTuple :: forall a b c d. (IsGVariant a, IsGVariant b,
IsGVariant c, IsGVariant d) =>
GVariant -> IO (Maybe (a,b,c,d))
gvariantToFourTuple variant = do
let expectedType = toGVariantFormatString (undefined :: (a,b,c,d))
maybeChildren <- withExplicitType expectedType gvariant_get_children variant
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
toGVariant = gvariantFromFiveTuple
fromGVariant = gvariantToFiveTuple
toGVariantFormatString _ = "("
<> toGVariantFormatString (undefined :: a)
<> toGVariantFormatString (undefined :: b)
<> toGVariantFormatString (undefined :: c)
<> toGVariantFormatString (undefined :: d)
<> toGVariantFormatString (undefined :: e)
<> ")"
gvariantFromFiveTuple :: (IsGVariant a, IsGVariant b, IsGVariant c,
IsGVariant d, IsGVariant e) =>
(a,b,c,d,e) -> IO GVariant
gvariantFromFiveTuple (a, b, c, d, e) = do
va <- toGVariant a
vb <- toGVariant b
vc <- toGVariant c
vd <- toGVariant d
ve <- toGVariant e
gvariantFromTuple [va, vb, vc, vd, ve]
gvariantToFiveTuple :: forall a b c d e.
(IsGVariant a, IsGVariant b, IsGVariant c,
IsGVariant d, IsGVariant e) =>
GVariant -> IO (Maybe (a,b,c,d,e))
gvariantToFiveTuple variant = do
let expectedType = toGVariantFormatString (undefined :: (a,b,c,d,e))
maybeChildren <- withExplicitType expectedType gvariant_get_children variant
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
|