/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * Author: Ted Gould */ #define G_LOG_DOMAIN "hudapplicationsource" #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "application-source.h" #include "application-source-context.h" #include "source.h" #include "app-iface.h" #include "menumodel-collector.h" #include "dbusmenu-collector.h" #include "source-list.h" #include #include struct _HudApplicationSourcePrivate { GDBusConnection * session; gchar * app_id; gchar * path; AppIfaceComCanonicalHudApplication * skel; gboolean used; HudApplicationInfo * bamf_app; guint32 focused_window; GHashTable * window_contexts; GPtrArray * contexts; GHashTable * connections; }; typedef struct _connection_watcher_t connection_watcher_t; struct _connection_watcher_t { guint watch; GList * ids; }; #define HUD_APPLICATION_SOURCE_GET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), HUD_TYPE_APPLICATION_SOURCE, HudApplicationSourcePrivate)) static void hud_application_source_class_init (HudApplicationSourceClass * klass); static void hud_application_source_init (HudApplicationSource * self); static void hud_application_source_dispose (GObject * object); static void hud_application_source_finalize (GObject * object); static void source_iface_init (HudSourceInterface * iface); static void source_use (HudSource * hud_source); static void source_unuse (HudSource * hud_source); static void source_search (HudSource * hud_source, HudTokenList * search_string, void (*append_func) (HudResult * result, gpointer user_data), gpointer user_data); static void source_list_applications (HudSource * hud_source, HudTokenList * search_string, void (*append_func) (const gchar *application_id, const gchar *application_icon, HudSourceItemType type, gpointer user_data), gpointer user_data); static void source_get_toolbar_entries (HudSource * hud_source, GArray * toolbar); static void source_activate_toolbar (HudSource * hud_source, HudClientQueryToolbarItems item, GVariant *platform_data); static HudSource * source_get (HudSource * hud_source, const gchar * application_id); static const gchar * source_get_app_id (HudSource * hud_source); static const gchar * source_get_app_icon (HudSource * hud_source); static gboolean dbus_add_sources (AppIfaceComCanonicalHudApplication * skel, GDBusMethodInvocation * invocation, GVariant * actions, GVariant * descs, gpointer user_data); static gboolean dbus_set_context (AppIfaceComCanonicalHudApplication * skel, GDBusMethodInvocation * invocation, guint window_id, const gchar * context, gpointer user_data); static GList * source_get_items (HudSource * object); G_DEFINE_TYPE_WITH_CODE (HudApplicationSource, hud_application_source, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (HUD_TYPE_SOURCE, source_iface_init)) /* Class Init */ static void hud_application_source_class_init (HudApplicationSourceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (HudApplicationSourcePrivate)); object_class->dispose = hud_application_source_dispose; object_class->finalize = hud_application_source_finalize; return; } /* Intialized the source interface */ static void source_iface_init (HudSourceInterface *iface) { iface->use = source_use; iface->unuse = source_unuse; iface->search = source_search; iface->list_applications = source_list_applications; iface->get = source_get; iface->get_items = source_get_items; iface->get_toolbar_entries = source_get_toolbar_entries; iface->activate_toolbar = source_activate_toolbar; iface->get_app_id = source_get_app_id; iface->get_app_icon = source_get_app_icon; return; } /* Free the struct and unwatch the name */ static void connection_watcher_free (gpointer data) { connection_watcher_t * watcher = (connection_watcher_t *)data; g_bus_unwatch_name(watcher->watch); g_list_free(watcher->ids); g_free(watcher); return; } /* Instance Init */ static void hud_application_source_init (HudApplicationSource *self) { self->priv = HUD_APPLICATION_SOURCE_GET_PRIVATE(self); self->priv->used = FALSE; self->priv->contexts = g_ptr_array_new_with_free_func(g_object_unref); self->priv->connections = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, connection_watcher_free); self->priv->session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); self->priv->window_contexts = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free); return; } /* Clean up references */ static void hud_application_source_dispose (GObject *object) { HudApplicationSource * self = HUD_APPLICATION_SOURCE(object); g_debug("hud_application_source_dispose %s", self->priv->app_id); if (self->priv->skel != NULL) { g_dbus_interface_skeleton_unexport(G_DBUS_INTERFACE_SKELETON(self->priv->skel)); g_clear_object(&self->priv->skel); } if (self->priv->contexts->len > 0) { g_ptr_array_remove_range(self->priv->contexts, 0, self->priv->contexts->len); } g_hash_table_remove_all(self->priv->connections); g_clear_object(&self->priv->bamf_app); g_clear_object(&self->priv->session); G_OBJECT_CLASS (hud_application_source_parent_class)->dispose (object); return; } /* Free memory */ static void hud_application_source_finalize (GObject *object) { HudApplicationSource * self = HUD_APPLICATION_SOURCE(object); g_clear_pointer(&self->priv->contexts, g_ptr_array_unref); g_clear_pointer(&self->priv->connections, g_hash_table_unref); g_clear_pointer(&self->priv->app_id, g_free); g_clear_pointer(&self->priv->path, g_free); g_clear_pointer(&self->priv->window_contexts, g_hash_table_unref); G_OBJECT_CLASS (hud_application_source_parent_class)->finalize (object); return; } /* Checks to see if a context is in use */ static gboolean context_is_current (HudApplicationSource * self, HudApplicationSourceContext * context) { /* See if this window is the one we're looking at */ guint32 context_window = hud_application_source_context_get_window_id(context); const gchar *context_id = hud_application_source_context_get_context_id(context); if (context_window == WINDOW_ID_ALL_WINDOWS && g_strcmp0(hud_application_source_get_context(self, WINDOW_ID_ALL_WINDOWS), context_id) == 0) { return TRUE; } if (context_window == self->priv->focused_window && g_strcmp0(hud_application_source_get_context(self, self->priv->focused_window), context_id) == 0) { return TRUE; } return FALSE; } /* Gets called when the items in a window's sources changes */ static void window_source_changed (HudSource * source, gpointer user_data) { HudApplicationSource * self = HUD_APPLICATION_SOURCE(user_data); HudApplicationSourceContext * context = HUD_APPLICATION_SOURCE_CONTEXT(source); if (context_is_current(self, context)) { hud_source_changed(HUD_SOURCE(self)); } return; } /* Source interface using this source */ static void source_use (HudSource *hud_source) { g_return_if_fail(HUD_IS_APPLICATION_SOURCE(hud_source)); HudApplicationSource * app = HUD_APPLICATION_SOURCE(hud_source); app->priv->used = TRUE; int i; for (i = 0; i < app->priv->contexts->len; i++) { HudApplicationSourceContext * context = g_ptr_array_index(app->priv->contexts, i); if (context_is_current(app, context)) { hud_source_use(HUD_SOURCE(context)); } } return; } /* Source interface unusing this source */ static void source_unuse (HudSource *hud_source) { HudApplicationSource * app = HUD_APPLICATION_SOURCE(hud_source); g_warn_if_fail(app->priv->used); app->priv->used = FALSE; int i; for (i = 0; i < app->priv->contexts->len; i++) { HudApplicationSourceContext * context = g_ptr_array_index(app->priv->contexts, i); if (context_is_current(app, context)) { hud_source_unuse(HUD_SOURCE(context)); } } return; } /* Search this source */ static void source_search (HudSource * hud_source, HudTokenList * search_string, void (*append_func) (HudResult * result, gpointer user_data), gpointer user_data) { HudApplicationSource * app = HUD_APPLICATION_SOURCE(hud_source); int i; for (i = 0; i < app->priv->contexts->len; i++) { HudApplicationSourceContext * context = g_ptr_array_index(app->priv->contexts, i); if (context_is_current(app, context)) { hud_source_search(HUD_SOURCE(context), search_string, append_func, user_data); } } return; } static void source_list_applications (HudSource * hud_source, HudTokenList * search_string, void (*append_func) (const gchar *application_id, const gchar *application_icon, HudSourceItemType type, gpointer user_data), gpointer user_data) { HudApplicationSource * app = HUD_APPLICATION_SOURCE(hud_source); int i; for (i = 0; i < app->priv->contexts->len; i++) { HudApplicationSourceContext * context = g_ptr_array_index(app->priv->contexts, i); if (context_is_current(app, context)) { hud_source_list_applications(HUD_SOURCE(context), search_string, append_func, user_data); } } return; } static HudSource * source_get (HudSource * hud_source, const gchar * application_id) { HudApplicationSource * app = HUD_APPLICATION_SOURCE(hud_source); if (g_strcmp0 (application_id, app->priv->app_id) == 0) { return hud_source; } return NULL; } static const gchar * source_get_app_id (HudSource * hud_source) { return hud_application_source_get_id(HUD_APPLICATION_SOURCE(hud_source)); } static const gchar * source_get_app_icon (HudSource * hud_source) { return hud_application_source_get_app_icon(HUD_APPLICATION_SOURCE(hud_source)); } HudApplicationInfo * hud_application_source_get_application_info (HudApplicationSource * app) { return app->priv->bamf_app; } static void source_get_toolbar_entries (HudSource * hud_source, GArray * toolbar) { HudApplicationSource * app = HUD_APPLICATION_SOURCE(hud_source); int i; for (i = 0; i < app->priv->contexts->len; i++) { HudApplicationSourceContext * context = g_ptr_array_index(app->priv->contexts, i); if (context_is_current(app, context)) { hud_source_get_toolbar_entries (HUD_SOURCE(context), toolbar); } } return; } static void source_activate_toolbar (HudSource * hud_source, HudClientQueryToolbarItems item, GVariant *platform_data) { HudApplicationSource * app = HUD_APPLICATION_SOURCE(hud_source); int i; for (i = 0; i < app->priv->contexts->len; i++) { HudApplicationSourceContext * context = g_ptr_array_index(app->priv->contexts, i); if (context_is_current(app, context)) { hud_source_activate_toolbar (HUD_SOURCE(context), item, platform_data); } } return; } /** * hud_application_source_new_for_app: * @bapp: A #BamfApplication object * * Build a new application object, but use a #BamfApplication to help * ourselves. * * Return value: New #HudApplicationSource object */ HudApplicationSource * hud_application_source_new_for_app (HudApplicationInfo * bapp) { const gchar * id = hud_window_info_get_app_id(bapp); HudApplicationSource * source = hud_application_source_new_for_id(id); const gchar * desktop_file = NULL; source->priv->bamf_app = g_object_ref(bapp); desktop_file = hud_window_info_get_desktop_file(bapp); app_iface_com_canonical_hud_application_set_desktop_path(source->priv->skel, desktop_file); return source; } /** * hud_application_source_new_for_id: * @id: The application ID * * Creates a new application source that doesn't have any windows, but is * based on the ID. You should really add windows to this after you * create it. * * Return value: New #HudApplicationSource object */ HudApplicationSource * hud_application_source_new_for_id (const gchar * id) { g_debug("hud_application_source_new_for_id: %s", id); HudApplicationSource * source = g_object_new(HUD_TYPE_APPLICATION_SOURCE, NULL); source->priv->app_id = g_strdup(id); source->priv->skel = app_iface_com_canonical_hud_application_skeleton_new(); char * app_id_path; app_id_path = nih_dbus_path(NULL, "/com/canonical/hud/applications", id, NULL); if (app_id_path == NULL) /* emulate glib memory handling and terminate */ g_error("Unable to allocate memory for nih_dbus_path()"); source->priv->path = g_strdup(app_id_path); int i = 0; GError * error = NULL; while (!g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(source->priv->skel), source->priv->session, source->priv->path, &error)) { if (error != NULL) { g_warning("Unable to export application '%s' skeleton on path '%s': %s", id, source->priv->path, error->message); gboolean exists_error = g_error_matches(error, G_IO_ERROR, G_IO_ERROR_EXISTS); g_error_free(error); error = NULL; if (!exists_error) { break; } } g_free(source->priv->path); g_clear_object(&source->priv->skel); source->priv->path = g_strdup_printf("%s_%d", app_id_path, ++i); source->priv->skel = app_iface_com_canonical_hud_application_skeleton_new(); } g_signal_connect(G_OBJECT(source->priv->skel), "handle-add-sources", G_CALLBACK(dbus_add_sources), source); g_signal_connect(G_OBJECT(source->priv->skel), "handle-set-window-context", G_CALLBACK(dbus_set_context), source); g_debug("Application ('%s') path: %s", id, source->priv->path); nih_free(app_id_path); return source; } /* Handle a name disappearing off of DBus */ static void connection_lost (GDBusConnection * session, const gchar * name, gpointer user_data) { HudApplicationSource * app = HUD_APPLICATION_SOURCE(user_data); connection_watcher_t * watcher = g_hash_table_lookup(app->priv->connections, name); if (watcher == NULL) { return; } gboolean focused_changed = FALSE; GList * idtemp; for (idtemp = watcher->ids; idtemp != NULL; idtemp = g_list_next(idtemp)) { guint32 winid = GPOINTER_TO_UINT(idtemp->data); if (winid == 0) { focused_changed = TRUE; } if (winid == app->priv->focused_window) { focused_changed = TRUE; } int i; for (i = 0; i < app->priv->contexts->len; i++) { HudApplicationSourceContext * context = g_ptr_array_index(app->priv->contexts, i); if (hud_application_source_context_get_window_id(context) == winid) { g_ptr_array_remove_index(app->priv->contexts, i); i--; } } } g_hash_table_remove(app->priv->connections, name); if (focused_changed) { /* If the focused window changed, let's tell everyone about it */ hud_source_changed(HUD_SOURCE(app)); } else if (app->priv->contexts->len == 0) { /* If we've not gotten to the situation of no more contexts, let's tell our parent so they can reap us. */ hud_source_changed(HUD_SOURCE(app)); } return; } /* Adds to make sure we're tracking the ID for this DBus connection. That way when it goes away, we know how to clean everything up. */ static void add_id_to_connection (HudApplicationSource * app, GDBusConnection * session, const gchar * sender, guint32 id) { connection_watcher_t * watcher = g_hash_table_lookup(app->priv->connections, sender); if (watcher == NULL) { watcher = g_new0(connection_watcher_t, 1); watcher->watch = g_bus_watch_name_on_connection(session, sender, G_BUS_NAME_WATCHER_FLAGS_NONE, NULL, connection_lost, app, NULL); g_hash_table_insert(app->priv->connections, g_strdup(sender), watcher); } GList * idtemp; for (idtemp = watcher->ids; idtemp != NULL; idtemp = g_list_next(idtemp)) { guint32 listid = GPOINTER_TO_UINT(idtemp->data); if (listid == id) { return; } } watcher->ids = g_list_prepend(watcher->ids, GUINT_TO_POINTER(id)); return; } /* Either find the context or build one */ static HudApplicationSourceContext * find_context (HudApplicationSource * app, guint32 winid, const gchar * conid) { GPtrArray * contexts = app->priv->contexts; HudApplicationSourceContext * retval = NULL; int i; for (i = 0; i < contexts->len; i++) { HudApplicationSourceContext * context = g_ptr_array_index(contexts, i); guint32 ctx_winid = hud_application_source_context_get_window_id(context); if (ctx_winid != winid) { continue; } const gchar * ctx_conid = hud_application_source_context_get_context_id(context); if (g_strcmp0(conid, ctx_conid) != 0) { continue; } retval = context; break; } /* Can't find, must build */ if (retval == NULL) { retval = hud_application_source_context_new(winid, conid, app->priv->app_id, hud_application_source_get_app_icon(app), app->priv->path); hud_application_source_add_context(app, retval); } return retval; } /* Respond to the DBus function to add sources */ static gboolean dbus_add_sources (AppIfaceComCanonicalHudApplication * skel, GDBusMethodInvocation * invocation, GVariant * actions, GVariant * descs, gpointer user_data) { HudApplicationSource * app = HUD_APPLICATION_SOURCE(user_data); GDBusConnection * session = g_dbus_method_invocation_get_connection(invocation); const gchar * sender = g_dbus_method_invocation_get_sender(invocation); GVariantIter action_iter; g_variant_iter_init(&action_iter, actions); guint32 idn = 0; gchar * context = NULL; gchar * prefix = NULL; gchar * object = NULL; gboolean changed = FALSE; /* NOTE: We are doing actions first as there are cases where the models need the actions, but it'd be hard to update them if we add the actions second. This order is the best. Don't change it. */ while (g_variant_iter_loop(&action_iter, "(usso)", &idn, &context, &prefix, &object)) { g_debug("Adding prefix '%s' at path: %s", prefix, object); /* Catch the NULL string case */ gchar * refinedcontext = NULL; if (context != NULL && context[0] != '\0') { refinedcontext = context; } /* FIXME: Currently the Qt API uses this context but yet, doesn't set one. So this makes things seem less broken. It should go away when the Qt API gets updated */ if (g_strcmp0(context, "/context_0") == 0) { hud_application_source_set_context(app, WINDOW_ID_ALL_WINDOWS, context); } HudApplicationSourceContext * ctx = find_context(app, idn, refinedcontext); if (!changed && context_is_current(app, ctx)) { changed = TRUE; } GDBusActionGroup * ag = g_dbus_action_group_get(session, sender, object); hud_application_source_context_add_action_group(ctx, G_ACTION_GROUP(ag), prefix); add_id_to_connection(app, session, sender, idn); g_object_unref(ag); } GVariantIter desc_iter; g_variant_iter_init(&desc_iter, descs); while (g_variant_iter_loop(&desc_iter, "(uso)", &idn, &context, &object)) { g_debug("Adding descriptions: %s", object); /* Catch the NULL string case */ gchar * refinedcontext = NULL; if (context != NULL && context[0] != '\0') { refinedcontext = context; } /* FIXME: Currently the Qt API uses this context but yet, doesn't set one. So this makes things seem less broken. It should go away when the Qt API gets updated */ if (g_strcmp0(context, "/context_0") == 0) { hud_application_source_set_context(app, WINDOW_ID_ALL_WINDOWS, context); } HudApplicationSourceContext * ctx = find_context(app, idn, refinedcontext); if (!changed && context_is_current(app, ctx)) { changed = TRUE; } GDBusMenuModel * model = g_dbus_menu_model_get(session, sender, object); hud_application_source_context_add_model(ctx, G_MENU_MODEL(model), HUD_APPLICATION_SOURCE_CONTEXT_MODEL_DBUS); add_id_to_connection(app, session, sender, idn); g_object_unref(model); } /* Send reply so they don't have to wait */ g_dbus_method_invocation_return_value(invocation, NULL); if (changed) { /* Update based on this new data */ hud_source_changed(HUD_SOURCE(app)); } return TRUE; } /* Application changing the context for a window */ static gboolean dbus_set_context (AppIfaceComCanonicalHudApplication * skel, GDBusMethodInvocation * invocation, guint window_id, const gchar * context, gpointer user_data) { g_return_val_if_fail(HUD_IS_APPLICATION_SOURCE(user_data), FALSE); HudApplicationSource * app = HUD_APPLICATION_SOURCE(user_data); if (context[0] == '\0') { context = NULL; } hud_application_source_set_context(app, window_id, context); g_dbus_method_invocation_return_value(invocation, NULL); return TRUE; } /** * hud_application_source_bamf_app_id: * @app: A #BamfApplication object * * Check to see if we don't have any collectors left. * * Return value: The state of the source */ gboolean hud_application_source_is_empty (HudApplicationSource * app) { g_return_val_if_fail(HUD_IS_APPLICATION_SOURCE(app), TRUE); return (app->priv->contexts->len == 0); } /** * hud_application_source_focus: * @app: A #HudApplicationSource object * @bapp: The #BamfApplication representing this @app * @window: The #BamfWindow that has focus * * Tells the application source that focus has changed to it. This * means that we can do things like figure out what window has focus * and make sure we're all good. */ void hud_application_source_focus (HudApplicationSource * app, HudApplicationInfo * bapp, HudWindowInfo * window) { g_return_if_fail(HUD_IS_APPLICATION_SOURCE(app)); g_return_if_fail(HUD_IS_WINDOW_INFO(bapp)); if (app->priv->bamf_app == NULL) { app->priv->bamf_app = g_object_ref(bapp); } const gchar *app_id_1 = hud_window_info_get_app_id(bapp); const gchar *app_id_2 = hud_window_info_get_app_id(app->priv->bamf_app); /* Check to make sure we're getting the right events */ if (g_strcmp0(app_id_1, app_id_2)) { g_critical("App '%s' has been given focus events for '%s'", hud_window_info_get_app_id(app->priv->bamf_app), hud_window_info_get_app_id(bapp)); return; } hud_application_source_add_window(app, window); hud_application_source_set_focused_win(app, hud_window_info_get_window_id(window)); } /** * hud_application_source_get_path: * @app: A #HudApplicationSource object * * Get the object path for this source on DBus * * Return value: The path as a string */ const gchar * hud_application_source_get_path (HudApplicationSource * app) { g_return_val_if_fail(HUD_IS_APPLICATION_SOURCE(app), NULL); return app->priv->path; } /** * hud_application_source_get_id: * @app: A #HudApplicationSource object * * Get the app id for this source on DBus * * Return value: The id as a string */ const gchar * hud_application_source_get_id (HudApplicationSource * app) { g_return_val_if_fail(HUD_IS_APPLICATION_SOURCE(app), NULL); return app->priv->app_id; } /** * hud_application_source_get_app_icon: * @app: A #HudApplicationSource object * * Get the application icon * * Return value: The icon as a string */ const gchar * hud_application_source_get_app_icon (HudApplicationSource * app) { g_return_val_if_fail(HUD_IS_APPLICATION_SOURCE(app), NULL); const gchar * icon = NULL; const gchar * desktop_file = NULL; if(app->priv->bamf_app == NULL) { g_warning("Window Info was null for: %s", app->priv->app_id); return NULL; } desktop_file = hud_window_info_get_desktop_file(app->priv->bamf_app); if (desktop_file != NULL) { GKeyFile * kfile = g_key_file_new(); g_key_file_load_from_file(kfile, desktop_file, G_KEY_FILE_NONE, NULL); icon = g_key_file_get_value(kfile, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ICON, NULL); g_key_file_free(kfile); } return icon; } /** * hud_application_source_window_closed: * @app: A #HudApplicationSource object * @window: The window to be removed from the application * * Signal that a window has been closed and the source should clean * up data associated with it. */ void hud_application_source_window_closed (HudApplicationSource * app, guint xid) { int i; for (i = 0; i < app->priv->contexts->len; i++) { HudApplicationSourceContext * context = g_ptr_array_index(app->priv->contexts, i); guint32 ctx_winid = hud_application_source_context_get_window_id(context); if (ctx_winid != xid) { continue; } g_ptr_array_remove_index(app->priv->contexts, i); i--; } if (xid == app->priv->focused_window) { hud_source_changed(HUD_SOURCE(app)); } return; } /** * hud_application_source_add_window: * @app: A #HudApplicationSource object * @window: The window to be added to the application * * Add a window to an application object. Basically this means we only have to * have one BAMF listener in the application list. */ void hud_application_source_add_window (HudApplicationSource * app, HudWindowInfo * window) { g_return_if_fail(HUD_IS_APPLICATION_SOURCE(app)); guint32 xid = hud_window_info_get_window_id(window); if (app->priv->bamf_app == NULL) { g_debug("No BAMF application object"); return; } HudApplicationSourceContext * context = find_context(app, xid, NULL); const gchar *icon = hud_application_source_get_app_icon(app); if (icon != NULL) { app_iface_com_canonical_hud_application_set_icon(app->priv->skel, icon); } hud_application_source_context_add_window(context, window); return; } /** * hud_application_source_has_xid: * @app: A #HudApplicationSource object * @xid: XID to lookup * * Looks to see if we know about this XID. * * Return value: Whether we're tracking @xid. */ gboolean hud_application_source_has_xid (HudApplicationSource * app, guint32 xid) { g_return_val_if_fail(HUD_IS_APPLICATION_SOURCE(app), FALSE); int i; for (i = 0; i < app->priv->contexts->len; i++) { HudApplicationSourceContext * context = g_ptr_array_index(app->priv->contexts, i); guint32 ctx_winid = hud_application_source_context_get_window_id(context); if (ctx_winid == xid) { return TRUE; } } return FALSE; } /* Gets all the items for the sources */ static GList * source_get_items (HudSource * object) { g_return_val_if_fail(HUD_IS_APPLICATION_SOURCE(object), NULL); HudApplicationSource *app = HUD_APPLICATION_SOURCE(object); GList * retval = NULL; int i; for (i = 0; i < app->priv->contexts->len; i++) { HudApplicationSourceContext * context = g_ptr_array_index(app->priv->contexts, i); if (context_is_current(app, context)) { retval = g_list_concat(hud_source_get_items(HUD_SOURCE(context)), retval); } } return retval; } /** * hud_application_source_set_context: * @app: A #HudApplicationSource * @xid: Window Identifier * @context: Context to set as the current context * * This functions sets the current context for the application * which may cause a changed signal. */ void hud_application_source_set_context (HudApplicationSource * app, guint32 xid, const gchar * context) { g_return_if_fail(HUD_IS_APPLICATION_SOURCE(app)); g_debug("%s: setting context for window %u to %s", hud_application_source_get_id(app), xid, context); gboolean was_used = app->priv->used; /* Make sure we clear the old contexts if we could have one */ if (was_used) { hud_source_unuse(HUD_SOURCE(app)); } /* Swap the context for this window */ g_hash_table_insert(app->priv->window_contexts, GUINT_TO_POINTER(xid), g_strdup(context)); /* Return our used state */ if (was_used) { hud_source_use(HUD_SOURCE(app)); } hud_source_changed(HUD_SOURCE(app)); return; } /** * hud_application_source_get_context: * @app: A #HudApplicationSource * @xid: Window Identifier * * Returns the current context of the application. * * Return value: The current context */ const gchar * hud_application_source_get_context (HudApplicationSource * app, guint32 xid) { g_return_val_if_fail(HUD_IS_APPLICATION_SOURCE(app), NULL); return g_hash_table_lookup(app->priv->window_contexts, GUINT_TO_POINTER(xid)); } /** * hud_application_source_add_context: * @app: A #HudApplicationSource * @context: An Application Context * * Adds a context to the application source. Interface used for * testing. */ void hud_application_source_add_context (HudApplicationSource * app, HudApplicationSourceContext * context) { g_return_if_fail(HUD_IS_APPLICATION_SOURCE(app)); g_return_if_fail(HUD_IS_APPLICATION_SOURCE_CONTEXT(context)); g_signal_connect(G_OBJECT(context), "changed", G_CALLBACK(window_source_changed), app); g_ptr_array_add(app->priv->contexts, context); return; } /** * hud_application_source_set_focused_win: * @app: A #HudApplicationSource * @xid: A window * * Sets which window the application source thinks has focus. This * is exposed for testing and generally set_focus() should be used * by external users. */ void hud_application_source_set_focused_win (HudApplicationSource * app, guint32 xid) { g_return_if_fail(HUD_IS_APPLICATION_SOURCE(app)); gboolean used = app->priv->used; if (used) { hud_source_unuse(HUD_SOURCE(app)); } app->priv->focused_window = xid; if (used) { hud_source_use(HUD_SOURCE(app)); } hud_source_changed(HUD_SOURCE(app)); return; }