3
// Copyright (C) 2007, 2008, 2009, 2010, 2011 Rob Caelers <robc@krandor.nl>
4
// All rights reserved.
6
// This program is free software: you can redistribute it and/or modify
7
// it under the terms of the GNU General Public License as published by
8
// the Free Software Foundation, either version 3 of the License, or
9
// (at your option) any later version.
11
// This program is distributed in the hope that it will be useful,
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
// GNU General Public License for more details.
16
// You should have received a copy of the GNU General Public License
17
// along with this program. If not, see <http://www.gnu.org/licenses/>.
19
// Based on code from pidgin.
21
// Glib integration based on dbus-glib and libgdbus:
23
// Copyright (C) 2007-2008 Intel Corporation
24
// Copyright (C) 2002, 2003 CodeFactory AB
25
// Copyright (C) 2005 Red Hat, Inc.
40
#include "DBus-freedesktop.hh"
41
#include "DBusBinding-freedesktop.hh"
42
#include "DBusException.hh"
45
using namespace workrave;
47
//! Construct a new D-BUS bridge
49
: connection(NULL), owner(false), context(NULL), queue(NULL), watches(NULL), timeouts(NULL)
54
//! Destruct the D-BUS bridge
57
if (connection != NULL)
59
dbus_connection_unref(connection);
64
//! Initialize D-BUS bridge
70
dbus_error_init(&error);
72
connection = dbus_bus_get_private(DBUS_BUS_STARTER, &error);
73
if (dbus_error_is_set(&error))
76
dbus_error_free(&error);
77
throw DBusSystemException("Unable to obtain session bus");
80
dbus_connection_set_exit_on_disconnect(connection, FALSE);
82
connection_setup(NULL);
83
//dbus_connection_setup_with_g_main(connection, NULL);
87
//! Registers the specified service
89
DBus::register_service(const std::string &service)
94
dbus_error_init(&error);
96
result = dbus_bus_request_name(connection,
98
DBUS_NAME_FLAG_ALLOW_REPLACEMENT | DBUS_NAME_FLAG_DO_NOT_QUEUE,
101
if (dbus_error_is_set(&error))
103
dbus_connection_unref(connection);
106
dbus_error_free(&error);
107
throw DBusSystemException("Unable to request service");
110
owner = (result == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER);
114
//! Registers the specified object path
116
DBus::register_object_path(const string &object_path)
118
static DBusObjectPathVTable vtable = { NULL,
119
&DBus::dispatch_static,
125
if (!dbus_connection_register_object_path(connection,
130
throw DBusSystemException("Unable to register object");
135
//! Connect a D-DBUS object/interface to a C object
137
DBus::connect(const std::string &object_path, const std::string &interface_name, void *cobject)
139
DBusBindingBase *binding = find_binding(interface_name);
142
throw DBusSystemException("No such interface");
145
ObjectIter it = objects.find(object_path);
146
if (it != objects.end())
148
Interfaces &interfaces = it->second;
150
interfaces[interface_name] = cobject;
154
Interfaces interfaces;
155
interfaces[interface_name] = cobject;
156
objects[object_path] = interfaces;
161
//! Disconnect a D-DBUS object/interface to a C object
163
DBus::disconnect(const std::string &object_path, const std::string &interface_name)
165
ObjectIter it = objects.find(object_path);
166
if (it != objects.end())
168
Interfaces &interfaces = it->second;
170
interfaces.erase(interface_name);
175
//! Register an interface binding
177
DBus::register_binding(const std::string &name, DBusBindingBase *interface)
179
bindings[name] = interface;
183
//! Find an interface binding
185
DBus::find_binding(const std::string &interface_name) const
187
DBusBindingBase *ret = NULL;
189
BindingCIter it = bindings.find(interface_name);
190
if (it != bindings.end())
200
DBus::send(DBusMessage *msg) const
202
dbus_connection_send(connection, msg, NULL);
203
dbus_message_unref(msg);
204
dbus_connection_flush(connection);
209
DBus::find_object(const std::string &path, const std::string &interface_name) const
211
void *cobject = NULL;
213
ObjectCIter object_it = objects.find(path);
214
if (object_it != objects.end())
216
InterfaceCIter interface_it = object_it->second.find(interface_name);
218
if (interface_it != object_it->second.end())
220
cobject = interface_it->second;
230
DBus::is_owner() const
237
DBus::is_available() const
239
return connection != NULL;
244
DBus::dispatch(DBusConnection *connection, DBusMessage *message)
248
if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL &&
249
dbus_message_has_interface(message, DBUS_INTERFACE_INTROSPECTABLE) &&
250
dbus_message_has_member(message, "Introspect"))
252
return handle_introspect(connection, message);
254
else if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL)
256
return handle_method(connection, message);
259
catch (DBusException &e)
262
reply = dbus_message_new_error(message, e.id().c_str(), e.details().c_str());
265
dbus_connection_send(connection, reply, NULL);
266
dbus_message_unref(reply);
269
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
274
DBus::dispatch_static(DBusConnection *connection,
275
DBusMessage *message,
278
DBus *dbus = (DBus *) user_data;
279
return dbus->dispatch(connection, message);
284
dbus_gettext(const char **ptr)
286
const char *text = *ptr;
287
*ptr += strlen(text) + 1;
293
DBus::handle_introspect(DBusConnection *connection, DBusMessage *message)
297
string path = dbus_message_get_path(message);
299
ObjectCIter object_it = objects.find(path);
300
if (object_it != objects.end())
302
str = "<!DOCTYPE node PUBLIC '-//freedesktop//DTD D-BUS Object Introspection 1.0//EN' 'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>\n";
303
str += "<node name='" + path + "'>\n";
305
str += "<interface name=\"org.freedesktop.DBus.Introspectable\">\n";
306
str += "<method name=\"Introspect\">\n";
307
str += "<arg name=\"data\" direction=\"out\" type=\"s\"/>\n";
308
str += "</method>\n";
309
str += "</interface>\n";
311
for (InterfaceCIter interface_it = object_it->second.begin();
312
interface_it != object_it->second.end();
315
string interface_name = interface_it->first;
317
DBusBindingBase *binding = find_binding(interface_name);
320
throw DBusSystemException("Internal error, unknown interface");
323
str += "<interface name='" + interface_name + "'>\n";
325
DBusIntrospect *table = binding->get_method_introspect();
326
while (table->name != NULL)
328
str += string("<method name='") + table->name + "'>\n";
330
const char *text = table->signature;
333
const char *name, *direction, *type;
335
direction = dbus_gettext(&text);
336
type = dbus_gettext(&text);
337
name = dbus_gettext(&text);
339
str += string("<arg name='") + name + "' type='" + type +"' direction='"+ direction +"'/>\n";
341
str += "</method>\n";
345
table = binding->get_signal_introspect();
346
while (table->name != NULL)
348
str += string("<signal name='") + table->name + "'>\n";
350
const char *text = table->signature;
353
const char *name, *type;
355
type = dbus_gettext(&text);
356
name = dbus_gettext(&text);
358
str += string("<arg name='") + name + "' type='" + type +"'/>\n";
360
str += "</signal>\n";
364
str += "</interface>\n";
369
const char *cstr = str.c_str();
371
DBusMessage *reply = dbus_message_new_method_return(message);
373
dbus_message_append_args(reply, DBUS_TYPE_STRING, &cstr, DBUS_TYPE_INVALID);
374
dbus_connection_send(connection, reply, NULL);
375
dbus_message_unref(reply);
377
return DBUS_HANDLER_RESULT_HANDLED;
380
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
385
DBus::handle_method(DBusConnection *connection, DBusMessage *message)
387
string path = dbus_message_get_path(message);
388
string interface_name = dbus_message_get_interface(message);
389
string method = dbus_message_get_member(message);
391
void *cobject = find_object(path, interface_name);
394
throw DBusUsageException(string("no such object: ") + path + " " + interface_name );
397
DBusBindingBase *binding = find_binding(interface_name);
400
throw DBusSystemException(string("No such binding: ") + interface_name );
403
DBusMessage *reply = binding->call(method, cobject, message);
406
throw DBusUsageException("Call failure");
409
dbus_connection_send(connection, reply, NULL);
410
dbus_message_unref(reply);
412
return DBUS_HANDLER_RESULT_HANDLED;
423
DBusTimeout *timeout;
430
DBusConnection *connection;
436
DBus::queue_prepare(GSource *source, gint *timeout)
438
DBusConnection *connection = ((QueueData *) source)->connection;
442
return (dbus_connection_get_dispatch_status(connection) == DBUS_DISPATCH_DATA_REMAINS);
447
DBus::queue_check(GSource *source)
455
DBus::queue_dispatch(GSource *source, GSourceFunc callback, gpointer data)
460
DBusConnection *connection = ((QueueData *) source)->connection;
462
dbus_connection_ref(connection);
463
dbus_connection_dispatch(connection);
464
dbus_connection_unref(connection);
470
DBus::watch_dispatch(GIOChannel *source, GIOCondition condition, gpointer data)
474
WatchData *watch_data = (WatchData *)data;
475
unsigned int flags = 0;
477
if (condition & G_IO_IN)
478
flags |= DBUS_WATCH_READABLE;
480
if (condition & G_IO_OUT)
481
flags |= DBUS_WATCH_WRITABLE;
483
if (condition & G_IO_ERR)
484
flags |= DBUS_WATCH_ERROR;
486
if (condition & G_IO_HUP)
487
flags |= DBUS_WATCH_HANGUP;
489
dbus_watch_handle(watch_data->watch, flags);
494
GSourceFuncs DBus::queue_funcs = {
497
DBus::queue_dispatch,
504
DBus::watch_finalize(gpointer data)
506
WatchData *watch_data = (WatchData*) data;
508
if (watch_data->watch)
509
dbus_watch_set_data(watch_data->watch, NULL, NULL);
515
DBus::watch_free(void *data)
517
WatchData *watch_data = (WatchData*) data;
519
if (watch_data->source != NULL)
521
watch_data->dbus->watches = g_slist_remove(watch_data->dbus->watches, watch_data);
523
g_source_destroy(watch_data->source);
524
g_source_unref(watch_data->source);
529
DBus::watch_add(DBusWatch *watch, void *data)
531
DBus *dbus = (DBus*)data;
532
GIOCondition condition = (GIOCondition) (G_IO_ERR | G_IO_HUP);
535
WatchData *watch_data;
539
if (dbus_watch_get_enabled(watch) == FALSE)
542
flags = dbus_watch_get_flags(watch);
544
if (flags & DBUS_WATCH_READABLE)
545
condition = (GIOCondition) (condition | G_IO_IN);
547
if (flags & DBUS_WATCH_WRITABLE)
548
condition = (GIOCondition) (condition | G_IO_OUT);
550
fd = dbus_watch_get_unix_fd(watch);
552
watch_data = g_new0(WatchData, 1);
554
channel = g_io_channel_unix_new(fd);
556
source = g_io_create_watch(channel, condition);
558
watch_data->watch = watch;
559
watch_data->source = source;
560
watch_data->dbus = dbus;
562
g_source_set_callback(source, (GSourceFunc) watch_dispatch,
563
watch_data, watch_finalize);
565
g_source_attach(source, dbus->context);
567
watch_data->dbus->watches = g_slist_prepend(watch_data->dbus->watches, watch_data);
569
dbus_watch_set_data(watch, watch_data, watch_free);
571
g_io_channel_unref(channel);
577
DBus::watch_remove(DBusWatch *watch, void *data)
579
WatchData *watch_data = (WatchData*) dbus_watch_get_data(watch);
580
DBus *dbus = (DBus*)data;
582
dbus_watch_set_data(watch, NULL, NULL);
584
if (watch_data != NULL)
586
dbus->watches = g_slist_remove(dbus->watches, watch_data);
588
if (watch_data->source != NULL)
590
g_source_destroy(watch_data->source);
591
g_source_unref(watch_data->source);
597
DBus::watch_toggled(DBusWatch *watch, void *data)
599
if (dbus_watch_get_enabled(watch))
600
watch_add(watch, data);
602
watch_remove(watch, data);
606
DBus::timeout_dispatch(gpointer data)
608
TimeoutData *timeout_data = (TimeoutData*) data;
610
dbus_timeout_handle(timeout_data->timeout);
616
DBus::timeout_free(void *data)
618
TimeoutData *timeout_data = (TimeoutData*)data;
620
if (timeout_data->id > 0)
621
g_source_remove(timeout_data->id);
623
g_free(timeout_data);
628
DBus::timeout_add(DBusTimeout *timeout, void *data)
630
TimeoutData *timeout_data;
631
DBus *dbus = (DBus *) data;
633
if (dbus_timeout_get_enabled(timeout) == FALSE)
636
timeout_data = g_new0(TimeoutData, 1);
638
timeout_data->timeout = timeout;
639
timeout_data->dbus = dbus;
641
timeout_data->id = g_timeout_add(dbus_timeout_get_interval(timeout),
642
timeout_dispatch, timeout_data);
644
dbus->timeouts = g_slist_prepend(dbus->timeouts, timeout_data);
646
dbus_timeout_set_data(timeout, timeout_data, timeout_free);
652
DBus::timeout_remove(DBusTimeout *timeout, void *data)
654
TimeoutData *timeout_data = (TimeoutData*)dbus_timeout_get_data(timeout);
655
DBus *dbus = (DBus *) data;
657
if (timeout_data == NULL)
660
dbus->timeouts = g_slist_remove(dbus->timeouts, timeout_data);
662
if (timeout_data->id > 0)
663
g_source_remove(timeout_data->id);
665
timeout_data->id = 0;
670
DBus::timeout_toggled(DBusTimeout *timeout, void *data)
672
if (dbus_timeout_get_enabled(timeout))
673
timeout_add(timeout, data);
675
timeout_remove(timeout, data);
680
DBus::wakeup(void *data)
682
DBus *dbus = (DBus*) data;
683
g_main_context_wakeup(dbus->context);
688
DBus::connection_setup(GMainContext *context)
691
context = g_main_context_default();
693
context = g_main_context_ref(context);
695
queue = g_source_new(&queue_funcs, sizeof(QueueData));
696
((QueueData*)queue)->connection = connection;
698
g_source_attach(queue, context);
700
dbus_connection_set_watch_functions(connection,
706
dbus_connection_set_timeout_functions(connection,
712
dbus_connection_set_wakeup_main_function(connection,