├── .editorconfig
├── .gitignore
├── Rakefile
├── Readme.md
├── dub.json
├── dub.selections.json
├── examples
├── .gitignore
├── client
│ ├── .gitignore
│ ├── dub.json
│ └── source
│ │ └── app.d
└── server
│ ├── .gitignore
│ ├── dub.json
│ └── source
│ └── app.d
├── meson.build
├── source
└── ddbus
│ ├── attributes.d
│ ├── bus.d
│ ├── c_lib.d
│ ├── conv.d
│ ├── exception.d
│ ├── meson.build
│ ├── package.d
│ ├── router.d
│ ├── simple.d
│ ├── thin.d
│ └── util.d
└── transformer.rb
/.editorconfig:
--------------------------------------------------------------------------------
1 | [**.d]
2 | indent_style = space
3 | indent_size = 2
4 |
5 | dfmt_brace_style = otbs
6 | dfmt_space_after_keywords = true
7 | dfmt_space_after_cast = true
8 | dfmt_template_constraint_style = always_newline_indent
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .dub
2 | docs.json
3 | __dummy.html
4 | *.o
5 | *.obj
6 | /ddbus
7 | libddbus.*
8 | __test__library__
9 | /ddbus-test-*
10 | *.lst
11 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | task :testCall do
2 | sh "dbus-send --type=method_call --print-reply --dest=ca.thume.ddbus.test /root ca.thume.test.test int32:5"
3 | end
4 |
5 | task :badCall do
6 | sh "dbus-send --type=method_call --print-reply --dest=ca.thume.ddbus.test /root ca.thume.test.test double:5.5"
7 | end
8 |
9 | task :testSignal do
10 | sh "dbus-send --dest=ca.thume.ddbus.test /signaler ca.thume.test.signal int32:9"
11 | end
12 |
13 | task :testDbus do
14 | sh "dbus-send --session --dest=org.freedesktop.DBus --type=method_call --print-reply /org/freedesktop/DBus org.freedesktop.DBus.ListNames"
15 | end
16 |
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 | # ddbus
2 |
3 |
4 |
5 |
6 | A [dbus](http://www.freedesktop.org/wiki/Software/dbus/) library for the [D programming language](http://dlang.org).
7 |
8 | Provides fancy and convenient highly templated methods that automagically serialize and deserialize things into DBus types so that calling DBus methods is almost as easy as calling local ones.
9 |
10 | It currently supports:
11 |
12 | - Calling methods
13 | - Creating wrapper objects for DBus interfaces
14 | - Seamlessly converting to and from D types
15 | - Handling method calls and signals (includes introspection support)
16 |
17 | # Installation
18 |
19 | Before using, you will need to have the DBus C library installed on your computer to link with, and probably also a DBus session bus running so that you can actually do things.
20 |
21 | `ddbus` is available on [DUB](http://code.dlang.org/packages/ddbus) so you can simply include it in your `dub.json`:
22 | ```json
23 | "dependencies": {
24 | "ddbus": "~>2.3.0"
25 | }
26 | ```
27 |
28 | # Usage
29 |
30 | ## Call Interface
31 |
32 | The simplest way to call methods over DBus is to create a connection and then a PathIface object
33 | which wraps a destination, path and interface. You can then call methods on that object with any
34 | parameters which ddbus knows how to serialize and it will return a reply message which you can convert
35 | to the correct return type using `.to!T()`. You can also use the templated `call` method. Example:
36 |
37 | ```d
38 | import ddbus;
39 | Connection conn = connectToBus();
40 | PathIface obj = new PathIface(conn, "org.freedesktop.DBus","/org/freedesktop/DBus",
41 | "org.freedesktop.DBus");
42 | // call any method with any parameters and then convert the result to the right type.
43 | auto name = obj.GetNameOwner("org.freedesktop.DBus").to!string();
44 | // alternative method
45 | obj.call!string("GetNameOwner","org.freedesktop.DBus");
46 | ```
47 |
48 | ### Working with properties
49 |
50 | ```d
51 | import ddbus;
52 | Connection conn = connectToBus();
53 | PathIface obj = new PathIface(conn, "org.freedesktop.secrets", "/org/freedesktop/secrets/collection/login", "org.freedesktop.DBus.Properties");
54 |
55 | // read property
56 | string loginLabel = obj.Get("org.freedesktop.Secret.Collection", "Label").to!string();
57 | loginLabel = "Secret"~login;
58 | // write it back (variant type requires variant() wrapper)
59 | obj.Set("org.freedesktop.Secret.Collection", "Label", variant(loginLabel));
60 | ```
61 | Setting read only properties results in a thrown `DBusException`.
62 |
63 | ## Server Interface
64 |
65 | You can register a delegate into a `MessageRouter` and a main loop in order to handle messages.
66 | After that you can request a name so that other DBus clients can connect to your program.
67 |
68 | You can return a `Tuple!(args)` to return multiple values (multiple out values in XML) or
69 | return a `Variant!DBusAny` to support returning any dynamic value.
70 |
71 | ```d
72 | import ddbus;
73 | MessageRouter router = new MessageRouter();
74 | // create a pattern to register a handler at a path, interface and method
75 | MessagePattern patt = MessagePattern("/root","ca.thume.test","test");
76 | router.setHandler!(int,int,Variant!DBusAny)(patt,(int par, Variant!DBusAny anyArgument) {
77 | // anyArgument can contain any type now, it must be specified as argument using Variant!DBusAny.
78 | writeln("Called with ", par, ", ", anyArgument);
79 | return par;
80 | });
81 | // handle a signal
82 | patt = MessagePattern("/signaler","ca.thume.test","signal",true);
83 | router.setHandler!(void,int)(patt,(int par) {
84 | writeln("Signalled with ", par);
85 | });
86 | // register all methods of an object
87 | class Tester {
88 | int lol(int x, string s) {return 5;}
89 | void wat() {}
90 | }
91 | Tester o = new Tester;
92 | registerMethods(router, "/","ca.thume.test",o);
93 | // get a name and start the server
94 | registerRouter(conn, router);
95 | bool gotem = requestName(conn, "ca.thume.ddbus.test");
96 | simpleMainLoop(conn);
97 | ```
98 |
99 | See the Concurrent Updates section for details how to implement this in a custom main loop.
100 |
101 | ## Thin(ish) Wrapper
102 |
103 | `ddbus` also includes a series of thin D struct wrappers over the DBus types.
104 | - `Message`: wraps `DBusMessage` and provides D methods for common functionality.
105 | - `Connection`: wraps `DBusConnection` and provides D methods for common functionality.
106 | - `DBusException`: used for errors produced by DBus turned into D exceptions.
107 |
108 | ## Type Marshaling
109 |
110 | `ddbus` includes fancy templated methods for marshaling D types in and out of DBus messages.
111 | All DBus-compatible basic types work (except file descriptors).
112 | Any forward range can be marshaled in as DBus array of that type but arrays must be taken out as dynamic arrays.
113 |
114 | As per version 2.3.0, D `struct` types are fully supported by `ddbus`. By default all public fields of a structure are marshaled. This behavior can be [changed by UDAs](#customizing-marshaling-of-struct-types). Mapping DBus structures to a matching instance of `std.typecons.Tuple`, like earlier versions of `ddbus` did, is also still supported.
115 |
116 | Example using the lower level interface, the simple interfaces use these behind the scenes:
117 | ```d
118 | Message msg = Message("org.example.wow","/wut","org.test.iface","meth");
119 |
120 | struct S {
121 | double a;
122 | int b;
123 | string[][] c;
124 | bool[] d;
125 | }
126 |
127 | auto s = S(6.2, 4, [["lol"]], []);
128 | auto args = tuple(5, true, "wow", [6, 5], s);
129 | msg.build(args.expand);
130 | msg.signature().assertEqual("ibsai(diaasab)");
131 | msg.readTuple!(typeof(args))().assertEqual(args);
132 | ```
133 | ### Basic types
134 | These are the basic types supported by `ddbus`:
135 | `bool`, `ubyte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `double`, `string`, `ObjectPath`, `InterfaceName`, `BusName`
136 |
137 | ObjectPath, InterfaceName and BusName are typesafe wrappers or aliases around strings which should be used to ensure type-safety. They do not allow implicit casts to each other but can be manually converted to strings either by casting to string.
138 |
139 | ### Overview of mappings of other types:
140 |
141 | | D type | DBus type | Comments
142 | | -------------------------------------------- | ------------------------ | ---
143 | | any `enum` | `enum` base type | Only the exact values present in the definition of the `enum` type will be allowed.
144 | | `std.typecons.BitFlags` | `enum` base type | Allows usage of OR'ed values of a flags `enum`.
145 | | dynamic array `T[]` | array |
146 | | associative array `V[K]` | array of key-value pairs | DBus has a special type for key-value pairs, which can be used as the element type of an array only.
147 | | `Tuple!(T...)` | structure | The DBus structure will map all of the `Tuple`'s values in sequence.
148 | | any `struct` | structure | The DBus structure will map all public fields of the `struct` type in order of definition, unless otherwise specified using UDAs.
149 | | `ddbus` style variant `Variant!T` | variant | `Variant!T` is in fact just a wrapper type to force representation as a variant in DBus, use `Variant!DBusAny` for actual dynamic typing.
150 | | Phobos style variants `std.variant.VariantN` | variant | Only supported if set of allowed types is limited to types that can be marshaled by `ddbus`, so `std.variant.Variant` is not supported, but `std.variant.Algebraic` may be, depending on allowed types
151 |
152 | ### Customizing marshaling of `struct` types
153 | Marshaling behavior can be changed for a `struct` type by adding the `@dbusMarshaling`
154 | UDA with the appropriate flag. The following flags are supported:
155 | - `includePrivateFields` enables marshaling of private fields
156 | - `manualOnly` disables marshaling of all fields
157 |
158 | Marshaling of individual fields can be enabled or disabled by setting the `DBusMarshal`
159 | flag as an UDA. I.e. `@Yes.DBusMarshal` or `@No.DBusMarshal`.
160 |
161 | Note: symbols `Yes` and `No` are defined in `std.typecons`.
162 |
163 | After converting a DBus structure to a D `struct`, any fields that are not marshaled
164 | will appear freshly initialized. This is true even when just converting a `struct` to
165 | `DBusAny` and back.
166 |
167 | ```d
168 | import ddbus.attributes;
169 | import std.typecons;
170 |
171 | @dbusMarshaling(MarshalingFlag.includePrivateFields)
172 | struct WeirdThing {
173 | int a; // marshaled (default behavior not overridden)
174 | @No.DBusMarshal int b; // explicitly not marshaled
175 | private int c; // marshaled, because of includePrivateFields
176 | }
177 | ```
178 |
179 | ## Modules
180 |
181 | - `attributes`: defines some UDAs (and related templates) that can be used to customize
182 | struct marshaling.
183 | - `bus`: bus functionality like requesting names and event loops.
184 | - `conv`: low level type marshaling methods.
185 | - `exception`: exception classes
186 | - `router`: message and signal routing based on `MessagePattern` structs.
187 | - `simple`: simpler wrappers around other functionality.
188 | - `thin`: thin wrapper types
189 | - `util`: templates for working with D type marshaling like `canDBus!T`.
190 | - `c_lib`: a D translation of the DBus C headers
191 | (you generally should not need to use these directly).
192 |
193 | Importing `ddbus` will publicly import the `thin`, `router`, `bus`, `simple` and
194 | `attributes` modules. These provide most of the functionality you probably want,
195 | you can import the others if you want lower level control.
196 |
197 | Nothing is hidden so if `ddbus` doesn't provide something, you can always import
198 | `c_lib` and use the pointers contained in the thin wrapper structs to do it yourself.
199 |
200 | # Concurrent Updates
201 |
202 | If you want to use the DBus connection concurrently with some other features
203 | or library like a GUI or vibe.d you can do so by placing this code in the update/main loop:
204 |
205 | ```d
206 | // initialize Connection conn; somewhere
207 | // on update:
208 | if (!conn.tick)
209 | return;
210 | ```
211 |
212 | Or in vibe.d:
213 |
214 | ```d
215 | runTask({
216 | import vibe.core.core : yield;
217 |
218 | while (conn.tick)
219 | yield(); // Or sleep(1.msecs);
220 | });
221 | ```
222 |
223 | It would be better to watch a file descriptor asynchronously in the event loop instead of checking on a timer, but that hasn't been implemented yet, see Todo.
224 |
225 | # Todo
226 |
227 | `ddbus` should be complete for everyday use but is missing some fanciness that it easily could and should have:
228 |
229 | - Support for adding file descriptors to event loops like vibe.d so that it only wakes up when messages arrive and not on a timer.
230 | - Marshaling of file descriptor objects
231 | - Better efficiency in some places, particularly the object wrapping allocates tons of delegates for every method.
232 |
233 | Pull requests are welcome, the codebase is pretty small and other than the template metaprogramming for type marshaling is fairly straightforward.
234 |
--------------------------------------------------------------------------------
/dub.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ddbus",
3 | "description": "A DBus library for D",
4 | "homepage": "https://github.com/trishume/ddbus",
5 | "copyright": "Copyright © 2017, Tristan Hume",
6 | "license": "MIT",
7 | "authors": ["Tristan Hume"],
8 | "lflags": ["-ldbus-1"],
9 | "dependencies": {
10 | "dunit": "~>1.0.14"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/dub.selections.json:
--------------------------------------------------------------------------------
1 | {
2 | "fileVersion": 1,
3 | "versions": {
4 | "dunit": "1.0.14"
5 | }
6 | }
--------------------------------------------------------------------------------
/examples/.gitignore:
--------------------------------------------------------------------------------
1 | dub.selections.json
2 |
--------------------------------------------------------------------------------
/examples/client/.gitignore:
--------------------------------------------------------------------------------
1 | client
2 |
--------------------------------------------------------------------------------
/examples/client/dub.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "client",
3 | "dependencies": {
4 | "ddbus": {"path": "../../"}
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/examples/client/source/app.d:
--------------------------------------------------------------------------------
1 | import core.time;
2 | import std.stdio;
3 | import ddbus;
4 |
5 | void testCall(Connection conn) {
6 | for(int i = 0; i < 50; i++) {
7 | Message msg = Message(busName("ca.thume.transience"), ObjectPath("/ca/thume/transience/screensurface"),
8 | interfaceName("ca.thume.transience.screensurface"), "testDot");
9 | conn.sendBlocking(msg);
10 | }
11 | Message msg2 = Message(busName("ca.thume.transience"), ObjectPath("/ca/thume/transience/screensurface"),
12 | interfaceName("ca.thume.transience.screensurface"), "testPing");
13 | Message res = conn.sendWithReplyBlocking(msg2, 3.seconds);
14 | int result = res.read!int();
15 | writeln(result);
16 | }
17 |
18 | void main() {
19 | Connection conn = connectToBus();
20 | testCall(conn);
21 | writeln("It worked!");
22 | }
23 |
--------------------------------------------------------------------------------
/examples/server/.gitignore:
--------------------------------------------------------------------------------
1 | server
2 |
--------------------------------------------------------------------------------
/examples/server/dub.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "server",
3 | "dependencies": {
4 | "ddbus": {"path": "../../"}
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/examples/server/source/app.d:
--------------------------------------------------------------------------------
1 | import std.stdio;
2 | import ddbus;
3 |
4 | void testServe(Connection conn) {
5 | auto router = new MessageRouter();
6 | MessagePattern patt = MessagePattern(ObjectPath("/root"), interfaceName("ca.thume.test"), "test");
7 | router.setHandler!(int,int)(patt,(int par) {
8 | writeln("Called with ", par);
9 | return par;
10 | });
11 | patt = MessagePattern(ObjectPath("/signaler"), interfaceName("ca.thume.test"), "signal",true);
12 | router.setHandler!(void,int)(patt,(int par) {
13 | writeln("Signalled with ", par);
14 | });
15 | registerRouter(conn, router);
16 | writeln("Getting name...");
17 | bool gotem = requestName(conn, busName("ca.thume.ddbus.test"));
18 | writeln("Got name: ",gotem);
19 | simpleMainLoop(conn);
20 | }
21 |
22 | void main() {
23 | Connection conn = connectToBus();
24 | testServe(conn);
25 | writeln("It worked!");
26 | }
27 |
--------------------------------------------------------------------------------
/meson.build:
--------------------------------------------------------------------------------
1 | project(
2 | 'ddbus',
3 | 'd',
4 | meson_version: '>=0.50',
5 | version: '3.0.0',
6 | )
7 |
8 | src_inc = include_directories('source')
9 |
10 | prefix = get_option('prefix')
11 | include_dir = join_paths(prefix, get_option('includedir'), 'd', 'apk-polkit')
12 |
13 | dbus_dep = dependency('dbus-1')
14 |
15 | subdir('source/ddbus')
16 |
--------------------------------------------------------------------------------
/source/ddbus/attributes.d:
--------------------------------------------------------------------------------
1 | module ddbus.attributes;
2 |
3 | import std.meta : allSatisfy;
4 | import std.traits : getUDAs;
5 | import std.typecons : BitFlags, Flag;
6 |
7 | /++
8 | Flags for use with dbusMarshaling UDA
9 |
10 | Default is to include public fields only
11 | +/
12 | enum MarshalingFlag : ubyte {
13 | /++
14 | Automatically include private fields
15 | +/
16 | includePrivateFields = 1 << 0,
17 |
18 | /++
19 | Only include fields with explicit `@Yes.DBusMarshal`. This overrides any
20 | `include` flags.
21 | +/
22 | manualOnly = 1 << 7
23 | }
24 |
25 | /++
26 | UDA for specifying DBus marshaling options on structs
27 | +/
28 | auto dbusMarshaling(Args)(Args args...)
29 | if (allSatisfy!(isMarshalingFlag, Args)) {
30 | return BitFlags!MarshalingFlag(args);
31 | }
32 |
33 | package(ddbus) template isAllowedField(alias field) {
34 | private enum flags = marshalingFlags!(__traits(parent, field));
35 | private alias getUDAs!(field, Flag!"DBusMarshal") UDAs;
36 |
37 | static if (UDAs.length != 0) {
38 | static assert(UDAs.length == 1,
39 | "Only one UDA of type Flag!\"DBusMarshal\" allowed on struct field.");
40 |
41 | static assert(is(typeof(UDAs[0]) == Flag!"DBusMarshal"),
42 | "Did you intend to add UDA Yes.DBusMarshal or No.DBusMarshal?");
43 |
44 | enum isAllowedField = cast(bool) UDAs[0];
45 | } else static if (!(flags & MarshalingFlag.manualOnly)) {
46 | static if (__traits(getProtection, field) == "public") {
47 | enum isAllowedField = true;
48 | } else static if (cast(bool)(flags & MarshalingFlag.includePrivateFields)) {
49 | enum isAllowedField = true;
50 | } else {
51 | enum isAllowedField = false;
52 | }
53 | } else {
54 | enum isAllowedField = false;
55 | }
56 | }
57 |
58 | private template isMarshalingFlag(T) {
59 | enum isMarshalingFlag = is(T == MarshalingFlag);
60 | }
61 |
62 | private template marshalingFlags(S)
63 | if (is(S == struct)) {
64 | private alias getUDAs!(S, BitFlags!MarshalingFlag) UDAs;
65 |
66 | static if (UDAs.length == 0) {
67 | enum marshalingFlags = BitFlags!MarshalingFlag.init;
68 | } else {
69 | static assert(UDAs.length == 1, "Only one @dbusMarshaling UDA allowed on type.");
70 | static assert(is(typeof(UDAs[0]) == BitFlags!MarshalingFlag),
71 | "Huh? Did you intend to use @dbusMarshaling UDA?");
72 | enum marshalingFlags = UDAs[0];
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/source/ddbus/bus.d:
--------------------------------------------------------------------------------
1 | module ddbus.bus;
2 |
3 | import ddbus.router;
4 | import ddbus.thin;
5 | import ddbus.c_lib;
6 | import std.string;
7 |
8 | enum BusService = busName("org.freedesktop.DBus");
9 | enum BusInterface = interfaceName("org.freedesktop.DBus");
10 | enum BusPath = ObjectPath("/org/freedesktop/DBus");
11 |
12 | enum NameFlags {
13 | AllowReplace = 1,
14 | ReplaceExisting = 2,
15 | NoQueue = 4
16 | }
17 |
18 | deprecated("Use the overload taking a BusName instead")
19 | bool requestName(Connection conn, string name,
20 | NameFlags flags = NameFlags.NoQueue | NameFlags.AllowReplace) {
21 | return requestName(conn, busName(name), flags);
22 | }
23 |
24 | /// Requests a DBus well-known name.
25 | /// returns if the name is owned after the call.
26 | /// Involves blocking call on a DBus method, may throw an exception on failure.
27 | bool requestName(Connection conn, BusName name,
28 | NameFlags flags = NameFlags.NoQueue | NameFlags.AllowReplace) {
29 | auto msg = Message(BusService, BusPath, BusInterface, "RequestName");
30 | msg.build(name, cast(uint)(flags));
31 | auto res = conn.sendWithReplyBlocking(msg).to!uint;
32 | return (res == 1) || (res == 4);
33 | }
34 |
35 | /// A simple main loop that isn't necessarily efficient
36 | /// and isn't guaranteed to work with other tasks and threads.
37 | /// Use only for apps that only do DBus triggered things.
38 | void simpleMainLoop(Connection conn) {
39 | while (dbus_connection_read_write_dispatch(conn.conn, -1)) {
40 | } // empty loop body
41 | }
42 |
43 | /// Single tick in the DBus connection which can be used for
44 | /// concurrent updates.
45 | bool tick(Connection conn) {
46 | return cast(bool) dbus_connection_read_write_dispatch(conn.conn, 0);
47 | }
48 |
49 | unittest {
50 | import dunit.toolkit;
51 |
52 | Connection conn = connectToBus();
53 | conn.requestName(busName("ca.thume.ddbus.testing")).assertTrue();
54 | }
55 |
--------------------------------------------------------------------------------
/source/ddbus/c_lib.d:
--------------------------------------------------------------------------------
1 | module ddbus.c_lib;
2 |
3 | import core.stdc.config;
4 | import core.stdc.stdarg;
5 |
6 | // dfmt off
7 |
8 | extern (C):
9 | // START dbus/dbus-arch-deps.d
10 | alias c_long dbus_int64_t;
11 | alias c_ulong dbus_uint64_t;
12 | alias int dbus_int32_t;
13 | alias uint dbus_uint32_t;
14 | alias short dbus_int16_t;
15 | alias ushort dbus_uint16_t;
16 | // END dbus/dbus-arch-deps.d
17 | // START dbus/dbus-types.d
18 | alias uint dbus_unichar_t;
19 | alias uint dbus_bool_t;
20 |
21 |
22 |
23 | struct DBus8ByteStruct
24 | {
25 | dbus_uint32_t first32;
26 | dbus_uint32_t second32;
27 | }
28 |
29 | union DBusBasicValue
30 | {
31 | ubyte[8] bytes;
32 | dbus_int16_t i16;
33 | dbus_uint16_t u16;
34 | dbus_int32_t i32;
35 | dbus_uint32_t u32;
36 | dbus_bool_t bool_val;
37 | dbus_int64_t i64;
38 | dbus_uint64_t u64;
39 | DBus8ByteStruct eight;
40 | double dbl;
41 | ubyte byt;
42 | char* str;
43 | int fd;
44 | }
45 | // END dbus/dbus-types.d
46 | // START dbus/dbus-protocol.d
47 |
48 | // END dbus/dbus-protocol.d
49 | // START dbus/dbus-errors.d
50 | struct DBusError
51 | {
52 | const(char)* name;
53 | const(char)* message;
54 | uint dummy1;
55 | uint dummy2;
56 | uint dummy3;
57 | uint dummy4;
58 | uint dummy5;
59 | void* padding1;
60 | }
61 |
62 | void dbus_error_init (DBusError* error);
63 | void dbus_error_free (DBusError* error);
64 | void dbus_set_error (DBusError* error, const(char)* name, const(char)* message, ...);
65 | void dbus_set_error_const (DBusError* error, const(char)* name, const(char)* message);
66 | void dbus_move_error (DBusError* src, DBusError* dest);
67 | dbus_bool_t dbus_error_has_name (const(DBusError)* error, const(char)* name);
68 | dbus_bool_t dbus_error_is_set (const(DBusError)* error);
69 | // END dbus/dbus-errors.d
70 | // START dbus/dbus-macros.d
71 |
72 | // END dbus/dbus-macros.d
73 | // START dbus/dbus-memory.d
74 | alias void function (void*) DBusFreeFunction;
75 |
76 | void* dbus_malloc (size_t bytes);
77 | void* dbus_malloc0 (size_t bytes);
78 | void* dbus_realloc (void* memory, size_t bytes);
79 | void dbus_free (void* memory);
80 | void dbus_free_string_array (char** str_array);
81 | void dbus_shutdown ();
82 | // END dbus/dbus-memory.d
83 | // START dbus/dbus-shared.d
84 | enum DBusBusType
85 | {
86 | DBUS_BUS_SESSION = 0,
87 | DBUS_BUS_SYSTEM = 1,
88 | DBUS_BUS_STARTER = 2
89 | }
90 |
91 | enum DBusHandlerResult
92 | {
93 | DBUS_HANDLER_RESULT_HANDLED = 0,
94 | DBUS_HANDLER_RESULT_NOT_YET_HANDLED = 1,
95 | DBUS_HANDLER_RESULT_NEED_MEMORY = 2
96 | }
97 | // END dbus/dbus-shared.d
98 | // START dbus/dbus-address.d
99 | struct DBusAddressEntry;
100 |
101 |
102 | dbus_bool_t dbus_parse_address (const(char)* address, DBusAddressEntry*** entry, int* array_len, DBusError* error);
103 | const(char)* dbus_address_entry_get_value (DBusAddressEntry* entry, const(char)* key);
104 | const(char)* dbus_address_entry_get_method (DBusAddressEntry* entry);
105 | void dbus_address_entries_free (DBusAddressEntry** entries);
106 | char* dbus_address_escape_value (const(char)* value);
107 | char* dbus_address_unescape_value (const(char)* value, DBusError* error);
108 | // END dbus/dbus-address.d
109 | // START dbus/dbus-syntax.d
110 | dbus_bool_t dbus_validate_path (const(char)* path, DBusError* error);
111 | dbus_bool_t dbus_validate_interface (const(char)* name, DBusError* error);
112 | dbus_bool_t dbus_validate_member (const(char)* name, DBusError* error);
113 | dbus_bool_t dbus_validate_error_name (const(char)* name, DBusError* error);
114 | dbus_bool_t dbus_validate_bus_name (const(char)* name, DBusError* error);
115 | dbus_bool_t dbus_validate_utf8 (const(char)* alleged_utf8, DBusError* error);
116 | // END dbus/dbus-syntax.d
117 | // START dbus/dbus-signature.d
118 | struct DBusSignatureIter
119 | {
120 | void* dummy1;
121 | void* dummy2;
122 | dbus_uint32_t dummy8;
123 | int dummy12;
124 | int dummy17;
125 | }
126 |
127 | void dbus_signature_iter_init (DBusSignatureIter* iter, const(char)* signature);
128 | int dbus_signature_iter_get_current_type (const(DBusSignatureIter)* iter);
129 | char* dbus_signature_iter_get_signature (const(DBusSignatureIter)* iter);
130 | int dbus_signature_iter_get_element_type (const(DBusSignatureIter)* iter);
131 | dbus_bool_t dbus_signature_iter_next (DBusSignatureIter* iter);
132 | void dbus_signature_iter_recurse (const(DBusSignatureIter)* iter, DBusSignatureIter* subiter);
133 | dbus_bool_t dbus_signature_validate (const(char)* signature, DBusError* error);
134 | dbus_bool_t dbus_signature_validate_single (const(char)* signature, DBusError* error);
135 | dbus_bool_t dbus_type_is_valid (int typecode);
136 | dbus_bool_t dbus_type_is_basic (int typecode);
137 | dbus_bool_t dbus_type_is_container (int typecode);
138 | dbus_bool_t dbus_type_is_fixed (int typecode);
139 | // END dbus/dbus-signature.d
140 | // START dbus/dbus-misc.d
141 | char* dbus_get_local_machine_id ();
142 | void dbus_get_version (int* major_version_p, int* minor_version_p, int* micro_version_p);
143 | dbus_bool_t dbus_setenv (const(char)* variable, const(char)* value);
144 | // END dbus/dbus-misc.d
145 | // START dbus/dbus-threads.d
146 | alias DBusMutex* function () DBusMutexNewFunction;
147 | alias void function (DBusMutex*) DBusMutexFreeFunction;
148 | alias uint function (DBusMutex*) DBusMutexLockFunction;
149 | alias uint function (DBusMutex*) DBusMutexUnlockFunction;
150 | alias DBusMutex* function () DBusRecursiveMutexNewFunction;
151 | alias void function (DBusMutex*) DBusRecursiveMutexFreeFunction;
152 | alias void function (DBusMutex*) DBusRecursiveMutexLockFunction;
153 | alias void function (DBusMutex*) DBusRecursiveMutexUnlockFunction;
154 | alias DBusCondVar* function () DBusCondVarNewFunction;
155 | alias void function (DBusCondVar*) DBusCondVarFreeFunction;
156 | alias void function (DBusCondVar*, DBusMutex*) DBusCondVarWaitFunction;
157 | alias uint function (DBusCondVar*, DBusMutex*, int) DBusCondVarWaitTimeoutFunction;
158 | alias void function (DBusCondVar*) DBusCondVarWakeOneFunction;
159 | alias void function (DBusCondVar*) DBusCondVarWakeAllFunction;
160 |
161 |
162 |
163 | enum DBusThreadFunctionsMask
164 | {
165 | DBUS_THREAD_FUNCTIONS_MUTEX_NEW_MASK = 1,
166 | DBUS_THREAD_FUNCTIONS_MUTEX_FREE_MASK = 2,
167 | DBUS_THREAD_FUNCTIONS_MUTEX_LOCK_MASK = 4,
168 | DBUS_THREAD_FUNCTIONS_MUTEX_UNLOCK_MASK = 8,
169 | DBUS_THREAD_FUNCTIONS_CONDVAR_NEW_MASK = 16,
170 | DBUS_THREAD_FUNCTIONS_CONDVAR_FREE_MASK = 32,
171 | DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_MASK = 64,
172 | DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_TIMEOUT_MASK = 128,
173 | DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ONE_MASK = 256,
174 | DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ALL_MASK = 512,
175 | DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_NEW_MASK = 1024,
176 | DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_FREE_MASK = 2048,
177 | DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_LOCK_MASK = 4096,
178 | DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_UNLOCK_MASK = 8192,
179 | DBUS_THREAD_FUNCTIONS_ALL_MASK = 16383
180 | }
181 |
182 | struct DBusThreadFunctions
183 | {
184 | uint mask;
185 | DBusMutexNewFunction mutex_new;
186 | DBusMutexFreeFunction mutex_free;
187 | DBusMutexLockFunction mutex_lock;
188 | DBusMutexUnlockFunction mutex_unlock;
189 | DBusCondVarNewFunction condvar_new;
190 | DBusCondVarFreeFunction condvar_free;
191 | DBusCondVarWaitFunction condvar_wait;
192 | DBusCondVarWaitTimeoutFunction condvar_wait_timeout;
193 | DBusCondVarWakeOneFunction condvar_wake_one;
194 | DBusCondVarWakeAllFunction condvar_wake_all;
195 | DBusRecursiveMutexNewFunction recursive_mutex_new;
196 | DBusRecursiveMutexFreeFunction recursive_mutex_free;
197 | DBusRecursiveMutexLockFunction recursive_mutex_lock;
198 | DBusRecursiveMutexUnlockFunction recursive_mutex_unlock;
199 | void function () padding1;
200 | void function () padding2;
201 | void function () padding3;
202 | void function () padding4;
203 | }
204 |
205 | struct DBusCondVar;
206 |
207 |
208 | struct DBusMutex;
209 |
210 |
211 | dbus_bool_t dbus_threads_init (const(DBusThreadFunctions)* functions);
212 | dbus_bool_t dbus_threads_init_default ();
213 | // END dbus/dbus-threads.d
214 | // START dbus/dbus-message.d
215 | struct DBusMessageIter
216 | {
217 | void* dummy1;
218 | void* dummy2;
219 | dbus_uint32_t dummy3;
220 | int dummy4;
221 | int dummy5;
222 | int dummy6;
223 | int dummy7;
224 | int dummy8;
225 | int dummy9;
226 | int dummy10;
227 | int dummy11;
228 | int pad1;
229 | int pad2;
230 | void* pad3;
231 | }
232 |
233 | struct DBusMessage;
234 |
235 |
236 | DBusMessage* dbus_message_new (int message_type);
237 | DBusMessage* dbus_message_new_method_call (const(char)* bus_name, const(char)* path, const(char)* iface, const(char)* method);
238 | DBusMessage* dbus_message_new_method_return (DBusMessage* method_call);
239 | DBusMessage* dbus_message_new_signal (const(char)* path, const(char)* iface, const(char)* name);
240 | DBusMessage* dbus_message_new_error (DBusMessage* reply_to, const(char)* error_name, const(char)* error_message);
241 | DBusMessage* dbus_message_new_error_printf (DBusMessage* reply_to, const(char)* error_name, const(char)* error_format, ...);
242 | DBusMessage* dbus_message_copy (const(DBusMessage)* message);
243 | DBusMessage* dbus_message_ref (DBusMessage* message);
244 | void dbus_message_unref (DBusMessage* message);
245 | int dbus_message_get_type (DBusMessage* message);
246 | dbus_bool_t dbus_message_set_path (DBusMessage* message, const(char)* object_path);
247 | const(char)* dbus_message_get_path (DBusMessage* message);
248 | dbus_bool_t dbus_message_has_path (DBusMessage* message, const(char)* object_path);
249 | dbus_bool_t dbus_message_set_interface (DBusMessage* message, const(char)* iface);
250 | const(char)* dbus_message_get_interface (DBusMessage* message);
251 | dbus_bool_t dbus_message_has_interface (DBusMessage* message, const(char)* iface);
252 | dbus_bool_t dbus_message_set_member (DBusMessage* message, const(char)* member);
253 | const(char)* dbus_message_get_member (DBusMessage* message);
254 | dbus_bool_t dbus_message_has_member (DBusMessage* message, const(char)* member);
255 | dbus_bool_t dbus_message_set_error_name (DBusMessage* message, const(char)* name);
256 | const(char)* dbus_message_get_error_name (DBusMessage* message);
257 | dbus_bool_t dbus_message_set_destination (DBusMessage* message, const(char)* destination);
258 | const(char)* dbus_message_get_destination (DBusMessage* message);
259 | dbus_bool_t dbus_message_set_sender (DBusMessage* message, const(char)* sender);
260 | const(char)* dbus_message_get_sender (DBusMessage* message);
261 | const(char)* dbus_message_get_signature (DBusMessage* message);
262 | void dbus_message_set_no_reply (DBusMessage* message, dbus_bool_t no_reply);
263 | dbus_bool_t dbus_message_get_no_reply (DBusMessage* message);
264 | dbus_bool_t dbus_message_is_method_call (DBusMessage* message, const(char)* iface, const(char)* method);
265 | dbus_bool_t dbus_message_is_signal (DBusMessage* message, const(char)* iface, const(char)* signal_name);
266 | dbus_bool_t dbus_message_is_error (DBusMessage* message, const(char)* error_name);
267 | dbus_bool_t dbus_message_has_destination (DBusMessage* message, const(char)* bus_name);
268 | dbus_bool_t dbus_message_has_sender (DBusMessage* message, const(char)* unique_bus_name);
269 | dbus_bool_t dbus_message_has_signature (DBusMessage* message, const(char)* signature);
270 | dbus_uint32_t dbus_message_get_serial (DBusMessage* message);
271 | void dbus_message_set_serial (DBusMessage* message, dbus_uint32_t serial);
272 | dbus_bool_t dbus_message_set_reply_serial (DBusMessage* message, dbus_uint32_t reply_serial);
273 | dbus_uint32_t dbus_message_get_reply_serial (DBusMessage* message);
274 | void dbus_message_set_auto_start (DBusMessage* message, dbus_bool_t auto_start);
275 | dbus_bool_t dbus_message_get_auto_start (DBusMessage* message);
276 | dbus_bool_t dbus_message_get_path_decomposed (DBusMessage* message, char*** path);
277 | dbus_bool_t dbus_message_append_args (DBusMessage* message, int first_arg_type, ...);
278 | dbus_bool_t dbus_message_append_args_valist (DBusMessage* message, int first_arg_type, va_list var_args);
279 | dbus_bool_t dbus_message_get_args (DBusMessage* message, DBusError* error, int first_arg_type, ...);
280 | dbus_bool_t dbus_message_get_args_valist (DBusMessage* message, DBusError* error, int first_arg_type, va_list var_args);
281 | dbus_bool_t dbus_message_contains_unix_fds (DBusMessage* message);
282 | dbus_bool_t dbus_message_iter_init (DBusMessage* message, DBusMessageIter* iter);
283 | dbus_bool_t dbus_message_iter_has_next (DBusMessageIter* iter);
284 | dbus_bool_t dbus_message_iter_next (DBusMessageIter* iter);
285 | char* dbus_message_iter_get_signature (DBusMessageIter* iter);
286 | int dbus_message_iter_get_arg_type (DBusMessageIter* iter);
287 | int dbus_message_iter_get_element_type (DBusMessageIter* iter);
288 | void dbus_message_iter_recurse (DBusMessageIter* iter, DBusMessageIter* sub);
289 | void dbus_message_iter_get_basic (DBusMessageIter* iter, void* value);
290 | int dbus_message_iter_get_array_len (DBusMessageIter* iter);
291 | void dbus_message_iter_get_fixed_array (DBusMessageIter* iter, void* value, int* n_elements);
292 | void dbus_message_iter_init_append (DBusMessage* message, DBusMessageIter* iter);
293 | dbus_bool_t dbus_message_iter_append_basic (DBusMessageIter* iter, int type, const(void)* value);
294 | dbus_bool_t dbus_message_iter_append_fixed_array (DBusMessageIter* iter, int element_type, const(void)* value, int n_elements);
295 | dbus_bool_t dbus_message_iter_open_container (DBusMessageIter* iter, int type, const(char)* contained_signature, DBusMessageIter* sub);
296 | dbus_bool_t dbus_message_iter_close_container (DBusMessageIter* iter, DBusMessageIter* sub);
297 | void dbus_message_iter_abandon_container (DBusMessageIter* iter, DBusMessageIter* sub);
298 | void dbus_message_lock (DBusMessage* message);
299 | dbus_bool_t dbus_set_error_from_message (DBusError* error, DBusMessage* message);
300 | dbus_bool_t dbus_message_allocate_data_slot (dbus_int32_t* slot_p);
301 | void dbus_message_free_data_slot (dbus_int32_t* slot_p);
302 | dbus_bool_t dbus_message_set_data (DBusMessage* message, dbus_int32_t slot, void* data, DBusFreeFunction free_data_func);
303 | void* dbus_message_get_data (DBusMessage* message, dbus_int32_t slot);
304 | int dbus_message_type_from_string (const(char)* type_str);
305 | const(char)* dbus_message_type_to_string (int type);
306 | dbus_bool_t dbus_message_marshal (DBusMessage* msg, char** marshalled_data_p, int* len_p);
307 | DBusMessage* dbus_message_demarshal (const(char)* str, int len, DBusError* error);
308 | int dbus_message_demarshal_bytes_needed (const(char)* str, int len);
309 | // END dbus/dbus-message.d
310 | // START dbus/dbus-connection.d
311 | alias uint function (DBusWatch*, void*) DBusAddWatchFunction;
312 | alias void function (DBusWatch*, void*) DBusWatchToggledFunction;
313 | alias void function (DBusWatch*, void*) DBusRemoveWatchFunction;
314 | alias uint function (DBusTimeout*, void*) DBusAddTimeoutFunction;
315 | alias void function (DBusTimeout*, void*) DBusTimeoutToggledFunction;
316 | alias void function (DBusTimeout*, void*) DBusRemoveTimeoutFunction;
317 | alias void function (DBusConnection*, DBusDispatchStatus, void*) DBusDispatchStatusFunction;
318 | alias void function (void*) DBusWakeupMainFunction;
319 | alias uint function (DBusConnection*, c_ulong, void*) DBusAllowUnixUserFunction;
320 | alias uint function (DBusConnection*, const(char)*, void*) DBusAllowWindowsUserFunction;
321 | alias void function (DBusPendingCall*, void*) DBusPendingCallNotifyFunction;
322 | alias DBusHandlerResult function (DBusConnection*, DBusMessage*, void*) DBusHandleMessageFunction;
323 | alias void function (DBusConnection*, void*) DBusObjectPathUnregisterFunction;
324 | alias DBusHandlerResult function (DBusConnection*, DBusMessage*, void*) DBusObjectPathMessageFunction;
325 |
326 | enum DBusWatchFlags
327 | {
328 | DBUS_WATCH_READABLE = 1,
329 | DBUS_WATCH_WRITABLE = 2,
330 | DBUS_WATCH_ERROR = 4,
331 | DBUS_WATCH_HANGUP = 8
332 | }
333 |
334 | enum DBusDispatchStatus
335 | {
336 | DBUS_DISPATCH_DATA_REMAINS = 0,
337 | DBUS_DISPATCH_COMPLETE = 1,
338 | DBUS_DISPATCH_NEED_MEMORY = 2
339 | }
340 |
341 | struct DBusObjectPathVTable
342 | {
343 | DBusObjectPathUnregisterFunction unregister_function;
344 | DBusObjectPathMessageFunction message_function;
345 | void function (void*) dbus_internal_pad1;
346 | void function (void*) dbus_internal_pad2;
347 | void function (void*) dbus_internal_pad3;
348 | void function (void*) dbus_internal_pad4;
349 | }
350 |
351 | struct DBusPreallocatedSend;
352 |
353 |
354 | struct DBusTimeout;
355 |
356 |
357 | struct DBusPendingCall;
358 |
359 |
360 | struct DBusConnection;
361 |
362 |
363 | struct DBusWatch;
364 |
365 |
366 | DBusConnection* dbus_connection_open (const(char)* address, DBusError* error);
367 | DBusConnection* dbus_connection_open_private (const(char)* address, DBusError* error);
368 | DBusConnection* dbus_connection_ref (DBusConnection* connection);
369 | void dbus_connection_unref (DBusConnection* connection);
370 | void dbus_connection_close (DBusConnection* connection);
371 | dbus_bool_t dbus_connection_get_is_connected (DBusConnection* connection);
372 | dbus_bool_t dbus_connection_get_is_authenticated (DBusConnection* connection);
373 | dbus_bool_t dbus_connection_get_is_anonymous (DBusConnection* connection);
374 | char* dbus_connection_get_server_id (DBusConnection* connection);
375 | dbus_bool_t dbus_connection_can_send_type (DBusConnection* connection, int type);
376 | void dbus_connection_set_exit_on_disconnect (DBusConnection* connection, dbus_bool_t exit_on_disconnect);
377 | void dbus_connection_flush (DBusConnection* connection);
378 | dbus_bool_t dbus_connection_read_write_dispatch (DBusConnection* connection, int timeout_milliseconds);
379 | dbus_bool_t dbus_connection_read_write (DBusConnection* connection, int timeout_milliseconds);
380 | DBusMessage* dbus_connection_borrow_message (DBusConnection* connection);
381 | void dbus_connection_return_message (DBusConnection* connection, DBusMessage* message);
382 | void dbus_connection_steal_borrowed_message (DBusConnection* connection, DBusMessage* message);
383 | DBusMessage* dbus_connection_pop_message (DBusConnection* connection);
384 | DBusDispatchStatus dbus_connection_get_dispatch_status (DBusConnection* connection);
385 | DBusDispatchStatus dbus_connection_dispatch (DBusConnection* connection);
386 | dbus_bool_t dbus_connection_has_messages_to_send (DBusConnection* connection);
387 | dbus_bool_t dbus_connection_send (DBusConnection* connection, DBusMessage* message, dbus_uint32_t* client_serial);
388 | dbus_bool_t dbus_connection_send_with_reply (DBusConnection* connection, DBusMessage* message, DBusPendingCall** pending_return, int timeout_milliseconds);
389 | DBusMessage* dbus_connection_send_with_reply_and_block (DBusConnection* connection, DBusMessage* message, int timeout_milliseconds, DBusError* error);
390 | dbus_bool_t dbus_connection_set_watch_functions (DBusConnection* connection, DBusAddWatchFunction add_function, DBusRemoveWatchFunction remove_function, DBusWatchToggledFunction toggled_function, void* data, DBusFreeFunction free_data_function);
391 | dbus_bool_t dbus_connection_set_timeout_functions (DBusConnection* connection, DBusAddTimeoutFunction add_function, DBusRemoveTimeoutFunction remove_function, DBusTimeoutToggledFunction toggled_function, void* data, DBusFreeFunction free_data_function);
392 | void dbus_connection_set_wakeup_main_function (DBusConnection* connection, DBusWakeupMainFunction wakeup_main_function, void* data, DBusFreeFunction free_data_function);
393 | void dbus_connection_set_dispatch_status_function (DBusConnection* connection, DBusDispatchStatusFunction function_, void* data, DBusFreeFunction free_data_function);
394 | dbus_bool_t dbus_connection_get_unix_user (DBusConnection* connection, c_ulong* uid);
395 | dbus_bool_t dbus_connection_get_unix_process_id (DBusConnection* connection, c_ulong* pid);
396 | dbus_bool_t dbus_connection_get_adt_audit_session_data (DBusConnection* connection, void** data, dbus_int32_t* data_size);
397 | void dbus_connection_set_unix_user_function (DBusConnection* connection, DBusAllowUnixUserFunction function_, void* data, DBusFreeFunction free_data_function);
398 | dbus_bool_t dbus_connection_get_windows_user (DBusConnection* connection, char** windows_sid_p);
399 | void dbus_connection_set_windows_user_function (DBusConnection* connection, DBusAllowWindowsUserFunction function_, void* data, DBusFreeFunction free_data_function);
400 | void dbus_connection_set_allow_anonymous (DBusConnection* connection, dbus_bool_t value);
401 | void dbus_connection_set_route_peer_messages (DBusConnection* connection, dbus_bool_t value);
402 | dbus_bool_t dbus_connection_add_filter (DBusConnection* connection, DBusHandleMessageFunction function_, void* user_data, DBusFreeFunction free_data_function);
403 | void dbus_connection_remove_filter (DBusConnection* connection, DBusHandleMessageFunction function_, void* user_data);
404 | dbus_bool_t dbus_connection_allocate_data_slot (dbus_int32_t* slot_p);
405 | void dbus_connection_free_data_slot (dbus_int32_t* slot_p);
406 | dbus_bool_t dbus_connection_set_data (DBusConnection* connection, dbus_int32_t slot, void* data, DBusFreeFunction free_data_func);
407 | void* dbus_connection_get_data (DBusConnection* connection, dbus_int32_t slot);
408 | void dbus_connection_set_change_sigpipe (dbus_bool_t will_modify_sigpipe);
409 | void dbus_connection_set_max_message_size (DBusConnection* connection, c_long size);
410 | c_long dbus_connection_get_max_message_size (DBusConnection* connection);
411 | void dbus_connection_set_max_received_size (DBusConnection* connection, c_long size);
412 | c_long dbus_connection_get_max_received_size (DBusConnection* connection);
413 | void dbus_connection_set_max_message_unix_fds (DBusConnection* connection, c_long n);
414 | c_long dbus_connection_get_max_message_unix_fds (DBusConnection* connection);
415 | void dbus_connection_set_max_received_unix_fds (DBusConnection* connection, c_long n);
416 | c_long dbus_connection_get_max_received_unix_fds (DBusConnection* connection);
417 | c_long dbus_connection_get_outgoing_size (DBusConnection* connection);
418 | c_long dbus_connection_get_outgoing_unix_fds (DBusConnection* connection);
419 | DBusPreallocatedSend* dbus_connection_preallocate_send (DBusConnection* connection);
420 | void dbus_connection_free_preallocated_send (DBusConnection* connection, DBusPreallocatedSend* preallocated);
421 | void dbus_connection_send_preallocated (DBusConnection* connection, DBusPreallocatedSend* preallocated, DBusMessage* message, dbus_uint32_t* client_serial);
422 | dbus_bool_t dbus_connection_try_register_object_path (DBusConnection* connection, const(char)* path, const(DBusObjectPathVTable)* vtable, void* user_data, DBusError* error);
423 | dbus_bool_t dbus_connection_register_object_path (DBusConnection* connection, const(char)* path, const(DBusObjectPathVTable)* vtable, void* user_data);
424 | dbus_bool_t dbus_connection_try_register_fallback (DBusConnection* connection, const(char)* path, const(DBusObjectPathVTable)* vtable, void* user_data, DBusError* error);
425 | dbus_bool_t dbus_connection_register_fallback (DBusConnection* connection, const(char)* path, const(DBusObjectPathVTable)* vtable, void* user_data);
426 | dbus_bool_t dbus_connection_unregister_object_path (DBusConnection* connection, const(char)* path);
427 | dbus_bool_t dbus_connection_get_object_path_data (DBusConnection* connection, const(char)* path, void** data_p);
428 | dbus_bool_t dbus_connection_list_registered (DBusConnection* connection, const(char)* parent_path, char*** child_entries);
429 | dbus_bool_t dbus_connection_get_unix_fd (DBusConnection* connection, int* fd);
430 | dbus_bool_t dbus_connection_get_socket (DBusConnection* connection, int* fd);
431 | int dbus_watch_get_fd (DBusWatch* watch);
432 | int dbus_watch_get_unix_fd (DBusWatch* watch);
433 | int dbus_watch_get_socket (DBusWatch* watch);
434 | uint dbus_watch_get_flags (DBusWatch* watch);
435 | void* dbus_watch_get_data (DBusWatch* watch);
436 | void dbus_watch_set_data (DBusWatch* watch, void* data, DBusFreeFunction free_data_function);
437 | dbus_bool_t dbus_watch_handle (DBusWatch* watch, uint flags);
438 | dbus_bool_t dbus_watch_get_enabled (DBusWatch* watch);
439 | int dbus_timeout_get_interval (DBusTimeout* timeout);
440 | void* dbus_timeout_get_data (DBusTimeout* timeout);
441 | void dbus_timeout_set_data (DBusTimeout* timeout, void* data, DBusFreeFunction free_data_function);
442 | dbus_bool_t dbus_timeout_handle (DBusTimeout* timeout);
443 | dbus_bool_t dbus_timeout_get_enabled (DBusTimeout* timeout);
444 | // END dbus/dbus-connection.d
445 | // START dbus/dbus-pending-call.d
446 | DBusPendingCall* dbus_pending_call_ref (DBusPendingCall* pending);
447 | void dbus_pending_call_unref (DBusPendingCall* pending);
448 | dbus_bool_t dbus_pending_call_set_notify (DBusPendingCall* pending, DBusPendingCallNotifyFunction function_, void* user_data, DBusFreeFunction free_user_data);
449 | void dbus_pending_call_cancel (DBusPendingCall* pending);
450 | dbus_bool_t dbus_pending_call_get_completed (DBusPendingCall* pending);
451 | DBusMessage* dbus_pending_call_steal_reply (DBusPendingCall* pending);
452 | void dbus_pending_call_block (DBusPendingCall* pending);
453 | dbus_bool_t dbus_pending_call_allocate_data_slot (dbus_int32_t* slot_p);
454 | void dbus_pending_call_free_data_slot (dbus_int32_t* slot_p);
455 | dbus_bool_t dbus_pending_call_set_data (DBusPendingCall* pending, dbus_int32_t slot, void* data, DBusFreeFunction free_data_func);
456 | void* dbus_pending_call_get_data (DBusPendingCall* pending, dbus_int32_t slot);
457 | // END dbus/dbus-pending-call.d
458 | // START dbus/dbus-server.d
459 | alias void function (DBusServer*, DBusConnection*, void*) DBusNewConnectionFunction;
460 |
461 | struct DBusServer;
462 |
463 |
464 | DBusServer* dbus_server_listen (const(char)* address, DBusError* error);
465 | DBusServer* dbus_server_ref (DBusServer* server);
466 | void dbus_server_unref (DBusServer* server);
467 | void dbus_server_disconnect (DBusServer* server);
468 | dbus_bool_t dbus_server_get_is_connected (DBusServer* server);
469 | char* dbus_server_get_address (DBusServer* server);
470 | char* dbus_server_get_id (DBusServer* server);
471 | void dbus_server_set_new_connection_function (DBusServer* server, DBusNewConnectionFunction function_, void* data, DBusFreeFunction free_data_function);
472 | dbus_bool_t dbus_server_set_watch_functions (DBusServer* server, DBusAddWatchFunction add_function, DBusRemoveWatchFunction remove_function, DBusWatchToggledFunction toggled_function, void* data, DBusFreeFunction free_data_function);
473 | dbus_bool_t dbus_server_set_timeout_functions (DBusServer* server, DBusAddTimeoutFunction add_function, DBusRemoveTimeoutFunction remove_function, DBusTimeoutToggledFunction toggled_function, void* data, DBusFreeFunction free_data_function);
474 | dbus_bool_t dbus_server_set_auth_mechanisms (DBusServer* server, const(char*)* mechanisms);
475 | dbus_bool_t dbus_server_allocate_data_slot (dbus_int32_t* slot_p);
476 | void dbus_server_free_data_slot (dbus_int32_t* slot_p);
477 | dbus_bool_t dbus_server_set_data (DBusServer* server, int slot, void* data, DBusFreeFunction free_data_func);
478 | void* dbus_server_get_data (DBusServer* server, int slot);
479 | // END dbus/dbus-server.d
480 | // START dbus/dbus-bus.d
481 | DBusConnection* dbus_bus_get (DBusBusType type, DBusError* error);
482 | DBusConnection* dbus_bus_get_private (DBusBusType type, DBusError* error);
483 | dbus_bool_t dbus_bus_register (DBusConnection* connection, DBusError* error);
484 | dbus_bool_t dbus_bus_set_unique_name (DBusConnection* connection, const(char)* unique_name);
485 | const(char)* dbus_bus_get_unique_name (DBusConnection* connection);
486 | c_ulong dbus_bus_get_unix_user (DBusConnection* connection, const(char)* name, DBusError* error);
487 | char* dbus_bus_get_id (DBusConnection* connection, DBusError* error);
488 | int dbus_bus_request_name (DBusConnection* connection, const(char)* name, uint flags, DBusError* error);
489 | int dbus_bus_release_name (DBusConnection* connection, const(char)* name, DBusError* error);
490 | dbus_bool_t dbus_bus_name_has_owner (DBusConnection* connection, const(char)* name, DBusError* error);
491 | dbus_bool_t dbus_bus_start_service_by_name (DBusConnection* connection, const(char)* name, dbus_uint32_t flags, dbus_uint32_t* reply, DBusError* error);
492 | void dbus_bus_add_match (DBusConnection* connection, const(char)* rule, DBusError* error);
493 | void dbus_bus_remove_match (DBusConnection* connection, const(char)* rule, DBusError* error);
494 | // END dbus/dbus-bus.d
495 | // START dbus/dbus.d
496 |
497 | // END dbus/dbus.d
498 |
--------------------------------------------------------------------------------
/source/ddbus/conv.d:
--------------------------------------------------------------------------------
1 | module ddbus.conv;
2 |
3 | import ddbus.attributes : isAllowedField;
4 | import ddbus.c_lib;
5 | import ddbus.exception : InvalidValueException, TypeMismatchException;
6 | import ddbus.util;
7 | import ddbus.thin;
8 |
9 | import std.exception : enforce;
10 | import std.meta : allSatisfy;
11 | import std.string;
12 | import std.typecons;
13 | import std.range;
14 | import std.traits;
15 | import std.variant : VariantN;
16 |
17 | void buildIter(TS...)(DBusMessageIter* iter, TS args)
18 | if (allCanDBus!TS) {
19 | foreach (index, arg; args) {
20 | alias T = Unqual!(TS[index]);
21 | static if (is(T == string) || is(T == InterfaceName) || is(T == BusName)) {
22 | immutable(char)* cStr = arg.toStringz();
23 | dbus_message_iter_append_basic(iter, typeCode!T, &cStr);
24 | } else static if (is(T == ObjectPath)) {
25 | immutable(char)* cStr = arg.toString().toStringz();
26 | dbus_message_iter_append_basic(iter, typeCode!T, &cStr);
27 | } else static if (is(T == bool)) {
28 | dbus_bool_t longerBool = arg; // dbus bools are ints
29 | dbus_message_iter_append_basic(iter, typeCode!T, &longerBool);
30 | } else static if (isTuple!T) {
31 | DBusMessageIter sub;
32 | dbus_message_iter_open_container(iter, 'r', null, &sub);
33 | buildIter(&sub, arg.expand);
34 | dbus_message_iter_close_container(iter, &sub);
35 | } else static if (isInputRange!T) {
36 | DBusMessageIter sub;
37 | const(char)* subSig = (typeSig!(ElementType!T)()).toStringz();
38 | dbus_message_iter_open_container(iter, 'a', subSig, &sub);
39 | foreach (x; arg) {
40 | static if (isInstanceOf!(DictionaryEntry, typeof(x))) {
41 | DBusMessageIter entry;
42 | dbus_message_iter_open_container(&sub, 'e', null, &entry);
43 | buildIter(&entry, x.key);
44 | buildIter(&entry, x.value);
45 | dbus_message_iter_close_container(&sub, &entry);
46 | } else {
47 | buildIter(&sub, x);
48 | }
49 | }
50 | dbus_message_iter_close_container(iter, &sub);
51 | } else static if (isAssociativeArray!T) {
52 | DBusMessageIter sub;
53 | const(char)* subSig = typeSig!T[1 .. $].toStringz();
54 | dbus_message_iter_open_container(iter, 'a', subSig, &sub);
55 | foreach (k, v; arg) {
56 | DBusMessageIter entry;
57 | dbus_message_iter_open_container(&sub, 'e', null, &entry);
58 | buildIter(&entry, k);
59 | buildIter(&entry, v);
60 | dbus_message_iter_close_container(&sub, &entry);
61 | }
62 | dbus_message_iter_close_container(iter, &sub);
63 | } else static if (isInstanceOf!(VariantN, T)) {
64 | enforce(arg.hasValue, new InvalidValueException(arg, "dbus:" ~ cast(char) typeCode!T));
65 |
66 | DBusMessageIter sub;
67 | foreach (AT; T.AllowedTypes) {
68 | if (arg.peek!AT) {
69 | dbus_message_iter_open_container(iter, 'v', typeSig!AT.ptr, &sub);
70 | buildIter(&sub, arg.get!AT);
71 | dbus_message_iter_close_container(iter, &sub);
72 | break;
73 | }
74 | }
75 | } else static if (is(T == DBusAny) || is(T == Variant!DBusAny)) {
76 | static if (is(T == Variant!DBusAny)) {
77 | auto val = arg.data;
78 | val.explicitVariant = true;
79 | } else {
80 | auto val = arg;
81 | }
82 | DBusMessageIter subStore;
83 | DBusMessageIter* sub = &subStore;
84 | const(char)[] sig = [cast(char) val.type];
85 | if (val.type == 'a') {
86 | sig ~= val.signature;
87 | } else if (val.type == 'r') {
88 | sig = val.signature;
89 | }
90 |
91 | sig ~= '\0';
92 |
93 | if (!val.explicitVariant) {
94 | sub = iter;
95 | } else {
96 | dbus_message_iter_open_container(iter, 'v', sig.ptr, sub);
97 | }
98 |
99 | if (val.type == 's') {
100 | buildIter(sub, val.str);
101 | } else if (val.type == 'o') {
102 | buildIter(sub, val.obj);
103 | } else if (val.type == 'b') {
104 | buildIter(sub, val.boolean);
105 | } else if (dbus_type_is_basic(val.type)) {
106 | dbus_message_iter_append_basic(sub, val.type, &val.int64);
107 | } else if (val.type == 'a') {
108 | DBusMessageIter arr;
109 | dbus_message_iter_open_container(sub, 'a', sig[1 .. $].ptr, &arr);
110 |
111 | if (val.signature == ['y']) {
112 | foreach (item; val.binaryData) {
113 | dbus_message_iter_append_basic(&arr, 'y', &item);
114 | }
115 | } else {
116 | foreach (item; val.array) {
117 | buildIter(&arr, item);
118 | }
119 | }
120 |
121 | dbus_message_iter_close_container(sub, &arr);
122 | } else if (val.type == 'r') {
123 | DBusMessageIter arr;
124 | dbus_message_iter_open_container(sub, 'r', null, &arr);
125 |
126 | foreach (item; val.tuple) {
127 | buildIter(&arr, item);
128 | }
129 |
130 | dbus_message_iter_close_container(sub, &arr);
131 | } else if (val.type == 'e') {
132 | DBusMessageIter entry;
133 | dbus_message_iter_open_container(sub, 'e', null, &entry);
134 | buildIter(&entry, val.entry.key);
135 | buildIter(&entry, val.entry.value);
136 | dbus_message_iter_close_container(sub, &entry);
137 | }
138 |
139 | if (val.explicitVariant) {
140 | dbus_message_iter_close_container(iter, sub);
141 | }
142 | } else static if (isInstanceOf!(Variant, T)) {
143 | DBusMessageIter sub;
144 | const(char)* subSig = typeSig!(VariantType!T).toStringz();
145 | dbus_message_iter_open_container(iter, 'v', subSig, &sub);
146 | buildIter(&sub, arg.data);
147 | dbus_message_iter_close_container(iter, &sub);
148 | } else static if (is(T == struct)) {
149 | DBusMessageIter sub;
150 | dbus_message_iter_open_container(iter, 'r', null, &sub);
151 |
152 | // Following failed because of missing 'this' for members of arg.
153 | // That sucks. It worked without Filter.
154 | // Reported: https://issues.dlang.org/show_bug.cgi?id=17692
155 | // buildIter(&sub, Filter!(isAllowedField, arg.tupleof));
156 |
157 | // Using foreach to work around the issue
158 | foreach (i, member; arg.tupleof) {
159 | // Ugly, but we need to use tupleof again in the condition, because when
160 | // we use `member`, isAllowedField will fail because it'll find this
161 | // nice `buildIter` function instead of T when it looks up the parent
162 | // scope of its argument.
163 | static if (isAllowedField!(arg.tupleof[i]))
164 | buildIter(&sub, member);
165 | }
166 |
167 | dbus_message_iter_close_container(iter, &sub);
168 | } else static if (basicDBus!T) {
169 | dbus_message_iter_append_basic(iter, typeCode!T, &arg);
170 | }
171 | }
172 | }
173 |
174 | T readIter(T)(DBusMessageIter* iter)
175 | if (is(T == enum) && !is(Unqual!T == InterfaceName) && !is(Unqual!T == BusName) && !is(Unqual!T == FileDescriptor)) {
176 | import std.algorithm.searching : canFind;
177 |
178 | alias B = Unqual!(OriginalType!T);
179 |
180 | B value = readIter!B(iter);
181 | enforce(only(EnumMembers!T).canFind(value), new InvalidValueException(value, T.stringof));
182 | return cast(T) value;
183 | }
184 |
185 | T readIter(T)(DBusMessageIter* iter)
186 | if (isInstanceOf!(BitFlags, T)) {
187 | import std.algorithm.iteration : fold;
188 |
189 | alias TemplateArgsOf!T[0] E;
190 | alias OriginalType!E B;
191 |
192 | B mask = only(EnumMembers!E).fold!((a, b) => cast(B)(a | b));
193 |
194 | B value = readIter!B(iter);
195 | enforce(!(value & ~mask), new InvalidValueException(value, T.stringof));
196 |
197 | return T(cast(E) value);
198 | }
199 |
200 | U readIter(U)(DBusMessageIter* iter)
201 | if (!(is(U == enum) && !is(Unqual!U == InterfaceName) && !is(Unqual!U == BusName) && !is(U == FileDescriptor))
202 | && !isInstanceOf!(BitFlags, U) && canDBus!U) {
203 | alias T = Unqual!U;
204 |
205 | auto argType = dbus_message_iter_get_arg_type(iter);
206 | T ret;
207 |
208 | static if (!isInstanceOf!(Variant, T) || is(T == Variant!DBusAny)) {
209 | if (argType == 'v') {
210 | DBusMessageIter sub;
211 | dbus_message_iter_recurse(iter, &sub);
212 | static if (is(T == Variant!DBusAny)) {
213 | ret = variant(readIter!DBusAny(&sub));
214 | } else {
215 | ret = readIter!T(&sub);
216 | static if (is(T == DBusAny))
217 | ret.explicitVariant = true;
218 | }
219 | dbus_message_iter_next(iter);
220 | return cast(U) ret;
221 | }
222 | }
223 |
224 | static if (!is(T == DBusAny) && !is(T == Variant!DBusAny) && !isInstanceOf!(VariantN, T)) {
225 | enforce(argType == typeCode!T(), new TypeMismatchException(typeCode!T(), argType));
226 | }
227 |
228 | static if (is(T == string) || is(T == InterfaceName) || is(T == BusName) || is(T == ObjectPath)) {
229 | const(char)* cStr;
230 | dbus_message_iter_get_basic(iter, &cStr);
231 | string str = cStr.fromStringz().idup; // copy string
232 | static if (is(T == string) || is(T : InterfaceName) || is(T : BusName)) {
233 | ret = cast(T)str;
234 | } else {
235 | ret = ObjectPath(str);
236 | }
237 | } else static if (is(T == bool)) {
238 | dbus_bool_t longerBool;
239 | dbus_message_iter_get_basic(iter, &longerBool);
240 | ret = cast(bool) longerBool;
241 | } else static if (isTuple!T) {
242 | DBusMessageIter sub;
243 | dbus_message_iter_recurse(iter, &sub);
244 | readIterTuple!T(&sub, ret);
245 | } else static if (is(T t : S[], S)) {
246 | assert(dbus_message_iter_get_element_type(iter) == typeCode!S);
247 |
248 | DBusMessageIter sub;
249 | dbus_message_iter_recurse(iter, &sub);
250 |
251 | while (dbus_message_iter_get_arg_type(&sub) != 0) {
252 | static if (is(S == DictionaryEntry!(K, V), K, V)) {
253 | DBusMessageIter entry;
254 | dbus_message_iter_recurse(&sub, &entry);
255 | ret ~= S(readIter!K(&entry), readIter!V(&entry));
256 | dbus_message_iter_next(&sub);
257 | } else {
258 | ret ~= readIter!S(&sub);
259 | }
260 | }
261 | } else static if (isInstanceOf!(Variant, T)) {
262 | DBusMessageIter sub;
263 | dbus_message_iter_recurse(iter, &sub);
264 | ret.data = readIter!(VariantType!T)(&sub);
265 | } else static if (isInstanceOf!(VariantN, T)) {
266 | scope const(char)[] argSig = dbus_message_iter_get_signature(iter).fromStringz();
267 | scope (exit)
268 | dbus_free(cast(void*) argSig.ptr);
269 |
270 | foreach (AT; T.AllowedTypes) {
271 | // We have to compare the full signature here, not just the typecode.
272 | // Otherwise, in case of container types, we might select the wrong one.
273 | // We would then be calling an incorrect instance of readIter, which would
274 | // probably throw a TypeMismatchException.
275 | if (typeSig!AT == argSig) {
276 | ret = readIter!AT(iter);
277 | break;
278 | }
279 | }
280 |
281 | // If no value is in ret, apparently none of the types matched.
282 | enforce(ret.hasValue, new TypeMismatchException(typeCode!T, argType));
283 | } else static if (isAssociativeArray!T) {
284 | DBusMessageIter sub;
285 | dbus_message_iter_recurse(iter, &sub);
286 |
287 | while (dbus_message_iter_get_arg_type(&sub) != 0) {
288 | DBusMessageIter entry;
289 | dbus_message_iter_recurse(&sub, &entry);
290 | auto k = readIter!(KeyType!T)(&entry);
291 | auto v = readIter!(ValueType!T)(&entry);
292 | ret[k] = v;
293 | dbus_message_iter_next(&sub);
294 | }
295 | } else static if (is(T == DBusAny)) {
296 | ret.type = argType;
297 | ret.explicitVariant = false;
298 |
299 | if (ret.type == 's') {
300 | ret.str = readIter!string(iter);
301 | return cast(U) ret;
302 | } else if (ret.type == 'o') {
303 | ret.obj = readIter!ObjectPath(iter);
304 | return cast(U) ret;
305 | } else if (ret.type == 'b') {
306 | ret.boolean = readIter!bool(iter);
307 | return cast(U) ret;
308 | } else if (dbus_type_is_basic(ret.type)) {
309 | dbus_message_iter_get_basic(iter, &ret.int64);
310 | } else if (ret.type == 'a') {
311 | DBusMessageIter sub;
312 | dbus_message_iter_recurse(iter, &sub);
313 | auto sig = dbus_message_iter_get_signature(&sub);
314 | ret.signature = sig.fromStringz.dup;
315 | dbus_free(sig);
316 | if (ret.signature == ['y']) {
317 | while (dbus_message_iter_get_arg_type(&sub) != 0) {
318 | ubyte b;
319 | assert(dbus_message_iter_get_arg_type(&sub) == 'y');
320 | dbus_message_iter_get_basic(&sub, &b);
321 | dbus_message_iter_next(&sub);
322 | ret.binaryData ~= b;
323 | }
324 | } else {
325 | while (dbus_message_iter_get_arg_type(&sub) != 0) {
326 | ret.array ~= readIter!DBusAny(&sub);
327 | }
328 | }
329 | } else if (ret.type == 'r') {
330 | auto sig = dbus_message_iter_get_signature(iter);
331 | ret.signature = sig.fromStringz.dup;
332 | dbus_free(sig);
333 |
334 | DBusMessageIter sub;
335 | dbus_message_iter_recurse(iter, &sub);
336 |
337 | while (dbus_message_iter_get_arg_type(&sub) != 0) {
338 | ret.tuple ~= readIter!DBusAny(&sub);
339 | }
340 | } else if (ret.type == 'e') {
341 | DBusMessageIter sub;
342 | dbus_message_iter_recurse(iter, &sub);
343 |
344 | ret.entry = new DictionaryEntry!(DBusAny, DBusAny);
345 | ret.entry.key = readIter!DBusAny(&sub);
346 | ret.entry.value = readIter!DBusAny(&sub);
347 | }
348 | } else static if (is(T == struct)) {
349 | DBusMessageIter sub;
350 | dbus_message_iter_recurse(iter, &sub);
351 | readIterStruct!T(&sub, ret);
352 | } else static if (basicDBus!T) {
353 | dbus_message_iter_get_basic(iter, &ret);
354 | }
355 |
356 | dbus_message_iter_next(iter);
357 | return cast(U) ret;
358 | }
359 |
360 | void readIterTuple(Tup)(DBusMessageIter* iter, ref Tup tuple)
361 | if (isTuple!Tup && allCanDBus!(Tup.Types)) {
362 | foreach (index, T; Tup.Types) {
363 | tuple[index] = readIter!T(iter);
364 | }
365 | }
366 |
367 | void readIterStruct(S)(DBusMessageIter* iter, ref S s)
368 | if (is(S == struct) && canDBus!S) {
369 | foreach (index, T; Fields!S) {
370 | static if (isAllowedField!(s.tupleof[index])) {
371 | s.tupleof[index] = readIter!T(iter);
372 | }
373 | }
374 | }
375 |
376 | unittest {
377 | import dunit.toolkit;
378 | import ddbus.thin;
379 |
380 | Variant!T var(T)(T data) {
381 | return Variant!T(data);
382 | }
383 |
384 | Message msg = Message(busName("org.example.wow"), ObjectPath("/wut"), interfaceName("org.test.iface"), "meth");
385 | bool[] emptyB;
386 | string[string] map;
387 | map["hello"] = "world";
388 | DBusAny anyVar = DBusAny(cast(ulong) 1561);
389 | anyVar.type.assertEqual('t');
390 | anyVar.uint64.assertEqual(1561);
391 | anyVar.explicitVariant.assertEqual(false);
392 | auto tupleMember = DBusAny(tuple(Variant!int(45), Variant!ushort(5), 32,
393 | [1, 2], tuple(variant(4), 5), map));
394 | Variant!DBusAny complexVar = variant(DBusAny([
395 | "hello world": variant(DBusAny(1337)),
396 | "array value": variant(DBusAny([42, 64])),
397 | "tuple value": variant(tupleMember),
398 | "optimized binary data": variant(DBusAny(cast(ubyte[])[1, 2, 3, 4, 5, 6]))
399 | ]));
400 | complexVar.data.type.assertEqual('a');
401 | complexVar.data.signature.assertEqual("{sv}".dup);
402 | tupleMember.signature.assertEqual("(vviai(vi)a{ss})");
403 |
404 | auto args = tuple(5, true, "wow", interfaceName("methodName"), var(5.9), [6, 5], tuple(6.2, 4, [["lol"]],
405 | emptyB, var([4, 2])), map, anyVar, complexVar);
406 | msg.build(args.expand);
407 | msg.signature().assertEqual("ibssvai(diaasabv)a{ss}tv");
408 |
409 | msg.read!string().assertThrow!TypeMismatchException();
410 | msg.readTuple!(Tuple!(int, bool, double)).assertThrow!TypeMismatchException();
411 | msg.readTuple!(Tuple!(int, bool, string, InterfaceName, double))
412 | .assertEqual(tuple(5, true, "wow", interfaceName("methodName"), 5.9));
413 |
414 | msg.readTuple!(typeof(args))().assertEqual(args);
415 | DBusMessageIter iter;
416 | dbus_message_iter_init(msg.msg, &iter);
417 | readIter!int(&iter).assertEqual(5);
418 | readIter!bool(&iter).assertEqual(true);
419 | readIter!string(&iter).assertEqual("wow");
420 | readIter!InterfaceName(&iter).assertEqual(interfaceName("methodName"));
421 | readIter!double(&iter).assertEqual(5.9);
422 | readIter!(int[])(&iter).assertEqual([6, 5]);
423 | readIter!(Tuple!(double, int, string[][], bool[], Variant!(int[])))(&iter).assertEqual(
424 | tuple(6.2, 4, [["lol"]], emptyB, var([4, 2])));
425 |
426 | // There are two ways to read a dictionary, so duplicate the iterator to test both.
427 | auto iter2 = iter;
428 | readIter!(string[string])(&iter).assertEqual(["hello": "world"]);
429 | auto dict = readIter!(DictionaryEntry!(string, string)[])(&iter2);
430 | dict.length.assertEqual(1);
431 | dict[0].key.assertEqual("hello");
432 | dict[0].value.assertEqual("world");
433 |
434 | readIter!DBusAny(&iter).assertEqual(anyVar);
435 | readIter!(Variant!DBusAny)(&iter).assertEqual(complexVar);
436 | }
437 |
438 | unittest {
439 | import dunit.toolkit;
440 | import ddbus.thin;
441 |
442 | import std.variant : Algebraic;
443 |
444 | enum E : int {
445 | a,
446 | b,
447 | c
448 | }
449 |
450 | enum F : uint {
451 | x = 1,
452 | y = 2,
453 | z = 4
454 | }
455 |
456 | alias V = Algebraic!(ubyte, short, int, long, string);
457 |
458 | Message msg = Message(busName("org.example.wow"), ObjectPath("/wut"), interfaceName("org.test.iface"), "meth2");
459 | V v1 = "hello from variant";
460 | V v2 = cast(short) 345;
461 | msg.build(E.c, 4, 5u, 8u, v1, v2);
462 |
463 | DBusMessageIter iter, iter2;
464 | dbus_message_iter_init(msg.msg, &iter);
465 |
466 | readIter!E(&iter).assertEqual(E.c);
467 | readIter!E(&iter).assertThrow!InvalidValueException();
468 |
469 | iter2 = iter;
470 | readIter!F(&iter).assertThrow!InvalidValueException();
471 | readIter!(BitFlags!F)(&iter2).assertEqual(BitFlags!F(F.x, F.z));
472 |
473 | readIter!F(&iter).assertThrow!InvalidValueException();
474 | readIter!(BitFlags!F)(&iter2).assertThrow!InvalidValueException();
475 |
476 | readIter!V(&iter).assertEqual(v1);
477 | readIter!short(&iter).assertEqual(v2.get!short);
478 | }
479 |
--------------------------------------------------------------------------------
/source/ddbus/exception.d:
--------------------------------------------------------------------------------
1 | module ddbus.exception;
2 |
3 | import ddbus.c_lib;
4 |
5 | package T wrapErrors(T)(T delegate(DBusError* err) del, string file = __FILE__,
6 | size_t line = __LINE__, Throwable next = null) {
7 | DBusError error;
8 | dbus_error_init(&error);
9 | T ret = del(&error);
10 | if (dbus_error_is_set(&error)) {
11 | auto ex = new DBusException(&error, file, line, next);
12 | dbus_error_free(&error);
13 | throw ex;
14 | }
15 | return ret;
16 | }
17 |
18 | /++
19 | Thrown when a DBus error code was returned by libdbus.
20 | +/
21 | class DBusException : Exception {
22 | private this(scope DBusError* err, string file = __FILE__,
23 | size_t line = __LINE__, Throwable next = null) pure nothrow {
24 | import std.string : fromStringz;
25 |
26 | super(err.message.fromStringz().idup, file, line, next);
27 | }
28 | }
29 |
30 | /++
31 | Thrown when the signature of a message does not match the requested types or
32 | when trying to get a value from a DBusAny object that does not match the type
33 | of its actual value.
34 | +/
35 | class TypeMismatchException : Exception {
36 | package this(int expectedType, int actualType, string file = __FILE__,
37 | size_t line = __LINE__, Throwable next = null) pure nothrow @safe {
38 | string message;
39 |
40 | // dfmt off
41 | if (expectedType == 'v') {
42 | message = "The type of value at the current position in the message is"
43 | ~ " incompatible to the target variant type." ~ " Type code of the value: '"
44 | ~ cast(char) actualType ~ '\'';
45 | } else {
46 | message = "The type of value at the current position in the message does"
47 | ~ " not match the type of value to be read." ~ " Expected: '"
48 | ~ cast(char) expectedType ~ "'," ~ " Got: '" ~ cast(char) actualType ~ '\'';
49 | }
50 | // dfmt on
51 |
52 | this(message, expectedType, actualType, file, line, next);
53 | }
54 |
55 | this(string message, int expectedType, int actualType, string file = __FILE__,
56 | size_t line = __LINE__, Throwable next = null) pure nothrow @safe {
57 | _expectedType = expectedType;
58 | _actualType = actualType;
59 | super(message, file, line, next);
60 | }
61 |
62 | int expectedType() @property pure const nothrow @safe @nogc {
63 | return _expectedType;
64 | }
65 |
66 | int actualType() @property pure const nothrow @safe @nogc {
67 | return _actualType;
68 | }
69 |
70 | private:
71 | int _expectedType;
72 | int _actualType;
73 | }
74 |
75 | /++
76 | Thrown during type conversion between DBus types and D types when a value is
77 | encountered that can not be represented in the target type.
78 |
79 | This exception should not normally be thrown except when dealing with D types
80 | that have a constrained value set, such as Enums.
81 | +/
82 | class InvalidValueException : Exception {
83 | package this(Source)(Source value, string targetType, string file = __FILE__,
84 | size_t line = __LINE__, Throwable next = null) {
85 | import std.conv : to;
86 |
87 | static if (__traits(compiles, value.to!string)) {
88 | string valueString = value.to!string;
89 | } else {
90 | string valueString = "(unprintable)";
91 | }
92 |
93 | super("Value " ~ valueString ~ " cannot be represented in type " ~ targetType);
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/source/ddbus/meson.build:
--------------------------------------------------------------------------------
1 | lib_src = [
2 | 'attributes.d',
3 | 'bus.d',
4 | 'c_lib.d',
5 | 'conv.d',
6 | 'exception.d',
7 | 'package.d',
8 | 'router.d',
9 | 'simple.d',
10 | 'thin.d',
11 | 'util.d',
12 | ]
13 |
14 | ddbus_lib = library(
15 | 'ddbus',
16 | lib_src,
17 | version: meson.project_version(),
18 | install: true,
19 | include_directories: src_inc,
20 | )
21 |
22 | pkgc = import('pkgconfig')
23 |
24 | pkgc.generate(
25 | ddbus_lib,
26 | subdirs: 'd/ddbus',
27 | requires: dbus_dep,
28 | )
29 |
30 | install_headers(
31 | lib_src,
32 | subdir: 'd/ddbus/ddbus',
33 | )
34 |
--------------------------------------------------------------------------------
/source/ddbus/package.d:
--------------------------------------------------------------------------------
1 | module ddbus;
2 |
3 | public import ddbus.thin;
4 | public import ddbus.router;
5 | public import ddbus.bus;
6 | public import ddbus.simple;
7 | public import ddbus.attributes;
8 |
--------------------------------------------------------------------------------
/source/ddbus/router.d:
--------------------------------------------------------------------------------
1 | module ddbus.router;
2 |
3 | import ddbus.thin;
4 | import ddbus.c_lib;
5 | import ddbus.util;
6 | import std.string;
7 | import std.typecons;
8 | import core.memory;
9 | import std.array;
10 | import std.algorithm;
11 | import std.format;
12 |
13 | struct MessagePattern {
14 | ObjectPath path;
15 | InterfaceName iface;
16 | string method;
17 | bool signal;
18 |
19 | this(Message msg) {
20 | path = msg.path();
21 | iface = msg.iface();
22 | method = msg.member();
23 | signal = (msg.type() == MessageType.Signal);
24 | }
25 |
26 | deprecated("Use the constructor taking a ObjectPath and InterfaceName instead")
27 | this(string path, string iface, string method, bool signal = false) {
28 | this(ObjectPath(path), interfaceName(iface), method, signal);
29 | }
30 |
31 | this(ObjectPath path, InterfaceName iface, string method, bool signal = false) {
32 | this.path = path;
33 | this.iface = iface;
34 | this.method = method;
35 | this.signal = signal;
36 | }
37 |
38 | size_t toHash() const @safe nothrow {
39 | size_t hash = 0;
40 | auto stringHash = &(typeid(path).getHash);
41 | hash += stringHash(&path);
42 | hash += stringHash(&iface);
43 | hash += stringHash(&method);
44 | hash += (signal ? 1 : 0);
45 | return hash;
46 | }
47 |
48 | bool opEquals(ref const typeof(this) s) const @safe pure nothrow {
49 | return (path == s.path) && (iface == s.iface) && (method == s.method) && (signal == s.signal);
50 | }
51 | }
52 |
53 | unittest {
54 | import dunit.toolkit;
55 |
56 | auto msg = Message(busName("org.example.test"), ObjectPath("/test"), interfaceName("org.example.testing"), "testMethod");
57 | auto patt = new MessagePattern(msg);
58 | patt.assertEqual(patt);
59 | patt.signal.assertFalse();
60 | patt.path.assertEqual("/test");
61 | }
62 |
63 | struct MessageHandler {
64 | alias HandlerFunc = void delegate(Message call, Connection conn);
65 | HandlerFunc func;
66 | string[] argSig;
67 | string[] retSig;
68 | }
69 |
70 | class MessageRouter {
71 | MessageHandler[MessagePattern] callTable;
72 |
73 | bool handle(Message msg, Connection conn) {
74 | MessageType type = msg.type();
75 | if (type != MessageType.Call && type != MessageType.Signal) {
76 | return false;
77 | }
78 |
79 | auto pattern = MessagePattern(msg);
80 | // import std.stdio; debug writeln("Handling ", pattern);
81 |
82 | if (pattern.iface == "org.freedesktop.DBus.Introspectable"
83 | && pattern.method == "Introspect" && !pattern.signal) {
84 | handleIntrospect(pattern.path, msg, conn);
85 | return true;
86 | }
87 |
88 | MessageHandler* handler = (pattern in callTable);
89 | if (handler is null) {
90 | return false;
91 | }
92 |
93 | // Check for matching argument types
94 | version (DDBusNoChecking) {
95 |
96 | } else {
97 | if (!equal(join(handler.argSig), msg.signature())) {
98 | return false;
99 | }
100 | }
101 |
102 | handler.func(msg, conn);
103 | return true;
104 | }
105 |
106 | void setHandler(Ret, Args...)(MessagePattern patt, Ret delegate(Args) handler) {
107 | void handlerWrapper(Message call, Connection conn) {
108 | Tuple!Args args = call.readTuple!(Tuple!Args)();
109 | auto retMsg = call.createReturn();
110 |
111 | static if (!is(Ret == void)) {
112 | Ret ret = handler(args.expand);
113 | static if (is(Ret == Tuple!T, T...)) {
114 | retMsg.build!T(ret.expand);
115 | } else {
116 | retMsg.build(ret);
117 | }
118 | } else {
119 | handler(args.expand);
120 | }
121 |
122 | if (!patt.signal) {
123 | conn.send(retMsg);
124 | }
125 | }
126 |
127 | static string[] args = typeSigArr!Args;
128 |
129 | static if (is(Ret == void)) {
130 | static string[] ret = [];
131 | } else {
132 | static string[] ret = typeSigReturn!Ret;
133 | }
134 |
135 | // dfmt off
136 | MessageHandler handleStruct = {
137 | func: &handlerWrapper,
138 | argSig: args,
139 | retSig: ret
140 | };
141 | // dfmt on
142 |
143 | callTable[patt] = handleStruct;
144 | }
145 |
146 | static string introspectHeader = `
147 | `;
148 |
149 | deprecated("Use introspectXML(ObjectPath path) instead")
150 | string introspectXML(string path) {
151 | return introspectXML(ObjectPath(path));
152 | }
153 |
154 | string introspectXML(ObjectPath path) {
155 | // dfmt off
156 | auto methods = callTable
157 | .byKey()
158 | .filter!(a => (a.path == path) && !a.signal)
159 | .array
160 | .sort!((a, b) => a.iface < b.iface)();
161 | // dfmt on
162 |
163 | auto ifaces = methods.groupBy();
164 | auto app = appender!string;
165 | formattedWrite(app, introspectHeader, path);
166 | foreach (iface; ifaces) {
167 | formattedWrite(app, ``, cast(string)iface.front.iface);
168 |
169 | foreach (methodPatt; iface.array()) {
170 | formattedWrite(app, ``, methodPatt.method);
171 | auto handler = callTable[methodPatt];
172 |
173 | foreach (arg; handler.argSig) {
174 | formattedWrite(app, ``, arg);
175 | }
176 |
177 | foreach (arg; handler.retSig) {
178 | formattedWrite(app, ``, arg);
179 | }
180 |
181 | app.put("");
182 | }
183 |
184 | app.put("");
185 | }
186 |
187 | auto childPath = path;
188 |
189 | auto children = callTable.byKey().filter!(a => a.path.startsWith(childPath)
190 | && a.path != childPath && !a.signal)().map!((s) => s.path.chompPrefix(childPath))
191 | .map!((s) => s.value[1 .. $].findSplit("/")[0])
192 | .array().sort().uniq();
193 |
194 | foreach (child; children) {
195 | formattedWrite(app, ``, child);
196 | }
197 |
198 | app.put("");
199 | return app.data;
200 | }
201 |
202 | deprecated("Use the method taking an ObjectPath instead")
203 | void handleIntrospect(string path, Message call, Connection conn) {
204 | handleIntrospect(ObjectPath(path), call, conn);
205 | }
206 |
207 | void handleIntrospect(ObjectPath path, Message call, Connection conn) {
208 | auto retMsg = call.createReturn();
209 | retMsg.build(introspectXML(path));
210 | conn.sendBlocking(retMsg);
211 | }
212 | }
213 |
214 | extern (C) private DBusHandlerResult filterFunc(DBusConnection* dConn,
215 | DBusMessage* dMsg, void* routerP) {
216 | MessageRouter router = cast(MessageRouter) routerP;
217 | dbus_message_ref(dMsg);
218 | Message msg = Message(dMsg);
219 | dbus_connection_ref(dConn);
220 | Connection conn = Connection(dConn);
221 | bool handled = router.handle(msg, conn);
222 |
223 | if (handled) {
224 | return DBusHandlerResult.DBUS_HANDLER_RESULT_HANDLED;
225 | } else {
226 | return DBusHandlerResult.DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
227 | }
228 | }
229 |
230 | extern (C) private void unrootUserData(void* userdata) {
231 | GC.removeRoot(userdata);
232 | }
233 |
234 | void registerRouter(Connection conn, MessageRouter router) {
235 | void* routerP = cast(void*) router;
236 | GC.addRoot(routerP);
237 | dbus_connection_add_filter(conn.conn, &filterFunc, routerP, &unrootUserData);
238 | }
239 |
240 | unittest {
241 | import dunit.toolkit;
242 |
243 | import std.typecons : BitFlags;
244 | import std.variant : Algebraic;
245 |
246 | auto router = new MessageRouter();
247 | // set up test messages
248 | MessagePattern patt = MessagePattern(ObjectPath("/root"), interfaceName("ca.thume.test"), "test");
249 | router.setHandler!(int, int)(patt, (int p) { return 6; });
250 | patt = MessagePattern(ObjectPath("/root"), interfaceName("ca.thume.tester"), "lolwut");
251 | router.setHandler!(void, int, string)(patt, (int p, string p2) { });
252 | patt = MessagePattern(ObjectPath("/root/wat"), interfaceName("ca.thume.tester"), "lolwut");
253 | router.setHandler!(int, int)(patt, (int p) { return 6; });
254 | patt = MessagePattern(ObjectPath("/root/bar"), interfaceName("ca.thume.tester"), "lolwut");
255 | router.setHandler!(Variant!DBusAny, int)(patt, (int p) {
256 | return variant(DBusAny(p));
257 | });
258 | patt = MessagePattern(ObjectPath("/root/foo"), interfaceName("ca.thume.tester"), "lolwut");
259 | router.setHandler!(Tuple!(string, string, int), int,
260 | Variant!DBusAny)(patt, (int p, Variant!DBusAny any) {
261 | Tuple!(string, string, int) ret;
262 | ret[0] = "a";
263 | ret[1] = "b";
264 | ret[2] = p;
265 | return ret;
266 | });
267 | patt = MessagePattern(ObjectPath("/troll"), interfaceName("ca.thume.tester"), "wow");
268 | router.setHandler!(void)(patt, { return; });
269 |
270 | patt = MessagePattern(ObjectPath("/root/fancy"), interfaceName("ca.thume.tester"), "crazyTest");
271 | enum F : ushort {
272 | a = 1,
273 | b = 8,
274 | c = 16
275 | }
276 |
277 | struct S {
278 | ubyte b;
279 | ulong ul;
280 | F f;
281 | }
282 |
283 | router.setHandler!(int)(patt, (Algebraic!(ushort, BitFlags!F, S) v) {
284 | if (v.type is typeid(ushort) || v.type is typeid(BitFlags!F)) {
285 | return v.coerce!int;
286 | } else if (v.type is typeid(S)) {
287 | auto s = v.get!S;
288 | final switch (s.f) {
289 | case F.a:
290 | return s.b;
291 | case F.b:
292 | return cast(int) s.ul;
293 | case F.c:
294 | return cast(int) s.ul + s.b;
295 | }
296 | }
297 |
298 | assert(false);
299 | });
300 |
301 | static assert(!__traits(compiles, {
302 | patt = MessagePattern("/root/bar", "ca.thume.tester", "lolwut");
303 | router.setHandler!(void, DBusAny)(patt, (DBusAny wrongUsage) { return; });
304 | }));
305 |
306 | // TODO: these tests rely on nondeterministic hash map ordering
307 | static string introspectResult = `
308 | `;
309 | router.introspectXML(ObjectPath("/root")).assertEqual(introspectResult);
310 | static string introspectResult2 = `
311 | `;
312 | router.introspectXML(ObjectPath("/root/foo")).assertEqual(introspectResult2);
313 | static string introspectResult3 = `
314 | `;
315 | router.introspectXML(ObjectPath("/root/fancy")).assertEqual(introspectResult3);
316 | router.introspectXML(ObjectPath("/"))
317 | .assertEndsWith(``);
318 | }
319 |
--------------------------------------------------------------------------------
/source/ddbus/simple.d:
--------------------------------------------------------------------------------
1 | module ddbus.simple;
2 |
3 | import ddbus.thin;
4 | import ddbus.util;
5 | import ddbus.c_lib;
6 | import ddbus.router;
7 | import std.string;
8 | import std.traits;
9 |
10 | class PathIface {
11 | this(Connection conn, BusName dest, ObjectPath path, InterfaceName iface) {
12 | this.conn = conn;
13 | this.dest = dest.toStringz();
14 | this.path = path.value.toStringz();
15 | this.iface = iface.toStringz();
16 | }
17 |
18 | deprecated("Use the constructor taking BusName, ObjectPath and InterfaceName instead")
19 | this(Connection conn, string dest, ObjectPath path, string iface) {
20 | this(conn, busName(dest), path, interfaceName(iface));
21 | }
22 |
23 | deprecated("Use the constructor taking BusName, ObjectPath and InterfaceName instead")
24 | this(Connection conn, string dest, string path, string iface) {
25 | this(conn, busName(dest), ObjectPath(path), interfaceName(iface));
26 | }
27 |
28 | Ret call(Ret, Args...)(string meth, Args args)
29 | if (allCanDBus!Args && canDBus!Ret) {
30 | Message msg = Message(dbus_message_new_method_call(dest, path, iface, meth.toStringz()));
31 | msg.build(args);
32 | Message ret = conn.sendWithReplyBlocking(msg);
33 | return ret.read!Ret();
34 | }
35 |
36 | void call(Ret, Args...)(string meth, Args args)
37 | if (allCanDBus!Args && is(Ret == void)) {
38 | Message msg = Message(dbus_message_new_method_call(dest, path, iface, meth.toStringz()));
39 | msg.build(args);
40 | conn.sendBlocking(msg);
41 | }
42 |
43 | Message opDispatch(string meth, Args...)(Args args) {
44 | Message msg = Message(dbus_message_new_method_call(dest, path, iface, meth.toStringz()));
45 | msg.build(args);
46 | return conn.sendWithReplyBlocking(msg);
47 | }
48 |
49 | Connection conn;
50 | const(char)* dest;
51 | const(char)* path;
52 | const(char)* iface;
53 | }
54 |
55 | unittest {
56 | import dunit.toolkit;
57 |
58 | Connection conn = connectToBus();
59 | PathIface obj = new PathIface(conn, busName("org.freedesktop.DBus"),
60 | ObjectPath("/org/freedesktop/DBus"), interfaceName("org.freedesktop.DBus"));
61 | auto names = obj.GetNameOwner(interfaceName("org.freedesktop.DBus")).to!BusName();
62 | names.assertEqual(busName("org.freedesktop.DBus"));
63 | obj.call!BusName("GetNameOwner", interfaceName("org.freedesktop.DBus")).assertEqual(busName("org.freedesktop.DBus"));
64 | }
65 |
66 | enum SignalMethod;
67 |
68 | deprecated("Use the registerMethods overload taking an ObjectPath and InterfaceName instead")
69 | void registerMethods(T : Object)(MessageRouter router, string path, string iface, T obj) {
70 | registerMethods(router, ObjectPath(path), interfaceName(iface), obj);
71 | }
72 |
73 | /**
74 | Registers all *possible* methods of an object in a router.
75 | It will not register methods that use types that ddbus can't handle.
76 |
77 | The implementation is rather hacky and uses the compiles trait to check for things
78 | working so if some methods randomly don't seem to be added, you should probably use
79 | setHandler on the router directly. It is also not efficient and creates a closure for every method.
80 |
81 | TODO: replace this with something that generates a wrapper class who's methods take and return messages
82 | and basically do what MessageRouter.setHandler does but avoiding duplication. Then this DBusWrapper!Class
83 | could be instantiated with any object efficiently and placed in the router table with minimal duplication.
84 | */
85 | void registerMethods(T : Object)(MessageRouter router, ObjectPath path, InterfaceName iface, T obj) {
86 | MessagePattern patt = MessagePattern(path, iface, "", false);
87 | foreach (member; __traits(allMembers, T)) {
88 | // dfmt off
89 | static if (__traits(compiles, __traits(getOverloads, obj, member))
90 | && __traits(getOverloads, obj, member).length > 0
91 | && __traits(compiles, router.setHandler(patt, &__traits(getOverloads, obj, member)[0]))) {
92 | patt.method = member;
93 | patt.signal = hasUDA!(__traits(getOverloads, obj, member)[0], SignalMethod);
94 | router.setHandler(patt, &__traits(getOverloads, obj, member)[0]);
95 | }
96 | // dfmt on
97 | }
98 | }
99 |
100 | unittest {
101 | import dunit.toolkit;
102 |
103 | class Tester {
104 | int lol(int x, string s, string[string] map, Variant!DBusAny any) {
105 | return 5;
106 | }
107 |
108 | void wat() {
109 | }
110 |
111 | @SignalMethod void signalRecv() {
112 | }
113 | }
114 |
115 | auto o = new Tester;
116 | auto router = new MessageRouter;
117 | registerMethods(router, ObjectPath("/"), interfaceName("ca.thume.test"), o);
118 | MessagePattern patt = MessagePattern(ObjectPath("/"), interfaceName("ca.thume.test"), "wat");
119 | router.callTable.assertHasKey(patt);
120 | patt.method = "signalRecv";
121 | patt.signal = true;
122 | router.callTable.assertHasKey(patt);
123 | patt.method = "lol";
124 | patt.signal = false;
125 | router.callTable.assertHasKey(patt);
126 | auto res = router.callTable[patt];
127 | res.argSig.assertEqual(["i", "s", "a{ss}", "v"]);
128 | res.retSig.assertEqual(["i"]);
129 | }
130 |
--------------------------------------------------------------------------------
/source/ddbus/thin.d:
--------------------------------------------------------------------------------
1 | /// Thin OO wrapper around DBus types
2 | module ddbus.thin;
3 |
4 | import core.time : Duration;
5 |
6 | import ddbus.attributes : isAllowedField;
7 | import ddbus.c_lib;
8 | import ddbus.conv;
9 | import ddbus.exception : TypeMismatchException;
10 | import ddbus.util;
11 |
12 | import std.meta : ApplyRight, Filter, staticIndexOf;
13 | import std.string;
14 | import std.typecons;
15 | import std.exception;
16 | import std.traits;
17 | import std.conv;
18 | import std.range;
19 | import std.algorithm;
20 |
21 | // This import is public for backwards compatibility
22 | public import ddbus.exception : wrapErrors, DBusException;
23 |
24 | struct ObjectPath {
25 | enum root = ObjectPath("/");
26 |
27 | private string _value;
28 |
29 | this(string objPath) pure @safe {
30 | enforce(isValid(objPath));
31 | _value = objPath;
32 | }
33 |
34 | string toString() const {
35 | return _value;
36 | }
37 |
38 | /++
39 | Returns the string representation of this ObjectPath.
40 | +/
41 | string value() const pure @nogc nothrow @safe {
42 | return _value;
43 | }
44 |
45 | size_t toHash() const pure @nogc nothrow @trusted {
46 | return hashOf(_value);
47 | }
48 |
49 | T opCast(T : ObjectPath)() const pure @nogc nothrow @safe {
50 | return this;
51 | }
52 |
53 | T opCast(T : string)() const pure @nogc nothrow @safe {
54 | return value;
55 | }
56 |
57 | bool opEquals(ref const typeof(this) b) const pure @nogc nothrow @safe {
58 | return _value == b._value;
59 | }
60 |
61 | bool opEquals(const typeof(this) b) const pure @nogc nothrow @safe {
62 | return _value == b._value;
63 | }
64 |
65 | bool opEquals(string b) const pure @nogc nothrow @safe {
66 | return _value == b;
67 | }
68 |
69 | ObjectPath opBinary(string op : "~")(string rhs) const pure @safe {
70 | if (!rhs.startsWith("/")) {
71 | return opBinary!"~"(ObjectPath("/" ~ rhs));
72 | } else {
73 | return opBinary!"~"(ObjectPath(rhs));
74 | }
75 | }
76 |
77 | ObjectPath opBinary(string op : "~")(ObjectPath rhs) const pure @safe
78 | in {
79 | assert(ObjectPath.isValid(_value) && ObjectPath.isValid(rhs._value));
80 | }
81 | out (v) {
82 | assert(ObjectPath.isValid(v._value));
83 | }
84 | do {
85 | ObjectPath ret;
86 |
87 | if (_value == "/") {
88 | ret._value = rhs._value;
89 | } else {
90 | ret._value = _value ~ rhs._value;
91 | }
92 |
93 | return ret;
94 | }
95 |
96 | void opOpAssign(string op : "~")(string rhs) pure @safe {
97 | _value = opBinary!"~"(rhs)._value;
98 | }
99 |
100 | void opOpAssign(string op : "~")(ObjectPath rhs) pure @safe {
101 | _value = opBinary!"~"(rhs)._value;
102 | }
103 |
104 | bool startsWith(ObjectPath withThat) pure @nogc nothrow @safe {
105 | if (withThat._value == "/")
106 | return true;
107 | else if (_value == "/")
108 | return false;
109 | else
110 | return _value.representation.splitter('/').startsWith(withThat._value.representation.splitter('/'));
111 | }
112 |
113 | /// Removes a prefix from this path and returns the remainder. Keeps leading slashes.
114 | /// Returns this unmodified if the prefix doesn't match.
115 | ObjectPath chompPrefix(ObjectPath prefix) pure @nogc nothrow @safe {
116 | if (prefix._value == "/" || !startsWith(prefix))
117 | return this;
118 | else if (prefix._value == _value)
119 | return ObjectPath.root;
120 | else
121 | return ObjectPath.assumePath(_value[prefix._value.length .. $]);
122 | }
123 |
124 | /++
125 | Returns: `false` for empty strings or strings that don't match the
126 | pattern `(/[0-9A-Za-z_]+)+|/`.
127 | +/
128 | static bool isValid(string objPath) pure @nogc nothrow @safe {
129 | import std.ascii : isAlphaNum;
130 |
131 | if (!objPath.length) {
132 | return false;
133 | }
134 |
135 | if (objPath == "/") {
136 | return true;
137 | }
138 |
139 | if (objPath[0] != '/' || objPath[$ - 1] == '/') {
140 | return false;
141 | }
142 |
143 | // .representation to avoid unicode exceptions -> @nogc & nothrow
144 | return objPath.representation.splitter('/').drop(1).all!(a => a.length
145 | && a.all!(c => c.isAlphaNum || c == '_'));
146 | }
147 |
148 | /// Does an unsafe assignment to an ObjectPath.
149 | static ObjectPath assumePath(string path) pure @nogc nothrow @safe {
150 | ObjectPath ret;
151 | ret._value = path;
152 | return ret;
153 | }
154 | }
155 |
156 | /// Serves as typesafe alias. Instances should be created using busName instead of casting.
157 | /// It prevents accidental usage of bus names in other string parameter fields and makes the API clearer.
158 | enum BusName : string {
159 | none = null
160 | }
161 |
162 | /// Casts a bus name argument to a BusName type. May include additional validation in the future.
163 | BusName busName(string name) pure @nogc nothrow @safe {
164 | return cast(BusName) name;
165 | }
166 |
167 | /// Serves as typesafe alias. Instances should be created using interfaceName instead of casting.
168 | /// It prevents accidental usage of interface paths in other string parameter fields and makes the API clearer.
169 | enum InterfaceName : string {
170 | none = null
171 | }
172 |
173 | /// Casts a interface path argument to an InterfaceName type. May include additional validation in the future.
174 | InterfaceName interfaceName(string path) pure @nogc nothrow @safe {
175 | return cast(InterfaceName) path;
176 | }
177 |
178 | /// Serving as a typesafe alias for a FileDescriptor.
179 | enum FileDescriptor : uint {
180 | none = uint.max,
181 | stdin = 0,
182 | stdout = 1,
183 | stderr = 2
184 | }
185 |
186 | /// Casts an integer to a FileDescriptor.
187 | FileDescriptor fileDescriptor(uint fileNo) pure @nogc nothrow @safe {
188 | return cast(FileDescriptor) fileNo;
189 | }
190 |
191 | unittest {
192 | import dunit.toolkit;
193 |
194 | ObjectPath("some.invalid/object_path").assertThrow();
195 | ObjectPath("/path/with/TrailingSlash/").assertThrow();
196 | ObjectPath("/path/without/TrailingSlash").assertNotThrown();
197 | string path = "/org/freedesktop/DBus";
198 | auto obj = ObjectPath(path);
199 | obj.value.assertEqual(path);
200 | obj.toHash().assertEqual(path.hashOf);
201 |
202 | ObjectPath("/some/path").startsWith(ObjectPath("/some")).assertTrue();
203 | ObjectPath("/some/path").startsWith(ObjectPath("/path")).assertFalse();
204 | ObjectPath("/some/path").startsWith(ObjectPath("/")).assertTrue();
205 | ObjectPath("/").startsWith(ObjectPath("/")).assertTrue();
206 | ObjectPath("/").startsWith(ObjectPath("/some/path")).assertFalse();
207 |
208 | ObjectPath("/some/path").chompPrefix(ObjectPath("/some")).assertEqual(ObjectPath("/path"));
209 | ObjectPath("/some/path").chompPrefix(ObjectPath("/bar")).assertEqual(ObjectPath("/some/path"));
210 | ObjectPath("/some/path").chompPrefix(ObjectPath("/")).assertEqual(ObjectPath("/some/path"));
211 | ObjectPath("/some/path").chompPrefix(ObjectPath("/some/path")).assertEqual(ObjectPath("/"));
212 | ObjectPath("/some/path").chompPrefix(ObjectPath("/some/path/extra")).assertEqual(ObjectPath("/some/path"));
213 | ObjectPath("/").chompPrefix(ObjectPath("/some")).assertEqual(ObjectPath("/"));
214 | ObjectPath("/").chompPrefix(ObjectPath("/")).assertEqual(ObjectPath("/"));
215 | }
216 |
217 | unittest {
218 | import dunit.toolkit;
219 |
220 | ObjectPath a = ObjectPath("/org/freedesktop");
221 | a.assertEqual(ObjectPath("/org/freedesktop"));
222 | a ~= ObjectPath("/UPower");
223 | a.assertEqual(ObjectPath("/org/freedesktop/UPower"));
224 | a ~= "Device";
225 | a.assertEqual(ObjectPath("/org/freedesktop/UPower/Device"));
226 | (a ~ "0").assertEqual(ObjectPath("/org/freedesktop/UPower/Device/0"));
227 | a.assertEqual(ObjectPath("/org/freedesktop/UPower/Device"));
228 | }
229 |
230 | /// Structure allowing typeless parameters
231 | struct DBusAny {
232 | /// DBus type of the value (never 'v'), see typeSig!T
233 | int type;
234 | /// Child signature for Arrays & Tuples
235 | string signature;
236 | /// If true, this value will get serialized as variant value, otherwise it is serialized like it wasn't in a DBusAny wrapper.
237 | /// Same functionality as Variant!T but with dynamic types if true.
238 | bool explicitVariant;
239 |
240 | union {
241 | ///
242 | ubyte uint8;
243 | ///
244 | short int16;
245 | ///
246 | ushort uint16;
247 | ///
248 | int int32;
249 | ///
250 | uint uint32;
251 | ///
252 | long int64;
253 | ///
254 | ulong uint64;
255 | ///
256 | double float64;
257 | ///
258 | string str;
259 | ///
260 | bool boolean;
261 | ///
262 | ObjectPath obj;
263 | ///
264 | DBusAny[] array;
265 | ///
266 | alias tuple = array;
267 | ///
268 | DictionaryEntry!(DBusAny, DBusAny)* entry;
269 | ///
270 | ubyte[] binaryData;
271 | ///
272 | FileDescriptor fd;
273 | }
274 |
275 | /++
276 | Manually creates a DBusAny object using a type, signature and explicit
277 | variant specifier.
278 |
279 | Direct use of this constructor from user code should be avoided.
280 | +/
281 | this(int type, string signature, bool explicit) {
282 | this.type = type;
283 | this.signature = signature;
284 | this.explicitVariant = explicit;
285 | }
286 |
287 | /++
288 | Automatically creates a DBusAny object with fitting parameters from a D
289 | type or Variant!T.
290 |
291 | Pass a `Variant!T` to make this an explicit variant.
292 | +/
293 | this(T)(T value) {
294 | static if (is(T == byte) || is(T == ubyte)) {
295 | this(typeCode!ubyte, null, false);
296 | uint8 = cast(ubyte) value;
297 | } else static if (is(T == short)) {
298 | this(typeCode!short, null, false);
299 | int16 = cast(short) value;
300 | } else static if (is(T == ushort)) {
301 | this(typeCode!ushort, null, false);
302 | uint16 = cast(ushort) value;
303 | } else static if (is(T == int)) {
304 | this(typeCode!int, null, false);
305 | int32 = cast(int) value;
306 | } else static if (is(T == FileDescriptor)) {
307 | this(typeCode!FileDescriptor, null, false);
308 | fd = cast(FileDescriptor) value;
309 | } else static if (is(T == uint)) {
310 | this(typeCode!uint, null, false);
311 | uint32 = cast(uint) value;
312 | } else static if (is(T == long)) {
313 | this(typeCode!long, null, false);
314 | int64 = cast(long) value;
315 | } else static if (is(T == ulong)) {
316 | this(typeCode!ulong, null, false);
317 | uint64 = cast(ulong) value;
318 | } else static if (is(T == double)) {
319 | this(typeCode!double, null, false);
320 | float64 = cast(double) value;
321 | } else static if (isSomeString!T) {
322 | this(typeCode!string, null, false);
323 | str = value.to!string;
324 | } else static if (is(T == bool)) {
325 | this(typeCode!bool, null, false);
326 | boolean = cast(bool) value;
327 | } else static if (is(T == ObjectPath)) {
328 | this(typeCode!ObjectPath, null, false);
329 | obj = value;
330 | } else static if (is(T == Variant!R, R)) {
331 | static if (is(R == DBusAny)) {
332 | type = value.data.type;
333 | signature = value.data.signature;
334 | explicitVariant = true;
335 | if (type == 'a' || type == 'r') {
336 | if (signature == ['y']) {
337 | binaryData = value.data.binaryData;
338 | } else {
339 | array = value.data.array;
340 | }
341 | } else if (type == 's') {
342 | str = value.data.str;
343 | } else if (type == 'e') {
344 | entry = value.data.entry;
345 | } else {
346 | uint64 = value.data.uint64;
347 | }
348 | } else {
349 | this(value.data);
350 | explicitVariant = true;
351 | }
352 | } else static if (is(T : DictionaryEntry!(K, V), K, V)) {
353 | this('e', null, false);
354 | entry = new DictionaryEntry!(DBusAny, DBusAny)();
355 | static if (is(K == DBusAny)) {
356 | entry.key = value.key;
357 | } else {
358 | entry.key = DBusAny(value.key);
359 | }
360 | static if (is(V == DBusAny)) {
361 | entry.value = value.value;
362 | } else {
363 | entry.value = DBusAny(value.value);
364 | }
365 | } else static if (is(T == ubyte[]) || is(T == byte[])) {
366 | this('a', ['y'], false);
367 | binaryData = cast(ubyte[]) value;
368 | } else static if (isInputRange!T) {
369 | this.type = 'a';
370 |
371 | static assert(!is(ElementType!T == DBusAny),
372 | "Array must consist of the same type, use Variant!DBusAny or DBusAny(tuple(...)) instead");
373 |
374 | static assert(.typeSig!(ElementType!T) != "y");
375 |
376 | this.signature = .typeSig!(ElementType!T);
377 | this.explicitVariant = false;
378 |
379 | foreach (elem; value) {
380 | array ~= DBusAny(elem);
381 | }
382 | } else static if (isTuple!T) {
383 | this.type = 'r';
384 | this.signature = ['('];
385 | this.explicitVariant = false;
386 |
387 | foreach (index, R; value.Types) {
388 | auto var = DBusAny(value[index]);
389 | tuple ~= var;
390 |
391 | if (var.explicitVariant) {
392 | this.signature ~= 'v';
393 | } else {
394 | if (var.type != 'r') {
395 | this.signature ~= cast(char) var.type;
396 | }
397 |
398 | if (var.type == 'a' || var.type == 'r') {
399 | this.signature ~= var.signature;
400 | }
401 | }
402 | }
403 |
404 | this.signature ~= ')';
405 | } else static if (is(T == struct) && canDBus!T) {
406 | this.type = 'r';
407 | this.signature = ['('];
408 | this.explicitVariant = false;
409 | foreach (index, R; Fields!T) {
410 | static if (isAllowedField!(value.tupleof[index])) {
411 | auto var = DBusAny(value.tupleof[index]);
412 | tuple ~= var;
413 | if (var.explicitVariant)
414 | this.signature ~= 'v';
415 | else {
416 | if (var.type != 'r')
417 | this.signature ~= cast(char) var.type;
418 | if (var.type == 'a' || var.type == 'r')
419 | this.signature ~= var.signature;
420 | }
421 | }
422 | }
423 | this.signature ~= ')';
424 | } else static if (isAssociativeArray!T) {
425 | this(value.byDictionaryEntries);
426 | } else {
427 | static assert(false, T.stringof ~ " not convertible to a Variant");
428 | }
429 | }
430 |
431 | ///
432 | string toString() const {
433 | string valueStr;
434 | switch (type) {
435 | case typeCode!ubyte:
436 | valueStr = uint8.to!string;
437 | break;
438 | case typeCode!short:
439 | valueStr = int16.to!string;
440 | break;
441 | case typeCode!ushort:
442 | valueStr = uint16.to!string;
443 | break;
444 | case typeCode!int:
445 | valueStr = int32.to!string;
446 | break;
447 | case typeCode!uint:
448 | valueStr = uint32.to!string;
449 | break;
450 | case typeCode!long:
451 | valueStr = int64.to!string;
452 | break;
453 | case typeCode!ulong:
454 | valueStr = uint64.to!string;
455 | break;
456 | case typeCode!double:
457 | valueStr = float64.to!string;
458 | break;
459 | case typeCode!string:
460 | valueStr = '"' ~ str ~ '"';
461 | break;
462 | case typeCode!ObjectPath:
463 | valueStr = '"' ~ obj.to!string ~ '"';
464 | break;
465 | case typeCode!bool:
466 | valueStr = boolean ? "true" : "false";
467 | break;
468 | case 'a':
469 | import std.digest : toHexString;
470 |
471 | if (signature == ['y']) {
472 | valueStr = "binary(" ~ binaryData.toHexString ~ ')';
473 | } else {
474 | valueStr = '[' ~ array.map!(a => a.toString).join(", ") ~ ']';
475 | }
476 |
477 | break;
478 | case 'r':
479 | valueStr = '(' ~ tuple.map!(a => a.toString).join(", ") ~ ')';
480 | break;
481 | case 'e':
482 | valueStr = entry.key.toString ~ ": " ~ entry.value.toString;
483 | break;
484 | case 'h':
485 | valueStr = int32.to!string;
486 | break;
487 | default:
488 | valueStr = "unknown";
489 | break;
490 | }
491 |
492 | return "DBusAny(" ~ cast(char) type ~ ", \"" ~ signature.idup ~ "\", " ~ (explicitVariant
493 | ? "explicit" : "implicit") ~ ", " ~ valueStr ~ ")";
494 | }
495 |
496 | /++
497 | Get the value stored in the DBusAny object.
498 |
499 | Parameters:
500 | T = The requested type. The currently stored value must match the
501 | requested type exactly.
502 |
503 | Returns:
504 | The current value of the DBusAny object.
505 |
506 | Throws:
507 | TypeMismatchException if the DBus type of the current value of the
508 | DBusAny object is not the same as the DBus type used to represent T.
509 | +/
510 | U get(U)() @property const
511 | if (staticIndexOf!(Unqual!U, BasicTypes) >= 0) {
512 | alias T = Unqual!U;
513 | enforce(type == typeCode!T, new TypeMismatchException(
514 | "Cannot get a " ~ T.stringof ~ " from a DBusAny with" ~ " a value of DBus type '" ~ typeSig ~ "'.",
515 | typeCode!T, type));
516 |
517 | static if (is(T == FileDescriptor)) {
518 | return cast(U) fd;
519 | } else static if (isIntegral!T) {
520 | enum memberName = (isUnsigned!T ? "uint" : "int") ~ (T.sizeof * 8).to!string;
521 | return cast(U) __traits(getMember, this, memberName);
522 | } else static if (is(T == double)) {
523 | return cast(U) float64;
524 | } else static if (is(T == string)) {
525 | return cast(U) str;
526 | } else static if (is(T == InterfaceName) || is(T == BusName)) {
527 | return cast(U) str;
528 | } else static if (is(T == ObjectPath)) {
529 | return cast(U) obj;
530 | } else static if (is(T == bool)) {
531 | return cast(U) boolean;
532 | } else {
533 | static assert(false);
534 | }
535 | }
536 |
537 | /// ditto
538 | T get(T)() @property const
539 | if (is(T == const(DBusAny)[])) {
540 | enforce((type == 'a' && signature != "y") || type == 'r', new TypeMismatchException(
541 | "Cannot get a " ~ T.stringof ~ " from a DBusAny with" ~ " a value of DBus type '" ~ this.typeSig ~ "'.",
542 | 'a', type));
543 |
544 | return array;
545 | }
546 |
547 | /// ditto
548 | T get(T)() @property const
549 | if (is(T == const(ubyte)[])) {
550 | enforce(type == 'a' && signature == "y", new TypeMismatchException(
551 | "Cannot get a " ~ T.stringof ~ " from a DBusAny with" ~ " a value of DBus type '" ~ this.typeSig ~ "'.",
552 | 'a', type));
553 |
554 | return binaryData;
555 | }
556 |
557 | /// If the value is an array of DictionaryEntries this will return a HashMap
558 | deprecated("Please use to!(V[K])") DBusAny[DBusAny] toAA() {
559 | enforce(type == 'a' && signature && signature[0] == '{');
560 | DBusAny[DBusAny] aa;
561 |
562 | foreach (val; array) {
563 | enforce(val.type == 'e');
564 | aa[val.entry.key] = val.entry.value;
565 | }
566 |
567 | return aa;
568 | }
569 |
570 | /++
571 | Get the DBus type signature of the value stored in the DBusAny object.
572 |
573 | Returns:
574 | The type signature of the value stored in this DBusAny object.
575 | +/
576 | string typeSig() @property const pure nothrow @safe {
577 | if (type == 'a') {
578 | return "a" ~ signature;
579 | } else if (type == 'r') {
580 | return signature;
581 | } else if (type == 'e') {
582 | return () @trusted{
583 | return "{" ~ entry.key.signature ~ entry.value.signature ~ "}";
584 | }();
585 | } else {
586 | return [cast(char) type];
587 | }
588 | }
589 |
590 | /++
591 | Converts a basic type, a tuple or an array to the D type with type checking.
592 |
593 | Tuples can be converted to an array of DBusAny, but not to any other array.
594 | +/
595 | T to(T)() @property const pure {
596 | // Just use `get` if possible
597 | static if (canDBus!T && __traits(compiles, get!T)) {
598 | if (this.typeSig == .typeSig!T)
599 | return get!T;
600 | }
601 |
602 | // If we get here, we need some type conversion
603 | static if (is(T == Variant!R, R)) {
604 | static if (is(R == DBusAny)) {
605 | auto v = to!R;
606 | v.explicitVariant = false;
607 | return Variant!R(v);
608 | } else {
609 | return Variant!R(to!R);
610 | }
611 | } else static if (is(T == DBusAny)) {
612 | return this;
613 | } else {
614 | // In here are all static if blocks that may fall through to the throw
615 | // statement at the bottom of this block.
616 |
617 | static if (is(T == DictionaryEntry!(K, V), K, V)) {
618 | if (type == 'e') {
619 | static if (is(T == typeof(entry))) {
620 | return entry;
621 | } else {
622 | return DictionaryEntry(entry.key.to!K, entry.value.to!V);
623 | }
624 | }
625 | } else static if (isAssociativeArray!T) {
626 | if (type == 'a' && (!array.length || array[0].type == 'e')) {
627 | alias K = Unqual!(KeyType!T);
628 | alias V = Unqual!(ValueType!T);
629 | V[K] ret;
630 |
631 | foreach (pair; array) {
632 | assert(pair.type == 'e');
633 | ret[pair.entry.key.to!K] = pair.entry.value.to!V;
634 | }
635 |
636 | return cast(T) ret;
637 | }
638 | } else static if (isDynamicArray!T && !isSomeString!T) {
639 | alias E = Unqual!(ElementType!T);
640 |
641 | if (typeSig == "ay") {
642 | auto data = get!(const(ubyte)[]);
643 | static if (is(E == ubyte) || is(E == byte)) {
644 | return cast(T) data.dup;
645 | } else {
646 | return cast(T) data.map!(elem => elem.to!E).array;
647 | }
648 | } else if (type == 'a' || (type == 'r' && is(E == DBusAny))) {
649 | return cast(T) get!(const(DBusAny)[]).map!(elem => elem.to!E).array;
650 | }
651 | } else static if (isTuple!T) {
652 | if (type == 'r') {
653 | T ret;
654 |
655 | foreach (i, T; ret.Types) {
656 | ret[i] = tuple[i].to!T;
657 | }
658 |
659 | return ret;
660 | }
661 | } else static if (is(T == struct) && canDBus!T) {
662 | if (type == 'r') {
663 | T ret;
664 | size_t j;
665 |
666 | foreach (i, F; Fields!T) {
667 | static if (isAllowedField!(ret.tupleof[i])) {
668 | ret.tupleof[i] = tuple[j++].to!F;
669 | }
670 | }
671 |
672 | return ret;
673 | }
674 | } else {
675 | alias isPreciselyConvertible = ApplyRight!(isImplicitlyConvertible, T);
676 |
677 | template isUnpreciselyConvertible(S) {
678 | enum isUnpreciselyConvertible = !isPreciselyConvertible!S
679 | && __traits(compiles, get!S.to!T);
680 | }
681 |
682 | // Try to be precise
683 | foreach (B; Filter!(isPreciselyConvertible, BasicTypes)) {
684 | if (type == typeCode!B)
685 | return get!B;
686 | }
687 |
688 | // Try to convert
689 | foreach (B; Filter!(isUnpreciselyConvertible, BasicTypes)) {
690 | if (type == typeCode!B)
691 | return get!B.to!T;
692 | }
693 | }
694 |
695 | throw new ConvException("Cannot convert from DBus type '" ~ this.typeSig ~ "' to "
696 | ~ T.stringof);
697 | }
698 | }
699 |
700 | bool opEquals(ref in DBusAny b) const {
701 | if (b.type != type || b.explicitVariant != explicitVariant) {
702 | return false;
703 | }
704 |
705 | if ((type == 'a' || type == 'r') && b.signature != signature) {
706 | return false;
707 | }
708 |
709 | if (type == 'a' && signature == ['y']) {
710 | return binaryData == b.binaryData;
711 | }
712 |
713 | if (type == 'a') {
714 | return array == b.array;
715 | } else if (type == 'r') {
716 | return tuple == b.tuple;
717 | } else if (type == 's') {
718 | return str == b.str;
719 | } else if (type == 'o') {
720 | return obj == b.obj;
721 | } else if (type == 'e') {
722 | return entry == b.entry || (entry && b.entry && *entry == *b.entry);
723 | } else {
724 | return uint64 == b.uint64;
725 | }
726 | }
727 |
728 | /// Returns: true if this variant is an integer.
729 | bool typeIsIntegral() const @property {
730 | return type.dbusIsIntegral;
731 | }
732 |
733 | /// Returns: true if this variant is a floating point value.
734 | bool typeIsFloating() const @property {
735 | return type.dbusIsFloating;
736 | }
737 |
738 | /// Returns: true if this variant is an integer or a floating point value.
739 | bool typeIsNumeric() const @property {
740 | return type.dbusIsNumeric;
741 | }
742 | }
743 |
744 | unittest {
745 | import dunit.toolkit;
746 |
747 | DBusAny set(string member, T)(DBusAny v, T value) {
748 | mixin("v." ~ member ~ " = value;");
749 | return v;
750 | }
751 |
752 | void test(bool testGet = false, T)(T value, DBusAny b) {
753 | assertEqual(DBusAny(value), b);
754 | assertEqual(b.to!T, value);
755 | static if (testGet)
756 | assertEqual(b.get!T, value);
757 | b.toString();
758 | }
759 |
760 | test!true(cast(ubyte) 184, set!"uint8"(DBusAny('y', null, false), cast(ubyte) 184));
761 | test(cast(byte) 184, set!"uint8"(DBusAny('y', null, false), cast(byte) 184));
762 | test!true(cast(short) 184, set!"int16"(DBusAny('n', null, false), cast(short) 184));
763 | test!true(cast(ushort) 184, set!"uint16"(DBusAny('q', null, false), cast(ushort) 184));
764 | test!true(cast(int) 184, set!"int32"(DBusAny('i', null, false), cast(int) 184));
765 | test!true(cast(uint) 184, set!"uint32"(DBusAny('u', null, false), cast(uint) 184));
766 | test!true(cast(long) 184, set!"int64"(DBusAny('x', null, false), cast(long) 184));
767 | test!true(cast(ulong) 184, set!"uint64"(DBusAny('t', null, false), cast(ulong) 184));
768 | test!true(1.84, set!"float64"(DBusAny('d', null, false), 1.84));
769 | test!true(true, set!"boolean"(DBusAny('b', null, false), true));
770 | test!true("abc", set!"str"(DBusAny('s', null, false), "abc"));
771 | test!true(ObjectPath("/foo/Bar"), set!"obj"(DBusAny('o', null, false), ObjectPath("/foo/Bar")));
772 | test(cast(ubyte[])[1, 2, 3], set!"binaryData"(DBusAny('a', ['y'], false),
773 | cast(ubyte[])[1, 2, 3]));
774 |
775 | test(variant(cast(ubyte) 184), set!"uint8"(DBusAny('y', null, true), cast(ubyte) 184));
776 | test(variant(cast(short) 184), set!"int16"(DBusAny('n', null, true), cast(short) 184));
777 | test(variant(cast(ushort) 184), set!"uint16"(DBusAny('q', null, true), cast(ushort) 184));
778 | test(variant(cast(int) 184), set!"int32"(DBusAny('i', null, true), cast(int) 184));
779 | test(variant(cast(FileDescriptor) 184), set!"uint32"(DBusAny('h', null, true), cast(FileDescriptor) 184));
780 | test(variant(cast(FileDescriptor) FileDescriptor.none), set!"uint32"(DBusAny('h', null, true), cast(FileDescriptor) FileDescriptor.none));
781 | test(variant(cast(uint) 184), set!"uint32"(DBusAny('u', null, true), cast(uint) 184));
782 | test(variant(cast(long) 184), set!"int64"(DBusAny('x', null, true), cast(long) 184));
783 | test(variant(cast(ulong) 184), set!"uint64"(DBusAny('t', null, true), cast(ulong) 184));
784 | test(variant(1.84), set!"float64"(DBusAny('d', null, true), 1.84));
785 | test(variant(true), set!"boolean"(DBusAny('b', null, true), true));
786 | test(variant("abc"), set!"str"(DBusAny('s', null, true), "abc"));
787 | test(variant(ObjectPath("/foo/Bar")), set!"obj"(DBusAny('o', null, true),
788 | ObjectPath("/foo/Bar")));
789 | test(variant(cast(ubyte[])[1, 2, 3]), set!"binaryData"(DBusAny('a', ['y'],
790 | true), cast(ubyte[])[1, 2, 3]));
791 |
792 | test(variant(DBusAny(5)), set!"int32"(DBusAny('i', null, true), 5));
793 |
794 | test([1, 2, 3], set!"array"(DBusAny('a', ['i'], false), [DBusAny(1), DBusAny(2), DBusAny(3)]));
795 | test(variant([1, 2, 3]), set!"array"(DBusAny('a', ['i'], true), [DBusAny(1),
796 | DBusAny(2), DBusAny(3)]));
797 |
798 | test(tuple("a", 4, [1, 2]), set!"tuple"(DBusAny('r', "(siai)".dup, false),
799 | [DBusAny("a"), DBusAny(4), DBusAny([1, 2])]));
800 | test(tuple("a", variant(4), variant([1, 2])), set!"tuple"(DBusAny('r',
801 | "(svv)", false), [DBusAny("a"), DBusAny(variant(4)), DBusAny(variant([1, 2]))]));
802 |
803 | test(["a" : "b"], set!"array"(DBusAny('a', "{ss}", false),
804 | [DBusAny(DictionaryEntry!(DBusAny, DBusAny)(DBusAny("a"), DBusAny("b")))]));
805 | test([variant("a") : 4], set!"array"(DBusAny('a', "{vi}", false),
806 | [DBusAny(DictionaryEntry!(DBusAny, DBusAny)(DBusAny(variant("a")), DBusAny(4)))]));
807 | }
808 |
809 | /// Marks the data as variant on serialization
810 | struct Variant(T) {
811 | ///
812 | T data;
813 | }
814 |
815 | Variant!T variant(T)(T data) {
816 | return Variant!T(data);
817 | }
818 |
819 | enum MessageType {
820 | Invalid = 0,
821 | Call,
822 | Return,
823 | Error,
824 | Signal
825 | }
826 |
827 | /// Represents a message in the dbus system. Use the constructor to
828 | struct Message {
829 | DBusMessage* msg;
830 |
831 | deprecated("Use the constructor taking a BusName, ObjectPath and InterfaceName instead")
832 | this(string dest, string path, string iface, string method) {
833 | msg = dbus_message_new_method_call(dest.toStringz(), path.toStringz(),
834 | iface.toStringz(), method.toStringz());
835 | }
836 |
837 | /// Prepares a new method call to an "instance" "object" "interface" "method".
838 | this(BusName dest, ObjectPath path, InterfaceName iface, string method) {
839 | msg = dbus_message_new_method_call(dest.toStringz(),
840 | path.value.toStringz(), iface.toStringz(), method.toStringz());
841 | }
842 |
843 | /// Wraps an existing low level message object.
844 | this(DBusMessage* m) {
845 | msg = m;
846 | }
847 |
848 | this(this) {
849 | dbus_message_ref(msg);
850 | }
851 |
852 | ~this() {
853 | if (msg) {
854 | dbus_message_unref(msg);
855 | msg = null;
856 | }
857 | }
858 |
859 | /// Creates a new iterator and puts in the arguments for calling a method.
860 | void build(TS...)(TS args)
861 | if (allCanDBus!TS) {
862 | DBusMessageIter iter;
863 | dbus_message_iter_init_append(msg, &iter);
864 | buildIter(&iter, args);
865 | }
866 |
867 | /**
868 | Reads the first argument of the message.
869 | Note that this creates a new iterator every time so calling it multiple times will always
870 | read the first argument. This is suitable for single item returns.
871 | To read multiple arguments use readTuple.
872 | */
873 | T read(T)()
874 | if (canDBus!T) {
875 | DBusMessageIter iter;
876 | dbus_message_iter_init(msg, &iter);
877 | return readIter!T(&iter);
878 | }
879 |
880 | alias read to;
881 |
882 | Tup readTuple(Tup)()
883 | if (isTuple!Tup && allCanDBus!(Tup.Types)) {
884 | DBusMessageIter iter;
885 | dbus_message_iter_init(msg, &iter);
886 | Tup ret;
887 | readIterTuple(&iter, ret);
888 | return ret;
889 | }
890 |
891 | Message createReturn() {
892 | return Message(dbus_message_new_method_return(msg));
893 | }
894 |
895 | MessageType type() {
896 | return cast(MessageType) dbus_message_get_type(msg);
897 | }
898 |
899 | bool isCall() {
900 | return type() == MessageType.Call;
901 | }
902 |
903 | // Various string members
904 | // TODO: make a mixin to avoid this copy-paste
905 | string signature() {
906 | const(char)* cStr = dbus_message_get_signature(msg);
907 | assert(cStr != null);
908 | return cStr.fromStringz().idup;
909 | }
910 |
911 | ObjectPath path() {
912 | const(char)* cStr = dbus_message_get_path(msg);
913 | assert(cStr != null);
914 | return ObjectPath(cStr.fromStringz().idup);
915 | }
916 |
917 | InterfaceName iface() {
918 | const(char)* cStr = dbus_message_get_interface(msg);
919 | assert(cStr != null);
920 | return interfaceName(cStr.fromStringz().idup);
921 | }
922 |
923 | string member() {
924 | const(char)* cStr = dbus_message_get_member(msg);
925 | assert(cStr != null);
926 | return cStr.fromStringz().idup;
927 | }
928 |
929 | string sender() {
930 | const(char)* cStr = dbus_message_get_sender(msg);
931 | assert(cStr != null);
932 | return cStr.fromStringz().idup;
933 | }
934 | }
935 |
936 | ///
937 | unittest {
938 | import dunit.toolkit;
939 |
940 | auto msg = Message(busName("org.example.test"), ObjectPath("/test"), interfaceName("org.example.testing"), "testMethod");
941 | msg.path().assertEqual("/test");
942 | }
943 |
944 | struct Connection {
945 | DBusConnection* conn;
946 | this(DBusConnection* connection) {
947 | conn = connection;
948 | }
949 |
950 | this(this) {
951 | dbus_connection_ref(conn);
952 | }
953 |
954 | ~this() {
955 | if (conn) {
956 | dbus_connection_unref(conn);
957 | conn = null;
958 | }
959 | }
960 |
961 | void close() {
962 | if (conn) {
963 | dbus_connection_close(conn);
964 | }
965 | }
966 |
967 | void send(Message msg) {
968 | dbus_connection_send(conn, msg.msg, null);
969 | }
970 |
971 | void sendBlocking(Message msg) {
972 | send(msg);
973 | dbus_connection_flush(conn);
974 | }
975 |
976 | Message sendWithReplyBlocking(Message msg, int timeout = -1) {
977 | DBusMessage* dbusMsg = msg.msg;
978 | dbus_message_ref(dbusMsg);
979 | DBusMessage* reply = wrapErrors((err) {
980 | auto ret = dbus_connection_send_with_reply_and_block(conn, dbusMsg, timeout, err);
981 | dbus_message_unref(dbusMsg);
982 | return ret;
983 | });
984 | return Message(reply);
985 | }
986 |
987 | Message sendWithReplyBlocking(Message msg, Duration timeout) {
988 | return sendWithReplyBlocking(msg, timeout.total!"msecs"().to!int);
989 | }
990 | }
991 |
992 | unittest {
993 | import dunit.toolkit;
994 | import ddbus.attributes : dbusMarshaling, MarshalingFlag;
995 |
996 | struct S1 {
997 | private int a;
998 | private @(Yes.DBusMarshal) double b;
999 | string s;
1000 | }
1001 |
1002 | @dbusMarshaling(MarshalingFlag.manualOnly)
1003 | struct S2 {
1004 | int h, i;
1005 | @(Yes.DBusMarshal) int j, k;
1006 | int* p;
1007 | }
1008 |
1009 | @dbusMarshaling(MarshalingFlag.includePrivateFields)
1010 | struct S3 {
1011 | private Variant!int c;
1012 | string d;
1013 | S1 e;
1014 | S2 f;
1015 | @(No.DBusMarshal) uint g;
1016 | }
1017 |
1018 | Message msg = Message(busName("org.example.wow"), ObjectPath("/wut"), interfaceName("org.test.iface"), "meth3");
1019 |
1020 | __gshared int dummy;
1021 |
1022 | enum testStruct = S3(variant(5), "blah", S1(-7, 63.5, "test"), S2(84, -123,
1023 | 78, 432, &dummy), 16);
1024 |
1025 | // Non-marshaled fields should appear as freshly initialized
1026 | enum expectedResult = S3(variant(5), "blah", S1(int.init, 63.5, "test"),
1027 | S2(int.init, int.init, 78, 432, null), uint.init);
1028 |
1029 | // Test struct conversion in building/reading messages
1030 | msg.build(testStruct);
1031 | msg.read!S3().assertEqual(expectedResult);
1032 |
1033 | // Test struct conversion in DBusAny
1034 | DBusAny(testStruct).to!S3.assertEqual(expectedResult);
1035 | }
1036 |
1037 | Connection connectToBus(DBusBusType bus = DBusBusType.DBUS_BUS_SESSION) {
1038 | DBusConnection* conn = wrapErrors((err) { return dbus_bus_get(bus, err); });
1039 | return Connection(conn);
1040 | }
1041 |
1042 | unittest {
1043 | import dunit.toolkit;
1044 |
1045 | // This test will only pass if DBus is installed.
1046 | Connection conn = connectToBus();
1047 | conn.conn.assertTruthy();
1048 | // We can only count on no system bus on OSX
1049 | version (OSX) {
1050 | connectToBus(DBusBusType.DBUS_BUS_SYSTEM).assertThrow!DBusException();
1051 | }
1052 | }
1053 |
--------------------------------------------------------------------------------
/source/ddbus/util.d:
--------------------------------------------------------------------------------
1 | module ddbus.util;
2 |
3 | import ddbus.thin;
4 | import std.meta : AliasSeq, staticIndexOf;
5 | import std.range;
6 | import std.traits;
7 | import std.typecons : BitFlags, isTuple, Tuple;
8 | import std.variant : VariantN;
9 |
10 | struct DictionaryEntry(K, V) {
11 | K key;
12 | V value;
13 | }
14 |
15 | auto byDictionaryEntries(K, V)(V[K] aa) {
16 | import std.algorithm : map;
17 |
18 | return aa.byKeyValue.map!(pair => DictionaryEntry!(K, V)(pair.key, pair.value));
19 | }
20 |
21 | /+
22 | Predicate template indicating whether T is an instance of ddbus.thin.Variant.
23 |
24 | Deprecated:
25 | This template used to be undocumented and user code should not depend on it.
26 | Its meaning became unclear when support for Phobos-style variants was added.
27 | It seemed best to remove it at that point.
28 | +/
29 | deprecated("Use std.traits.isInstanceOf instead.") template isVariant(T) {
30 | static if (isBasicType!T || isInputRange!T) {
31 | enum isVariant = false;
32 | } else static if (__traits(compiles, TemplateOf!T) && __traits(isSame, TemplateOf!T, Variant)) {
33 | enum isVariant = true;
34 | } else {
35 | enum isVariant = false;
36 | }
37 | }
38 |
39 | template VariantType(T) {
40 | alias VariantType = TemplateArgsOf!(T)[0];
41 | }
42 |
43 | template allCanDBus(TS...) {
44 | static if (TS.length == 0) {
45 | enum allCanDBus = true;
46 | } else static if (!canDBus!(TS[0])) {
47 | enum allCanDBus = false;
48 | } else {
49 | enum allCanDBus = allCanDBus!(TS[1 .. $]);
50 | }
51 | }
52 |
53 | /++
54 | AliasSeq of all basic types in terms of the DBus typesystem
55 | +/
56 | package // Don't add to the API yet, 'cause I intend to move it later
57 | alias BasicTypes = AliasSeq!(bool, ubyte, short, ushort, int, uint, long, ulong,
58 | double, string, ObjectPath, InterfaceName, BusName, FileDescriptor);
59 |
60 | template basicDBus(U) {
61 | alias T = Unqual!U;
62 | static if (staticIndexOf!(T, BasicTypes) >= 0) {
63 | enum basicDBus = true;
64 | } else static if (is(T B == enum)) {
65 | enum basicDBus = basicDBus!B;
66 | } else static if (isInstanceOf!(BitFlags, T)) {
67 | alias TemplateArgsOf!T[0] E;
68 | enum basicDBus = basicDBus!E;
69 | } else {
70 | enum basicDBus = false;
71 | }
72 | }
73 |
74 | template canDBus(U) {
75 | alias T = Unqual!U;
76 | static if (basicDBus!T || is(T == DBusAny)) {
77 | enum canDBus = true;
78 | } else static if (isInstanceOf!(Variant, T)) {
79 | enum canDBus = canDBus!(VariantType!T);
80 | } else static if (isInstanceOf!(VariantN, T)) {
81 | // Phobos-style variants are supported if limited to DBus compatible types.
82 | enum canDBus = (T.AllowedTypes.length > 0) && allCanDBus!(T.AllowedTypes);
83 | } else static if (isTuple!T) {
84 | enum canDBus = allCanDBus!(T.Types);
85 | } else static if (isInputRange!T) {
86 | static if (is(ElementType!T == DictionaryEntry!(K, V), K, V)) {
87 | enum canDBus = basicDBus!K && canDBus!V;
88 | } else {
89 | enum canDBus = canDBus!(ElementType!T);
90 | }
91 | } else static if (isAssociativeArray!T) {
92 | enum canDBus = basicDBus!(KeyType!T) && canDBus!(ValueType!T);
93 | } else static if (is(T == struct) && !isInstanceOf!(DictionaryEntry, T)) {
94 | enum canDBus = allCanDBus!(AllowedFieldTypes!T);
95 | } else {
96 | enum canDBus = false;
97 | }
98 | }
99 |
100 | unittest {
101 | import dunit.toolkit;
102 |
103 | (canDBus!int).assertTrue();
104 | (canDBus!(int[])).assertTrue();
105 | (allCanDBus!(int, string, bool)).assertTrue();
106 | (canDBus!(Tuple!(int[], bool, Variant!short))).assertTrue();
107 | (canDBus!(Tuple!(int[], int[string]))).assertTrue();
108 | (canDBus!(int[string])).assertTrue();
109 | (canDBus!FileDescriptor).assertTrue();
110 | }
111 |
112 | string typeSig(U)()
113 | if (canDBus!U) {
114 | alias T = Unqual!U;
115 | static if (is(T == ubyte)) {
116 | return "y";
117 | } else static if (is(T == bool)) {
118 | return "b";
119 | } else static if (is(T == short)) {
120 | return "n";
121 | } else static if (is(T == ushort)) {
122 | return "q";
123 | } else static if (is(T == FileDescriptor)) {
124 | return "h";
125 | } else static if (is(T == int)) {
126 | return "i";
127 | } else static if (is(T == uint)) {
128 | return "u";
129 | } else static if (is(T == long)) {
130 | return "x";
131 | } else static if (is(T == ulong)) {
132 | return "t";
133 | } else static if (is(T == double)) {
134 | return "d";
135 | } else static if (is(T == string) || is(T == InterfaceName) || is(T == BusName)) {
136 | return "s";
137 | } else static if (is(T == ObjectPath)) {
138 | return "o";
139 | } else static if (isInstanceOf!(Variant, T) || isInstanceOf!(VariantN, T)) {
140 | return "v";
141 | } else static if (is(T B == enum)) {
142 | return typeSig!B;
143 | } else static if (isInstanceOf!(BitFlags, T)) {
144 | alias TemplateArgsOf!T[0] E;
145 | return typeSig!E;
146 | } else static if (is(T == DBusAny)) {
147 | static assert(false,
148 | "Cannot determine type signature of DBusAny. Change to Variant!DBusAny if a variant was desired.");
149 | } else static if (isTuple!T) {
150 | string sig = "(";
151 | foreach (i, S; T.Types) {
152 | sig ~= typeSig!S();
153 | }
154 | sig ~= ")";
155 | return sig;
156 | } else static if (isInputRange!T) {
157 | return "a" ~ typeSig!(ElementType!T)();
158 | } else static if (isAssociativeArray!T) {
159 | return "a{" ~ typeSig!(KeyType!T) ~ typeSig!(ValueType!T) ~ "}";
160 | } else static if (is(T == struct)) {
161 | string sig = "(";
162 | foreach (i, S; AllowedFieldTypes!T) {
163 | sig ~= typeSig!S();
164 | }
165 | sig ~= ")";
166 | return sig;
167 | }
168 | }
169 |
170 | string typeSig(T)()
171 | if (isInstanceOf!(DictionaryEntry, T)) {
172 | alias typeof(T.key) K;
173 | alias typeof(T.value) V;
174 | return "{" ~ typeSig!K ~ typeSig!V ~ '}';
175 | }
176 |
177 | string[] typeSigReturn(T)()
178 | if (canDBus!T) {
179 | static if (is(T == Tuple!TS, TS...))
180 | return typeSigArr!TS;
181 | else
182 | return [typeSig!T];
183 | }
184 |
185 | string typeSigAll(TS...)()
186 | if (allCanDBus!TS) {
187 | string sig = "";
188 | foreach (i, T; TS) {
189 | sig ~= typeSig!T();
190 | }
191 | return sig;
192 | }
193 |
194 | string[] typeSigArr(TS...)()
195 | if (allCanDBus!TS) {
196 | string[] sig = [];
197 | foreach (i, T; TS) {
198 | sig ~= typeSig!T();
199 | }
200 | return sig;
201 | }
202 |
203 | int typeCode(T)()
204 | if (canDBus!T) {
205 | int code = typeSig!T()[0];
206 | return (code != '(') ? code : 'r';
207 | }
208 |
209 | int typeCode(T)()
210 | if (isInstanceOf!(DictionaryEntry, T) && canDBus!(T[])) {
211 | return 'e';
212 | }
213 |
214 | /**
215 | Params:
216 | type = the type code of a type (first character in a type string)
217 | Returns: true if the given type is an integer.
218 | */
219 | bool dbusIsIntegral(int type) @property {
220 | return type == 'y' || type == 'n' || type == 'q' || type == 'i' || type == 'u' || type == 'x' || type == 't';
221 | }
222 |
223 | /**
224 | Params:
225 | type = the type code of a type (first character in a type string)
226 | Returns: true if the given type is a floating point value.
227 | */
228 | bool dbusIsFloating(int type) @property {
229 | return type == 'd';
230 | }
231 |
232 | /**
233 | Params:
234 | type = the type code of a type (first character in a type string)
235 | Returns: true if the given type is an integer or a floating point value.
236 | */
237 | bool dbusIsNumeric(int type) @property {
238 | return dbusIsIntegral(type) || dbusIsFloating(type);
239 | }
240 |
241 | unittest {
242 | import dunit.toolkit;
243 |
244 | static assert(canDBus!ObjectPath);
245 | static assert(canDBus!InterfaceName);
246 | static assert(canDBus!BusName);
247 |
248 | // basics
249 | typeSig!int().assertEqual("i");
250 | typeSig!bool().assertEqual("b");
251 | typeSig!string().assertEqual("s");
252 | typeSig!InterfaceName().assertEqual("s");
253 | typeSig!(immutable(InterfaceName))().assertEqual("s");
254 | typeSig!ObjectPath().assertEqual("o");
255 | typeSig!(immutable(ObjectPath))().assertEqual("o");
256 | typeSig!(Variant!int)().assertEqual("v");
257 | typeSig!FileDescriptor().assertEqual("h");
258 | // enums
259 | enum E : ubyte {
260 | a,
261 | b,
262 | c
263 | }
264 |
265 | typeSig!E().assertEqual(typeSig!ubyte());
266 | enum U : string {
267 | One = "One",
268 | Two = "Two"
269 | }
270 |
271 | typeSig!U().assertEqual(typeSig!string());
272 | // bit flags
273 | enum F : uint {
274 | a = 1,
275 | b = 2,
276 | c = 4
277 | }
278 |
279 | typeSig!(BitFlags!F)().assertEqual(typeSig!uint());
280 | // tuples (represented as structs in DBus)
281 | typeSig!(Tuple!(int, string, string)).assertEqual("(iss)");
282 | typeSig!(Tuple!(int, string, Variant!int, Tuple!(int, "k", double, "x"))).assertEqual(
283 | "(isv(id))");
284 | // structs
285 | struct S1 {
286 | int a;
287 | double b;
288 | string s;
289 | }
290 |
291 | typeSig!S1.assertEqual("(ids)");
292 | struct S2 {
293 | Variant!int c;
294 | string d;
295 | S1 e;
296 | uint f;
297 | FileDescriptor g;
298 | }
299 |
300 | typeSig!S2.assertEqual("(vs(ids)uh)");
301 | // arrays
302 | typeSig!(int[]).assertEqual("ai");
303 | typeSig!(Variant!int[]).assertEqual("av");
304 | typeSig!(Tuple!(ubyte)[][]).assertEqual("aa(y)");
305 | // dictionaries
306 | typeSig!(int[string]).assertEqual("a{si}");
307 | typeSig!(DictionaryEntry!(string, int)[]).assertEqual("a{si}");
308 | // multiple arguments
309 | typeSigAll!(int, bool).assertEqual("ib");
310 | // Phobos-style variants
311 | canDBus!(std.variant.Variant).assertFalse();
312 | typeSig!(std.variant.Algebraic!(int, double, string)).assertEqual("v");
313 | // type codes
314 | typeCode!int().assertEqual(cast(int)('i'));
315 | typeCode!bool().assertEqual(cast(int)('b'));
316 | typeCode!(Tuple!(int, string))().assertEqual(cast(int)('r'));
317 | // ctfe-capable
318 | static string sig = typeSig!ulong();
319 | sig.assertEqual("t");
320 | static string sig2 = typeSig!(Tuple!(int, string, string));
321 | sig2.assertEqual("(iss)");
322 | static string sig3 = typeSigAll!(int, string, InterfaceName, BusName);
323 | sig3.assertEqual("isss");
324 | }
325 |
326 | private template AllowedFieldTypes(S)
327 | if (is(S == struct)) {
328 | import ddbus.attributes : isAllowedField;
329 | import std.meta : Filter, staticMap;
330 |
331 | static alias TypeOf(alias sym) = typeof(sym);
332 |
333 | alias AllowedFieldTypes = staticMap!(TypeOf, Filter!(isAllowedField, S.tupleof));
334 | }
335 |
--------------------------------------------------------------------------------
/transformer.rb:
--------------------------------------------------------------------------------
1 | # Script used to transform dbus headers into sources/ddbus/c_lib.d
2 | # Uses dstep and then fixes a bunch of things up
3 | # This should only need to be done once but I'm including it in case things
4 | # need to be re-done. Put dbus headers into the "dbus" folder and install dstep.
5 |
6 | Dir["dbus/*.h"].each do |h|
7 | system "dstep #{h} -DDBUS_INSIDE_DBUS_H -I."
8 | end
9 |
10 | FILES_ORDER =
11 | [
12 | "dbus/dbus-arch-deps.d",
13 | "dbus/dbus-types.d",
14 | "dbus/dbus-protocol.d",
15 | "dbus/dbus-errors.d",
16 | "dbus/dbus-macros.d",
17 | "dbus/dbus-memory.d",
18 | "dbus/dbus-shared.d",
19 | "dbus/dbus-address.d",
20 | "dbus/dbus-syntax.d",
21 | "dbus/dbus-signature.d",
22 | "dbus/dbus-misc.d",
23 | "dbus/dbus-threads.d",
24 | "dbus/dbus-message.d",
25 | "dbus/dbus-connection.d",
26 | "dbus/dbus-pending-call.d",
27 | "dbus/dbus-server.d",
28 | "dbus/dbus-bus.d",
29 | "dbus/dbus.d"
30 | ]
31 |
32 | ANON_ALIAS = /^alias _Anonymous_(\d) (.*);$/
33 | def fixup(cont)
34 | cont.gsub!("extern (C):",'')
35 | cont.gsub!(/^import .*$/,'')
36 |
37 | anons = cont.scan(ANON_ALIAS)
38 | cont.gsub!(ANON_ALIAS,'')
39 | anons.each do |num,name|
40 | cont.gsub!("_Anonymous_#{num}",name)
41 | end
42 |
43 | # Special case for bug in translator
44 | cont.gsub!("alias function", "alias DBusHandlerResult function")
45 | cont.gsub!(/^alias (\S*) (\S*);$/){ |s| ($1 == $2) ? '' : s }
46 |
47 | cont.strip
48 | end
49 |
50 | f = File.open("c_lib.d","w")
51 | f.puts <