diff --git a/eekboard/eekboard-context.c b/eekboard/eekboard-context.c index 85f4c3ba..0b85bb31 100644 --- a/eekboard/eekboard-context.c +++ b/eekboard/eekboard-context.c @@ -37,6 +37,7 @@ enum { DISABLED, KEY_PRESSED, KEY_RELEASED, + DESTROYED, LAST_SIGNAL }; @@ -146,6 +147,12 @@ eekboard_context_real_key_released (EekboardContext *self, } } +static void +eekboard_context_real_destroyed (EekboardContext *self) +{ + // g_debug ("eekboard_context_real_destroyed"); +} + static void eekboard_context_get_property (GObject *object, guint prop_id, @@ -195,6 +202,7 @@ eekboard_context_class_init (EekboardContextClass *klass) klass->disabled = eekboard_context_real_disabled; klass->key_pressed = eekboard_context_real_key_pressed; klass->key_released = eekboard_context_real_key_released; + klass->destroyed = eekboard_context_real_destroyed; proxy_class->g_signal = eekboard_context_real_g_signal; @@ -288,6 +296,24 @@ eekboard_context_class_init (EekboardContextClass *klass) G_TYPE_NONE, 1, G_TYPE_UINT); + + /** + * EekboardContext::destroyed: + * @context: an #EekboardContext + * + * The ::destroyed signal is emitted each time the name of remote + * end is vanished. + */ + signals[DESTROYED] = + g_signal_new (I_("destroyed"), + G_TYPE_FROM_CLASS(gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(EekboardContextClass, destroyed), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); } static void @@ -306,6 +332,15 @@ eekboard_context_init (EekboardContext *self) (GDestroyNotify)g_object_unref); } +static void +context_name_vanished_callback (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + EekboardContext *context = user_data; + g_signal_emit_by_name (context, "destroyed", NULL); +} + /** * eekboard_context_new: * @connection: a #GDBusConnection @@ -337,8 +372,24 @@ eekboard_context_new (GDBusConnection *connection, "g-interface-name", "com.redhat.Eekboard.Context", "g-object-path", object_path, NULL); - if (initable != NULL) - return EEKBOARD_CONTEXT (initable); + if (initable != NULL) { + EekboardContext *context = EEKBOARD_CONTEXT (initable); + gchar *name_owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY(context)); + + if (name_owner == NULL) { + g_object_unref (context); + return NULL; + } + + g_bus_watch_name_on_connection (connection, + name_owner, + G_BUS_NAME_WATCHER_FLAGS_NONE, + NULL, + context_name_vanished_callback, + context, + NULL); + return context; + } return NULL; } diff --git a/eekboard/eekboard-context.h b/eekboard/eekboard-context.h index 6a0a0eca..b19ab831 100644 --- a/eekboard/eekboard-context.h +++ b/eekboard/eekboard-context.h @@ -66,10 +66,11 @@ struct _EekboardContextClass { guint keycode); void (*key_released) (EekboardContext *self, guint keycode); + void (*destroyed) (EekboardContext *self); /*< private >*/ /* padding */ - gpointer pdummy[24]; + gpointer pdummy[23]; }; GType eekboard_context_get_type (void) G_GNUC_CONST; diff --git a/eekboard/eekboard-eekboard.c b/eekboard/eekboard-eekboard.c index a188c860..2cf3b7a4 100644 --- a/eekboard/eekboard-eekboard.c +++ b/eekboard/eekboard-eekboard.c @@ -29,6 +29,13 @@ #include "eekboard/eekboard-eekboard.h" +enum { + DESTROYED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0, }; + G_DEFINE_TYPE (EekboardEekboard, eekboard_eekboard, G_TYPE_DBUS_PROXY); #define EEKBOARD_EEKBOARD_GET_PRIVATE(obj) \ @@ -39,6 +46,15 @@ struct _EekboardEekboardPrivate GHashTable *context_hash; }; +static void +eekboard_eekboard_real_destroyed (EekboardEekboard *self) +{ + EekboardEekboardPrivate *priv = EEKBOARD_EEKBOARD_GET_PRIVATE(self); + + // g_debug ("eekboard_eekboard_real_destroyed"); + g_hash_table_remove_all (priv->context_hash); +} + static void eekboard_eekboard_dispose (GObject *object) { @@ -60,7 +76,27 @@ eekboard_eekboard_class_init (EekboardEekboardClass *klass) g_type_class_add_private (gobject_class, sizeof (EekboardEekboardPrivate)); + klass->destroyed = eekboard_eekboard_real_destroyed; + gobject_class->dispose = eekboard_eekboard_dispose; + + /** + * EekboardEekboard::destroyed: + * @eekboard: an #EekboardEekboard + * + * The ::destroyed signal is emitted each time the name of remote + * end is vanished. + */ + signals[DESTROYED] = + g_signal_new (I_("destroyed"), + G_TYPE_FROM_CLASS(gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(EekboardEekboardClass, destroyed), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); } static void @@ -76,6 +112,15 @@ eekboard_eekboard_init (EekboardEekboard *self) (GDestroyNotify)g_object_unref); } +static void +eekboard_name_vanished_callback (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + EekboardEekboard *eekboard = user_data; + g_signal_emit_by_name (eekboard, "destroyed", NULL); +} + /** * eekboard_eekboard_new: * @connection: a #GDBusConnection @@ -102,11 +147,38 @@ eekboard_eekboard_new (GDBusConnection *connection, "g-interface-name", "com.redhat.Eekboard.Server", "g-object-path", "/com/redhat/Eekboard/Server", NULL); - if (initable != NULL) - return EEKBOARD_EEKBOARD (initable); + if (initable != NULL) { + EekboardEekboard *eekboard = EEKBOARD_EEKBOARD (initable); + gchar *name_owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY(eekboard)); + if (name_owner == NULL) { + g_object_unref (eekboard); + return NULL; + } + + g_bus_watch_name_on_connection (connection, + name_owner, + G_BUS_NAME_WATCHER_FLAGS_NONE, + NULL, + eekboard_name_vanished_callback, + eekboard, + NULL); + + return eekboard; + } return NULL; } +static void +on_context_destroyed (EekboardContext *context, + gpointer user_data) +{ + EekboardEekboard *eekboard = user_data; + EekboardEekboardPrivate *priv = EEKBOARD_EEKBOARD_GET_PRIVATE(eekboard); + + g_hash_table_remove (priv->context_hash, + g_dbus_proxy_get_object_path (G_DBUS_PROXY(context))); +} + /** * eekboard_eekboard_create_context: * @eekboard: an #EekboardEekboard @@ -153,6 +225,8 @@ eekboard_eekboard_create_context (EekboardEekboard *eekboard, g_hash_table_insert (priv->context_hash, g_strdup (object_path), g_object_ref (context)); + g_signal_connect (context, "destroyed", + G_CALLBACK(on_context_destroyed), eekboard); return context; } diff --git a/eekboard/eekboard-eekboard.h b/eekboard/eekboard-eekboard.h index 0d4d25eb..bf944004 100644 --- a/eekboard/eekboard-eekboard.h +++ b/eekboard/eekboard-eekboard.h @@ -45,9 +45,12 @@ struct _EekboardEekboardClass { /*< private >*/ GDBusProxyClass parent_class; + /* signals */ + void (* destroyed) (EekboardEekboard *self); + /*< private >*/ /* padding */ - gpointer pdummy[24]; + gpointer pdummy[23]; }; GType eekboard_eekboard_get_type (void) G_GNUC_CONST; diff --git a/src/desktop-client-main.c b/src/desktop-client-main.c index edb2e1dd..9f4c889c 100644 --- a/src/desktop-client-main.c +++ b/src/desktop-client-main.c @@ -77,10 +77,29 @@ on_notify_keyboard_visible (GObject *object, g_main_loop_quit (loop); } +static void +on_context_destroyed (EekboardContext *context, + gpointer user_data) +{ + GMainLoop *loop = user_data; + + g_main_loop_quit (loop); +} + +static void +on_destroyed (EekboardEekboard *eekboard, + gpointer user_data) +{ + GMainLoop *loop = user_data; + + g_main_loop_quit (loop); +} + int main (int argc, char **argv) { EekboardDesktopClient *client; + EekboardEekboard *eekboard; EekboardContext *context; GBusType bus_type; GDBusConnection *connection; @@ -136,6 +155,10 @@ main (int argc, char **argv) } client = eekboard_desktop_client_new (connection); + if (client == NULL) { + g_printerr ("Can't create a client\n"); + exit (1); + } gconfc = gconf_client_get_default (); @@ -196,9 +219,15 @@ main (int argc, char **argv) g_object_get (client, "context", &context, NULL); g_signal_connect (context, "notify::keyboard-visible", G_CALLBACK(on_notify_keyboard_visible), loop); + g_signal_connect (context, "destroyed", + G_CALLBACK(on_context_destroyed), loop); g_object_unref (context); } + g_object_get (client, "eekboard", &eekboard, NULL); + g_signal_connect (eekboard, "destroyed", + G_CALLBACK(on_destroyed), loop); + g_main_loop_run (loop); g_main_loop_unref (loop); diff --git a/src/desktop-client.c b/src/desktop-client.c index db9673b8..9b343432 100644 --- a/src/desktop-client.c +++ b/src/desktop-client.c @@ -43,6 +43,7 @@ enum { PROP_0, PROP_CONNECTION, + PROP_EEKBOARD, PROP_CONTEXT, PROP_LAST }; @@ -124,15 +125,19 @@ eekboard_desktop_client_set_property (GObject *object, connection = g_value_get_object (value); client->eekboard = eekboard_eekboard_new (connection, NULL); - g_assert (client->eekboard); - - client->context = - eekboard_eekboard_create_context (client->eekboard, - "eekboard-desktop-client", - NULL); - g_assert (client->context); - - eekboard_eekboard_push_context (client->eekboard, client->context, NULL); + if (client->eekboard != NULL) { + client->context = + eekboard_eekboard_create_context (client->eekboard, + "eekboard-desktop-client", + NULL); + if (client->context == NULL) { + g_object_unref (client->eekboard); + client->eekboard = NULL; + } else + eekboard_eekboard_push_context (client->eekboard, + client->context, + NULL); + } break; default: g_object_set_property (object, @@ -151,6 +156,9 @@ eekboard_desktop_client_get_property (GObject *object, EekboardDesktopClient *client = EEKBOARD_DESKTOP_CLIENT(object); switch (prop_id) { + case PROP_EEKBOARD: + g_value_set_object (value, client->eekboard); + break; case PROP_CONTEXT: g_value_set_object (value, client->context); break; @@ -230,6 +238,15 @@ eekboard_desktop_client_class_init (EekboardDesktopClientClass *klass) PROP_CONNECTION, pspec); + pspec = g_param_spec_object ("eekboard", + "Eekboard", + "Eekboard", + EEKBOARD_TYPE_EEKBOARD, + G_PARAM_READABLE); + g_object_class_install_property (gobject_class, + PROP_EEKBOARD, + pspec); + pspec = g_param_spec_object ("context", "Context", "Context", @@ -460,9 +477,12 @@ keystroke_listener_cb (const AccessibleKeystroke *stroke, EekboardDesktopClient * eekboard_desktop_client_new (GDBusConnection *connection) { - return g_object_new (EEKBOARD_TYPE_DESKTOP_CLIENT, - "connection", connection, - NULL); + EekboardDesktopClient *client = g_object_new (EEKBOARD_TYPE_DESKTOP_CLIENT, + "connection", connection, + NULL); + if (client->context) + return client; + return NULL; } static GdkFilterReturn