2
* @file geis_dbus_client.c
3
* @brief Implementations of the GEIS DBus client.
7
* Copyright 2011 Canonical Ltd.
9
* This library is free software; you can redistribute it and/or modify it under
10
* the terms of the GNU Lesser General Public License as published by the Free
11
* Software Foundation; either version 3 of the License, or (at your option) any
14
* This library is distributed in the hope that it will be useful, but WITHOUT
15
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
19
* You should have received a copy of the GNU General Public License
20
* along with this program. If not, see <http://www.gnu.org/licenses/>.
22
#include "geis_config.h"
23
#include "geis_dbus_client.h"
25
#include "geis_dbus.h"
26
#include "geis_dbus_class.h"
27
#include "geis_dbus_device.h"
28
#include "geis_dbus_gesture_event.h"
29
#include "geis_dbus_locator.h"
30
#include "geis_dbus_region.h"
31
#include "geis_dbus_subscription.h"
32
#include "geis_event.h"
33
#include "geis_logging.h"
34
#include "geis_private.h"
39
typedef enum GeisDBusClientState
41
GEIS_DBUS_CLIENT_DISCONNECTED, /* no server available */
42
GEIS_DBUS_CLIENT_INITIALIZING, /* server connected, client initializing */
43
GEIS_DBUS_CLIENT_CONNECTING, /* server connected, not initialized */
44
GEIS_DBUS_CLIENT_CONNECTED /* server connected, all systems go */
45
} GeisDBusClientState;
51
GeisDBusDispatcher dispatcher;
52
GeisDBusLocator locator;
53
GeisDBusClientState state;
54
DBusConnection *connection;
55
GeisSubBag subscription_bag;
60
* Handles a device-available message from the server.
62
* @param[in] client A %GeisDBusClient.
63
* @param[in] message The %DBusMessage.
66
_client_device_available(GeisDBusClient client, DBusMessage *message)
68
GeisDevice device = geis_dbus_device_device_from_available_message(message);
71
geis_register_device(client->geis, device, 0, NULL);
75
geis_error("no device received from remote back end");
81
* Handles a device-unavailable message from the server.
83
* @param[in] client A %GeisDBusClient.
84
* @param[in] message The %DBusMessage.
87
_client_device_unavailable(GeisDBusClient client, DBusMessage *message)
89
GeisDevice device = geis_dbus_device_device_from_unavailable_message(message);
92
geis_unregister_device(client->geis, device);
96
geis_error("no device received from remote back end");
102
* Handles a class-available message from the server.
104
* @param[in] client A %GeisDBusClient.
105
* @param[in] message The %DBusMessage.
108
_client_class_available(GeisDBusClient client, DBusMessage *message)
110
GeisGestureClass gesture_class;
112
gesture_class = geis_dbus_class_class_from_available_message(message);
115
geis_register_gesture_class(client->geis, gesture_class, 0, NULL);
119
geis_error("no gesture class received from remote back end");
125
* Handles a region-available message from the server.
127
* @param[in] client A %GeisDBusClient.
128
* @param[in] message The %DBusMessage.
131
_client_region_available(GeisDBusClient client, DBusMessage *message)
133
GeisFilterableAttribute attr;
135
attr = geis_dbus_region_from_region_available_message(message);
138
attr->add_term_callback = 0;
139
attr->add_term_context = 0;
140
geis_register_region(client->geis, NULL, 1, attr);
144
geis_error("no region attr received from remote back end");
150
* Handles a class-unavailable message from the server.
152
* @param[in] client A %GeisDBusClient.
153
* @param[in] message The %DBusMessage.
156
_client_gesture_event(GeisDBusClient client, DBusMessage *message)
158
GeisEvent event = geis_dbus_gesture_event_from_message(client->geis, message);
161
geis_error("no gesture event received from remote back end");
165
geis_post_event(client->geis, event);
171
* Processes an subscription-activate reply from the server.
173
* @param[in] pending A DBusPendingCall object.
174
* @param[in] user_data The %GeisDBusClient object.
177
_geis_dbus_client_activate_reply(DBusPendingCall *pending, void *user_data)
179
GeisDBusClient client GEIS_UNUSED = (GeisDBusClient)user_data;
180
DBusMessage *reply = dbus_pending_call_steal_reply(pending);
182
if (DBUS_MESSAGE_TYPE_ERROR == dbus_message_get_type(reply))
184
const char *s = NULL;
185
dbus_message_get_args(reply, NULL, DBUS_TYPE_STRING, &s, DBUS_TYPE_INVALID);
186
geis_error("error %s: %s", dbus_message_get_error_name(reply), s);
192
* Processes a subscription-create reply from the server.
194
* @param[in] pending A DBusPendingCall object.
195
* @param[in] user_data The %GeisDBusClient object.
198
_geis_dbus_client_subscribe_reply(DBusPendingCall *pending, void *user_data)
200
GeisDBusClient client = (GeisDBusClient)user_data;
201
DBusMessage *reply = dbus_pending_call_steal_reply(pending);
203
if (DBUS_MESSAGE_TYPE_ERROR == dbus_message_get_type(reply))
205
const char *s = NULL;
206
dbus_message_get_args(reply, NULL, DBUS_TYPE_STRING, &s, DBUS_TYPE_INVALID);
207
geis_error("error %s: %s", dbus_message_get_error_name(reply), s);
212
DBusPendingCall *pending;
213
DBusError error = DBUS_ERROR_INIT;
214
dbus_int32_t client_sub_id;
215
dbus_int32_t server_sub_id;
216
GeisSubscription subscription;
218
dbus_message_get_args(reply,
220
DBUS_TYPE_INT32, &client_sub_id,
221
DBUS_TYPE_INT32, &server_sub_id,
223
if (dbus_error_is_set(&error))
225
geis_error("error %s: %s", error.name, error.message);
226
dbus_error_free(&error);
229
subscription = geis_subscription_bag_find(client->subscription_bag,
233
geis_error("invalid client subcription id %d returned from server",
238
geis_subscription_set_pdata(subscription, (GeisPointer)(intptr_t)server_sub_id);
240
msg = geis_dbus_subscription_activate_call_message(subscription);
241
dbus_connection_send_with_reply(client->connection, msg, &pending, -1);
242
dbus_message_unref(msg);
245
geis_error("error sending DBus CreateSubscription method call");
249
dbus_pending_call_set_notify(pending,
250
_geis_dbus_client_activate_reply,
256
dbus_message_unref(reply);
257
dbus_pending_call_unref(pending);
262
* Processes a deactivate-subscription reply from the server.
264
* @param[in] pending A DBusPendingCall object.
265
* @param[in] user_data The %GeisDBusClient object.
268
_geis_dbus_client_unsubscribe_reply(DBusPendingCall *pending, void *user_data)
270
GeisDBusClient client GEIS_UNUSED = (GeisDBusClient)user_data;
271
DBusMessage *reply = dbus_pending_call_steal_reply(pending);
273
if (DBUS_MESSAGE_TYPE_ERROR == dbus_message_get_type(reply))
275
const char *s = NULL;
276
dbus_message_get_args(reply, NULL, DBUS_TYPE_STRING, &s, DBUS_TYPE_INVALID);
277
geis_error("error %s: %s", dbus_message_get_error_name(reply), s);
281
geis_warning("signature=\"%s\"", dbus_message_get_signature(reply));
282
geis_warning("path=\"%s\"", dbus_message_get_path(reply));
283
geis_warning("interface=\"%s\"", dbus_message_get_interface(reply));
284
geis_warning("member=\"%s\"", dbus_message_get_member(reply));
287
dbus_message_unref(reply);
288
dbus_pending_call_unref(pending);
293
* Creates a remote subscription.
296
_dbus_client_subscribe(GeisDBusClient client,
297
GeisSubscription subscription)
299
DBusPendingCall *pending_return;
301
GeisSubscription sub = geis_subscription_bag_find(client->subscription_bag,
302
geis_subscription_id(subscription));
303
if (sub && geis_subscription_pdata(sub))
305
geis_warning("subscription already activated!");
309
DBusMessage *msg = geis_dbus_subscription_create_call_message(subscription);
310
dbus_connection_send_with_reply(client->connection, msg, &pending_return, -1);
311
dbus_message_unref(msg);
314
geis_error("error sending DBus CreateSubscription method call");
318
dbus_pending_call_set_notify(pending_return,
319
_geis_dbus_client_subscribe_reply,
327
* Re-subscribes all existing sibscriptions when the server appears or
331
_dbus_client_resubscribe_all(GeisDBusClient client)
333
GeisSubBagIterator it;
334
for (it = geis_subscription_bag_begin(client->subscription_bag);
335
it != geis_subscription_bag_end(client->subscription_bag);
336
it = geis_subscription_bag_iterator_next(client->subscription_bag, it))
338
geis_subscription_set_pdata(*it, 0);
339
_dbus_client_subscribe(client, *it);
345
* The DBus message dispatch function for the GEIS DBus client.
347
* @param[in] connection The %GeisDBusClient DBus connection.
348
* @param[in] message The DBus message received.
349
* @param[in] user_data The %GeisDBusClient.
351
static DBusHandlerResult
352
_geis_dbus_client_message_handler(DBusConnection *connection GEIS_UNUSED,
353
DBusMessage *message,
356
DBusHandlerResult result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
357
GeisDBusClient client = (GeisDBusClient)user_data;
358
int type = dbus_message_get_type(message);
360
if (dbus_message_is_signal(message,
361
DBUS_INTERFACE_LOCAL,
364
geis_warning("server disconnected?");
365
result = DBUS_HANDLER_RESULT_HANDLED;
367
else if (dbus_message_is_signal(message,
368
GEIS_DBUS_SERVICE_INTERFACE,
369
GEIS_DBUS_DEVICE_AVAILABLE))
371
_client_device_available(client, message);
372
result = DBUS_HANDLER_RESULT_HANDLED;
374
else if (dbus_message_is_signal(message,
375
GEIS_DBUS_SERVICE_INTERFACE,
376
GEIS_DBUS_DEVICE_UNAVAILABLE))
379
_client_device_unavailable(client, message);
380
result = DBUS_HANDLER_RESULT_HANDLED;
382
else if (dbus_message_is_signal(message,
383
GEIS_DBUS_SERVICE_INTERFACE,
384
GEIS_DBUS_CLASS_AVAILABLE))
386
_client_class_available(client, message);
387
result = DBUS_HANDLER_RESULT_HANDLED;
389
else if (dbus_message_is_signal(message,
390
GEIS_DBUS_SERVICE_INTERFACE,
391
GEIS_DBUS_REGION_AVAILABLE))
393
_client_region_available(client, message);
394
result = DBUS_HANDLER_RESULT_HANDLED;
396
else if (dbus_message_is_signal(message,
397
GEIS_DBUS_SERVICE_INTERFACE,
398
GEIS_DBUS_INIT_COMPLETE))
400
if (client->state == GEIS_DBUS_CLIENT_INITIALIZING)
402
geis_post_event(client->geis, geis_event_new(GEIS_EVENT_INIT_COMPLETE));
404
client->state = GEIS_DBUS_CLIENT_CONNECTED;
405
_dbus_client_resubscribe_all(client);
406
result = DBUS_HANDLER_RESULT_HANDLED;
408
else if (geis_dbus_message_is_gesture_event(message))
410
_client_gesture_event(client, message);
411
result = DBUS_HANDLER_RESULT_HANDLED;
413
else if (type == DBUS_MESSAGE_TYPE_ERROR)
415
const char *str = NULL;
416
dbus_message_get_args(message, NULL,
417
DBUS_TYPE_STRING, &str,
419
geis_warning("error %s: %s", dbus_message_get_error_name(message), str);
423
geis_warning("unhandled DBus %s received:",
424
dbus_message_type_to_string(dbus_message_get_type(message)));
425
geis_warning(" signature=\"%s\"", dbus_message_get_signature(message));
426
geis_warning(" sender=\"%s\"", dbus_message_get_sender(message));
427
geis_warning(" path=\"%s\"",
428
dbus_message_get_path(message) ?
429
dbus_message_get_path(message) :
431
geis_warning(" interface=\"%s\"",
432
dbus_message_get_interface(message) ?
433
dbus_message_get_interface(message) :
435
geis_warning(" member=\"%s\"",
436
dbus_message_get_member(message) ?
437
dbus_message_get_member(message) :
445
* Adds the client watches to the dispatcher watch list.
447
* @param[in] watch A %DBusWatch.
448
* @param[in] data The %GeisDBusClientProxy.
451
_client_add_watch(DBusWatch *watch, void *data)
453
dbus_bool_t status = TRUE;
454
GeisDBusClient client = (GeisDBusClient)data;
456
geis_dbus_dispatcher_register(client->dispatcher, client->connection, watch);
462
* Toggles the enabled/disabled status of the client watches.
464
* @param[in] watch A %DBusWatch.
465
* @param[in] data The %GeisDBusClientProxy.
468
_client_toggle_watch(DBusWatch *watch, void *data)
470
GeisDBusClient client = (GeisDBusClient)data;
472
geis_dbus_dispatcher_toggle_watch(client->dispatcher, watch);
477
* Removes the client watches from the dispatcher watch list.
479
* @param[in] watch A %DBusWatch.
480
* @param[in] data The %GeisDBusClientProxy.
483
_client_remove_watch(DBusWatch *watch, void *data)
485
GeisDBusClient client = (GeisDBusClient)data;
487
geis_dbus_dispatcher_unregister(client->dispatcher, watch);
492
* Connects to the GEIS server once an address is located.
494
* @param[in] client A %GeisDBusClient object.
495
* @param[in] address The address of the server.
498
_client_connect(GeisDBusClient client, const char *address)
500
geis_debug("server address=\"%s\"", address);
501
DBusError error = DBUS_ERROR_INIT;
502
client->connection = dbus_connection_open(address, &error);
503
if (!client->connection || dbus_error_is_set(&error))
506
snprintf(msg, sizeof(msg), "error %s connecting to server at address %s: %s",
507
error.name, address, error.message);
508
geis_error("%s", msg);
509
dbus_error_free(&error);
513
/* Integrate with the app event loop via the GEIS multiplexor. */
514
dbus_connection_set_watch_functions(client->connection,
516
_client_remove_watch,
517
_client_toggle_watch,
520
/* Install a handler for any and all messages. */
521
dbus_connection_add_filter(client->connection,
522
_geis_dbus_client_message_handler,
524
if (client->state != GEIS_DBUS_CLIENT_INITIALIZING)
526
client->state = GEIS_DBUS_CLIENT_CONNECTING;
535
* Creates a new GeisDBusClient.
538
geis_dbus_client_new(Geis geis)
540
GeisDBusClient client = calloc(1, sizeof(struct GeisDBusClient));
547
client->state = GEIS_DBUS_CLIENT_INITIALIZING;
549
client->dispatcher = geis_dbus_dispatcher_new(geis);
550
if (!client->dispatcher)
555
client->locator = geis_dbus_locator_new(client);
556
if (!client->locator)
558
goto unwind_dispatcher;
561
client->subscription_bag = geis_subscription_bag_new(1);
562
if (!client->subscription_bag)
570
geis_dbus_locator_delete(client->locator);
572
geis_dbus_dispatcher_delete(client->dispatcher);
582
* Destroys a GeisDBusClient.
585
geis_dbus_client_delete(GeisDBusClient client)
587
geis_subscription_bag_delete(client->subscription_bag);
588
geis_dbus_locator_delete(client->locator);
589
if (client->connection)
591
dbus_connection_unref(client->connection);
593
geis_dbus_dispatcher_delete(client->dispatcher);
599
* Gets the client dispatcher.
602
geis_dbus_client_dispatcher(GeisDBusClient client)
604
return client->dispatcher;
609
* Signals the client the server has been located.
612
geis_dbus_client_server_located(GeisDBusClient client)
614
_client_connect(client, geis_dbus_locator_server_address(client->locator));
619
* Signals the client the server has been dislocated.
622
geis_dbus_client_server_dislocated(GeisDBusClient client)
624
GeisEvent event = geis_event_new(GEIS_EVENT_ERROR);
625
client->state = GEIS_DBUS_CLIENT_DISCONNECTED;
626
geis_post_event(client->geis, event);
631
* Requests a subscription on the remote end.
634
geis_dbus_client_subscribe(GeisDBusClient client,
635
GeisSubscription subscription)
637
GeisStatus status = GEIS_STATUS_SUCCESS;
639
if (client->state == GEIS_DBUS_CLIENT_CONNECTED)
641
_dbus_client_subscribe(client, subscription);
643
geis_subscription_bag_insert(client->subscription_bag, subscription);
650
* Destroys a subscription on the remote end.
653
geis_dbus_client_unsubscribe(GeisDBusClient client,
654
GeisSubscription subscription)
656
GeisStatus status = GEIS_STATUS_UNKNOWN_ERROR;
657
if (geis_subscription_bag_find(client->subscription_bag,
658
geis_subscription_id(subscription)))
661
DBusPendingCall *pending_return;
663
msg = geis_dbus_subscription_destroy_call_message(subscription);
664
dbus_connection_send_with_reply(client->connection, msg, &pending_return, -1);
665
dbus_message_unref(msg);
668
geis_error("error sending DBus CreateSubscription method call");
672
dbus_pending_call_set_notify(pending_return,
673
_geis_dbus_client_unsubscribe_reply,
675
geis_subscription_bag_remove(client->subscription_bag, subscription);
676
status = GEIS_STATUS_SUCCESS;