mirror of https://github.com/adamdruppe/arsd.git
2134 lines
74 KiB
D
2134 lines
74 KiB
D
/++
|
|
A module mostly copied from https://github.com/trishume/ddbus to help access the C dbus library on Linux.
|
|
+/
|
|
module arsd.dbus;
|
|
|
|
pragma(lib, "dbus-1");
|
|
|
|
import core.time : Duration;
|
|
import std.meta;
|
|
import std.string;
|
|
import std.typecons;
|
|
import std.exception;
|
|
import std.traits;
|
|
import std.conv;
|
|
import std.range;
|
|
import std.algorithm;
|
|
|
|
import core.memory;
|
|
import std.array;
|
|
import std.format;
|
|
|
|
import std.meta : AliasSeq, staticIndexOf;
|
|
import std.range;
|
|
import std.traits;
|
|
import std.variant : VariantN;
|
|
|
|
|
|
/++
|
|
Flags for use with dbusMarshaling UDA
|
|
|
|
Default is to include public fields only
|
|
+/
|
|
enum MarshalingFlag : ubyte {
|
|
includePrivateFields = 1 << 0, /// Automatically include private fields
|
|
manualOnly = 1 << 7 /// Only include fields with explicit
|
|
/// `@Yes.DBusMarshal`. This overrides any
|
|
/// `include` flags.
|
|
}
|
|
|
|
/++
|
|
UDA for specifying DBus marshaling options on structs
|
|
+/
|
|
auto dbusMarshaling(Args)(Args args ...)
|
|
if (allSatisfy!(isMarshalingFlag, Args)) {
|
|
return BitFlags!MarshalingFlag(args);
|
|
}
|
|
|
|
private template isAllowedField(alias field) {
|
|
private enum flags = marshalingFlags!(__traits(parent, field));
|
|
private alias getUDAs!(field, Flag!"DBusMarshal") UDAs;
|
|
|
|
static if (UDAs.length != 0) {
|
|
static assert (UDAs.length == 1,
|
|
"Only one UDA of type Flag!\"DBusMarshal\" allowed on struct field.");
|
|
static assert (is(typeof(UDAs[0]) == Flag!"DBusMarshal"),
|
|
"Did you intend to add UDA Yes.DBusMarshal or No.DBusMarshal?");
|
|
enum isAllowedField = cast(bool) UDAs[0];
|
|
} else static if (!(flags & MarshalingFlag.manualOnly)) {
|
|
static if (__traits(getProtection, field) == "public")
|
|
enum isAllowedField = true;
|
|
else static if (cast(bool) (flags & MarshalingFlag.includePrivateFields))
|
|
enum isAllowedField = true;
|
|
else
|
|
enum isAllowedField = false;
|
|
} else
|
|
enum isAllowedField = false;
|
|
}
|
|
|
|
private template isMarshalingFlag(T) {
|
|
enum isMarshalingFlag = is(T == MarshalingFlag);
|
|
}
|
|
|
|
private template marshalingFlags(S) if (is(S == struct)) {
|
|
private alias getUDAs!(S, BitFlags!MarshalingFlag) UDAs;
|
|
|
|
static if (UDAs.length == 0)
|
|
enum marshalingFlags = BitFlags!MarshalingFlag.init;
|
|
else {
|
|
static assert (UDAs.length == 1,
|
|
"Only one @dbusMarshaling UDA allowed on type.");
|
|
static assert (is(typeof(UDAs[0]) == BitFlags!MarshalingFlag),
|
|
"Huh? Did you intend to use @dbusMarshaling UDA?");
|
|
enum marshalingFlags = UDAs[0];
|
|
}
|
|
}
|
|
|
|
struct DictionaryEntry(K, V) {
|
|
K key;
|
|
V value;
|
|
}
|
|
|
|
auto byDictionaryEntries(K, V)(V[K] aa) {
|
|
return aa.byKeyValue.map!(pair => DictionaryEntry!(K, V)(pair.key, pair.value));
|
|
}
|
|
|
|
template VariantType(T) {
|
|
alias VariantType = TemplateArgsOf!(T)[0];
|
|
}
|
|
|
|
template allCanDBus(TS...) {
|
|
static if (TS.length == 0) {
|
|
enum allCanDBus = true;
|
|
} else static if(!canDBus!(TS[0])) {
|
|
enum allCanDBus = false;
|
|
} else {
|
|
enum allCanDBus = allCanDBus!(TS[1..$]);
|
|
}
|
|
}
|
|
|
|
/++
|
|
AliasSeq of all basic types in terms of the DBus typesystem
|
|
+/
|
|
private // Don't add to the API yet, 'cause I intend to move it later
|
|
alias BasicTypes = AliasSeq!(
|
|
bool,
|
|
byte,
|
|
short,
|
|
ushort,
|
|
int,
|
|
uint,
|
|
long,
|
|
ulong,
|
|
double,
|
|
string,
|
|
ObjectPath
|
|
);
|
|
|
|
template basicDBus(T) {
|
|
static if(staticIndexOf!(T, BasicTypes) >= 0) {
|
|
enum basicDBus = true;
|
|
} else static if(is(T B == enum)) {
|
|
enum basicDBus = basicDBus!B;
|
|
} else static if(isInstanceOf!(BitFlags, T)) {
|
|
alias TemplateArgsOf!T[0] E;
|
|
enum basicDBus = basicDBus!E;
|
|
} else {
|
|
enum basicDBus = false;
|
|
}
|
|
}
|
|
|
|
template canDBus(T) {
|
|
static if(basicDBus!T || is(T == DBusAny)) {
|
|
enum canDBus = true;
|
|
} else static if(isInstanceOf!(Variant, T)) {
|
|
enum canDBus = canDBus!(VariantType!T);
|
|
} else static if(isInstanceOf!(VariantN, T)) {
|
|
// Phobos-style variants are supported if limited to DBus compatible types.
|
|
enum canDBus = (T.AllowedTypes.length > 0) && allCanDBus!(T.AllowedTypes);
|
|
} else static if(isTuple!T) {
|
|
enum canDBus = allCanDBus!(T.Types);
|
|
} else static if(isInputRange!T) {
|
|
static if(is(ElementType!T == DictionaryEntry!(K, V), K, V)) {
|
|
enum canDBus = basicDBus!K && canDBus!V;
|
|
} else {
|
|
enum canDBus = canDBus!(ElementType!T);
|
|
}
|
|
} else static if(isAssociativeArray!T) {
|
|
enum canDBus = basicDBus!(KeyType!T) && canDBus!(ValueType!T);
|
|
} else static if(is(T == struct) && !isInstanceOf!(DictionaryEntry, T)) {
|
|
enum canDBus = allCanDBus!(AllowedFieldTypes!T);
|
|
} else {
|
|
enum canDBus = false;
|
|
}
|
|
}
|
|
|
|
string typeSig(T)() if(canDBus!T) {
|
|
static if(is(T == byte)) {
|
|
return "y";
|
|
} else static if(is(T == bool)) {
|
|
return "b";
|
|
} else static if(is(T == short)) {
|
|
return "n";
|
|
} else static if(is(T == ushort)) {
|
|
return "q";
|
|
} else static if(is(T == int)) {
|
|
return "i";
|
|
} else static if(is(T == uint)) {
|
|
return "u";
|
|
} else static if(is(T == long)) {
|
|
return "x";
|
|
} else static if(is(T == ulong)) {
|
|
return "t";
|
|
} else static if(is(T == double)) {
|
|
return "d";
|
|
} else static if(is(T == string)) {
|
|
return "s";
|
|
} else static if(is(T == ObjectPath)) {
|
|
return "o";
|
|
} else static if(isInstanceOf!(Variant, T) || isInstanceOf!(VariantN, T)) {
|
|
return "v";
|
|
} else static if(is(T B == enum)) {
|
|
return typeSig!B;
|
|
} else static if(isInstanceOf!(BitFlags, T)) {
|
|
alias TemplateArgsOf!T[0] E;
|
|
return typeSig!E;
|
|
} else static if(is(T == DBusAny)) {
|
|
static assert(false, "Cannot determine type signature of DBusAny. Change to Variant!DBusAny if a variant was desired.");
|
|
} else static if(isTuple!T) {
|
|
string sig = "(";
|
|
foreach(i, S; T.Types) {
|
|
sig ~= typeSig!S();
|
|
}
|
|
sig ~= ")";
|
|
return sig;
|
|
} else static if(isInputRange!T) {
|
|
return "a" ~ typeSig!(ElementType!T)();
|
|
} else static if(isAssociativeArray!T) {
|
|
return "a{" ~ typeSig!(KeyType!T) ~ typeSig!(ValueType!T) ~ "}";
|
|
} else static if(is(T == struct)) {
|
|
string sig = "(";
|
|
foreach(i, S; AllowedFieldTypes!T) {
|
|
sig ~= typeSig!S();
|
|
}
|
|
sig ~= ")";
|
|
return sig;
|
|
}
|
|
}
|
|
|
|
string typeSig(T)() if(isInstanceOf!(DictionaryEntry, T)) {
|
|
alias typeof(T.key) K;
|
|
alias typeof(T.value) V;
|
|
return "{" ~ typeSig!K ~ typeSig!V ~ '}';
|
|
}
|
|
|
|
string[] typeSigReturn(T)() if(canDBus!T) {
|
|
static if(is(T == Tuple!TS, TS...))
|
|
return typeSigArr!TS;
|
|
else
|
|
return [typeSig!T];
|
|
}
|
|
|
|
string typeSigAll(TS...)() if(allCanDBus!TS) {
|
|
string sig = "";
|
|
foreach(i,T; TS) {
|
|
sig ~= typeSig!T();
|
|
}
|
|
return sig;
|
|
}
|
|
|
|
string[] typeSigArr(TS...)() if(allCanDBus!TS) {
|
|
string[] sig = [];
|
|
foreach(i,T; TS) {
|
|
sig ~= typeSig!T();
|
|
}
|
|
return sig;
|
|
}
|
|
|
|
int typeCode(T)() if(canDBus!T) {
|
|
int code = typeSig!T()[0];
|
|
return (code != '(') ? code : 'r';
|
|
}
|
|
|
|
int typeCode(T)() if(isInstanceOf!(DictionaryEntry, T) && canDBus!(T[])) {
|
|
return 'e';
|
|
}
|
|
|
|
private template AllowedFieldTypes(S) if (is(S == struct)) {
|
|
static alias TypeOf(alias sym) = typeof(sym);
|
|
|
|
alias AllowedFieldTypes =
|
|
staticMap!(TypeOf, Filter!(isAllowedField, S.tupleof));
|
|
}
|
|
|
|
struct ObjectPath {
|
|
private string _value;
|
|
|
|
this(string objPath) pure @safe {
|
|
enforce(isValid(objPath));
|
|
_value = objPath;
|
|
}
|
|
|
|
string toString() const {
|
|
return _value;
|
|
}
|
|
|
|
/++
|
|
Returns the string representation of this ObjectPath.
|
|
+/
|
|
string value() const pure @nogc nothrow @safe {
|
|
return _value;
|
|
}
|
|
|
|
size_t toHash() const pure @nogc nothrow @trusted {
|
|
return hashOf(_value);
|
|
}
|
|
|
|
bool opEquals(ref const typeof(this) b) const pure @nogc nothrow @safe {
|
|
return _value == b._value;
|
|
}
|
|
|
|
ObjectPath opBinary(string op : "~")(string rhs) const pure @safe {
|
|
if (!rhs.startsWith("/"))
|
|
return opBinary!"~"(ObjectPath("/" ~ rhs));
|
|
else
|
|
return opBinary!"~"(ObjectPath(rhs));
|
|
}
|
|
|
|
ObjectPath opBinary(string op : "~")(ObjectPath rhs) const pure @safe
|
|
in {
|
|
assert(ObjectPath.isValid(_value) && ObjectPath.isValid(rhs._value));
|
|
} out (v) {
|
|
assert(ObjectPath.isValid(v._value));
|
|
} do {
|
|
ObjectPath ret;
|
|
|
|
if (_value == "/")
|
|
ret._value = rhs._value;
|
|
else
|
|
ret._value = _value ~ rhs._value;
|
|
|
|
return ret;
|
|
}
|
|
|
|
void opOpAssign(string op : "~")(string rhs) pure @safe {
|
|
_value = opBinary!"~"(rhs)._value;
|
|
}
|
|
|
|
void opOpAssign(string op : "~")(ObjectPath rhs) pure @safe {
|
|
_value = opBinary!"~"(rhs)._value;
|
|
}
|
|
|
|
/++
|
|
Returns: `false` for empty strings or strings that don't match the
|
|
pattern `(/[0-9A-Za-z_]+)+|/`.
|
|
+/
|
|
static bool isValid(string objPath) pure @nogc nothrow @safe {
|
|
import std.ascii : isAlphaNum;
|
|
|
|
if (!objPath.length)
|
|
return false;
|
|
if (objPath == "/")
|
|
return true;
|
|
if (objPath[0] != '/' || objPath[$ - 1] == '/')
|
|
return false;
|
|
// .representation to avoid unicode exceptions -> @nogc & nothrow
|
|
return objPath.representation.splitter('/').drop(1)
|
|
.all!(a =>
|
|
a.length &&
|
|
a.all!(c =>
|
|
c.isAlphaNum || c == '_'
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Structure allowing typeless parameters
|
|
struct DBusAny {
|
|
/// DBus type of the value (never 'v'), see typeSig!T
|
|
int type;
|
|
/// Child signature for Arrays & Tuples
|
|
string signature;
|
|
/// If true, this value will get serialized as variant value, otherwise it is serialized like it wasn't in a DBusAny wrapper.
|
|
/// Same functionality as Variant!T but with dynamic types if true.
|
|
bool explicitVariant;
|
|
|
|
union
|
|
{
|
|
///
|
|
byte int8;
|
|
///
|
|
short int16;
|
|
///
|
|
ushort uint16;
|
|
///
|
|
int int32;
|
|
///
|
|
uint uint32;
|
|
///
|
|
long int64;
|
|
///
|
|
ulong uint64;
|
|
///
|
|
double float64;
|
|
///
|
|
string str;
|
|
///
|
|
bool boolean;
|
|
///
|
|
ObjectPath obj;
|
|
///
|
|
DBusAny[] array;
|
|
///
|
|
alias tuple = array;
|
|
///
|
|
DictionaryEntry!(DBusAny, DBusAny)* entry;
|
|
///
|
|
ubyte[] binaryData;
|
|
}
|
|
|
|
/// Manually creates a DBusAny object using a type, signature and implicit specifier.
|
|
this(int type, string signature, bool explicit) {
|
|
this.type = type;
|
|
this.signature = signature;
|
|
this.explicitVariant = explicit;
|
|
}
|
|
|
|
/// Automatically creates a DBusAny object with fitting parameters from a D type or Variant!T.
|
|
/// Pass a `Variant!T` to make this an explicit variant.
|
|
this(T)(T value) {
|
|
static if(is(T == byte) || is(T == ubyte)) {
|
|
this(typeCode!byte, null, false);
|
|
int8 = cast(byte) value;
|
|
} else static if(is(T == short)) {
|
|
this(typeCode!short, null, false);
|
|
int16 = cast(short) value;
|
|
} else static if(is(T == ushort)) {
|
|
this(typeCode!ushort, null, false);
|
|
uint16 = cast(ushort) value;
|
|
} else static if(is(T == int)) {
|
|
this(typeCode!int, null, false);
|
|
int32 = cast(int) value;
|
|
} else static if(is(T == uint)) {
|
|
this(typeCode!uint, null, false);
|
|
uint32 = cast(uint) value;
|
|
} else static if(is(T == long)) {
|
|
this(typeCode!long, null, false);
|
|
int64 = cast(long) value;
|
|
} else static if(is(T == ulong)) {
|
|
this(typeCode!ulong, null, false);
|
|
uint64 = cast(ulong) value;
|
|
} else static if(is(T == double)) {
|
|
this(typeCode!double, null, false);
|
|
float64 = cast(double) value;
|
|
} else static if(isSomeString!T) {
|
|
this(typeCode!string, null, false);
|
|
str = value.to!string;
|
|
} else static if(is(T == bool)) {
|
|
this(typeCode!bool, null, false);
|
|
boolean = cast(bool) value;
|
|
} else static if(is(T == ObjectPath)) {
|
|
this(typeCode!ObjectPath, null, false);
|
|
obj = value;
|
|
} else static if(is(T == Variant!R, R)) {
|
|
static if(is(R == DBusAny)) {
|
|
type = value.data.type;
|
|
signature = value.data.signature;
|
|
explicitVariant = true;
|
|
if(type == 'a' || type == 'r') {
|
|
if(signature == ['y'])
|
|
binaryData = value.data.binaryData;
|
|
else
|
|
array = value.data.array;
|
|
} else if(type == 's')
|
|
str = value.data.str;
|
|
else if(type == 'e')
|
|
entry = value.data.entry;
|
|
else
|
|
uint64 = value.data.uint64;
|
|
} else {
|
|
this(value.data);
|
|
explicitVariant = true;
|
|
}
|
|
} else static if(is(T : DictionaryEntry!(K, V), K, V)) {
|
|
this('e', null, false);
|
|
entry = new DictionaryEntry!(DBusAny, DBusAny)();
|
|
static if(is(K == DBusAny))
|
|
entry.key = value.key;
|
|
else
|
|
entry.key = DBusAny(value.key);
|
|
static if(is(V == DBusAny))
|
|
entry.value = value.value;
|
|
else
|
|
entry.value = DBusAny(value.value);
|
|
} else static if(is(T == ubyte[]) || is(T == byte[])) {
|
|
this('a', ['y'], false);
|
|
binaryData = cast(ubyte[]) value;
|
|
} else static if(isInputRange!T) {
|
|
this.type = 'a';
|
|
static assert(!is(ElementType!T == DBusAny), "Array must consist of the same type, use Variant!DBusAny or DBusAny(tuple(...)) instead");
|
|
static assert(.typeSig!(ElementType!T) != "y");
|
|
this.signature = .typeSig!(ElementType!T);
|
|
this.explicitVariant = false;
|
|
foreach(elem; value)
|
|
array ~= DBusAny(elem);
|
|
} else static if(isTuple!T) {
|
|
this.type = 'r';
|
|
this.signature = ['('];
|
|
this.explicitVariant = false;
|
|
foreach(index, R; value.Types) {
|
|
auto var = DBusAny(value[index]);
|
|
tuple ~= var;
|
|
if(var.explicitVariant)
|
|
this.signature ~= 'v';
|
|
else {
|
|
if (var.type != 'r')
|
|
this.signature ~= cast(char) var.type;
|
|
if(var.type == 'a' || var.type == 'r')
|
|
this.signature ~= var.signature;
|
|
}
|
|
}
|
|
this.signature ~= ')';
|
|
} else static if(isAssociativeArray!T) {
|
|
this(value.byDictionaryEntries);
|
|
} else static assert(false, T.stringof ~ " not convertible to a Variant");
|
|
}
|
|
|
|
///
|
|
string toString() const {
|
|
string valueStr;
|
|
switch(type) {
|
|
case typeCode!byte:
|
|
valueStr = int8.to!string;
|
|
break;
|
|
case typeCode!short:
|
|
valueStr = int16.to!string;
|
|
break;
|
|
case typeCode!ushort:
|
|
valueStr = uint16.to!string;
|
|
break;
|
|
case typeCode!int:
|
|
valueStr = int32.to!string;
|
|
break;
|
|
case typeCode!uint:
|
|
valueStr = uint32.to!string;
|
|
break;
|
|
case typeCode!long:
|
|
valueStr = int64.to!string;
|
|
break;
|
|
case typeCode!ulong:
|
|
valueStr = uint64.to!string;
|
|
break;
|
|
case typeCode!double:
|
|
valueStr = float64.to!string;
|
|
break;
|
|
case typeCode!string:
|
|
valueStr = '"' ~ str ~ '"';
|
|
break;
|
|
case typeCode!ObjectPath:
|
|
valueStr = '"' ~ obj.to!string ~ '"';
|
|
break;
|
|
case typeCode!bool:
|
|
valueStr = boolean ? "true" : "false";
|
|
break;
|
|
case 'a':
|
|
import std.digest : toHexString;
|
|
|
|
if(signature == ['y'])
|
|
valueStr = "binary(" ~ binaryData.toHexString ~ ')';
|
|
else
|
|
valueStr = '[' ~ array.map!(a => a.toString).join(", ") ~ ']';
|
|
break;
|
|
case 'r':
|
|
valueStr = '(' ~ tuple.map!(a => a.toString).join(", ") ~ ')';
|
|
break;
|
|
case 'e':
|
|
valueStr = entry.key.toString ~ ": " ~ entry.value.toString;
|
|
break;
|
|
default:
|
|
valueStr = "unknown";
|
|
break;
|
|
}
|
|
return "DBusAny(" ~ cast(char) type
|
|
~ ", \"" ~ signature.idup
|
|
~ "\", " ~ (explicitVariant ? "explicit" : "implicit")
|
|
~ ", " ~ valueStr ~ ")";
|
|
}
|
|
|
|
/++
|
|
Get the value stored in the DBusAny object.
|
|
|
|
Parameters:
|
|
T = The requested type. The currently stored value must match the
|
|
requested type exactly.
|
|
|
|
Returns:
|
|
The current value of the DBusAny object.
|
|
|
|
Throws:
|
|
TypeMismatchException if the DBus type of the current value of the
|
|
DBusAny object is not the same as the DBus type used to represent T.
|
|
+/
|
|
T get(T)() @property const
|
|
if(staticIndexOf!(T, BasicTypes) >= 0)
|
|
{
|
|
enforce(type == typeCode!T,
|
|
new TypeMismatchException(
|
|
"Cannot get a " ~ T.stringof ~ " from a DBusAny with"
|
|
~ " a value of DBus type '" ~ typeSig ~ "'.", typeCode!T, type));
|
|
|
|
static if(isIntegral!T) {
|
|
enum memberName =
|
|
(isUnsigned!T ? "uint" : "int") ~ (T.sizeof * 8).to!string;
|
|
return __traits(getMember, this, memberName);
|
|
} else static if(is(T == double)) {
|
|
return float64;
|
|
} else static if(is(T == string)) {
|
|
return str;
|
|
} else static if(is(T == ObjectPath)) {
|
|
return obj;
|
|
} else static if(is(T == bool)) {
|
|
return boolean;
|
|
} else {
|
|
static assert(false);
|
|
}
|
|
}
|
|
|
|
/// ditto
|
|
T get(T)() @property const
|
|
if(is(T == const(DBusAny)[]))
|
|
{
|
|
enforce((type == 'a' && signature != "y") || type == 'r',
|
|
new TypeMismatchException(
|
|
"Cannot get a " ~ T.stringof ~ " from a DBusAny with"
|
|
~ " a value of DBus type '" ~ this.typeSig ~ "'.",
|
|
typeCode!T, type));
|
|
|
|
return array;
|
|
}
|
|
|
|
/// ditto
|
|
T get(T)() @property const
|
|
if (is(T == const(ubyte)[]))
|
|
{
|
|
enforce(type == 'a' && signature == "y",
|
|
new TypeMismatchException(
|
|
"Cannot get a " ~ T.stringof ~ " from a DBusAny with"
|
|
~ " a value of DBus type '" ~ this.typeSig ~ "'.",
|
|
typeCode!T, type));
|
|
|
|
return binaryData;
|
|
}
|
|
|
|
/// If the value is an array of DictionaryEntries this will return a HashMap
|
|
DBusAny[DBusAny] toAA() {
|
|
enforce(type == 'a' && signature && signature[0] == '{');
|
|
DBusAny[DBusAny] aa;
|
|
foreach(val; array) {
|
|
enforce(val.type == 'e');
|
|
aa[val.entry.key] = val.entry.value;
|
|
}
|
|
return aa;
|
|
}
|
|
|
|
/++
|
|
Get the DBus type signature of the value stored in the DBusAny object.
|
|
|
|
Returns:
|
|
The type signature of the value stored in this DBusAny object.
|
|
+/
|
|
string typeSig() @property const pure nothrow @safe
|
|
{
|
|
if(type == 'a') {
|
|
return "a" ~ signature;
|
|
} else if(type == 'r') {
|
|
return signature;
|
|
} else if(type == 'e') {
|
|
return () @trusted {
|
|
return "{" ~ entry.key.signature ~ entry.value.signature ~ "}";
|
|
} ();
|
|
} else {
|
|
return [ cast(char) type ];
|
|
}
|
|
}
|
|
|
|
/// Converts a basic type, a tuple or an array to the D type with type checking. Tuples can get converted to an array too.
|
|
T to(T)() {
|
|
static if(is(T == Variant!R, R)) {
|
|
static if(is(R == DBusAny)) {
|
|
auto v = to!R;
|
|
v.explicitVariant = false;
|
|
return Variant!R(v);
|
|
} else
|
|
return Variant!R(to!R);
|
|
} else static if(is(T == DBusAny)) {
|
|
return this;
|
|
} else static if(isIntegral!T || isFloatingPoint!T) {
|
|
switch(type) {
|
|
case typeCode!byte:
|
|
return cast(T) int8;
|
|
case typeCode!short:
|
|
return cast(T) int16;
|
|
case typeCode!ushort:
|
|
return cast(T) uint16;
|
|
case typeCode!int:
|
|
return cast(T) int32;
|
|
case typeCode!uint:
|
|
return cast(T) uint32;
|
|
case typeCode!long:
|
|
return cast(T) int64;
|
|
case typeCode!ulong:
|
|
return cast(T) uint64;
|
|
case typeCode!double:
|
|
return cast(T) float64;
|
|
default:
|
|
throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof);
|
|
}
|
|
} else static if(is(T == bool)) {
|
|
if(type == 'b')
|
|
return boolean;
|
|
else
|
|
throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof);
|
|
} else static if(isSomeString!T) {
|
|
if(type == 's')
|
|
return str.to!T;
|
|
else if(type == 'o')
|
|
return obj.toString();
|
|
else
|
|
throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof);
|
|
} else static if(is(T == ObjectPath)) {
|
|
if(type == 'o')
|
|
return obj;
|
|
else
|
|
throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof);
|
|
} else static if(isDynamicArray!T) {
|
|
if(type != 'a' && type != 'r')
|
|
throw new Exception("Can't convert type " ~ cast(char) type ~ " to an array");
|
|
T ret;
|
|
if(signature == ['y']) {
|
|
static if(isIntegral!(ElementType!T))
|
|
foreach(elem; binaryData)
|
|
ret ~= elem.to!(ElementType!T);
|
|
} else
|
|
foreach(elem; array)
|
|
ret ~= elem.to!(ElementType!T);
|
|
return ret;
|
|
} else static if(isTuple!T) {
|
|
if(type != 'r')
|
|
throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof);
|
|
T ret;
|
|
enforce(ret.Types.length == tuple.length, "Tuple length mismatch");
|
|
foreach(index, T; ret.Types)
|
|
ret[index] = tuple[index].to!T;
|
|
return ret;
|
|
} else static if(isAssociativeArray!T) {
|
|
if(type != 'a' || !signature || signature[0] != '{')
|
|
throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof);
|
|
T ret;
|
|
foreach(pair; array) {
|
|
enforce(pair.type == 'e');
|
|
ret[pair.entry.key.to!(KeyType!T)] = pair.entry.value.to!(ValueType!T);
|
|
}
|
|
return ret;
|
|
} else static assert(false, "Can't convert variant to " ~ T.stringof);
|
|
}
|
|
|
|
bool opEquals(ref in DBusAny b) const {
|
|
if(b.type != type || b.explicitVariant != explicitVariant)
|
|
return false;
|
|
if((type == 'a' || type == 'r') && b.signature != signature)
|
|
return false;
|
|
if(type == 'a' && signature == ['y'])
|
|
return binaryData == b.binaryData;
|
|
if(type == 'a')
|
|
return array == b.array;
|
|
else if(type == 'r')
|
|
return tuple == b.tuple;
|
|
else if(type == 's')
|
|
return str == b.str;
|
|
else if(type == 'o')
|
|
return obj == b.obj;
|
|
else if(type == 'e')
|
|
return entry == b.entry || (entry && b.entry && *entry == *b.entry);
|
|
else
|
|
return uint64 == b.uint64;
|
|
}
|
|
}
|
|
|
|
/// Marks the data as variant on serialization
|
|
struct Variant(T) {
|
|
///
|
|
T data;
|
|
}
|
|
|
|
Variant!T variant(T)(T data) {
|
|
return Variant!T(data);
|
|
}
|
|
|
|
enum MessageType {
|
|
Invalid = 0,
|
|
Call, Return, Error, Signal
|
|
}
|
|
|
|
void emitSignal(Args...)(Connection conn, string path, string iface, string name, Args args) {
|
|
Message msg = Message(null, path, iface, name, true);
|
|
msg.build(args);
|
|
conn.send(msg);
|
|
}
|
|
|
|
struct Message {
|
|
DBusMessage *msg;
|
|
|
|
this(string dest, string path, string iface, string method, bool signal = false) {
|
|
if(signal)
|
|
msg = dbus_message_new_signal(path.toStringz(), iface.toStringz(), method.toStringz());
|
|
else
|
|
msg = dbus_message_new_method_call(dest.toStringz(), path.toStringz(), iface.toStringz(), method.toStringz());
|
|
}
|
|
|
|
this(DBusMessage *m) {
|
|
msg = m;
|
|
}
|
|
|
|
this(this) {
|
|
dbus_message_ref(msg);
|
|
}
|
|
|
|
~this() {
|
|
dbus_message_unref(msg);
|
|
}
|
|
|
|
void build(TS...)(TS args) if(allCanDBus!TS) {
|
|
DBusMessageIter iter;
|
|
dbus_message_iter_init_append(msg, &iter);
|
|
buildIter(&iter, args);
|
|
}
|
|
|
|
/**
|
|
Reads the first argument of the message.
|
|
Note that this creates a new iterator every time so calling it multiple times will always
|
|
read the first argument. This is suitable for single item returns.
|
|
To read multiple arguments use readTuple.
|
|
*/
|
|
T read(T)() if(canDBus!T) {
|
|
DBusMessageIter iter;
|
|
dbus_message_iter_init(msg, &iter);
|
|
return readIter!T(&iter);
|
|
}
|
|
alias read to;
|
|
|
|
Tup readTuple(Tup)() if(isTuple!Tup && allCanDBus!(Tup.Types)) {
|
|
DBusMessageIter iter;
|
|
dbus_message_iter_init(msg, &iter);
|
|
Tup ret;
|
|
readIterTuple(&iter, ret);
|
|
return ret;
|
|
}
|
|
|
|
Message createReturn() {
|
|
return Message(dbus_message_new_method_return(msg));
|
|
}
|
|
|
|
MessageType type() {
|
|
return cast(MessageType)dbus_message_get_type(msg);
|
|
}
|
|
|
|
bool isCall() {
|
|
return type() == MessageType.Call;
|
|
}
|
|
|
|
// Various string members
|
|
// TODO: make a mixin to avoid this copy-paste
|
|
string signature() {
|
|
const(char)* cStr = dbus_message_get_signature(msg);
|
|
assert(cStr != null);
|
|
return cStr.fromStringz().idup;
|
|
}
|
|
string path() {
|
|
const(char)* cStr = dbus_message_get_path(msg);
|
|
assert(cStr != null);
|
|
return cStr.fromStringz().idup;
|
|
}
|
|
string iface() {
|
|
const(char)* cStr = dbus_message_get_interface(msg);
|
|
assert(cStr != null);
|
|
return cStr.fromStringz().idup;
|
|
}
|
|
string member() {
|
|
const(char)* cStr = dbus_message_get_member(msg);
|
|
assert(cStr != null);
|
|
return cStr.fromStringz().idup;
|
|
}
|
|
string sender() {
|
|
const(char)* cStr = dbus_message_get_sender(msg);
|
|
assert(cStr != null);
|
|
return cStr.fromStringz().idup;
|
|
}
|
|
}
|
|
|
|
struct Connection {
|
|
DBusConnection *conn;
|
|
this(DBusConnection *connection) {
|
|
conn = connection;
|
|
}
|
|
|
|
this(this) {
|
|
dbus_connection_ref(conn);
|
|
}
|
|
|
|
~this() {
|
|
dbus_connection_unref(conn);
|
|
}
|
|
|
|
void close() {
|
|
dbus_connection_close(conn);
|
|
}
|
|
|
|
void send(Message msg) {
|
|
dbus_connection_send(conn,msg.msg, null);
|
|
}
|
|
|
|
void sendBlocking(Message msg) {
|
|
send(msg);
|
|
dbus_connection_flush(conn);
|
|
}
|
|
|
|
Message sendWithReplyBlocking(Message msg, int timeout = -1) {
|
|
DBusMessage *dbusMsg = msg.msg;
|
|
dbus_message_ref(dbusMsg);
|
|
DBusMessage *reply = wrapErrors((err) {
|
|
auto ret = dbus_connection_send_with_reply_and_block(conn,dbusMsg,timeout,err);
|
|
dbus_message_unref(dbusMsg);
|
|
return ret;
|
|
});
|
|
return Message(reply);
|
|
}
|
|
|
|
Message sendWithReplyBlocking(Message msg, Duration timeout) {
|
|
return sendWithReplyBlocking(msg, timeout.total!"msecs"().to!int);
|
|
}
|
|
}
|
|
|
|
Connection connectToBus(DBusBusType bus = DBusBusType.DBUS_BUS_SESSION) {
|
|
DBusConnection *conn = wrapErrors((err) { return dbus_bus_get(bus,err); });
|
|
return Connection(conn);
|
|
}
|
|
|
|
class PathIface {
|
|
this(Connection conn, string dest, ObjectPath path, string iface) {
|
|
this(conn, dest, path.value, iface);
|
|
}
|
|
|
|
this(Connection conn, string dest, string path, string iface) {
|
|
this.conn = conn;
|
|
this.dest = dest.toStringz();
|
|
this.path = path.toStringz();
|
|
this.iface = iface.toStringz();
|
|
}
|
|
|
|
Ret call(Ret, Args...)(string meth, Args args) if(allCanDBus!Args && canDBus!Ret) {
|
|
Message msg = Message(dbus_message_new_method_call(dest,path,iface,meth.toStringz()));
|
|
msg.build(args);
|
|
Message ret = conn.sendWithReplyBlocking(msg);
|
|
return ret.read!Ret();
|
|
}
|
|
|
|
Message opDispatch(string meth, Args...)(Args args) {
|
|
Message msg = Message(dbus_message_new_method_call(dest,path,iface,meth.toStringz()));
|
|
msg.build(args);
|
|
return conn.sendWithReplyBlocking(msg);
|
|
}
|
|
|
|
Connection conn;
|
|
const(char)* dest;
|
|
const(char)* path;
|
|
const(char)* iface;
|
|
}
|
|
|
|
enum SignalMethod;
|
|
|
|
/**
|
|
Registers all *possible* methods of an object in a router.
|
|
It will not register methods that use types that ddbus can't handle.
|
|
|
|
The implementation is rather hacky and uses the compiles trait to check for things
|
|
working so if some methods randomly don't seem to be added, you should probably use
|
|
setHandler on the router directly. It is also not efficient and creates a closure for every method.
|
|
|
|
TODO: replace this with something that generates a wrapper class who's methods take and return messages
|
|
and basically do what MessageRouter.setHandler does but avoiding duplication. Then this DBusWrapper!Class
|
|
could be instantiated with any object efficiently and placed in the router table with minimal duplication.
|
|
*/
|
|
void registerMethods(T : Object)(MessageRouter router, string path, string iface, T obj) {
|
|
MessagePattern patt = MessagePattern(path,iface,"",false);
|
|
foreach(member; __traits(allMembers, T)) {
|
|
static if (__traits(compiles, __traits(getOverloads, obj, member))
|
|
&& __traits(getOverloads, obj, member).length > 0
|
|
&& __traits(compiles, router.setHandler(patt, &__traits(getOverloads,obj,member)[0]))) {
|
|
patt.method = member;
|
|
patt.signal = hasUDA!(__traits(getOverloads,obj,member)[0], SignalMethod);
|
|
router.setHandler(patt, &__traits(getOverloads,obj,member)[0]);
|
|
}
|
|
}
|
|
}
|
|
|
|
struct MessagePattern {
|
|
string path;
|
|
string iface;
|
|
string method;
|
|
bool signal;
|
|
|
|
this(Message msg) {
|
|
path = msg.path();
|
|
iface = msg.iface();
|
|
method = msg.member();
|
|
signal = (msg.type() == MessageType.Signal);
|
|
}
|
|
|
|
this(string path, string iface, string method, bool signal = false) {
|
|
this.path = path;
|
|
this.iface = iface;
|
|
this.method = method;
|
|
this.signal = signal;
|
|
}
|
|
|
|
size_t toHash() const @safe nothrow {
|
|
size_t hash = 0;
|
|
auto stringHash = &(typeid(path).getHash);
|
|
hash += stringHash(&path);
|
|
hash += stringHash(&iface);
|
|
hash += stringHash(&method);
|
|
hash += (signal?1:0);
|
|
return hash;
|
|
}
|
|
|
|
bool opEquals(ref const typeof(this) s) const @safe pure nothrow {
|
|
return (path == s.path) && (iface == s.iface) && (method == s.method) && (signal == s.signal);
|
|
}
|
|
}
|
|
|
|
struct MessageHandler {
|
|
alias HandlerFunc = void delegate(Message call, Connection conn);
|
|
HandlerFunc func;
|
|
string[] argSig;
|
|
string[] retSig;
|
|
}
|
|
|
|
class MessageRouter {
|
|
MessageHandler[MessagePattern] callTable;
|
|
|
|
bool handle(Message msg, Connection conn) {
|
|
MessageType type = msg.type();
|
|
if(type != MessageType.Call && type != MessageType.Signal)
|
|
return false;
|
|
auto pattern = MessagePattern(msg);
|
|
// import std.stdio; debug writeln("Handling ", pattern);
|
|
|
|
if(pattern.iface == "org.freedesktop.DBus.Introspectable" &&
|
|
pattern.method == "Introspect" && !pattern.signal) {
|
|
handleIntrospect(pattern.path, msg, conn);
|
|
return true;
|
|
}
|
|
|
|
MessageHandler* handler = (pattern in callTable);
|
|
if(handler is null) return false;
|
|
|
|
// Check for matching argument types
|
|
version(DDBusNoChecking) {
|
|
|
|
} else {
|
|
if(!equal(join(handler.argSig), msg.signature())) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
handler.func(msg,conn);
|
|
return true;
|
|
}
|
|
|
|
void setHandler(Ret, Args...)(MessagePattern patt, Ret delegate(Args) handler) {
|
|
void handlerWrapper(Message call, Connection conn) {
|
|
Tuple!Args args = call.readTuple!(Tuple!Args)();
|
|
auto retMsg = call.createReturn();
|
|
static if(!is(Ret == void)) {
|
|
Ret ret = handler(args.expand);
|
|
static if (is(Ret == Tuple!T, T...))
|
|
retMsg.build!T(ret.expand);
|
|
else
|
|
retMsg.build(ret);
|
|
} else {
|
|
handler(args.expand);
|
|
}
|
|
if(!patt.signal)
|
|
conn.send(retMsg);
|
|
}
|
|
static string[] args = typeSigArr!Args;
|
|
static if(is(Ret==void)) {
|
|
static string[] ret = [];
|
|
} else {
|
|
static string[] ret = typeSigReturn!Ret;
|
|
}
|
|
MessageHandler handleStruct = {func: &handlerWrapper, argSig: args, retSig: ret};
|
|
callTable[patt] = handleStruct;
|
|
}
|
|
|
|
static string introspectHeader = `<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
|
<node name="%s">`;
|
|
|
|
string introspectXML(string path) {
|
|
auto methods = callTable.byKey().filter!(a => (a.path == path) && !a.signal)().array()
|
|
// .schwartzSort!((a) => a.iface, "a<b")();
|
|
.sort!((a,b) => a.iface < b.iface)();
|
|
auto ifaces = methods.groupBy();
|
|
auto app = appender!string;
|
|
formattedWrite(app,introspectHeader,path);
|
|
foreach(iface; ifaces) {
|
|
formattedWrite(app,`<interface name="%s">`,iface.front.iface);
|
|
foreach(methodPatt; iface.array()) {
|
|
formattedWrite(app,`<method name="%s">`,methodPatt.method);
|
|
auto handler = callTable[methodPatt];
|
|
foreach(arg; handler.argSig) {
|
|
formattedWrite(app,`<arg type="%s" direction="in"/>`,arg);
|
|
}
|
|
foreach(arg; handler.retSig) {
|
|
formattedWrite(app,`<arg type="%s" direction="out"/>`,arg);
|
|
}
|
|
app.put("</method>");
|
|
}
|
|
app.put("</interface>");
|
|
}
|
|
|
|
string childPath = path;
|
|
if(!childPath.endsWith("/")) {
|
|
childPath ~= "/";
|
|
}
|
|
auto children = callTable.byKey().filter!(a => (a.path.startsWith(childPath)) && !a.signal)()
|
|
.map!((s) => s.path.chompPrefix(childPath))
|
|
.map!((s) => s.splitter('/').front)
|
|
.array().sort().uniq();
|
|
foreach(child; children) {
|
|
formattedWrite(app,`<node name="%s"/>`,child);
|
|
}
|
|
|
|
app.put("</node>");
|
|
return app.data;
|
|
}
|
|
|
|
void handleIntrospect(string path, Message call, Connection conn) {
|
|
auto retMsg = call.createReturn();
|
|
retMsg.build(introspectXML(path));
|
|
conn.sendBlocking(retMsg);
|
|
}
|
|
}
|
|
|
|
extern(C) private DBusHandlerResult filterFunc(DBusConnection *dConn, DBusMessage *dMsg, void *routerP) {
|
|
MessageRouter router = cast(MessageRouter)routerP;
|
|
dbus_message_ref(dMsg);
|
|
Message msg = Message(dMsg);
|
|
dbus_connection_ref(dConn);
|
|
Connection conn = Connection(dConn);
|
|
bool handled = router.handle(msg, conn);
|
|
if(handled) {
|
|
return DBusHandlerResult.DBUS_HANDLER_RESULT_HANDLED;
|
|
} else {
|
|
return DBusHandlerResult.DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
|
}
|
|
}
|
|
|
|
extern(C) private void unrootUserData(void *userdata) {
|
|
GC.removeRoot(userdata);
|
|
}
|
|
|
|
void registerRouter(Connection conn, MessageRouter router) {
|
|
void *routerP = cast(void*)router;
|
|
GC.addRoot(routerP);
|
|
dbus_connection_add_filter(conn.conn, &filterFunc, routerP, &unrootUserData);
|
|
}
|
|
|
|
private T wrapErrors(T)(
|
|
T delegate(DBusError *err) del,
|
|
string file = __FILE__,
|
|
size_t line = __LINE__,
|
|
Throwable next = null
|
|
) {
|
|
DBusError error;
|
|
dbus_error_init(&error);
|
|
T ret = del(&error);
|
|
if(dbus_error_is_set(&error)) {
|
|
auto ex = new DBusException(&error, file, line, next);
|
|
dbus_error_free(&error);
|
|
throw ex;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/++
|
|
Thrown when a DBus error code was returned by libdbus.
|
|
+/
|
|
class DBusException : Exception {
|
|
private this(
|
|
scope DBusError *err,
|
|
string file = __FILE__,
|
|
size_t line = __LINE__,
|
|
Throwable next = null
|
|
) pure nothrow {
|
|
|
|
super(err.message.fromStringz().idup, file, line, next);
|
|
}
|
|
}
|
|
|
|
/++
|
|
Thrown when the signature of a message does not match the requested types or
|
|
when trying to get a value from a DBusAny object that does not match the type
|
|
of its actual value.
|
|
+/
|
|
class TypeMismatchException : Exception {
|
|
private this(
|
|
int expectedType,
|
|
int actualType,
|
|
string file = __FILE__,
|
|
size_t line = __LINE__,
|
|
Throwable next = null
|
|
) pure nothrow @safe {
|
|
string message;
|
|
|
|
if (expectedType == 'v') {
|
|
message = "The type of value at the current position in the message is"
|
|
~ " incompatible to the target variant type."
|
|
~ " Type code of the value: '" ~ cast(char) actualType ~ '\'';
|
|
} else {
|
|
message = "The type of value at the current position in the message does"
|
|
~ " not match the type of value to be read."
|
|
~ " Expected: '" ~ cast(char) expectedType ~ "',"
|
|
~ " Got: '" ~ cast(char) actualType ~ '\'';
|
|
}
|
|
|
|
this(message, expectedType, actualType, file, line, next);
|
|
}
|
|
|
|
this(
|
|
string message,
|
|
int expectedType,
|
|
int actualType,
|
|
string file = __FILE__,
|
|
size_t line = __LINE__,
|
|
Throwable next = null
|
|
) pure nothrow @safe {
|
|
_expectedType = expectedType;
|
|
_actualType = actualType;
|
|
super(message, file, line, next);
|
|
}
|
|
|
|
int expectedType() @property pure const nothrow @safe @nogc {
|
|
return _expectedType;
|
|
}
|
|
|
|
int actualType() @property pure const nothrow @safe @nogc {
|
|
return _actualType;
|
|
}
|
|
|
|
private:
|
|
int _expectedType;
|
|
int _actualType;
|
|
}
|
|
|
|
/++
|
|
Thrown during type conversion between DBus types and D types when a value is
|
|
encountered that can not be represented in the target type.
|
|
|
|
This exception should not normally be thrown except when dealing with D types
|
|
that have a constrained value set, such as Enums.
|
|
+/
|
|
class InvalidValueException : Exception {
|
|
private this(Source)(
|
|
Source value,
|
|
string targetType,
|
|
string file = __FILE__,
|
|
size_t line = __LINE__,
|
|
Throwable next = null
|
|
) {
|
|
import std.conv : to;
|
|
|
|
static if(__traits(compiles, value.to!string))
|
|
string valueString = value.to!string;
|
|
else
|
|
string valueString = "(unprintable)";
|
|
|
|
super("Value " ~ valueString ~ " cannot be represented in type " ~ targetType);
|
|
}
|
|
}
|
|
|
|
import std.exception : enforce;
|
|
import std.meta: allSatisfy;
|
|
import std.range;
|
|
import std.traits;
|
|
import std.variant : VariantN;
|
|
|
|
void buildIter(TS...)(DBusMessageIter *iter, TS args) if(allCanDBus!TS) {
|
|
foreach(index, arg; args) {
|
|
alias TS[index] T;
|
|
static if(is(T == string)) {
|
|
immutable(char)* cStr = arg.toStringz();
|
|
dbus_message_iter_append_basic(iter,typeCode!T,&cStr);
|
|
} else static if(is(T == ObjectPath)) {
|
|
immutable(char)* cStr = arg.toString().toStringz();
|
|
dbus_message_iter_append_basic(iter,typeCode!T,&cStr);
|
|
} else static if(is(T==bool)) {
|
|
dbus_bool_t longerBool = arg; // dbus bools are ints
|
|
dbus_message_iter_append_basic(iter,typeCode!T,&longerBool);
|
|
} else static if(isTuple!T) {
|
|
DBusMessageIter sub;
|
|
dbus_message_iter_open_container(iter, 'r', null, &sub);
|
|
buildIter(&sub, arg.expand);
|
|
dbus_message_iter_close_container(iter, &sub);
|
|
} else static if(isInputRange!T) {
|
|
DBusMessageIter sub;
|
|
const(char)* subSig = (typeSig!(ElementType!T)()).toStringz();
|
|
dbus_message_iter_open_container(iter, 'a', subSig, &sub);
|
|
foreach(x; arg) {
|
|
static if(isInstanceOf!(DictionaryEntry, typeof(x))) {
|
|
DBusMessageIter entry;
|
|
dbus_message_iter_open_container(&sub, 'e', null, &entry);
|
|
buildIter(&entry, x.key);
|
|
buildIter(&entry, x.value);
|
|
dbus_message_iter_close_container(&sub, &entry);
|
|
} else {
|
|
buildIter(&sub, x);
|
|
}
|
|
}
|
|
dbus_message_iter_close_container(iter, &sub);
|
|
} else static if(isAssociativeArray!T) {
|
|
DBusMessageIter sub;
|
|
const(char)* subSig = typeSig!T[1..$].toStringz();
|
|
dbus_message_iter_open_container(iter, 'a', subSig, &sub);
|
|
foreach(k, v; arg) {
|
|
DBusMessageIter entry;
|
|
dbus_message_iter_open_container(&sub, 'e', null, &entry);
|
|
buildIter(&entry, k);
|
|
buildIter(&entry, v);
|
|
dbus_message_iter_close_container(&sub, &entry);
|
|
}
|
|
dbus_message_iter_close_container(iter, &sub);
|
|
} else static if(isInstanceOf!(VariantN, T)) {
|
|
enforce(arg.hasValue,
|
|
new InvalidValueException(arg, "dbus:" ~ cast(char) typeCode!T));
|
|
|
|
DBusMessageIter sub;
|
|
foreach(AT; T.AllowedTypes) {
|
|
if (arg.peek!AT) {
|
|
dbus_message_iter_open_container(iter, 'v', typeSig!AT.ptr, &sub);
|
|
buildIter(&sub, arg.get!AT);
|
|
dbus_message_iter_close_container(iter, &sub);
|
|
break;
|
|
}
|
|
}
|
|
} else static if(is(T == DBusAny) || is(T == Variant!DBusAny)) {
|
|
static if(is(T == Variant!DBusAny)) {
|
|
auto val = arg.data;
|
|
val.explicitVariant = true;
|
|
} else {
|
|
auto val = arg;
|
|
}
|
|
DBusMessageIter subStore;
|
|
DBusMessageIter* sub = &subStore;
|
|
const(char)[] sig = [ cast(char) val.type ];
|
|
if(val.type == 'a')
|
|
sig ~= val.signature;
|
|
else if(val.type == 'r')
|
|
sig = val.signature;
|
|
sig ~= '\0';
|
|
if (!val.explicitVariant)
|
|
sub = iter;
|
|
else
|
|
dbus_message_iter_open_container(iter, 'v', sig.ptr, sub);
|
|
if(val.type == 's') {
|
|
buildIter(sub, val.str);
|
|
} else if(val.type == 'o') {
|
|
buildIter(sub, val.obj);
|
|
} else if(val.type == 'b') {
|
|
buildIter(sub,val.boolean);
|
|
} else if(dbus_type_is_basic(val.type)) {
|
|
dbus_message_iter_append_basic(sub,val.type,&val.int64);
|
|
} else if(val.type == 'a') {
|
|
DBusMessageIter arr;
|
|
dbus_message_iter_open_container(sub, 'a', sig[1 .. $].ptr, &arr);
|
|
if (val.signature == ['y'])
|
|
foreach (item; val.binaryData)
|
|
dbus_message_iter_append_basic(&arr, 'y', &item);
|
|
else
|
|
foreach(item; val.array)
|
|
buildIter(&arr, item);
|
|
dbus_message_iter_close_container(sub, &arr);
|
|
} else if(val.type == 'r') {
|
|
DBusMessageIter arr;
|
|
dbus_message_iter_open_container(sub, 'r', null, &arr);
|
|
foreach(item; val.tuple)
|
|
buildIter(&arr, item);
|
|
dbus_message_iter_close_container(sub, &arr);
|
|
} else if(val.type == 'e') {
|
|
DBusMessageIter entry;
|
|
dbus_message_iter_open_container(sub, 'e', null, &entry);
|
|
buildIter(&entry, val.entry.key);
|
|
buildIter(&entry, val.entry.value);
|
|
dbus_message_iter_close_container(sub, &entry);
|
|
}
|
|
if(val.explicitVariant)
|
|
dbus_message_iter_close_container(iter, sub);
|
|
} else static if(isInstanceOf!(Variant, T)) {
|
|
DBusMessageIter sub;
|
|
const(char)* subSig = typeSig!(VariantType!T).toStringz();
|
|
dbus_message_iter_open_container(iter, 'v', subSig, &sub);
|
|
buildIter(&sub, arg.data);
|
|
dbus_message_iter_close_container(iter, &sub);
|
|
} else static if(is(T == struct)) {
|
|
DBusMessageIter sub;
|
|
dbus_message_iter_open_container(iter, 'r', null, &sub);
|
|
|
|
// Following failed because of missing 'this' for members of arg.
|
|
// That sucks. It worked without Filter.
|
|
// Reported: https://issues.dlang.org/show_bug.cgi?id=17692
|
|
// buildIter(&sub, Filter!(isAllowedField, arg.tupleof));
|
|
|
|
// Using foreach to work around the issue
|
|
foreach(i, member; arg.tupleof) {
|
|
// Ugly, but we need to use tupleof again in the condition, because when
|
|
// we use `member`, isAllowedField will fail because it'll find this
|
|
// nice `buildIter` function instead of T when it looks up the parent
|
|
// scope of its argument.
|
|
static if (isAllowedField!(arg.tupleof[i]))
|
|
buildIter(&sub, member);
|
|
}
|
|
|
|
dbus_message_iter_close_container(iter, &sub);
|
|
} else static if(basicDBus!T) {
|
|
dbus_message_iter_append_basic(iter,typeCode!T,&arg);
|
|
}
|
|
}
|
|
}
|
|
|
|
T readIter(T)(DBusMessageIter *iter) if (is(T == enum)) {
|
|
import std.algorithm.searching : canFind;
|
|
|
|
alias OriginalType!T B;
|
|
|
|
B value = readIter!B(iter);
|
|
enforce(
|
|
only(EnumMembers!T).canFind(value),
|
|
new InvalidValueException(value, T.stringof)
|
|
);
|
|
return cast(T) value;
|
|
}
|
|
|
|
T readIter(T)(DBusMessageIter *iter) if (isInstanceOf!(BitFlags, T)) {
|
|
import std.algorithm.iteration : fold;
|
|
|
|
alias TemplateArgsOf!T[0] E;
|
|
alias OriginalType!E B;
|
|
|
|
B mask = only(EnumMembers!E).fold!((a, b) => cast(B) (a | b));
|
|
|
|
B value = readIter!B(iter);
|
|
enforce(
|
|
!(value & ~mask),
|
|
new InvalidValueException(value, T.stringof)
|
|
);
|
|
|
|
return T(cast(E) value);
|
|
}
|
|
|
|
T readIter(T)(DBusMessageIter *iter) if (!is(T == enum) && !isInstanceOf!(BitFlags, T) && canDBus!T) {
|
|
auto argType = dbus_message_iter_get_arg_type(iter);
|
|
T ret;
|
|
|
|
static if(!isInstanceOf!(Variant, T) || is(T == Variant!DBusAny)) {
|
|
if(argType == 'v') {
|
|
DBusMessageIter sub;
|
|
dbus_message_iter_recurse(iter, &sub);
|
|
static if(is(T == Variant!DBusAny)) {
|
|
ret = variant(readIter!DBusAny(&sub));
|
|
} else {
|
|
ret = readIter!T(&sub);
|
|
static if(is(T == DBusAny))
|
|
ret.explicitVariant = true;
|
|
}
|
|
dbus_message_iter_next(iter);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
static if(
|
|
!is(T == DBusAny)
|
|
&& !is(T == Variant!DBusAny)
|
|
&& !isInstanceOf!(VariantN, T)
|
|
) {
|
|
enforce(argType == typeCode!T(),
|
|
new TypeMismatchException(typeCode!T(), argType));
|
|
}
|
|
static if(is(T==string) || is(T==ObjectPath)) {
|
|
const(char)* cStr;
|
|
dbus_message_iter_get_basic(iter, &cStr);
|
|
string str = cStr.fromStringz().idup; // copy string
|
|
static if(is(T==string))
|
|
ret = str;
|
|
else
|
|
ret = ObjectPath(str);
|
|
} else static if(is(T==bool)) {
|
|
dbus_bool_t longerBool;
|
|
dbus_message_iter_get_basic(iter, &longerBool);
|
|
ret = cast(bool)longerBool;
|
|
} else static if(isTuple!T) {
|
|
DBusMessageIter sub;
|
|
dbus_message_iter_recurse(iter, &sub);
|
|
readIterTuple!T(&sub, ret);
|
|
} else static if(is(T t : U[], U)) {
|
|
assert(dbus_message_iter_get_element_type(iter) == typeCode!U);
|
|
DBusMessageIter sub;
|
|
dbus_message_iter_recurse(iter, &sub);
|
|
while(dbus_message_iter_get_arg_type(&sub) != 0) {
|
|
static if(is(U == DictionaryEntry!(K,V), K, V)) {
|
|
DBusMessageIter entry;
|
|
dbus_message_iter_recurse(&sub, &entry);
|
|
ret ~= U(readIter!K(&entry), readIter!V(&entry));
|
|
dbus_message_iter_next(&sub);
|
|
} else {
|
|
ret ~= readIter!U(&sub);
|
|
}
|
|
}
|
|
} else static if(isInstanceOf!(Variant, T)) {
|
|
DBusMessageIter sub;
|
|
dbus_message_iter_recurse(iter, &sub);
|
|
ret.data = readIter!(VariantType!T)(&sub);
|
|
} else static if(isInstanceOf!(VariantN, T)) {
|
|
scope const(char)[] argSig =
|
|
dbus_message_iter_get_signature(iter).fromStringz();
|
|
scope(exit)
|
|
dbus_free(cast(void*) argSig.ptr);
|
|
|
|
foreach(AT; T.AllowedTypes) {
|
|
// We have to compare the full signature here, not just the typecode.
|
|
// Otherwise, in case of container types, we might select the wrong one.
|
|
// We would then be calling an incorrect instance of readIter, which would
|
|
// probably throw a TypeMismatchException.
|
|
if (typeSig!AT == argSig) {
|
|
ret = readIter!AT(iter);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If no value is in ret, apparently none of the types matched.
|
|
enforce(ret.hasValue, new TypeMismatchException(typeCode!T, argType));
|
|
} else static if(isAssociativeArray!T) {
|
|
DBusMessageIter sub;
|
|
dbus_message_iter_recurse(iter, &sub);
|
|
while(dbus_message_iter_get_arg_type(&sub) != 0) {
|
|
DBusMessageIter entry;
|
|
dbus_message_iter_recurse(&sub, &entry);
|
|
auto k = readIter!(KeyType!T)(&entry);
|
|
auto v = readIter!(ValueType!T)(&entry);
|
|
ret[k] = v;
|
|
dbus_message_iter_next(&sub);
|
|
}
|
|
} else static if(is(T == DBusAny)) {
|
|
ret.type = argType;
|
|
ret.explicitVariant = false;
|
|
if(ret.type == 's') {
|
|
ret.str = readIter!string(iter);
|
|
return ret;
|
|
} else if(ret.type == 'o') {
|
|
ret.obj = readIter!ObjectPath(iter);
|
|
return ret;
|
|
} else if(ret.type == 'b') {
|
|
ret.boolean = readIter!bool(iter);
|
|
return ret;
|
|
} else if(dbus_type_is_basic(ret.type)) {
|
|
dbus_message_iter_get_basic(iter, &ret.int64);
|
|
} else if(ret.type == 'a') {
|
|
DBusMessageIter sub;
|
|
dbus_message_iter_recurse(iter, &sub);
|
|
auto sig = dbus_message_iter_get_signature(&sub);
|
|
ret.signature = sig.fromStringz.dup;
|
|
dbus_free(sig);
|
|
if (ret.signature == ['y'])
|
|
while(dbus_message_iter_get_arg_type(&sub) != 0) {
|
|
ubyte b;
|
|
assert(dbus_message_iter_get_arg_type(&sub) == 'y');
|
|
dbus_message_iter_get_basic(&sub, &b);
|
|
dbus_message_iter_next(&sub);
|
|
ret.binaryData ~= b;
|
|
}
|
|
else
|
|
while(dbus_message_iter_get_arg_type(&sub) != 0) {
|
|
ret.array ~= readIter!DBusAny(&sub);
|
|
}
|
|
} else if(ret.type == 'r') {
|
|
auto sig = dbus_message_iter_get_signature(iter);
|
|
ret.signature = sig.fromStringz.dup;
|
|
dbus_free(sig);
|
|
DBusMessageIter sub;
|
|
dbus_message_iter_recurse(iter, &sub);
|
|
while(dbus_message_iter_get_arg_type(&sub) != 0) {
|
|
ret.tuple ~= readIter!DBusAny(&sub);
|
|
}
|
|
} else if(ret.type == 'e') {
|
|
DBusMessageIter sub;
|
|
dbus_message_iter_recurse(iter, &sub);
|
|
ret.entry = new DictionaryEntry!(DBusAny, DBusAny);
|
|
ret.entry.key = readIter!DBusAny(&sub);
|
|
ret.entry.value = readIter!DBusAny(&sub);
|
|
}
|
|
} else static if(is(T == struct)) {
|
|
DBusMessageIter sub;
|
|
dbus_message_iter_recurse(iter, &sub);
|
|
readIterStruct!T(&sub, ret);
|
|
} else static if(basicDBus!T) {
|
|
dbus_message_iter_get_basic(iter, &ret);
|
|
}
|
|
|
|
dbus_message_iter_next(iter);
|
|
return ret;
|
|
}
|
|
|
|
void readIterTuple(Tup)(DBusMessageIter *iter, ref Tup tuple) if(isTuple!Tup && allCanDBus!(Tup.Types)) {
|
|
foreach(index, T; Tup.Types) {
|
|
tuple[index] = readIter!T(iter);
|
|
}
|
|
}
|
|
|
|
void readIterStruct(S)(DBusMessageIter *iter, ref S s) if(is(S == struct) && canDBus!S)
|
|
{
|
|
foreach(index, T; Fields!S) {
|
|
static if (isAllowedField!(s.tupleof[index])) {
|
|
s.tupleof[index] = readIter!T(iter);
|
|
}
|
|
}
|
|
}
|
|
|
|
import core.stdc.config;
|
|
import core.stdc.stdarg;
|
|
extern (C) {
|
|
// START dbus/dbus-arch-deps.d
|
|
alias c_long dbus_int64_t;
|
|
alias c_ulong dbus_uint64_t;
|
|
alias int dbus_int32_t;
|
|
alias uint dbus_uint32_t;
|
|
alias short dbus_int16_t;
|
|
alias ushort dbus_uint16_t;
|
|
// END dbus/dbus-arch-deps.d
|
|
// START dbus/dbus-types.d
|
|
alias uint dbus_unichar_t;
|
|
alias uint dbus_bool_t;
|
|
|
|
|
|
|
|
struct DBus8ByteStruct
|
|
{
|
|
dbus_uint32_t first32;
|
|
dbus_uint32_t second32;
|
|
}
|
|
|
|
union DBusBasicValue
|
|
{
|
|
ubyte[8] bytes;
|
|
dbus_int16_t i16;
|
|
dbus_uint16_t u16;
|
|
dbus_int32_t i32;
|
|
dbus_uint32_t u32;
|
|
dbus_bool_t bool_val;
|
|
dbus_int64_t i64;
|
|
dbus_uint64_t u64;
|
|
DBus8ByteStruct eight;
|
|
double dbl;
|
|
ubyte byt;
|
|
char* str;
|
|
int fd;
|
|
}
|
|
// END dbus/dbus-types.d
|
|
// START dbus/dbus-protocol.d
|
|
|
|
// END dbus/dbus-protocol.d
|
|
// START dbus/dbus-errors.d
|
|
struct DBusError
|
|
{
|
|
const(char)* name;
|
|
const(char)* message;
|
|
uint dummy1;
|
|
uint dummy2;
|
|
uint dummy3;
|
|
uint dummy4;
|
|
uint dummy5;
|
|
void* padding1;
|
|
}
|
|
|
|
void dbus_error_init (DBusError* error);
|
|
void dbus_error_free (DBusError* error);
|
|
void dbus_set_error (DBusError* error, const(char)* name, const(char)* message, ...);
|
|
void dbus_set_error_const (DBusError* error, const(char)* name, const(char)* message);
|
|
void dbus_move_error (DBusError* src, DBusError* dest);
|
|
dbus_bool_t dbus_error_has_name (const(DBusError)* error, const(char)* name);
|
|
dbus_bool_t dbus_error_is_set (const(DBusError)* error);
|
|
// END dbus/dbus-errors.d
|
|
// START dbus/dbus-macros.d
|
|
|
|
// END dbus/dbus-macros.d
|
|
// START dbus/dbus-memory.d
|
|
alias void function (void*) DBusFreeFunction;
|
|
|
|
void* dbus_malloc (size_t bytes);
|
|
void* dbus_malloc0 (size_t bytes);
|
|
void* dbus_realloc (void* memory, size_t bytes);
|
|
void dbus_free (void* memory);
|
|
void dbus_free_string_array (char** str_array);
|
|
void dbus_shutdown ();
|
|
// END dbus/dbus-memory.d
|
|
// START dbus/dbus-shared.d
|
|
enum DBusBusType
|
|
{
|
|
DBUS_BUS_SESSION = 0,
|
|
DBUS_BUS_SYSTEM = 1,
|
|
DBUS_BUS_STARTER = 2
|
|
}
|
|
|
|
enum DBusHandlerResult
|
|
{
|
|
DBUS_HANDLER_RESULT_HANDLED = 0,
|
|
DBUS_HANDLER_RESULT_NOT_YET_HANDLED = 1,
|
|
DBUS_HANDLER_RESULT_NEED_MEMORY = 2
|
|
}
|
|
// END dbus/dbus-shared.d
|
|
// START dbus/dbus-address.d
|
|
struct DBusAddressEntry;
|
|
|
|
|
|
dbus_bool_t dbus_parse_address (const(char)* address, DBusAddressEntry*** entry, int* array_len, DBusError* error);
|
|
const(char)* dbus_address_entry_get_value (DBusAddressEntry* entry, const(char)* key);
|
|
const(char)* dbus_address_entry_get_method (DBusAddressEntry* entry);
|
|
void dbus_address_entries_free (DBusAddressEntry** entries);
|
|
char* dbus_address_escape_value (const(char)* value);
|
|
char* dbus_address_unescape_value (const(char)* value, DBusError* error);
|
|
// END dbus/dbus-address.d
|
|
// START dbus/dbus-syntax.d
|
|
dbus_bool_t dbus_validate_path (const(char)* path, DBusError* error);
|
|
dbus_bool_t dbus_validate_interface (const(char)* name, DBusError* error);
|
|
dbus_bool_t dbus_validate_member (const(char)* name, DBusError* error);
|
|
dbus_bool_t dbus_validate_error_name (const(char)* name, DBusError* error);
|
|
dbus_bool_t dbus_validate_bus_name (const(char)* name, DBusError* error);
|
|
dbus_bool_t dbus_validate_utf8 (const(char)* alleged_utf8, DBusError* error);
|
|
// END dbus/dbus-syntax.d
|
|
// START dbus/dbus-signature.d
|
|
struct DBusSignatureIter
|
|
{
|
|
void* dummy1;
|
|
void* dummy2;
|
|
dbus_uint32_t dummy8;
|
|
int dummy12;
|
|
int dummy17;
|
|
}
|
|
|
|
void dbus_signature_iter_init (DBusSignatureIter* iter, const(char)* signature);
|
|
int dbus_signature_iter_get_current_type (const(DBusSignatureIter)* iter);
|
|
char* dbus_signature_iter_get_signature (const(DBusSignatureIter)* iter);
|
|
int dbus_signature_iter_get_element_type (const(DBusSignatureIter)* iter);
|
|
dbus_bool_t dbus_signature_iter_next (DBusSignatureIter* iter);
|
|
void dbus_signature_iter_recurse (const(DBusSignatureIter)* iter, DBusSignatureIter* subiter);
|
|
dbus_bool_t dbus_signature_validate (const(char)* signature, DBusError* error);
|
|
dbus_bool_t dbus_signature_validate_single (const(char)* signature, DBusError* error);
|
|
dbus_bool_t dbus_type_is_valid (int typecode);
|
|
dbus_bool_t dbus_type_is_basic (int typecode);
|
|
dbus_bool_t dbus_type_is_container (int typecode);
|
|
dbus_bool_t dbus_type_is_fixed (int typecode);
|
|
// END dbus/dbus-signature.d
|
|
// START dbus/dbus-misc.d
|
|
char* dbus_get_local_machine_id ();
|
|
void dbus_get_version (int* major_version_p, int* minor_version_p, int* micro_version_p);
|
|
dbus_bool_t dbus_setenv (const(char)* variable, const(char)* value);
|
|
// END dbus/dbus-misc.d
|
|
// START dbus/dbus-threads.d
|
|
alias DBusMutex* function () DBusMutexNewFunction;
|
|
alias void function (DBusMutex*) DBusMutexFreeFunction;
|
|
alias uint function (DBusMutex*) DBusMutexLockFunction;
|
|
alias uint function (DBusMutex*) DBusMutexUnlockFunction;
|
|
alias DBusMutex* function () DBusRecursiveMutexNewFunction;
|
|
alias void function (DBusMutex*) DBusRecursiveMutexFreeFunction;
|
|
alias void function (DBusMutex*) DBusRecursiveMutexLockFunction;
|
|
alias void function (DBusMutex*) DBusRecursiveMutexUnlockFunction;
|
|
alias DBusCondVar* function () DBusCondVarNewFunction;
|
|
alias void function (DBusCondVar*) DBusCondVarFreeFunction;
|
|
alias void function (DBusCondVar*, DBusMutex*) DBusCondVarWaitFunction;
|
|
alias uint function (DBusCondVar*, DBusMutex*, int) DBusCondVarWaitTimeoutFunction;
|
|
alias void function (DBusCondVar*) DBusCondVarWakeOneFunction;
|
|
alias void function (DBusCondVar*) DBusCondVarWakeAllFunction;
|
|
|
|
|
|
|
|
enum DBusThreadFunctionsMask
|
|
{
|
|
DBUS_THREAD_FUNCTIONS_MUTEX_NEW_MASK = 1,
|
|
DBUS_THREAD_FUNCTIONS_MUTEX_FREE_MASK = 2,
|
|
DBUS_THREAD_FUNCTIONS_MUTEX_LOCK_MASK = 4,
|
|
DBUS_THREAD_FUNCTIONS_MUTEX_UNLOCK_MASK = 8,
|
|
DBUS_THREAD_FUNCTIONS_CONDVAR_NEW_MASK = 16,
|
|
DBUS_THREAD_FUNCTIONS_CONDVAR_FREE_MASK = 32,
|
|
DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_MASK = 64,
|
|
DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_TIMEOUT_MASK = 128,
|
|
DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ONE_MASK = 256,
|
|
DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ALL_MASK = 512,
|
|
DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_NEW_MASK = 1024,
|
|
DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_FREE_MASK = 2048,
|
|
DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_LOCK_MASK = 4096,
|
|
DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_UNLOCK_MASK = 8192,
|
|
DBUS_THREAD_FUNCTIONS_ALL_MASK = 16383
|
|
}
|
|
|
|
struct DBusThreadFunctions
|
|
{
|
|
uint mask;
|
|
DBusMutexNewFunction mutex_new;
|
|
DBusMutexFreeFunction mutex_free;
|
|
DBusMutexLockFunction mutex_lock;
|
|
DBusMutexUnlockFunction mutex_unlock;
|
|
DBusCondVarNewFunction condvar_new;
|
|
DBusCondVarFreeFunction condvar_free;
|
|
DBusCondVarWaitFunction condvar_wait;
|
|
DBusCondVarWaitTimeoutFunction condvar_wait_timeout;
|
|
DBusCondVarWakeOneFunction condvar_wake_one;
|
|
DBusCondVarWakeAllFunction condvar_wake_all;
|
|
DBusRecursiveMutexNewFunction recursive_mutex_new;
|
|
DBusRecursiveMutexFreeFunction recursive_mutex_free;
|
|
DBusRecursiveMutexLockFunction recursive_mutex_lock;
|
|
DBusRecursiveMutexUnlockFunction recursive_mutex_unlock;
|
|
void function () padding1;
|
|
void function () padding2;
|
|
void function () padding3;
|
|
void function () padding4;
|
|
}
|
|
|
|
struct DBusCondVar;
|
|
|
|
|
|
struct DBusMutex;
|
|
|
|
|
|
dbus_bool_t dbus_threads_init (const(DBusThreadFunctions)* functions);
|
|
dbus_bool_t dbus_threads_init_default ();
|
|
// END dbus/dbus-threads.d
|
|
// START dbus/dbus-message.d
|
|
struct DBusMessageIter
|
|
{
|
|
void* dummy1;
|
|
void* dummy2;
|
|
dbus_uint32_t dummy3;
|
|
int dummy4;
|
|
int dummy5;
|
|
int dummy6;
|
|
int dummy7;
|
|
int dummy8;
|
|
int dummy9;
|
|
int dummy10;
|
|
int dummy11;
|
|
int pad1;
|
|
int pad2;
|
|
void* pad3;
|
|
}
|
|
|
|
struct DBusMessage;
|
|
|
|
|
|
DBusMessage* dbus_message_new (int message_type);
|
|
DBusMessage* dbus_message_new_method_call (const(char)* bus_name, const(char)* path, const(char)* iface, const(char)* method);
|
|
DBusMessage* dbus_message_new_method_return (DBusMessage* method_call);
|
|
DBusMessage* dbus_message_new_signal (const(char)* path, const(char)* iface, const(char)* name);
|
|
DBusMessage* dbus_message_new_error (DBusMessage* reply_to, const(char)* error_name, const(char)* error_message);
|
|
DBusMessage* dbus_message_new_error_printf (DBusMessage* reply_to, const(char)* error_name, const(char)* error_format, ...);
|
|
DBusMessage* dbus_message_copy (const(DBusMessage)* message);
|
|
DBusMessage* dbus_message_ref (DBusMessage* message);
|
|
void dbus_message_unref (DBusMessage* message);
|
|
int dbus_message_get_type (DBusMessage* message);
|
|
dbus_bool_t dbus_message_set_path (DBusMessage* message, const(char)* object_path);
|
|
const(char)* dbus_message_get_path (DBusMessage* message);
|
|
dbus_bool_t dbus_message_has_path (DBusMessage* message, const(char)* object_path);
|
|
dbus_bool_t dbus_message_set_interface (DBusMessage* message, const(char)* iface);
|
|
const(char)* dbus_message_get_interface (DBusMessage* message);
|
|
dbus_bool_t dbus_message_has_interface (DBusMessage* message, const(char)* iface);
|
|
dbus_bool_t dbus_message_set_member (DBusMessage* message, const(char)* member);
|
|
const(char)* dbus_message_get_member (DBusMessage* message);
|
|
dbus_bool_t dbus_message_has_member (DBusMessage* message, const(char)* member);
|
|
dbus_bool_t dbus_message_set_error_name (DBusMessage* message, const(char)* name);
|
|
const(char)* dbus_message_get_error_name (DBusMessage* message);
|
|
dbus_bool_t dbus_message_set_destination (DBusMessage* message, const(char)* destination);
|
|
const(char)* dbus_message_get_destination (DBusMessage* message);
|
|
dbus_bool_t dbus_message_set_sender (DBusMessage* message, const(char)* sender);
|
|
const(char)* dbus_message_get_sender (DBusMessage* message);
|
|
const(char)* dbus_message_get_signature (DBusMessage* message);
|
|
void dbus_message_set_no_reply (DBusMessage* message, dbus_bool_t no_reply);
|
|
dbus_bool_t dbus_message_get_no_reply (DBusMessage* message);
|
|
dbus_bool_t dbus_message_is_method_call (DBusMessage* message, const(char)* iface, const(char)* method);
|
|
dbus_bool_t dbus_message_is_signal (DBusMessage* message, const(char)* iface, const(char)* signal_name);
|
|
dbus_bool_t dbus_message_is_error (DBusMessage* message, const(char)* error_name);
|
|
dbus_bool_t dbus_message_has_destination (DBusMessage* message, const(char)* bus_name);
|
|
dbus_bool_t dbus_message_has_sender (DBusMessage* message, const(char)* unique_bus_name);
|
|
dbus_bool_t dbus_message_has_signature (DBusMessage* message, const(char)* signature);
|
|
dbus_uint32_t dbus_message_get_serial (DBusMessage* message);
|
|
void dbus_message_set_serial (DBusMessage* message, dbus_uint32_t serial);
|
|
dbus_bool_t dbus_message_set_reply_serial (DBusMessage* message, dbus_uint32_t reply_serial);
|
|
dbus_uint32_t dbus_message_get_reply_serial (DBusMessage* message);
|
|
void dbus_message_set_auto_start (DBusMessage* message, dbus_bool_t auto_start);
|
|
dbus_bool_t dbus_message_get_auto_start (DBusMessage* message);
|
|
dbus_bool_t dbus_message_get_path_decomposed (DBusMessage* message, char*** path);
|
|
dbus_bool_t dbus_message_append_args (DBusMessage* message, int first_arg_type, ...);
|
|
dbus_bool_t dbus_message_append_args_valist (DBusMessage* message, int first_arg_type, va_list var_args);
|
|
dbus_bool_t dbus_message_get_args (DBusMessage* message, DBusError* error, int first_arg_type, ...);
|
|
dbus_bool_t dbus_message_get_args_valist (DBusMessage* message, DBusError* error, int first_arg_type, va_list var_args);
|
|
dbus_bool_t dbus_message_contains_unix_fds (DBusMessage* message);
|
|
dbus_bool_t dbus_message_iter_init (DBusMessage* message, DBusMessageIter* iter);
|
|
dbus_bool_t dbus_message_iter_has_next (DBusMessageIter* iter);
|
|
dbus_bool_t dbus_message_iter_next (DBusMessageIter* iter);
|
|
char* dbus_message_iter_get_signature (DBusMessageIter* iter);
|
|
int dbus_message_iter_get_arg_type (DBusMessageIter* iter);
|
|
int dbus_message_iter_get_element_type (DBusMessageIter* iter);
|
|
void dbus_message_iter_recurse (DBusMessageIter* iter, DBusMessageIter* sub);
|
|
void dbus_message_iter_get_basic (DBusMessageIter* iter, void* value);
|
|
int dbus_message_iter_get_array_len (DBusMessageIter* iter);
|
|
void dbus_message_iter_get_fixed_array (DBusMessageIter* iter, void* value, int* n_elements);
|
|
void dbus_message_iter_init_append (DBusMessage* message, DBusMessageIter* iter);
|
|
dbus_bool_t dbus_message_iter_append_basic (DBusMessageIter* iter, int type, const(void)* value);
|
|
dbus_bool_t dbus_message_iter_append_fixed_array (DBusMessageIter* iter, int element_type, const(void)* value, int n_elements);
|
|
dbus_bool_t dbus_message_iter_open_container (DBusMessageIter* iter, int type, const(char)* contained_signature, DBusMessageIter* sub);
|
|
dbus_bool_t dbus_message_iter_close_container (DBusMessageIter* iter, DBusMessageIter* sub);
|
|
void dbus_message_iter_abandon_container (DBusMessageIter* iter, DBusMessageIter* sub);
|
|
void dbus_message_lock (DBusMessage* message);
|
|
dbus_bool_t dbus_set_error_from_message (DBusError* error, DBusMessage* message);
|
|
dbus_bool_t dbus_message_allocate_data_slot (dbus_int32_t* slot_p);
|
|
void dbus_message_free_data_slot (dbus_int32_t* slot_p);
|
|
dbus_bool_t dbus_message_set_data (DBusMessage* message, dbus_int32_t slot, void* data, DBusFreeFunction free_data_func);
|
|
void* dbus_message_get_data (DBusMessage* message, dbus_int32_t slot);
|
|
int dbus_message_type_from_string (const(char)* type_str);
|
|
const(char)* dbus_message_type_to_string (int type);
|
|
dbus_bool_t dbus_message_marshal (DBusMessage* msg, char** marshalled_data_p, int* len_p);
|
|
DBusMessage* dbus_message_demarshal (const(char)* str, int len, DBusError* error);
|
|
int dbus_message_demarshal_bytes_needed (const(char)* str, int len);
|
|
// END dbus/dbus-message.d
|
|
// START dbus/dbus-connection.d
|
|
alias uint function (DBusWatch*, void*) DBusAddWatchFunction;
|
|
alias void function (DBusWatch*, void*) DBusWatchToggledFunction;
|
|
alias void function (DBusWatch*, void*) DBusRemoveWatchFunction;
|
|
alias uint function (DBusTimeout*, void*) DBusAddTimeoutFunction;
|
|
alias void function (DBusTimeout*, void*) DBusTimeoutToggledFunction;
|
|
alias void function (DBusTimeout*, void*) DBusRemoveTimeoutFunction;
|
|
alias void function (DBusConnection*, DBusDispatchStatus, void*) DBusDispatchStatusFunction;
|
|
alias void function (void*) DBusWakeupMainFunction;
|
|
alias uint function (DBusConnection*, c_ulong, void*) DBusAllowUnixUserFunction;
|
|
alias uint function (DBusConnection*, const(char)*, void*) DBusAllowWindowsUserFunction;
|
|
alias void function (DBusPendingCall*, void*) DBusPendingCallNotifyFunction;
|
|
alias DBusHandlerResult function (DBusConnection*, DBusMessage*, void*) DBusHandleMessageFunction;
|
|
alias void function (DBusConnection*, void*) DBusObjectPathUnregisterFunction;
|
|
alias DBusHandlerResult function (DBusConnection*, DBusMessage*, void*) DBusObjectPathMessageFunction;
|
|
|
|
enum DBusWatchFlags
|
|
{
|
|
DBUS_WATCH_READABLE = 1,
|
|
DBUS_WATCH_WRITABLE = 2,
|
|
DBUS_WATCH_ERROR = 4,
|
|
DBUS_WATCH_HANGUP = 8
|
|
}
|
|
|
|
enum DBusDispatchStatus
|
|
{
|
|
DBUS_DISPATCH_DATA_REMAINS = 0,
|
|
DBUS_DISPATCH_COMPLETE = 1,
|
|
DBUS_DISPATCH_NEED_MEMORY = 2
|
|
}
|
|
|
|
struct DBusObjectPathVTable
|
|
{
|
|
DBusObjectPathUnregisterFunction unregister_function;
|
|
DBusObjectPathMessageFunction message_function;
|
|
void function (void*) dbus_internal_pad1;
|
|
void function (void*) dbus_internal_pad2;
|
|
void function (void*) dbus_internal_pad3;
|
|
void function (void*) dbus_internal_pad4;
|
|
}
|
|
|
|
struct DBusPreallocatedSend;
|
|
|
|
|
|
struct DBusTimeout;
|
|
|
|
|
|
struct DBusPendingCall;
|
|
|
|
|
|
struct DBusConnection;
|
|
|
|
|
|
struct DBusWatch;
|
|
|
|
|
|
DBusConnection* dbus_connection_open (const(char)* address, DBusError* error);
|
|
DBusConnection* dbus_connection_open_private (const(char)* address, DBusError* error);
|
|
DBusConnection* dbus_connection_ref (DBusConnection* connection);
|
|
void dbus_connection_unref (DBusConnection* connection);
|
|
void dbus_connection_close (DBusConnection* connection);
|
|
dbus_bool_t dbus_connection_get_is_connected (DBusConnection* connection);
|
|
dbus_bool_t dbus_connection_get_is_authenticated (DBusConnection* connection);
|
|
dbus_bool_t dbus_connection_get_is_anonymous (DBusConnection* connection);
|
|
char* dbus_connection_get_server_id (DBusConnection* connection);
|
|
dbus_bool_t dbus_connection_can_send_type (DBusConnection* connection, int type);
|
|
void dbus_connection_set_exit_on_disconnect (DBusConnection* connection, dbus_bool_t exit_on_disconnect);
|
|
void dbus_connection_flush (DBusConnection* connection);
|
|
dbus_bool_t dbus_connection_read_write_dispatch (DBusConnection* connection, int timeout_milliseconds);
|
|
dbus_bool_t dbus_connection_read_write (DBusConnection* connection, int timeout_milliseconds);
|
|
DBusMessage* dbus_connection_borrow_message (DBusConnection* connection);
|
|
void dbus_connection_return_message (DBusConnection* connection, DBusMessage* message);
|
|
void dbus_connection_steal_borrowed_message (DBusConnection* connection, DBusMessage* message);
|
|
DBusMessage* dbus_connection_pop_message (DBusConnection* connection);
|
|
DBusDispatchStatus dbus_connection_get_dispatch_status (DBusConnection* connection);
|
|
DBusDispatchStatus dbus_connection_dispatch (DBusConnection* connection);
|
|
dbus_bool_t dbus_connection_has_messages_to_send (DBusConnection* connection);
|
|
dbus_bool_t dbus_connection_send (DBusConnection* connection, DBusMessage* message, dbus_uint32_t* client_serial);
|
|
dbus_bool_t dbus_connection_send_with_reply (DBusConnection* connection, DBusMessage* message, DBusPendingCall** pending_return, int timeout_milliseconds);
|
|
DBusMessage* dbus_connection_send_with_reply_and_block (DBusConnection* connection, DBusMessage* message, int timeout_milliseconds, DBusError* error);
|
|
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);
|
|
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);
|
|
void dbus_connection_set_wakeup_main_function (DBusConnection* connection, DBusWakeupMainFunction wakeup_main_function, void* data, DBusFreeFunction free_data_function);
|
|
void dbus_connection_set_dispatch_status_function (DBusConnection* connection, DBusDispatchStatusFunction function_, void* data, DBusFreeFunction free_data_function);
|
|
dbus_bool_t dbus_connection_get_unix_user (DBusConnection* connection, c_ulong* uid);
|
|
dbus_bool_t dbus_connection_get_unix_process_id (DBusConnection* connection, c_ulong* pid);
|
|
dbus_bool_t dbus_connection_get_adt_audit_session_data (DBusConnection* connection, void** data, dbus_int32_t* data_size);
|
|
void dbus_connection_set_unix_user_function (DBusConnection* connection, DBusAllowUnixUserFunction function_, void* data, DBusFreeFunction free_data_function);
|
|
dbus_bool_t dbus_connection_get_windows_user (DBusConnection* connection, char** windows_sid_p);
|
|
void dbus_connection_set_windows_user_function (DBusConnection* connection, DBusAllowWindowsUserFunction function_, void* data, DBusFreeFunction free_data_function);
|
|
void dbus_connection_set_allow_anonymous (DBusConnection* connection, dbus_bool_t value);
|
|
void dbus_connection_set_route_peer_messages (DBusConnection* connection, dbus_bool_t value);
|
|
dbus_bool_t dbus_connection_add_filter (DBusConnection* connection, DBusHandleMessageFunction function_, void* user_data, DBusFreeFunction free_data_function);
|
|
void dbus_connection_remove_filter (DBusConnection* connection, DBusHandleMessageFunction function_, void* user_data);
|
|
dbus_bool_t dbus_connection_allocate_data_slot (dbus_int32_t* slot_p);
|
|
void dbus_connection_free_data_slot (dbus_int32_t* slot_p);
|
|
dbus_bool_t dbus_connection_set_data (DBusConnection* connection, dbus_int32_t slot, void* data, DBusFreeFunction free_data_func);
|
|
void* dbus_connection_get_data (DBusConnection* connection, dbus_int32_t slot);
|
|
void dbus_connection_set_change_sigpipe (dbus_bool_t will_modify_sigpipe);
|
|
void dbus_connection_set_max_message_size (DBusConnection* connection, c_long size);
|
|
c_long dbus_connection_get_max_message_size (DBusConnection* connection);
|
|
void dbus_connection_set_max_received_size (DBusConnection* connection, c_long size);
|
|
c_long dbus_connection_get_max_received_size (DBusConnection* connection);
|
|
void dbus_connection_set_max_message_unix_fds (DBusConnection* connection, c_long n);
|
|
c_long dbus_connection_get_max_message_unix_fds (DBusConnection* connection);
|
|
void dbus_connection_set_max_received_unix_fds (DBusConnection* connection, c_long n);
|
|
c_long dbus_connection_get_max_received_unix_fds (DBusConnection* connection);
|
|
c_long dbus_connection_get_outgoing_size (DBusConnection* connection);
|
|
c_long dbus_connection_get_outgoing_unix_fds (DBusConnection* connection);
|
|
DBusPreallocatedSend* dbus_connection_preallocate_send (DBusConnection* connection);
|
|
void dbus_connection_free_preallocated_send (DBusConnection* connection, DBusPreallocatedSend* preallocated);
|
|
void dbus_connection_send_preallocated (DBusConnection* connection, DBusPreallocatedSend* preallocated, DBusMessage* message, dbus_uint32_t* client_serial);
|
|
dbus_bool_t dbus_connection_try_register_object_path (DBusConnection* connection, const(char)* path, const(DBusObjectPathVTable)* vtable, void* user_data, DBusError* error);
|
|
dbus_bool_t dbus_connection_register_object_path (DBusConnection* connection, const(char)* path, const(DBusObjectPathVTable)* vtable, void* user_data);
|
|
dbus_bool_t dbus_connection_try_register_fallback (DBusConnection* connection, const(char)* path, const(DBusObjectPathVTable)* vtable, void* user_data, DBusError* error);
|
|
dbus_bool_t dbus_connection_register_fallback (DBusConnection* connection, const(char)* path, const(DBusObjectPathVTable)* vtable, void* user_data);
|
|
dbus_bool_t dbus_connection_unregister_object_path (DBusConnection* connection, const(char)* path);
|
|
dbus_bool_t dbus_connection_get_object_path_data (DBusConnection* connection, const(char)* path, void** data_p);
|
|
dbus_bool_t dbus_connection_list_registered (DBusConnection* connection, const(char)* parent_path, char*** child_entries);
|
|
dbus_bool_t dbus_connection_get_unix_fd (DBusConnection* connection, int* fd);
|
|
dbus_bool_t dbus_connection_get_socket (DBusConnection* connection, int* fd);
|
|
int dbus_watch_get_fd (DBusWatch* watch);
|
|
int dbus_watch_get_unix_fd (DBusWatch* watch);
|
|
int dbus_watch_get_socket (DBusWatch* watch);
|
|
uint dbus_watch_get_flags (DBusWatch* watch);
|
|
void* dbus_watch_get_data (DBusWatch* watch);
|
|
void dbus_watch_set_data (DBusWatch* watch, void* data, DBusFreeFunction free_data_function);
|
|
dbus_bool_t dbus_watch_handle (DBusWatch* watch, uint flags);
|
|
dbus_bool_t dbus_watch_get_enabled (DBusWatch* watch);
|
|
int dbus_timeout_get_interval (DBusTimeout* timeout);
|
|
void* dbus_timeout_get_data (DBusTimeout* timeout);
|
|
void dbus_timeout_set_data (DBusTimeout* timeout, void* data, DBusFreeFunction free_data_function);
|
|
dbus_bool_t dbus_timeout_handle (DBusTimeout* timeout);
|
|
dbus_bool_t dbus_timeout_get_enabled (DBusTimeout* timeout);
|
|
// END dbus/dbus-connection.d
|
|
// START dbus/dbus-pending-call.d
|
|
DBusPendingCall* dbus_pending_call_ref (DBusPendingCall* pending);
|
|
void dbus_pending_call_unref (DBusPendingCall* pending);
|
|
dbus_bool_t dbus_pending_call_set_notify (DBusPendingCall* pending, DBusPendingCallNotifyFunction function_, void* user_data, DBusFreeFunction free_user_data);
|
|
void dbus_pending_call_cancel (DBusPendingCall* pending);
|
|
dbus_bool_t dbus_pending_call_get_completed (DBusPendingCall* pending);
|
|
DBusMessage* dbus_pending_call_steal_reply (DBusPendingCall* pending);
|
|
void dbus_pending_call_block (DBusPendingCall* pending);
|
|
dbus_bool_t dbus_pending_call_allocate_data_slot (dbus_int32_t* slot_p);
|
|
void dbus_pending_call_free_data_slot (dbus_int32_t* slot_p);
|
|
dbus_bool_t dbus_pending_call_set_data (DBusPendingCall* pending, dbus_int32_t slot, void* data, DBusFreeFunction free_data_func);
|
|
void* dbus_pending_call_get_data (DBusPendingCall* pending, dbus_int32_t slot);
|
|
// END dbus/dbus-pending-call.d
|
|
// START dbus/dbus-server.d
|
|
alias void function (DBusServer*, DBusConnection*, void*) DBusNewConnectionFunction;
|
|
|
|
struct DBusServer;
|
|
|
|
|
|
DBusServer* dbus_server_listen (const(char)* address, DBusError* error);
|
|
DBusServer* dbus_server_ref (DBusServer* server);
|
|
void dbus_server_unref (DBusServer* server);
|
|
void dbus_server_disconnect (DBusServer* server);
|
|
dbus_bool_t dbus_server_get_is_connected (DBusServer* server);
|
|
char* dbus_server_get_address (DBusServer* server);
|
|
char* dbus_server_get_id (DBusServer* server);
|
|
void dbus_server_set_new_connection_function (DBusServer* server, DBusNewConnectionFunction function_, void* data, DBusFreeFunction free_data_function);
|
|
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);
|
|
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);
|
|
dbus_bool_t dbus_server_set_auth_mechanisms (DBusServer* server, const(char*)* mechanisms);
|
|
dbus_bool_t dbus_server_allocate_data_slot (dbus_int32_t* slot_p);
|
|
void dbus_server_free_data_slot (dbus_int32_t* slot_p);
|
|
dbus_bool_t dbus_server_set_data (DBusServer* server, int slot, void* data, DBusFreeFunction free_data_func);
|
|
void* dbus_server_get_data (DBusServer* server, int slot);
|
|
// END dbus/dbus-server.d
|
|
// START dbus/dbus-bus.d
|
|
DBusConnection* dbus_bus_get (DBusBusType type, DBusError* error);
|
|
DBusConnection* dbus_bus_get_private (DBusBusType type, DBusError* error);
|
|
dbus_bool_t dbus_bus_register (DBusConnection* connection, DBusError* error);
|
|
dbus_bool_t dbus_bus_set_unique_name (DBusConnection* connection, const(char)* unique_name);
|
|
const(char)* dbus_bus_get_unique_name (DBusConnection* connection);
|
|
c_ulong dbus_bus_get_unix_user (DBusConnection* connection, const(char)* name, DBusError* error);
|
|
char* dbus_bus_get_id (DBusConnection* connection, DBusError* error);
|
|
int dbus_bus_request_name (DBusConnection* connection, const(char)* name, uint flags, DBusError* error);
|
|
int dbus_bus_release_name (DBusConnection* connection, const(char)* name, DBusError* error);
|
|
dbus_bool_t dbus_bus_name_has_owner (DBusConnection* connection, const(char)* name, DBusError* error);
|
|
dbus_bool_t dbus_bus_start_service_by_name (DBusConnection* connection, const(char)* name, dbus_uint32_t flags, dbus_uint32_t* reply, DBusError* error);
|
|
void dbus_bus_add_match (DBusConnection* connection, const(char)* rule, DBusError* error);
|
|
void dbus_bus_remove_match (DBusConnection* connection, const(char)* rule, DBusError* error);
|
|
// END dbus/dbus-bus.d
|
|
// START dbus/dbus.d
|
|
|
|
// END dbus/dbus.d
|
|
}
|
|
|
|
|
|
enum BusService = "org.freedesktop.DBus";
|
|
enum BusPath = "/org/freedesktop/DBus";
|
|
enum BusInterface = "org.freedesktop.DBus";
|
|
|
|
enum NameFlags {
|
|
AllowReplace = 1, ReplaceExisting = 2, NoQueue = 4
|
|
}
|
|
|
|
/// Requests a DBus well-known name.
|
|
/// returns if the name is owned after the call.
|
|
/// Involves blocking call on a DBus method, may throw an exception on failure.
|
|
bool requestName(Connection conn, string name,
|
|
NameFlags flags = NameFlags.NoQueue | NameFlags.AllowReplace) {
|
|
auto msg = Message(BusService,BusPath,BusInterface,"RequestName");
|
|
msg.build(name,cast(uint)(flags));
|
|
auto res = conn.sendWithReplyBlocking(msg).to!uint;
|
|
return (res == 1) || (res == 4);
|
|
}
|
|
|
|
/// A simple main loop that isn't necessarily efficient
|
|
/// and isn't guaranteed to work with other tasks and threads.
|
|
/// Use only for apps that only do DBus triggered things.
|
|
void simpleMainLoop(Connection conn) {
|
|
while(dbus_connection_read_write_dispatch(conn.conn, -1)) {} // empty loop body
|
|
}
|
|
|
|
/// Single tick in the DBus connection which can be used for
|
|
/// concurrent updates.
|
|
bool tick(Connection conn) {
|
|
return cast(bool) dbus_connection_read_write_dispatch(conn.conn, 0);
|
|
}
|
|
|
|
|