Add basic server functionality.
This commit is contained in:
parent
5905d50e1a
commit
1457b249b7
25
source/app.d
25
source/app.d
|
@ -1,10 +1,10 @@
|
||||||
import std.stdio;
|
import std.stdio;
|
||||||
import ddbus.c_lib;
|
import ddbus.c_lib;
|
||||||
import ddbus.thin;
|
import ddbus.thin;
|
||||||
|
import ddbus.router;
|
||||||
|
import ddbus.bus;
|
||||||
|
|
||||||
void main()
|
void testCall(Connection conn) {
|
||||||
{
|
|
||||||
Connection conn = connectToBus();
|
|
||||||
for(int i = 0; i < 50; i++) {
|
for(int i = 0; i < 50; i++) {
|
||||||
Message msg = Message("ca.thume.transience","/ca/thume/transience/screensurface",
|
Message msg = Message("ca.thume.transience","/ca/thume/transience/screensurface",
|
||||||
"ca.thume.transience.screensurface","testDot");
|
"ca.thume.transience.screensurface","testDot");
|
||||||
|
@ -15,5 +15,24 @@ void main()
|
||||||
Message res = conn.sendWithReplyBlocking(msg2,3000);
|
Message res = conn.sendWithReplyBlocking(msg2,3000);
|
||||||
int result = res.read!int();
|
int result = res.read!int();
|
||||||
writeln(result);
|
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!");
|
writeln("It worked!");
|
||||||
}
|
}
|
||||||
|
|
39
source/ddbus/bus.d
Normal file
39
source/ddbus/bus.d
Normal file
|
@ -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();
|
||||||
|
}
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
module ddbus.router;
|
module ddbus.router;
|
||||||
|
|
||||||
import ddbus.thin;
|
import ddbus.thin;
|
||||||
|
import ddbus.c_lib;
|
||||||
import std.string;
|
import std.string;
|
||||||
import std.typecons;
|
import std.typecons;
|
||||||
|
import core.memory;
|
||||||
|
|
||||||
struct MessagePattern {
|
struct MessagePattern {
|
||||||
string sender;
|
|
||||||
string path;
|
string path;
|
||||||
string iface;
|
string iface;
|
||||||
string method;
|
string method;
|
||||||
|
string sender;
|
||||||
|
|
||||||
this(Message msg) {
|
this(Message msg) {
|
||||||
path = msg.path();
|
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 toHash() const @safe nothrow {
|
||||||
size_t hash = 0;
|
size_t hash = 0;
|
||||||
auto stringHash = &(typeid(path).getHash);
|
auto stringHash = &(typeid(path).getHash);
|
||||||
|
@ -38,14 +47,9 @@ struct MessagePattern {
|
||||||
|
|
||||||
class MessageRouter {
|
class MessageRouter {
|
||||||
alias HandlerFunc = void delegate(Message call, Connection conn);
|
alias HandlerFunc = void delegate(Message call, Connection conn);
|
||||||
Connection conn;
|
|
||||||
HandlerFunc[MessagePattern] callTable;
|
HandlerFunc[MessagePattern] callTable;
|
||||||
|
|
||||||
this(Connection conn) {
|
bool handle(Message msg, Connection conn) {
|
||||||
this.conn = conn;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool handle(Message msg) {
|
|
||||||
MessageType type = msg.type();
|
MessageType type = msg.type();
|
||||||
if(type != MessageType.Call && type != MessageType.Signal)
|
if(type != MessageType.Call && type != MessageType.Signal)
|
||||||
return false;
|
return false;
|
||||||
|
@ -56,7 +60,7 @@ class MessageRouter {
|
||||||
return true;
|
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) {
|
void handlerWrapper(Message call, Connection conn) {
|
||||||
Tuple!Args args = call.readTuple!(Tuple!Args)();
|
Tuple!Args args = call.readTuple!(Tuple!Args)();
|
||||||
Ret ret = handler(args.expand);
|
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 {
|
unittest {
|
||||||
import dunit.toolkit;
|
import dunit.toolkit;
|
||||||
auto msg = Message("org.example.test", "/test","org.example.testing","testMethod");
|
auto msg = Message("org.example.test", "/test","org.example.testing","testMethod");
|
||||||
auto patt= MessagePattern(msg);
|
auto patt= new MessagePattern(msg);
|
||||||
patt.assertEqual(patt);
|
patt.assertEqual(patt);
|
||||||
patt.sender.assertNull();
|
patt.sender.assertNull();
|
||||||
patt.path.assertEqual("/test");
|
patt.path.assertEqual("/test");
|
||||||
|
|
|
@ -94,27 +94,27 @@ struct Message {
|
||||||
string signature() {
|
string signature() {
|
||||||
const(char)* cStr = dbus_message_get_signature(msg);
|
const(char)* cStr = dbus_message_get_signature(msg);
|
||||||
assert(cStr != null);
|
assert(cStr != null);
|
||||||
return cStr.fromStringz().assumeUnique();
|
return cStr.fromStringz().idup;
|
||||||
}
|
}
|
||||||
string path() {
|
string path() {
|
||||||
const(char)* cStr = dbus_message_get_path(msg);
|
const(char)* cStr = dbus_message_get_path(msg);
|
||||||
assert(cStr != null);
|
assert(cStr != null);
|
||||||
return cStr.fromStringz().assumeUnique();
|
return cStr.fromStringz().idup;
|
||||||
}
|
}
|
||||||
string iface() {
|
string iface() {
|
||||||
const(char)* cStr = dbus_message_get_interface(msg);
|
const(char)* cStr = dbus_message_get_interface(msg);
|
||||||
assert(cStr != null);
|
assert(cStr != null);
|
||||||
return cStr.fromStringz().assumeUnique();
|
return cStr.fromStringz().idup;
|
||||||
}
|
}
|
||||||
string member() {
|
string member() {
|
||||||
const(char)* cStr = dbus_message_get_member(msg);
|
const(char)* cStr = dbus_message_get_member(msg);
|
||||||
assert(cStr != null);
|
assert(cStr != null);
|
||||||
return cStr.fromStringz().assumeUnique();
|
return cStr.fromStringz().idup;
|
||||||
}
|
}
|
||||||
string sender() {
|
string sender() {
|
||||||
const(char)* cStr = dbus_message_get_sender(msg);
|
const(char)* cStr = dbus_message_get_sender(msg);
|
||||||
assert(cStr != null);
|
assert(cStr != null);
|
||||||
return cStr.fromStringz().assumeUnique();
|
return cStr.fromStringz().idup;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,7 +151,7 @@ struct Connection {
|
||||||
dbus_connection_flush(conn);
|
dbus_connection_flush(conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
Message sendWithReplyBlocking(Message msg, int timeout = 100) {
|
Message sendWithReplyBlocking(Message msg, int timeout = -1) {
|
||||||
DBusMessage *dbusMsg = msg.msg;
|
DBusMessage *dbusMsg = msg.msg;
|
||||||
dbus_message_ref(dbusMsg);
|
dbus_message_ref(dbusMsg);
|
||||||
DBusMessage *reply = wrapErrors((err) {
|
DBusMessage *reply = wrapErrors((err) {
|
||||||
|
|
Loading…
Reference in a new issue