├── .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 | Dub version 4 | Dub downloads 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 <