summaryrefslogtreecommitdiffstats
path: root/src/lassi-server.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lassi-server.c')
-rw-r--r--src/lassi-server.c1554
1 files changed, 1554 insertions, 0 deletions
diff --git a/src/lassi-server.c b/src/lassi-server.c
new file mode 100644
index 0000000..f160279
--- /dev/null
+++ b/src/lassi-server.c
@@ -0,0 +1,1554 @@
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <dbus/dbus.h>
+
+#include <gtk/gtk.h>
+
+#include "lassi-server.h"
+#include "lassi-grab.h"
+#include "lassi-order.h"
+#include "lassi-clipboard.h"
+#include "lassi-avahi.h"
+#include "lassi-tray.h"
+
+#define LASSI_INTERFACE "org.gnome.MangoLassi"
+
+#define PORT_MIN 7421
+#define PORT_MAX (PORT_MIN + 50)
+
+#define CONNECTIONS_MAX 16
+
+static void server_disconnect_all(LassiServer *ls, gboolean clear_order);
+static void server_send_update_grab(LassiServer *ls, int y);
+
+static void server_broadcast(LassiServer *ls, DBusMessage *m, LassiConnection *except) {
+ GList *i;
+
+ g_assert(ls);
+ g_assert(m);
+
+ for (i = ls->connections; i; i = i->next) {
+ dbus_bool_t b;
+ LassiConnection *lc = i->data;
+ DBusMessage *n;
+
+ if (lc == except || !lc->id)
+ continue;
+
+ n = dbus_message_copy(m);
+ g_assert(n);
+ b = dbus_connection_send(lc->dbus_connection, n, NULL);
+ g_assert(b);
+ dbus_message_unref(n);
+ }
+}
+
+static void server_layout_changed(LassiServer *ls, int y) {
+ g_assert(ls);
+
+ g_debug("updating layout");
+
+ lassi_grab_enable_triggers(&ls->grab_info, !!ls->connections_left, !!ls->connections_right);
+
+ if (ls->active_connection) {
+ char *t;
+ gboolean to_left = !!g_list_find(ls->connections_left, ls->active_connection);
+
+ t = g_strdup_printf("Mouse and keyboard are being redirected to <b>%s</b>, which is located to the <b>%s</b> of this screen.\n"
+ "To redirect input back to this screen, press and release both shift keys simultaneously.",
+ ls->active_connection->id, to_left ? "left" : "right");
+
+ if (to_left)
+ lassi_osd_set_text(&ls->osd_info, t, "go-previous", NULL);
+ else
+ lassi_osd_set_text(&ls->osd_info, t, NULL, "go-next");
+
+ lassi_grab_start(&ls->grab_info, to_left);
+
+ } else {
+ lassi_grab_stop(&ls->grab_info, y);
+ lassi_osd_hide(&ls->osd_info);
+ }
+}
+
+static void server_set_order(LassiServer *ls, GList *order) {
+ GList *l;
+ gboolean on_left = TRUE;
+ g_assert(ls);
+
+ lassi_list_free(ls->order);
+ ls->order = order;
+
+ g_list_free(ls->connections_left);
+ g_list_free(ls->connections_right);
+
+ ls->connections_left = ls->connections_right = NULL;
+
+ for (l = ls->order; l; l = l->next) {
+ LassiConnection *lc;
+
+ if (!(lc = g_hash_table_lookup(ls->connections_by_id, l->data))) {
+
+ if (strcmp(ls->id, l->data))
+ continue;
+ }
+
+ g_assert(lc || on_left);
+
+ if (!lc)
+ on_left = FALSE;
+ else if (on_left)
+ ls->connections_left = g_list_prepend(ls->connections_left, lc);
+ else
+ ls->connections_right = g_list_prepend(ls->connections_right, lc);
+ }
+
+ for (l = ls->connections; l; l = l->next) {
+ LassiConnection *lc = l->data;
+
+ if (!lc->id)
+ continue;
+
+ if (g_list_find(ls->connections_left, lc))
+ continue;
+
+ if (g_list_find(ls->connections_right, lc))
+ continue;
+
+ ls->order = g_list_append(ls->order, lc->id);
+ ls->connections_right = g_list_prepend(ls->connections_right, lc);
+ }
+
+ ls->connections_right = g_list_reverse(ls->connections_right);
+ server_layout_changed(ls, -1);
+
+ lassi_prefs_update(&ls->prefs_info);
+}
+
+static void server_dump(LassiServer *ls) {
+ GList *l;
+ int n = 0;
+
+ g_assert(ls);
+
+ g_debug("BEGIN Current connections:");
+
+ g_debug("Displays left of us:");
+ for (l = ls->connections_left; l; l = l->next) {
+ LassiConnection *lc = l->data;
+ if (!lc->id)
+ continue;
+ g_debug("%2i) %s %s %s", n++, ls->active_connection == lc ? "ACTIVE" : " ", lc->id, lc->address);
+ }
+
+ g_debug("Displays right of us:");
+ for (l = ls->connections_right; l; l = l->next) {
+ LassiConnection *lc = l->data;
+ if (!lc->id)
+ continue;
+ g_debug("%2i) %s %s %s", n++, ls->active_connection == lc ? "ACTIVE" : " ", lc->id, lc->address);
+ }
+
+ if (!ls->active_connection)
+ g_debug("We're the active connection");
+
+ g_debug("END");
+}
+
+static void connection_destroy(LassiConnection *lc) {
+ g_assert(lc);
+
+ dbus_connection_flush(lc->dbus_connection);
+ dbus_connection_close(lc->dbus_connection);
+ dbus_connection_unref(lc->dbus_connection);
+ g_free(lc->id);
+ g_free(lc->address);
+ g_free(lc);
+}
+
+static void server_pick_active_connection(LassiServer *ls) {
+ LassiConnection *pick;
+ GList *l;
+ char *id;
+
+ pick = NULL;
+ id = ls->id;
+
+ for (l = ls->connections; l; l = l->next) {
+ LassiConnection *lc = l->data;
+
+ if (!lc->id)
+ continue;
+
+ if (strcmp(lc->id, id) > 0) {
+ id = lc->id;
+ pick = lc;
+ }
+ }
+
+ ls->active_connection = pick;
+
+ server_send_update_grab(ls, -1);
+ server_layout_changed(ls, -1);
+}
+
+static void server_send_update_grab(LassiServer *ls, int y) {
+ char *active;
+ DBusMessage *n;
+ dbus_bool_t b;
+ gint32 g;
+
+ g_assert(ls);
+
+ active = ls->active_connection ? ls->active_connection->id : ls->id;
+
+ n = dbus_message_new_signal("/", LASSI_INTERFACE, "UpdateGrab");
+ g_assert(n);
+
+ g = ++ ls->active_generation;
+ b = dbus_message_append_args(
+ n,
+ DBUS_TYPE_INT32, &g,
+ DBUS_TYPE_STRING, &active,
+ DBUS_TYPE_INT32, &y,
+ DBUS_TYPE_INVALID);
+ g_assert(b);
+
+ server_broadcast(ls, n, NULL);
+ dbus_message_unref(n);
+}
+
+static void server_send_update_order(LassiServer *ls, LassiConnection *except) {
+ DBusMessage *n;
+ dbus_bool_t b;
+ gint32 g;
+ DBusMessageIter iter, sub;
+ GList *l;
+
+ g_assert(ls);
+
+ n = dbus_message_new_signal("/", LASSI_INTERFACE, "UpdateOrder");
+ g_assert(n);
+
+ g = ++ ls->order_generation;
+ b = dbus_message_append_args(
+ n,
+ DBUS_TYPE_INT32, &g,
+ DBUS_TYPE_INVALID);
+ g_assert(b);
+
+ dbus_message_iter_init_append(n, &iter);
+
+ b = dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &sub);
+ g_assert(b);
+
+ for (l = ls->order; l; l = l->next) {
+ b = dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &l->data);
+ g_assert(b);
+ }
+
+ b = dbus_message_iter_close_container(&iter, &sub);
+ g_assert(b);
+
+ server_broadcast(ls, n, except);
+ dbus_message_unref(n);
+}
+
+int lassi_server_change_grab(LassiServer *ls, gboolean to_left, int y) {
+ LassiConnection *lc;
+ GList *l;
+
+ g_assert(ls);
+
+ l = to_left ? ls->connections_left : ls->connections_right;
+ lc = l ? l->data : NULL;
+
+ if (!lc)
+ return -1;
+
+ ls->active_connection = lc;
+
+ server_send_update_grab(ls, y);
+ server_layout_changed(ls, y);
+ return 0;
+}
+
+int lassi_server_acquire_grab(LassiServer *ls) {
+ g_assert(ls);
+
+ ls->active_connection = NULL;
+
+ server_send_update_grab(ls, -1);
+ server_layout_changed(ls, -1);
+ return 0;
+}
+
+int lassi_server_motion_event(LassiServer *ls, int dx, int dy) {
+ DBusMessage *n;
+ dbus_bool_t b;
+
+ g_assert(ls);
+
+ if (!ls->active_connection)
+ return -1;
+
+ n = dbus_message_new_signal("/", LASSI_INTERFACE, "MotionEvent");
+ g_assert(n);
+
+ b = dbus_message_append_args(n, DBUS_TYPE_INT32, &dx, DBUS_TYPE_INT32, &dy, DBUS_TYPE_INVALID);
+ g_assert(b);
+
+ b = dbus_connection_send(ls->active_connection->dbus_connection, n, NULL);
+ g_assert(b);
+
+ dbus_message_unref(n);
+
+ dbus_connection_flush(ls->active_connection->dbus_connection);
+
+ return 0;
+}
+
+int lassi_server_button_event(LassiServer *ls, unsigned button, gboolean is_press) {
+ DBusMessage *n;
+ dbus_bool_t b;
+
+ if (!ls->active_connection)
+ return -1;
+
+ n = dbus_message_new_signal("/", LASSI_INTERFACE, "ButtonEvent");
+ g_assert(n);
+
+ b = dbus_message_append_args(n, DBUS_TYPE_UINT32, &button, DBUS_TYPE_BOOLEAN, &is_press, DBUS_TYPE_INVALID);
+ g_assert(b);
+
+ b = dbus_connection_send(ls->active_connection->dbus_connection, n, NULL);
+ g_assert(b);
+
+ dbus_message_unref(n);
+
+ dbus_connection_flush(ls->active_connection->dbus_connection);
+
+ return 0;
+}
+
+int lassi_server_key_event(LassiServer *ls, unsigned key, gboolean is_press) {
+ DBusMessage *n;
+ dbus_bool_t b;
+
+ if (!ls->active_connection)
+ return -1;
+
+ n = dbus_message_new_signal("/", LASSI_INTERFACE, "KeyEvent");
+ g_assert(n);
+
+ b = dbus_message_append_args(n, DBUS_TYPE_UINT32, &key, DBUS_TYPE_BOOLEAN, &is_press, DBUS_TYPE_INVALID);
+ g_assert(b);
+
+ b = dbus_connection_send(ls->active_connection->dbus_connection, n, NULL);
+ g_assert(b);
+
+ dbus_message_unref(n);
+
+ dbus_connection_flush(ls->active_connection->dbus_connection);
+
+ return 0;
+}
+
+static void show_welcome(LassiConnection *lc, gboolean connect) {
+ gboolean to_left;
+ LassiServer *ls;
+ char *summary, *body;
+
+ g_assert(lc);
+
+ ls = lc->server;
+ to_left = !!g_list_find(ls->connections_left, lc);
+
+ if (connect) {
+ summary = g_strdup_printf("%s now shares input with this desktop", lc->id);
+ body = g_strdup_printf("You're now sharing keyboard and mouse with <b>%s</b> which is located to the <b>%s</b>.", lc->id, to_left ? "left" : "right");
+ } else {
+ summary = g_strdup_printf("%s no longer shares input with this desktop", lc->id);
+ body = g_strdup_printf("You're no longer sharing keyboard and mouse with <b>%s</b> which was located to the <b>%s</b>.", lc->id, to_left ? "left" : "right");
+ }
+
+ lassi_tray_show_notification(&ls->tray_info, summary, body, to_left ? LASSI_TRAY_NOTIFICATION_LEFT : LASSI_TRAY_NOTIFICATION_RIGHT);
+
+ g_free(summary);
+ g_free(body);
+}
+
+static void connection_unlink(LassiConnection *lc, gboolean remove_from_order) {
+ LassiServer *ls;
+ g_assert(lc);
+
+ g_debug("Unlinking %s (%s)", lc->id, lc->address);
+
+ ls = lc->server;
+
+ if (lc->id) {
+ DBusMessage *n;
+ dbus_bool_t b;
+
+ /* Tell everyone */
+ n = dbus_message_new_signal("/", LASSI_INTERFACE, "NodeRemoved");
+ g_assert(n);
+
+ b = dbus_message_append_args(n,
+ DBUS_TYPE_STRING, &lc->id,
+ DBUS_TYPE_STRING, &lc->address,
+ DBUS_TYPE_BOOLEAN, &remove_from_order,
+ DBUS_TYPE_INVALID);
+ g_assert(b);
+
+ server_broadcast(ls, n, NULL);
+ dbus_message_unref(n);
+ }
+
+ ls->connections = g_list_remove(ls->connections, lc);
+ ls->n_connections --;
+
+ if (lc->id) {
+ show_welcome(lc, FALSE);
+
+ g_hash_table_remove(ls->connections_by_id, lc->id);
+ ls->connections_left = g_list_remove(ls->connections_left, lc);
+ ls->connections_right = g_list_remove(ls->connections_right, lc);
+
+ if (ls->active_connection == lc)
+ server_pick_active_connection(ls);
+
+ if (ls->clipboard_connection == lc) {
+ ls->clipboard_connection = NULL;
+ ls->clipboard_empty = TRUE;
+ lassi_clipboard_clear(&lc->server->clipboard_info, FALSE);
+ }
+
+ if (ls->primary_connection == lc) {
+ ls->primary_connection = NULL;
+ ls->primary_empty = TRUE;
+ lassi_clipboard_clear(&lc->server->clipboard_info, TRUE);
+ }
+
+ if (remove_from_order) {
+ GList *i = g_list_find_custom(ls->order, lc->id, (GCompareFunc) strcmp);
+
+ if (i)
+ ls->order = g_list_delete_link(ls->order, i);
+ }
+
+ server_layout_changed(ls, -1);
+ lassi_prefs_update(&ls->prefs_info);
+ server_dump(ls);
+ }
+
+ lassi_tray_update(&ls->tray_info, ls->n_connections);
+
+ connection_destroy(lc);
+}
+
+static void server_position_connection(LassiServer *ls, LassiConnection *lc) {
+ GList *l;
+ LassiConnection *last = NULL;
+
+ g_assert(ls);
+ g_assert(lc);
+
+ g_assert(!g_list_find(ls->connections_left, lc));
+ g_assert(!g_list_find(ls->connections_right, lc));
+
+ for (l = ls->order; l; l = l->next) {
+ LassiConnection *k;
+
+ if (strcmp(l->data, lc->id) == 0)
+ break;
+
+ if ((k = g_hash_table_lookup(ls->connections_by_id, l->data)))
+ last = k;
+ }
+
+ if (l) {
+ /* OK, We found a spot to add this */
+
+ if (last) {
+ GList *j;
+
+ /*Ok, this one belongs to the right of 'last' */
+
+ if ((j = g_list_find(ls->connections_left, last)))
+ /* This one belongs in the left list */
+ ls->connections_left = g_list_insert_before(ls->connections_left, j, lc);
+ else {
+ /* This one belongs in the rightlist */
+ ls->connections_right = g_list_reverse(ls->connections_right);
+ j = g_list_find(ls->connections_right, last);
+ g_assert(j);
+ ls->connections_right = g_list_insert_before(ls->connections_right, j, lc);
+ ls->connections_right = g_list_reverse(ls->connections_right);
+ }
+ } else
+ /* Hmm, this is before the left end */
+ ls->connections_left = g_list_append(ls->connections_left, lc);
+ } else {
+ ls->order = g_list_append(ls->order, g_strdup(lc->id));
+ /* No spot found, let's add it to the right end */
+ ls->connections_right = g_list_append(ls->connections_right, lc);
+ }
+}
+
+int lassi_server_acquire_clipboard(LassiServer *ls, gboolean primary, char**targets) {
+ DBusMessageIter iter, sub;
+ DBusMessage *n;
+ gint32 g;
+ gboolean b;
+
+ g_assert(ls);
+ g_assert(targets);
+
+ if (primary) {
+ ls->primary_empty = FALSE;
+ ls->primary_connection = NULL;
+ } else {
+ ls->clipboard_empty = FALSE;
+ ls->clipboard_connection = NULL;
+ }
+
+ n = dbus_message_new_signal("/", LASSI_INTERFACE, "AcquireClipboard");
+ g_assert(n);
+
+ if (primary)
+ g = ++ ls->primary_generation;
+ else
+ g = ++ ls->clipboard_generation;
+
+ b = dbus_message_append_args(n, DBUS_TYPE_INT32, &g, DBUS_TYPE_BOOLEAN, &primary, DBUS_TYPE_INVALID);
+ g_assert(b);
+
+ dbus_message_iter_init_append(n, &iter);
+
+ b = dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &sub);
+ g_assert(b);
+
+ for (; *targets; targets++) {
+ g_debug("Exporting target %s", *targets);
+ b = dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, targets);
+ g_assert(b);
+ }
+
+ b = dbus_message_iter_close_container(&iter, &sub);
+ g_assert(b);
+
+ server_broadcast(ls, n, NULL);
+ g_assert(b);
+
+ dbus_message_unref(n);
+ return 0;
+}
+
+int lassi_server_return_clipboard(LassiServer *ls, gboolean primary) {
+ DBusMessage *n;
+ guint32 g;
+ gboolean b;
+
+ g_assert(ls);
+
+ if (primary) {
+
+ if (ls->primary_empty || ls->primary_connection != NULL)
+ return -1;
+
+ ls->primary_empty = TRUE;
+ ls->primary_connection = NULL;
+
+ } else {
+
+ if (ls->clipboard_empty || ls->clipboard_connection != NULL)
+ return -1;
+
+ ls->clipboard_empty = TRUE;
+ ls->clipboard_connection = NULL;
+ }
+
+ n = dbus_message_new_signal("/", LASSI_INTERFACE, "ReturnClipboard");
+ g_assert(n);
+
+ if (primary)
+ g = ++ ls->primary_generation;
+ else
+ g = ++ ls->clipboard_generation;
+
+ b = dbus_message_append_args(n, DBUS_TYPE_UINT32, &g, DBUS_TYPE_BOOLEAN, &primary, DBUS_TYPE_INVALID);
+ g_assert(b);
+
+ server_broadcast(ls, n, NULL);
+
+ dbus_message_unref(n);
+ return 0;
+}
+
+int lassi_server_get_clipboard(LassiServer *ls, gboolean primary, const char *t, int *f, gpointer *p, int *l) {
+ DBusMessage *n, *reply;
+ DBusConnection *c;
+ DBusError e;
+ int ret = -1;
+ DBusMessageIter iter, sub;
+ gboolean b;
+
+ g_assert(ls);
+
+ dbus_error_init(&e);
+
+ if (primary) {
+
+ if (ls->primary_empty || !ls->primary_connection)
+ return -1;
+
+ c = ls->primary_connection->dbus_connection;
+
+ } else {
+
+ if (ls->clipboard_empty || !ls->clipboard_connection)
+ return -1;
+
+ c = ls->clipboard_connection->dbus_connection;
+ }
+
+ n = dbus_message_new_method_call(NULL, "/", LASSI_INTERFACE, "GetClipboard");
+ g_assert(n);
+
+ b = dbus_message_append_args(n, DBUS_TYPE_BOOLEAN, &primary, DBUS_TYPE_STRING, &t, DBUS_TYPE_INVALID);
+ g_assert(b);
+
+ if (!(reply = dbus_connection_send_with_reply_and_block(c, n, -1, &e))) {
+ g_debug("Getting clipboard failed: %s", e.message);
+ goto finish;
+ }
+
+ dbus_message_iter_init(reply, &iter);
+ dbus_message_iter_get_basic(&iter, f);
+ dbus_message_iter_next(&iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_BYTE) {
+ g_debug("Invalid clipboard data");
+ goto finish;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+ dbus_message_iter_get_fixed_array(&sub, p, l);
+
+ *p = g_memdup(*p, *l);
+
+ ret = 0;
+
+finish:
+
+ dbus_message_unref(n);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ dbus_error_free(&e);
+
+ return ret;
+}
+
+static int signal_hello(LassiConnection *lc, DBusMessage *m) {
+ const char *id, *address;
+ DBusError e;
+ GList *i;
+ dbus_bool_t b;
+ DBusMessage *n;
+ gint32 active_generation, order_generation, clipboard_generation;
+
+ dbus_error_init(&e);
+
+ if (lc->id) {
+ g_debug("Received duplicate HelloNode.");
+ return -1;
+ }
+
+ if (!(dbus_message_get_args(
+ m, &e,
+ DBUS_TYPE_STRING, &id,
+ DBUS_TYPE_STRING, &address,
+ DBUS_TYPE_INT32, &active_generation,
+ DBUS_TYPE_INT32, &order_generation,
+ DBUS_TYPE_INT32, &clipboard_generation,
+ DBUS_TYPE_INVALID))) {
+ g_debug("Received invalid message: %s", e.message);
+ dbus_error_free(&e);
+ return -1;
+ }
+
+ if (strcmp(id, lc->server->id) == 0) {
+ g_debug("Dropping looped back connection.");
+ return -1;
+ }
+
+ if (g_hash_table_lookup(lc->server->connections_by_id, id)) {
+ g_debug("Dropping duplicate connection.");
+ return -1;
+ }
+
+ lc->server->active_generation = MAX(lc->server->active_generation, active_generation);
+ lc->server->order_generation = MAX(lc->server->order_generation, order_generation);
+ lc->server->clipboard_generation = MAX(lc->server->clipboard_generation, clipboard_generation);
+
+ g_debug("Got welcome from %s (%s)", id, address);
+
+ lc->id = g_strdup(id);
+ lc->address = g_strdup(address);
+ g_hash_table_insert(lc->server->connections_by_id, lc->id, lc);
+ server_position_connection(lc->server, lc);
+
+ /* Notify all old nodes of the new one */
+ n = dbus_message_new_signal("/", LASSI_INTERFACE, "NodeAdded");
+ g_assert(n);
+
+ b = dbus_message_append_args(n, DBUS_TYPE_STRING, &id, DBUS_TYPE_STRING, &address, DBUS_TYPE_INVALID);
+ g_assert(b);
+
+ server_broadcast(lc->server, n, lc);
+ dbus_message_unref(n);
+
+ /* Notify new node about old nodes */
+ for (i = lc->server->connections; i; i = i->next) {
+ LassiConnection *k = i->data;
+ dbus_bool_t b;
+
+ if (k == lc || !k->id)
+ continue;
+
+ n = dbus_message_new_signal("/", LASSI_INTERFACE, "NodeAdded");
+ g_assert(n);
+
+ b = dbus_message_append_args(n, DBUS_TYPE_STRING, &id, DBUS_TYPE_STRING, &address, DBUS_TYPE_INVALID);
+ g_assert(b);
+
+ b = dbus_connection_send(lc->dbus_connection, n, NULL);
+ g_assert(b);
+
+ dbus_message_unref(n);
+ }
+
+ if (lc->we_are_client) {
+ server_send_update_grab(lc->server, -1);
+ server_send_update_order(lc->server, NULL);
+
+ lc->delayed_welcome = FALSE;
+ show_welcome(lc, TRUE);
+ } else
+ lc->delayed_welcome = TRUE;
+
+ server_layout_changed(lc->server, -1);
+ lassi_prefs_update(&lc->server->prefs_info);
+
+ server_dump(lc->server);
+
+ return 0;
+}
+
+static int signal_node_added(LassiConnection *lc, DBusMessage *m) {
+ const char *id, *address;
+ DBusError e;
+
+ dbus_error_init(&e);
+
+ if (!(dbus_message_get_args(m, &e, DBUS_TYPE_STRING, &id, DBUS_TYPE_STRING, &address, DBUS_TYPE_INVALID))) {
+ g_debug("Received invalid message: %s", e.message);
+ dbus_error_free(&e);
+ return -1;
+ }
+
+ if (strcmp(id, lc->server->id) == 0)
+ return 0;
+
+ if (g_hash_table_lookup(lc->server->connections_by_id, id))
+ return 0;
+
+ if (!(lassi_server_connect(lc->server, address))) {
+ DBusMessage *n;
+ dbus_bool_t b;
+
+ /* Failed to connnect to this client, tell everyone */
+ n = dbus_message_new_signal("/", LASSI_INTERFACE, "NodeRemoved");
+ g_assert(n);
+
+ b = dbus_message_append_args(n, DBUS_TYPE_STRING, &id, DBUS_TYPE_STRING, &address, DBUS_TYPE_INVALID);
+ g_assert(b);
+
+ server_broadcast(lc->server, n, NULL);
+ dbus_message_unref(n);
+ }
+
+ return 0;
+}
+
+static int signal_node_removed(LassiConnection *lc, DBusMessage *m) {
+ const char *id, *address;
+ DBusError e;
+ LassiConnection *k;
+ gboolean remove_from_order;
+ LassiServer *ls;
+
+ dbus_error_init(&e);
+
+ if (!(dbus_message_get_args(m, &e,
+ DBUS_TYPE_STRING, &id,
+ DBUS_TYPE_STRING, &address,
+ DBUS_TYPE_BOOLEAN, &remove_from_order,
+ DBUS_TYPE_INVALID))) {
+ g_debug("Received invalid message: %s", e.message);
+ dbus_error_free(&e);
+ return -1;
+ }
+
+ if (strcmp(id, lc->server->id) == 0) {
+ g_debug("We've been kicked ourselves.");
+
+ server_disconnect_all(lc->server, TRUE);
+ return 0;
+ }
+
+ if (remove_from_order) {
+ GList *i = g_list_find_custom(ls->order, id, (GCompareFunc) strcmp);
+
+ if (i)
+ ls->order = g_list_delete_link(ls->order, i);
+ }
+
+ ls = lc->server;
+
+ if ((k = g_hash_table_lookup(lc->server->connections_by_id, id)))
+ connection_unlink(k, remove_from_order);
+
+ server_broadcast(ls, m, lc == k ? NULL : lc);
+
+ return 0;
+}
+
+static int signal_update_grab(LassiConnection *lc, DBusMessage *m) {
+ const char*id, *current_id;
+ gint32 generation;
+ LassiConnection *k = NULL;
+ DBusError e;
+ int y;
+
+ dbus_error_init(&e);
+
+ if (!(dbus_message_get_args(
+ m, &e,
+ DBUS_TYPE_INT32, &generation,
+ DBUS_TYPE_STRING, &id,
+ DBUS_TYPE_INT32, &y,
+ DBUS_TYPE_INVALID))) {
+ g_debug("Received invalid message: %s", e.message);
+ dbus_error_free(&e);
+ return -1;
+ }
+
+ g_debug("received grab request for %s (%i vs %i)", id, lc->server->active_generation, generation);
+
+ if (strcmp(id, lc->server->id) && !(k = g_hash_table_lookup(lc->server->connections_by_id, id))) {
+ g_debug("Unknown connection");
+ return -1;
+ }
+
+ if (k == lc->server->active_connection) {
+ g_debug("Connection already active");
+ return 0;
+ }
+
+ current_id = lc->server->active_connection ? lc->server->active_connection->id : lc->server->id;
+
+ if ((lc->server->active_generation > generation || (lc->server->active_generation == generation && strcmp(current_id, id) > 0))) {
+ g_debug("Ignoring request for active connection");
+ return 0;
+ }
+
+ lc->server->active_connection = k;
+ lc->server->active_generation = generation;
+
+ if (!k)
+ g_debug("We're now the active server.");
+ else
+ g_debug("Connection '%s' activated.", k->id);
+
+ server_broadcast(lc->server, m, lc);
+ server_layout_changed(lc->server, y);
+
+ return 0;
+}
+
+static int signal_update_order(LassiConnection *lc, DBusMessage *m) {
+ gint32 generation;
+ DBusError e;
+ DBusMessageIter iter, sub;
+ GList *new_order = NULL, *merged_order = NULL;
+ int r = 0;
+ int c = 0;
+
+ dbus_error_init(&e);
+
+ if (!(dbus_message_get_args(
+ m, &e,
+ DBUS_TYPE_INT32, &generation,
+ DBUS_TYPE_INVALID))) {
+ g_debug("Received invalid message: %s", e.message);
+ dbus_error_free(&e);
+ return -1;
+ }
+
+ dbus_message_iter_init(m, &iter);
+ dbus_message_iter_next(&iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING) {
+ g_debug("Bad connection list fo the left");
+ return -1;
+ }
+
+ if (lc->server->order_generation > generation) {
+ g_debug("Ignoring request for layout");
+ return 0;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+ const char *id;
+ dbus_message_iter_get_basic(&sub, &id);
+ new_order = g_list_prepend(new_order, g_strdup(id));
+ dbus_message_iter_next(&sub);
+ }
+
+ new_order = g_list_reverse(new_order);
+
+ if (!lassi_list_nodups(new_order)) {
+ g_debug("Received invalid list.");
+ r = -1;
+ goto finish;
+ }
+
+ c = lassi_list_compare(lc->server->order, new_order);
+
+ if (c == 0) {
+ g_debug("Requested order identical to ours.");
+ goto finish;
+ }
+
+ if (lc->server->order_generation == generation && c > 0) {
+ g_debug("Ignoring request for layout 2");
+ goto finish;
+ }
+
+ merged_order = lassi_list_merge(lassi_list_copy(new_order), lc->server->order);
+
+ if (lassi_list_compare(lc->server->order, merged_order)) {
+ server_set_order(lc->server, merged_order);
+ merged_order = NULL;
+ }
+
+ server_send_update_order(lc->server, lassi_list_compare(lc->server->order, new_order) ? NULL : lc);
+
+ lc->server->order_generation = generation;
+
+finish:
+
+ lassi_list_free(new_order);
+ lassi_list_free(merged_order);
+
+ if (lc->delayed_welcome) {
+ lc->delayed_welcome = FALSE;
+ show_welcome(lc, TRUE);
+ }
+
+ return r;
+}
+
+static int signal_key_event(LassiConnection *lc, DBusMessage *m) {
+ DBusError e;
+ guint32 key;
+ gboolean is_press;
+
+ dbus_error_init(&e);
+
+ if (!(dbus_message_get_args(m, &e, DBUS_TYPE_UINT32, &key, DBUS_TYPE_BOOLEAN, &is_press, DBUS_TYPE_INVALID))) {
+ g_debug("Received invalid message: %s", e.message);
+ dbus_error_free(&e);
+ return -1;
+ }
+
+/* g_debug("got dbus key %i %i", key, !!is_press); */
+ lassi_grab_press_key(&lc->server->grab_info, key, is_press);
+
+ return 0;
+}
+
+static int signal_motion_event(LassiConnection *lc, DBusMessage *m) {
+ DBusError e;
+ int dx, dy;
+
+ dbus_error_init(&e);
+
+ if (!(dbus_message_get_args(m, &e, DBUS_TYPE_INT32, &dx, DBUS_TYPE_INT32, &dy, DBUS_TYPE_INVALID))) {
+ g_debug("Received invalid message: %s", e.message);
+ dbus_error_free(&e);
+ return -1;
+ }
+
+/* g_debug("got dbus motion %i %i", dx, dy); */
+ lassi_grab_move_pointer_relative(&lc->server->grab_info, dx, dy);
+
+ return 0;
+}
+
+static int signal_button_event(LassiConnection *lc, DBusMessage *m) {
+ DBusError e;
+ guint32 button;
+ gboolean is_press;
+
+ dbus_error_init(&e);
+
+ if (!(dbus_message_get_args(m, &e, DBUS_TYPE_UINT32, &button, DBUS_TYPE_BOOLEAN, &is_press, DBUS_TYPE_INVALID))) {
+ g_debug("Received invalid message: %s", e.message);
+ dbus_error_free(&e);
+ return -1;
+ }
+
+/* g_debug("got dbus button %i %i", button, !!is_press); */
+ lassi_grab_press_button(&lc->server->grab_info, button, is_press);
+
+ return 0;
+}
+
+static int signal_acquire_clipboard(LassiConnection *lc, DBusMessage *m) {
+ DBusError e;
+ gint32 g;
+ gboolean primary;
+ DBusMessageIter iter, sub;
+ char **targets;
+ unsigned alloc_targets, j;
+
+ dbus_error_init(&e);
+
+ if (!(dbus_message_get_args(m, &e, DBUS_TYPE_INT32, &g, DBUS_TYPE_BOOLEAN, &primary, DBUS_TYPE_INVALID))) {
+ g_debug("Received invalid message: %s", e.message);
+ dbus_error_free(&e);
+ return -1;
+ }
+
+ if ((primary ? lc->server->primary_generation : lc->server->clipboard_generation) > g) {
+ g_debug("Ignoring request for clipboard.");
+ return 0;
+ }
+
+ /* FIXME, tie break missing */
+
+ dbus_message_iter_init(m, &iter);
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_next(&iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING) {
+ g_debug("Bad target list");
+ return -1;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ targets = g_new(char*, alloc_targets = 20);
+ j = 0;
+
+ while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+ const char *t;
+ dbus_message_iter_get_basic(&sub, &t);
+
+ if (j >= alloc_targets) {
+ alloc_targets *= 2;
+ targets = g_realloc(targets, sizeof(char*) * (alloc_targets+1));
+ }
+
+ g_assert(j < alloc_targets);
+
+ targets[j++] = (char*) t;
+ dbus_message_iter_next(&sub);
+
+ g_debug("Received target %s on %s", t, lc->id);
+ }
+
+ targets[j] = NULL;
+
+ lassi_clipboard_set(&lc->server->clipboard_info, primary, targets);
+
+ g_free(targets);
+
+ if (primary) {
+ lc->server->primary_connection = lc;
+ lc->server->primary_empty = FALSE;
+ lc->server->primary_generation = g;
+ } else {
+ lc->server->clipboard_connection = lc;
+ lc->server->clipboard_empty = FALSE;
+ lc->server->clipboard_generation = g;
+ }
+
+ return 0;
+}
+
+static int signal_return_clipboard(LassiConnection *lc, DBusMessage *m) {
+ DBusError e;
+ gint32 g;
+ gboolean primary;
+
+ dbus_error_init(&e);
+
+ if (!(dbus_message_get_args(m, &e, DBUS_TYPE_INT32, &g, DBUS_TYPE_BOOLEAN, &primary, DBUS_TYPE_INVALID))) {
+ g_debug("Received invalid message: %s", e.message);
+ dbus_error_free(&e);
+ return -1;
+ }
+
+ if ((primary ? lc->server->primary_generation : lc->server->clipboard_generation) > g) {
+ g_debug("Ignoring request for clipboard empty.");
+ return 0;
+ }
+
+ /* FIXME, tie break missing */
+
+ lassi_clipboard_clear(&lc->server->clipboard_info, primary);
+
+ if (primary) {
+ lc->server->primary_connection = NULL;
+ lc->server->primary_empty = TRUE;
+ lc->server->primary_generation = g;
+ } else {
+ lc->server->clipboard_connection = NULL;
+ lc->server->clipboard_empty = TRUE;
+ lc->server->clipboard_generation = g;
+ }
+
+ return 0;
+}
+
+static int method_get_clipboard(LassiConnection *lc, DBusMessage *m) {
+ DBusError e;
+ char *type;
+ gboolean primary;
+ DBusMessage *n = NULL;
+ gint32 f;
+ gpointer p = NULL;
+ int l = 0;
+ DBusMessageIter iter, sub;
+ gboolean b;
+
+ dbus_error_init(&e);
+
+ if (!(dbus_message_get_args(m, &e, DBUS_TYPE_BOOLEAN, &primary, DBUS_TYPE_STRING, &type, DBUS_TYPE_INVALID))) {
+ g_debug("Received invalid message: %s", e.message);
+ dbus_error_free(&e);
+ return -1;
+ }
+
+ if ((primary && (lc->server->primary_connection || lc->server->primary_empty)) ||
+ (!primary && (lc->server->clipboard_connection || lc->server->clipboard_empty))) {
+ n = dbus_message_new_error(m, LASSI_INTERFACE ".NotOwner", "We're not the clipboard owner");
+ goto finish;
+ }
+
+ if (lassi_clipboard_get(&lc->server->clipboard_info, primary, type, &f, &p, &l) < 0) {
+ n = dbus_message_new_error(m, LASSI_INTERFACE ".ClipboardFailure", "Failed to read clipboard data");
+ goto finish;
+ }
+
+ if (l > dbus_connection_get_max_message_size(lc->dbus_connection)*9/10) {
+ n = dbus_message_new_error(m, LASSI_INTERFACE ".TooLarge", "Clipboard data too large");
+ goto finish;
+ }
+
+ n = dbus_message_new_method_return(m);
+ g_assert(n);
+
+ dbus_message_iter_init_append(n, &iter);
+ b = dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &f);
+ g_assert(b);
+
+ b = dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &sub);
+ g_assert(b);
+
+ b = dbus_message_iter_append_fixed_array(&sub, DBUS_TYPE_BYTE, &p, l);
+ g_assert(b);
+
+ b = dbus_message_iter_close_container(&iter, &sub);
+ g_assert(b);
+
+finish:
+ g_assert(n);
+
+ dbus_connection_send(lc->dbus_connection, n, NULL);
+ dbus_message_unref(n);
+
+ g_free(p);
+
+ return 0;
+}
+
+DBusHandlerResult message_function(DBusConnection *c, DBusMessage *m, void *userdata) {
+ DBusError e;
+ LassiConnection *lc = userdata;
+
+ g_assert(c);
+ g_assert(m);
+ g_assert(lc);
+
+ dbus_error_init(&e);
+
+/* g_debug("[%s] interface=%s, path=%s, member=%s serial=%u", */
+/* lc->id, */
+/* dbus_message_get_interface(m), */
+/* dbus_message_get_path(m), */
+/* dbus_message_get_member(m), */
+/* dbus_message_get_serial(m)); */
+
+ if (dbus_message_is_signal(m, DBUS_INTERFACE_LOCAL, "Disconnected"))
+ goto fail;
+
+ else if (dbus_message_is_signal(m, LASSI_INTERFACE, "Hello")) {
+ if (signal_hello(lc, m) < 0)
+ goto fail;
+
+ } else if (lc->id) {
+
+ if (dbus_message_is_signal(m, LASSI_INTERFACE, "NodeAdded")) {
+
+ if (signal_node_added(lc, m) < 0)
+ goto fail;
+
+ } else if (dbus_message_is_signal(m, LASSI_INTERFACE, "NodeRemoved")) {
+
+ if (signal_node_removed(lc, m) < 0)
+ goto fail;
+
+ } else if (dbus_message_is_signal(m, LASSI_INTERFACE, "UpdateGrab")) {
+
+ if (signal_update_grab(lc, m) < 0)
+ goto fail;
+
+ } else if (dbus_message_is_signal(m, LASSI_INTERFACE, "UpdateOrder")) {
+
+ if (signal_update_order(lc, m) < 0)
+ goto fail;
+
+ } else if (dbus_message_is_signal(m, LASSI_INTERFACE, "KeyEvent")) {
+
+ if (signal_key_event(lc, m) < 0)
+ goto fail;
+
+ } else if (dbus_message_is_signal(m, LASSI_INTERFACE, "MotionEvent")) {
+
+ if (signal_motion_event(lc, m) < 0)
+ goto fail;
+
+ } else if (dbus_message_is_signal(m, LASSI_INTERFACE, "ButtonEvent")) {
+
+ if (signal_button_event(lc, m) < 0)
+ goto fail;
+
+ } else if (dbus_message_is_signal(m, LASSI_INTERFACE, "AcquireClipboard")) {
+
+ if (signal_acquire_clipboard(lc, m) < 0)
+ goto fail;
+
+ } else if (dbus_message_is_signal(m, LASSI_INTERFACE, "ReturnClipboard")) {
+
+ if (signal_return_clipboard(lc, m) < 0)
+ goto fail;
+
+ } else if (dbus_message_is_method_call(m, LASSI_INTERFACE, "GetClipboard")) {
+
+ if (method_get_clipboard(lc, m) < 0)
+ goto fail;
+
+ } else
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ } else
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+
+fail:
+
+ dbus_error_free(&e);
+
+ connection_unlink(lc, TRUE);
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static LassiConnection* connection_add(LassiServer *ls, DBusConnection *c, gboolean we_are_client) {
+ LassiConnection *lc;
+ dbus_bool_t b;
+ DBusMessage *m;
+ gint32 ag, og, cg;
+ int fd, one = 1;
+
+ g_assert(ls);
+ g_assert(c);
+
+ lc = g_new(LassiConnection, 1);
+ lc->dbus_connection = dbus_connection_ref(c);
+ lc->server = ls;
+ lc->id = lc->address = NULL;
+ lc->we_are_client = we_are_client;
+ lc->delayed_welcome = FALSE;
+ ls->connections = g_list_prepend(ls->connections, lc);
+ ls->n_connections++;
+
+ dbus_connection_setup_with_g_main(c, NULL);
+
+ b = dbus_connection_add_filter(c, message_function, lc, NULL);
+ g_assert(b);
+
+ m = dbus_message_new_signal("/", LASSI_INTERFACE, "Hello");
+ g_assert(m);
+
+ ag = ls->active_generation;
+ og = ls->order_generation;
+ cg = ls->clipboard_generation;
+
+ b = dbus_message_append_args(
+ m,
+ DBUS_TYPE_STRING, &ls->id,
+ DBUS_TYPE_STRING, &ls->address,
+ DBUS_TYPE_INT32, &ag,
+ DBUS_TYPE_INT32, &og,
+ DBUS_TYPE_INT32, &cg,
+ DBUS_TYPE_INVALID);
+ g_assert(b);
+
+ fd = -1;
+ dbus_connection_get_socket(c, &fd);
+ g_assert(fd >= 0);
+
+ if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) < 0)
+ g_warning("Failed to enable TCP_NODELAY");
+
+ b = dbus_connection_send(c, m, NULL);
+ g_assert(b);
+
+ dbus_message_unref(m);
+
+ lassi_tray_update(&ls->tray_info, ls->n_connections);
+ return lc;
+}
+
+static void new_connection(DBusServer *s, DBusConnection *c, void *userdata) {
+ LassiServer *ls = userdata;
+
+ g_assert(s);
+ g_assert(c);
+
+ if (ls->n_connections >= CONNECTIONS_MAX)
+ return;
+
+ dbus_connection_set_allow_anonymous(c, TRUE);
+ connection_add(ls, c, FALSE);
+}
+
+static int server_init(LassiServer *ls) {
+ DBusError e;
+ int r = -1;
+ guint16 port;
+
+ g_assert(ls);
+
+ memset(ls, 0, sizeof(*ls));
+
+ dbus_error_init(&e);
+
+ for (port = PORT_MIN; port < PORT_MAX; port++) {
+ char *t;
+
+ t = g_strdup_printf("tcp:port=%u,host=0.0.0.0", port);
+ ls->dbus_server = dbus_server_listen(t, &e);
+ g_free(t);
+
+ if (ls->dbus_server) {
+ ls->port = port;
+ break;
+ }
+
+ if (!dbus_error_has_name(&e, DBUS_ERROR_ADDRESS_IN_USE)) {
+ g_warning("Failed to create D-Bus server: %s %s", e.message, e.name);
+ goto finish;
+ }
+
+ dbus_error_free(&e);
+ }
+
+ if (!ls->dbus_server) {
+ g_warning("All ports blocked.");
+ goto finish;
+ }
+
+ g_debug("Listening on port %u", port);
+
+ dbus_server_setup_with_g_main(ls->dbus_server, NULL);
+ dbus_server_set_new_connection_function(ls->dbus_server, new_connection, ls, NULL);
+
+ ls->connections_by_id = g_hash_table_new(g_str_hash, g_str_equal);
+
+ ls->id = g_strdup_printf("%s's desktop on %s", g_get_user_name(), g_get_host_name());
+
+ if (lassi_avahi_init(&ls->avahi_info, ls) < 0)
+ goto finish;
+
+ /* The initialization of Avahi might have changed ls->id! */
+
+ ls->address = dbus_server_get_address(ls->dbus_server);
+ ls->order = g_list_prepend(NULL, g_strdup(ls->id));
+
+ if (lassi_grab_init(&ls->grab_info, ls) < 0)
+ goto finish;
+
+ if (lassi_osd_init(&ls->osd_info) < 0)
+ goto finish;
+
+ if (lassi_clipboard_init(&ls->clipboard_info, ls) < 0)
+ goto finish;
+
+
+ if (lassi_tray_init(&ls->tray_info, ls) < 0)
+ goto finish;
+
+ if (lassi_prefs_init(&ls->prefs_info, ls) < 0)
+ goto finish;
+
+ r = 0;
+
+finish:
+ dbus_error_free(&e);
+ return r;
+}
+
+void lassi_server_disconnect(LassiServer *ls, const char *id, gboolean remove_from_order) {
+ LassiConnection *lc;
+
+ g_assert(ls);
+ g_assert(id);
+
+ if ((lc = g_hash_table_lookup(ls->connections_by_id, id)))
+ connection_unlink(lc, remove_from_order);
+ else if (remove_from_order) {
+ GList *i = g_list_find_custom(ls->order, id, (GCompareFunc) strcmp);
+
+ if (i)
+ ls->order = g_list_delete_link(ls->order, i);
+ }
+}
+
+static void server_disconnect_all(LassiServer *ls, gboolean clear_order) {
+
+ while (ls->connections)
+ connection_unlink(ls->connections->data, clear_order);
+
+ if (clear_order) {
+ lassi_list_free(ls->order);
+ ls->order = NULL;
+ }
+}
+
+static void server_done(LassiServer *ls) {
+
+ g_assert(ls);
+
+ if (ls->dbus_server) {
+ dbus_server_disconnect(ls->dbus_server);
+ dbus_server_unref(ls->dbus_server);
+ }
+
+ server_disconnect_all(ls, FALSE);
+
+ if (ls->connections_by_id)
+ g_hash_table_destroy(ls->connections_by_id);
+
+ g_free(ls->id);
+ g_free(ls->address);
+
+ lassi_list_free(ls->order);
+
+ lassi_grab_done(&ls->grab_info);
+ lassi_osd_done(&ls->osd_info);
+ lassi_clipboard_done(&ls->clipboard_info);
+ lassi_avahi_done(&ls->avahi_info);
+ lassi_tray_done(&ls->tray_info);
+ lassi_prefs_done(&ls->prefs_info);
+
+ memset(ls, 0, sizeof(*ls));
+}
+
+gboolean lassi_server_is_connected(LassiServer *ls, const char *id) {
+ g_assert(ls);
+ g_assert(id);
+
+ return strcmp(id, ls->id) == 0 || g_hash_table_lookup(ls->connections_by_id, id);
+}
+
+gboolean lassi_server_is_known(LassiServer *ls, const char *id) {
+ g_assert(ls);
+ g_assert(id);
+
+ return !!g_list_find_custom(ls->order, id, (GCompareFunc) strcmp);
+}
+
+LassiConnection* lassi_server_connect(LassiServer *ls, const char *a) {
+ DBusError e;
+ DBusConnection *c;
+ LassiConnection *lc = NULL;
+
+ dbus_error_init(&e);
+
+ if (ls->n_connections >= CONNECTIONS_MAX)
+ goto finish;
+
+ if (!(c = dbus_connection_open_private(a, &e))) {
+ g_warning("Failed to connect to client: %s", e.message);
+ goto finish;
+ }
+
+ lc = connection_add(ls, c, TRUE);
+
+finish:
+
+ if (c)
+ dbus_connection_unref(c);
+
+ dbus_error_free(&e);
+ return lc;
+}
+
+int main(int argc, char *argv[]) {
+ LassiServer ls;
+
+ gtk_init(&argc, &argv);
+
+ memset(&ls, 0, sizeof(ls));
+
+ if (server_init(&ls) < 0)
+ goto fail;
+
+ gtk_main();
+
+fail:
+
+ server_done(&ls);
+
+ return 0;
+}