From 1457b249b737c2b955d590a1b518978bb34d9d36 Mon Sep 17 00:00:00 2001 From: Tristan Hume Date: Fri, 1 May 2015 20:36:06 -0400 Subject: [PATCH] Add basic server functionality. --- source/app.d | 25 ++++++++++++++++++++--- source/ddbus/bus.d | 39 ++++++++++++++++++++++++++++++++++++ source/ddbus/router.d | 46 ++++++++++++++++++++++++++++++++++--------- source/ddbus/thin.d | 12 +++++------ 4 files changed, 104 insertions(+), 18 deletions(-) create mode 100644 source/ddbus/bus.d diff --git a/source/app.d b/source/app.d index d3d5cd0..69b660b 100644 --- a/source/app.d +++ b/source/app.d @@ -1,10 +1,10 @@ import std.stdio; import ddbus.c_lib; import ddbus.thin; +import ddbus.router; +import ddbus.bus; -void main() -{ - Connection conn = connectToBus(); +void testCall(Connection conn) { for(int i = 0; i < 50; i++) { Message msg = Message("ca.thume.transience","/ca/thume/transience/screensurface", "ca.thume.transience.screensurface","testDot"); @@ -15,5 +15,24 @@ void main() Message res = conn.sendWithReplyBlocking(msg2,3000); int result = res.read!int(); writeln(result); +} + +void testServe(Connection conn) { + auto router = new MessageRouter(); + MessagePattern patt = MessagePattern("/root","ca.thume.test","test"); + router.setHandler!(int,int)(patt,(int par) { + writeln("Called with ", par); + return par; + }); + registerRouter(conn, router); + writeln("Getting name..."); + bool gotem = requestName(conn, "ca.thume.ddbus.test"); + writeln("Got name: ",gotem); + simpleMainLoop(conn); +} + +void main() { + Connection conn = connectToBus(); + testServe(conn); writeln("It worked!"); } diff --git a/source/ddbus/bus.d b/source/ddbus/bus.d new file mode 100644 index 0000000..f88cd28 --- /dev/null +++ b/source/ddbus/bus.d @@ -0,0 +1,39 @@ +module ddbus.bus; + +import ddbus.router; +import ddbus.thin; +import ddbus.c_lib; +import std.string; + +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 +} + +unittest { + import dunit.toolkit; + Connection conn = connectToBus(); + conn.requestName("ca.thume.ddbus.testing").assertTrue(); +} + diff --git a/source/ddbus/router.d b/source/ddbus/router.d index c873112..a26a779 100644 --- a/source/ddbus/router.d +++ b/source/ddbus/router.d @@ -1,14 +1,16 @@ module ddbus.router; import ddbus.thin; +import ddbus.c_lib; import std.string; import std.typecons; +import core.memory; struct MessagePattern { - string sender; string path; string iface; string method; + string sender; this(Message msg) { path = msg.path(); @@ -21,6 +23,13 @@ struct MessagePattern { } } + this(string path, string iface, string method, string sender = null) { + this.path = path; + this.iface = iface; + this.method = method; + this.sender = sender; + } + size_t toHash() const @safe nothrow { size_t hash = 0; auto stringHash = &(typeid(path).getHash); @@ -38,14 +47,9 @@ struct MessagePattern { class MessageRouter { alias HandlerFunc = void delegate(Message call, Connection conn); - Connection conn; HandlerFunc[MessagePattern] callTable; - this(Connection conn) { - this.conn = conn; - } - - bool handle(Message msg) { + bool handle(Message msg, Connection conn) { MessageType type = msg.type(); if(type != MessageType.Call && type != MessageType.Signal) return false; @@ -56,7 +60,7 @@ class MessageRouter { return true; } - void setHandler(Ret, Args...)(MessagePattern patt, Connection conn, Ret delegate(Args) handler) { + void setHandler(Ret, Args...)(MessagePattern patt, Ret delegate(Args) handler) { void handlerWrapper(Message call, Connection conn) { Tuple!Args args = call.readTuple!(Tuple!Args)(); Ret ret = handler(args.expand); @@ -70,10 +74,34 @@ class MessageRouter { } } +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); +} + unittest { import dunit.toolkit; auto msg = Message("org.example.test", "/test","org.example.testing","testMethod"); - auto patt= MessagePattern(msg); + auto patt= new MessagePattern(msg); patt.assertEqual(patt); patt.sender.assertNull(); patt.path.assertEqual("/test"); diff --git a/source/ddbus/thin.d b/source/ddbus/thin.d index 4118927..545f6fe 100644 --- a/source/ddbus/thin.d +++ b/source/ddbus/thin.d @@ -94,27 +94,27 @@ struct Message { string signature() { const(char)* cStr = dbus_message_get_signature(msg); assert(cStr != null); - return cStr.fromStringz().assumeUnique(); + return cStr.fromStringz().idup; } string path() { const(char)* cStr = dbus_message_get_path(msg); assert(cStr != null); - return cStr.fromStringz().assumeUnique(); + return cStr.fromStringz().idup; } string iface() { const(char)* cStr = dbus_message_get_interface(msg); assert(cStr != null); - return cStr.fromStringz().assumeUnique(); + return cStr.fromStringz().idup; } string member() { const(char)* cStr = dbus_message_get_member(msg); assert(cStr != null); - return cStr.fromStringz().assumeUnique(); + return cStr.fromStringz().idup; } string sender() { const(char)* cStr = dbus_message_get_sender(msg); assert(cStr != null); - return cStr.fromStringz().assumeUnique(); + return cStr.fromStringz().idup; } } @@ -151,7 +151,7 @@ struct Connection { dbus_connection_flush(conn); } - Message sendWithReplyBlocking(Message msg, int timeout = 100) { + Message sendWithReplyBlocking(Message msg, int timeout = -1) { DBusMessage *dbusMsg = msg.msg; dbus_message_ref(dbusMsg); DBusMessage *reply = wrapErrors((err) {