/*
* 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 "hudsource.h"
#include "app-iface.h"
#include "hudmenumodelcollector.h"
#include "huddbusmenucollector.h"
#include "hudsourcelist.h"
struct _HudApplicationSourcePrivate {
GDBusConnection * session;
gchar * app_id;
gchar * path;
AppIfaceComCanonicalHudApplication * skel;
#ifdef HAVE_BAMF
AbstractApplication * bamf_app;
#endif
#ifdef HAVE_HYBRIS
gchar * desktop_file;
#endif
guint32 focused_window;
HudSource * used_source;
guint how_used;
GHashTable * windows;
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_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 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->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->windows = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, 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);
return;
}
/* Clean up references */
static void
hud_application_source_dispose (GObject *object)
{
HudApplicationSource * self = HUD_APPLICATION_SOURCE(object);
if (self->priv->used_source != NULL) {
hud_source_unuse(self->priv->used_source);
g_clear_object(&self->priv->used_source);
}
g_clear_pointer(&self->priv->windows, g_hash_table_unref);
g_clear_pointer(&self->priv->connections, g_hash_table_unref);
if (self->priv->skel != NULL) {
g_dbus_interface_skeleton_unexport(G_DBUS_INTERFACE_SKELETON(self->priv->skel));
g_clear_object(&self->priv->skel);
}
#ifdef HAVE_BAMF
g_clear_object(&self->priv->bamf_app);
#endif
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->app_id, g_free);
g_clear_pointer(&self->priv->path, g_free);
#ifdef HAVE_HYBRIS
g_clear_pointer(&self->priv->desktop_file, g_free);
#endif
G_OBJECT_CLASS (hud_application_source_parent_class)->finalize (object);
return;
}
static HudSource *
get_used_source (HudApplicationSource *app)
{
if (app->priv->used_source != NULL )
{
return app->priv->used_source;
}
if (g_hash_table_size (app->priv->windows) == 1)
{
GHashTableIter iter;
gpointer key, value;
g_hash_table_iter_init (&iter, app->priv->windows);
g_hash_table_iter_next (&iter, &key, &value);
HudSourceList *list = HUD_SOURCE_LIST(value);
if (list != NULL )
{
return HUD_SOURCE(list) ;
}
g_warning("An app '%s' single window but no source list.", app->priv->app_id);
return NULL ;
}
g_warning("An app '%s' without a single window.", app->priv->app_id);
return NULL ;
}
/* 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);
if (get_used_source(self) == source) {
hud_source_changed(HUD_SOURCE(self));
}
return;
}
/* Source interface using this source */
static void
source_use (HudSource *hud_source)
{
HudApplicationSource * app = HUD_APPLICATION_SOURCE(hud_source);
if (app->priv->used_source == NULL) {
app->priv->used_source = g_hash_table_lookup(app->priv->windows, GINT_TO_POINTER(app->priv->focused_window));
g_object_ref(app->priv->used_source);
app->priv->how_used = 0;
}
if (app->priv->used_source == NULL) {
g_warning("Application '%s' has no focused window.", app->priv->app_id);
return;
}
if (app->priv->how_used == 0) {
hud_source_use(app->priv->used_source);
}
app->priv->how_used++;
return;
}
/* Source interface unusing this source */
static void
source_unuse (HudSource *hud_source)
{
HudApplicationSource * app = HUD_APPLICATION_SOURCE(hud_source);
if (app->priv->used_source == NULL) {
g_warning("An asymetric number of uses");
return;
}
app->priv->how_used--;
if (app->priv->how_used == 0) {
hud_source_unuse(app->priv->used_source);
g_clear_object(&app->priv->used_source);
}
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);
HudSource *source = get_used_source (app);
if (source != NULL )
{
hud_source_search (source, search_string, append_func, 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)
{
HudApplicationSource * app = HUD_APPLICATION_SOURCE(hud_source);
HudSource *source = get_used_source (app);
if (source != NULL )
{
hud_source_list_applications (source, search_string, append_func, user_data);
}
}
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));
}
static void
source_activate_toolbar (HudSource * hud_source, HudClientQueryToolbarItems item, GVariant *platform_data)
{
HudApplicationSource * app = HUD_APPLICATION_SOURCE(hud_source);
HudSource *source = get_used_source (app);
if (source != NULL )
{
hud_source_activate_toolbar (source, item, platform_data);
}
}
/**
* 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 (AbstractApplication * bapp)
{
gchar * id = hud_application_source_bamf_app_id(bapp);
if (id == NULL) {
return NULL;
}
HudApplicationSource * source = hud_application_source_new_for_id(id);
g_free(id);
const gchar * desktop_file = NULL;
#ifdef HAVE_BAMF
source->priv->bamf_app = g_object_ref(bapp);
desktop_file = bamf_application_get_desktop_file(bapp);
#endif
#ifdef HAVE_HYBRIS
source->priv->desktop_file = g_strdup(ubuntu_ui_session_properties_get_desktop_file_hint(*bapp));
desktop_file = source->priv->desktop_file;
#endif
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)
{
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();
gchar * app_id_clean = g_strdup(id);
gchar * app_id_cleanp;
for (app_id_cleanp = app_id_clean; app_id_cleanp[0] != '\0'; app_id_cleanp++) {
if (!g_ascii_isalnum(app_id_cleanp[0])) {
app_id_cleanp[0] = '_';
}
}
source->priv->path = g_strdup_printf("/com/canonical/hud/applications/%s", app_id_clean);
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("/com/canonical/hud/applications/%s_%d", app_id_clean, ++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_debug("Application ('%s') path: %s", id, source->priv->path);
g_free(app_id_clean);
return source;
}
/* Get the collectors if we need them */
static void
get_collectors (HudApplicationSource * app, guint32 xid, const gchar * appid, HudDbusmenuCollector ** dcollector, HudMenuModelCollector ** mcollector)
{
HudSourceList * collector_list = g_hash_table_lookup(app->priv->windows, GINT_TO_POINTER(xid));
if (collector_list == NULL) {
collector_list = hud_source_list_new();
g_signal_connect(collector_list, "changed", G_CALLBACK(window_source_changed), app);
g_hash_table_insert(app->priv->windows, GINT_TO_POINTER(xid), collector_list);
}
HudMenuModelCollector * mm_collector = NULL;
HudDbusmenuCollector * dm_collector = NULL;
GSList * sources = hud_source_list_get_list(collector_list);
GSList * source;
for (source = sources; source != NULL; source = g_slist_next(source)) {
if (HUD_IS_MENU_MODEL_COLLECTOR(source->data)) {
mm_collector = HUD_MENU_MODEL_COLLECTOR(source->data);
}
if (HUD_IS_DBUSMENU_COLLECTOR(source->data)) {
dm_collector = HUD_DBUSMENU_COLLECTOR(source->data);
}
}
if (mm_collector == NULL) {
gchar * export_path = g_strdup_printf("%s/window%X", app->priv->path, xid);
mm_collector = hud_menu_model_collector_new(appid, NULL, 0, export_path, HUD_SOURCE_ITEM_TYPE_BACKGROUND_APP);
g_free(export_path);
if (mm_collector != NULL) {
hud_source_list_add(collector_list, HUD_SOURCE(mm_collector));
g_object_unref(mm_collector);
}
}
if (dcollector != NULL) {
*dcollector = dm_collector;
}
if (mcollector != NULL) {
*mcollector = mm_collector;
}
return;
}
/* 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;
}
GList * idtemp;
for (idtemp = watcher->ids; idtemp != NULL; idtemp = g_list_next(idtemp)) {
g_hash_table_remove(app->priv->windows, idtemp->data);
}
g_hash_table_remove(app->priv->connections, name);
/* all the items have been removed. When application-list sees this it
* will happily unref us to complete the cleanup (missing the last unref)
*/
hud_source_changed(HUD_SOURCE(app));
g_object_unref(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);
g_object_ref(app);
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;
}
/* 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);
GVariant * id = NULL;
gchar * prefix = NULL;
gchar * object = NULL;
/* 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, "(vso)", &id, &prefix, &object)) {
g_debug("Adding prefix '%s' at path: %s", prefix, object);
#ifdef HAVE_HYBRIS
guint32 idn = WINDOW_ID_CONSTANT;
#else
guint32 idn = g_variant_get_int32(id);
#endif
HudMenuModelCollector * collector = NULL;
get_collectors(app, idn, app->priv->app_id, NULL, &collector);
if (collector == NULL) continue;
GDBusActionGroup * ag = g_dbus_action_group_get(session, sender, object);
hud_menu_model_collector_add_actions(collector, 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, "(vo)", &id, &object)) {
g_debug("Adding descriptions: %s", object);
#ifdef HAVE_HYBRIS
guint32 idn = WINDOW_ID_CONSTANT;
#else
guint32 idn = g_variant_get_int32(id);
#endif
HudMenuModelCollector * collector = NULL;
get_collectors(app, idn, app->priv->app_id, NULL, &collector);
if (collector == NULL) continue;
GDBusMenuModel * model = g_dbus_menu_model_get(session, sender, object);
hud_menu_model_collector_add_model(collector, G_MENU_MODEL(model), NULL, 1);
g_object_unref(model);
add_id_to_connection(app, session, sender, idn);
}
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 (g_hash_table_size(app->priv->windows) == 0);
}
/**
* hud_application_source_bamf_app_id:
* @bapp: A #BamfApplication object
*
* A little helper function to genereate a constant app ID out of
* BAMF Application objects. Putting this here as it seems to make
* the most sense, but isn't really part of the object.
*
* Return value: (transfer full): ID for the application
*/
gchar *
hud_application_source_bamf_app_id (AbstractApplication * bapp)
{
#ifdef HAVE_BAMF
g_return_val_if_fail(BAMF_IS_APPLICATION(bapp), NULL);
#endif
#ifdef HAVE_HYBRIS
/* Hybris has no way to check if the pointer is valid */
#endif
const gchar * desktop_file = NULL;
#ifdef HAVE_BAMF
desktop_file = bamf_application_get_desktop_file(bapp);
#endif
#ifdef HAVE_HYBRIS
desktop_file = ubuntu_ui_session_properties_get_desktop_file_hint(*bapp);
#endif
if (desktop_file == NULL) {
/* Some apps might not be identifiable. Eh, don't care then */
return NULL;
}
gchar * basename = g_path_get_basename(desktop_file);
if (basename == NULL || basename[0] == '\0' || !g_str_has_suffix(basename, ".desktop")) {
/* Check to make sure it's not NULL and it returns a desktop file */
g_free(basename);
return NULL;
}
/* This is probably excessively clever, but I like it. Basically we find
the last instance of .desktop and put the null there. For all practical
purposes this is a NULL terminated string of the first part of the dekstop
file name */
g_strrstr(basename, ".desktop")[0] = '\0';
return basename;
}
/**
* 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, AbstractApplication * bapp, AbstractWindow * window)
{
g_return_if_fail(HUD_IS_APPLICATION_SOURCE(app));
#ifdef HAVE_BAMF
g_return_if_fail(BAMF_IS_APPLICATION(bapp));
#endif
#ifdef HAVE_HYBRIS
/* Hybris has no way to check if the pointer is valid */
#endif
#ifdef HAVE_BAMF
if (app->priv->bamf_app == NULL) {
app->priv->bamf_app = g_object_ref(bapp);
}
g_return_if_fail(app->priv->bamf_app == bapp);
#endif
#ifdef HAVE_HYBRIS
if (app->priv->desktop_file == NULL) {
app->priv->desktop_file = g_strdup(ubuntu_ui_session_properties_get_desktop_file_hint(*bapp));
app_iface_com_canonical_hud_application_set_desktop_path(app->priv->skel, app->priv->desktop_file);
}
#endif
hud_application_source_add_window(app, window);
#ifdef HAVE_BAMF
app->priv->focused_window = bamf_window_get_xid(window);
#endif
#ifdef HAVE_HYBRIS
app->priv->focused_window = _ubuntu_ui_session_properties_get_window_id(window);
#endif
return;
}
/**
* 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;
#ifdef HAVE_BAMF
desktop_file = bamf_application_get_desktop_file(app->priv->bamf_app);
#endif
#ifdef HAVE_HYBRIS
desktop_file = app->priv->desktop_file;
#endif
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;
}
typedef struct _window_info_t window_info_t;
struct _window_info_t {
HudApplicationSource * source; /* Not a ref */
#ifdef HAVE_BAMF
AbstractWindow * window; /* Not a ref */
#endif
guint32 xid; /* Can't be a ref */
};
#ifdef HAVE_BAMF
/* When I window gets destroyed we want to clean up it's collectors
and all that jazz. */
static void
window_destroyed (gpointer data, GObject * old_address)
{
window_info_t * window_info = (window_info_t *)data;
window_info->window = NULL;
if (window_info->source->priv->focused_window == window_info->xid) {
g_clear_object(&window_info->source->priv->used_source);
}
g_hash_table_remove(window_info->source->priv->windows, GINT_TO_POINTER(window_info->xid));
/* NOTE: DO NOT use the window_info after this point as
it may be free'd by the remove above. */
return;
}
#endif
/* If the collector gets free'd first we need to deallocate the memory
and make sure we don't keep the weak reference. */
static void
free_window_info (gpointer data)
{
window_info_t * window_info = (window_info_t *)data;
#ifdef HAVE_BAMF
if (window_info->window != NULL) {
g_object_weak_unref(G_OBJECT(window_info->window), window_destroyed, window_info);
}
#endif
g_free(window_info);
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, AbstractWindow * window)
{
g_return_if_fail(HUD_IS_APPLICATION_SOURCE(app));
#ifdef HAVE_BAMF
g_return_if_fail(BAMF_IS_WINDOW(window));
#endif
#ifdef HAVE_HYBRIS
/* Hybris has no way to check if the pointer is valid */
#endif
guint32 xid = 0;
#ifdef HAVE_BAMF
xid = bamf_window_get_xid(window);
#endif
#ifdef HAVE_HYBRIS
xid = _ubuntu_ui_session_properties_get_window_id(window);
#endif
#ifdef HAVE_BAMF
if (app->priv->bamf_app == NULL) {
g_debug("No BAMF application object");
return;
}
#endif
window_info_t * window_info = g_new0(window_info_t, 1);
window_info->xid = xid;
window_info->source = app;
#ifdef HAVE_BAMF
/* Uhm, this is how we were managing this memory... uhg, hybris */
window_info->window = window;
g_object_weak_ref(G_OBJECT(window), window_destroyed, window_info);
#endif
HudSourceList * collector_list = g_hash_table_lookup(app->priv->windows, GINT_TO_POINTER(xid));
if (collector_list == NULL) {
collector_list = hud_source_list_new();
g_signal_connect(collector_list, "changed", G_CALLBACK(window_source_changed), app);
g_hash_table_insert(app->priv->windows, GINT_TO_POINTER(xid), collector_list);
}
/* We're managing the lifecycle of the window info here as
that allows it to have some sort of destroy function */
g_object_set_data_full(G_OBJECT(collector_list), "hud-application-source-window-info", window_info, free_window_info);
HudMenuModelCollector * mm_collector = NULL;
HudDbusmenuCollector * dm_collector = NULL;
GSList * sources = hud_source_list_get_list(collector_list);
GSList * source;
for (source = sources; source != NULL; source = g_slist_next(source)) {
if (HUD_IS_MENU_MODEL_COLLECTOR(source->data)) {
mm_collector = HUD_MENU_MODEL_COLLECTOR(source->data);
}
if (HUD_IS_DBUSMENU_COLLECTOR(source->data)) {
dm_collector = HUD_DBUSMENU_COLLECTOR(source->data);
}
}
#ifdef HAVE_BAMF
gchar * app_id = hud_application_source_bamf_app_id(app->priv->bamf_app);
#endif
#ifdef HAVE_HYBRIS
gchar * app_id = g_strdup(app->priv->app_id);
#endif
const gchar * icon = NULL;
#ifdef HAVE_BAMF
icon = bamf_view_get_icon(BAMF_VIEW(window));
#endif
#ifdef HAVE_BAMF
/* Hybris can't find window icons, so we want to pull it from the desktop file */
#endif
if (icon == NULL) {
icon = hud_application_source_get_app_icon(app);
}
if (icon != NULL) {
app_iface_com_canonical_hud_application_set_icon(app->priv->skel, icon);
}
if (mm_collector == NULL) {
gchar * export_path = g_strdup_printf("%s/window%X", app->priv->path, xid);
mm_collector = hud_menu_model_collector_new(app_id, icon, 0, export_path, HUD_SOURCE_ITEM_TYPE_BACKGROUND_APP);
g_free(export_path);
if (mm_collector != NULL) {
#ifdef HAVE_BAMF
hud_menu_model_collector_add_window(mm_collector, window);
#endif
#ifdef HAVE_HYBRIS
/* We only have GApplication based windows on the desktop, so we don't need this currently */
#endif
hud_source_list_add(collector_list, HUD_SOURCE(mm_collector));
g_object_unref(mm_collector);
}
}
if (dm_collector == NULL) {
dm_collector = hud_dbusmenu_collector_new_for_window(window, app_id, icon, HUD_SOURCE_ITEM_TYPE_BACKGROUND_APP);
if (dm_collector != NULL) {
hud_source_list_add(collector_list, HUD_SOURCE(dm_collector));
g_object_unref(dm_collector);
}
}
g_free (app_id);
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);
return g_hash_table_lookup(app->priv->windows, GINT_TO_POINTER(xid)) != NULL;
}
static GList *
source_get_items (HudSource * object)
{
g_return_val_if_fail(HUD_IS_APPLICATION_SOURCE(object), NULL);
HudApplicationSource *app = HUD_APPLICATION_SOURCE(object);
HudSource *source = get_used_source (app);
if (source != NULL )
{
return hud_source_get_items (source);
}
return NULL ;
}