From 94f92072c2e39f43dfbc281cccc46cfc89c63fc0 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Thu, 25 Jun 2009 17:42:46 -0400 Subject: [PATCH] ShellAppMonitor now always assocates windows with desktop files Track all windows; at the time of opening (and shell startup) we call into ShellAppSystem to take the WM_CLASS property and try to find an associated .desktop file. Add mozilla-firefox to the list of our WM_CLASS workarounds. Add shell_global_get_screen, since it's often used. --- src/shell-app-monitor.c | 319 +++++++++++++++++++++++++++++++++------- src/shell-app-monitor.h | 5 +- src/shell-global.c | 13 +- src/shell-global.h | 2 + 4 files changed, 283 insertions(+), 56 deletions(-) diff --git a/src/shell-app-monitor.c b/src/shell-app-monitor.c index 34afc3c20..ce10908a1 100644 --- a/src/shell-app-monitor.c +++ b/src/shell-app-monitor.c @@ -13,9 +13,11 @@ #include "shell-app-monitor.h" +#include "shell-app-system.h" #include "shell-global.h" #include "display.h" +#include "window.h" /* This file includes modified code from * desktop-data-engine/engine-dbus/hippo-application-monitor.c @@ -63,6 +65,7 @@ static struct const char *pattern; GRegex *regex; } title_patterns[] = { + {"mozilla-firefox", ".* - Mozilla Firefox", NULL}, \ {"openoffice.org-writer", ".* - OpenOffice.org Writer$", NULL}, \ {"openoffice.org-calc", ".* - OpenOffice.org Calc$", NULL}, \ {"openoffice.org-impress", ".* - OpenOffice.org Impress$", NULL}, \ @@ -93,6 +96,12 @@ struct _ShellAppMonitor gboolean currently_idle; gboolean enable_monitoring; + /* */ + GHashTable *running_appids; + + /* */ + GHashTable *window_to_appid; + GHashTable *apps_by_wm_class; /* Seen apps by wm_class */ GHashTable *popularities; /* One AppPopularity struct list per activity */ int upload_apps_burst_count; @@ -191,6 +200,232 @@ destroy_popularity (gpointer key, g_slist_free (list); } +static char * +get_wmclass_for_window (MetaWindow *window) +{ + static gboolean patterns_initialized = FALSE; + const char *wm_class; + char *title; + int i; + + wm_class = meta_window_get_wm_class (window); + g_object_get (window, "title", &title, NULL); + + if (!patterns_initialized) /* Generate match patterns once for all */ + { + patterns_initialized = TRUE; + for (i = 0; title_patterns[i].app_id; i++) + { + title_patterns[i].regex = g_regex_new (title_patterns[i].pattern, + 0, 0, NULL); + } + } + + /* Match window title patterns to identifiers for non-standard apps */ + if (title) + { + for (i = 0; title_patterns[i].app_id; i++) + { + if (g_regex_match (title_patterns[i].regex, title, 0, NULL)) + { + /* Set a pseudo WM class, handled like true ones */ + wm_class = title_patterns[i].app_id; + break; + } + } + } + + g_free (title); + return g_strdup (wm_class); +} + +/** + * get_cleaned_wmclass_for_window: + * + * A "cleaned" wmclass is the WM_CLASS property of a window, + * after some transformations to turn it into a form + * somewhat more resilient to changes, such as lowercasing. + */ +static char * +get_cleaned_wmclass_for_window (MetaWindow *window) +{ + char *wmclass; + char *cleaned_wmclass; + + if (meta_window_get_window_type (window) != META_WINDOW_NORMAL) + return NULL; + + wmclass = get_wmclass_for_window (window); + if (!wmclass) + return NULL; + + cleaned_wmclass = g_utf8_strdown (wmclass, -1); + g_free (wmclass); + /* This handles "Fedora Eclipse", probably others */ + g_strdelimit (cleaned_wmclass, " ", '-'); + wmclass = g_strdup (cleaned_wmclass); + g_free (cleaned_wmclass); + return wmclass; +} + +/** + * get_appid_for_window: + * + * Returns a desktop file ID for an application, or %NULL if + * we're unable to determine one. + */ +static char * +get_appid_for_window (MetaWindow *window) +{ + char *wmclass; + char *with_desktop; + char *fullpath; + ShellAppSystem *appsys; + + wmclass = get_cleaned_wmclass_for_window (window); + + if (!wmclass) + return NULL; + + with_desktop = g_strjoin (NULL, wmclass, ".desktop", NULL); + g_free (wmclass); + + appsys = shell_app_system_get_default (); + + fullpath = shell_app_system_lookup_basename (appsys, with_desktop); + + return fullpath; +} + +static void +track_window (ShellAppMonitor *self, + MetaWindow *window) +{ + char *appid; + guint window_count; + + appid = get_appid_for_window (window); + if (!appid) + return; + + g_hash_table_insert (self->window_to_appid, window, g_strdup (appid)); + + window_count = GPOINTER_TO_UINT (g_hash_table_lookup (self->running_appids, appid)); + + window_count += 1; + g_hash_table_insert (self->running_appids, g_strdup (appid), GUINT_TO_POINTER (window_count)); + if (window_count == 1) + g_signal_emit (self, signals[CHANGED], 0); +} + +static void +shell_app_monitor_on_window_added (MetaWorkspace *workspace, + MetaWindow *window, + gpointer user_data) +{ + ShellAppMonitor *self = SHELL_APP_MONITOR (user_data); + + track_window (self, window); +} + +static void +shell_app_monitor_on_window_removed (MetaWorkspace *workspace, + MetaWindow *window, + gpointer user_data) +{ + ShellAppMonitor *self = SHELL_APP_MONITOR (user_data); + char *appid; + guint window_count; + + appid = g_hash_table_lookup (self->window_to_appid, window); + if (!appid) + return; + + window_count = GPOINTER_TO_UINT (g_hash_table_lookup (self->running_appids, appid)); + + window_count -= 1; + if (window_count == 0) + { + g_hash_table_remove (self->running_appids, appid); + g_free (appid); + g_signal_emit (self, signals[CHANGED], 0); + } + else + { + g_hash_table_insert (self->running_appids, appid, GUINT_TO_POINTER (window_count)); + } + g_hash_table_remove (self->window_to_appid, window); +} + +static void +load_initial_windows (ShellAppMonitor *monitor) +{ + GList *workspaces, *iter; + MetaScreen *screen = shell_global_get_screen (shell_global_get ()); + workspaces = meta_screen_get_workspaces (screen); + + for (iter = workspaces; iter; iter = iter->next) + { + MetaWorkspace *workspace = iter->data; + GList *windows = meta_workspace_list_windows (workspace); + GList *window_iter; + + for (window_iter = windows; window_iter; window_iter = window_iter->next) + track_window (monitor, (MetaWindow*)window_iter->data); + + g_list_free (windows); + } +} + +guint +shell_app_monitor_get_window_count (ShellAppMonitor *self, + const char *appid) +{ + return GPOINTER_TO_UINT (g_hash_table_lookup (self->running_appids, appid)); +} + +static void +shell_app_monitor_on_n_workspaces_changed (MetaScreen *screen, + gpointer user_data) +{ + ShellAppMonitor *self = SHELL_APP_MONITOR (user_data); + GList *workspaces, *iter; + + workspaces = meta_screen_get_workspaces (screen); + + for (iter = workspaces; iter; iter = iter->next) + { + MetaWorkspace *workspace = iter->data; + + /* This pair of disconnect/connect is idempotent if we were + * already connected, while ensuring we get connected for + * new workspaces. + */ + g_signal_handlers_disconnect_by_func (workspace, + shell_app_monitor_on_window_added, + self); + g_signal_handlers_disconnect_by_func (workspace, + shell_app_monitor_on_window_removed, + self); + + g_signal_connect (workspace, "window-added", + G_CALLBACK (shell_app_monitor_on_window_added), self); + g_signal_connect (workspace, "window-removed", + G_CALLBACK (shell_app_monitor_on_window_removed), self); + } +} + +static void +init_window_monitoring (ShellAppMonitor *self) +{ + MetaScreen *screen = shell_global_get_screen (shell_global_get ()); + + g_signal_connect (screen, "notify::n-workspaces", + G_CALLBACK (shell_app_monitor_on_n_workspaces_changed), self); + + shell_app_monitor_on_n_workspaces_changed (screen, self); +} + static void shell_app_monitor_init (ShellAppMonitor *self) { @@ -223,6 +458,15 @@ shell_app_monitor_init (ShellAppMonitor *self) (GDestroyNotify) g_free, (GDestroyNotify) g_free); + self->running_appids = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + + self->window_to_appid = g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, (GDestroyNotify) g_free); + + load_initial_windows (self); + init_window_monitoring (self); + g_object_get (shell_global_get(), "configdir", &shell_config_dir, NULL), path = g_build_filename (shell_config_dir, DATA_FILENAME, NULL); g_free (shell_config_dir); @@ -299,16 +543,31 @@ shell_app_monitor_get_most_used_apps (ShellAppMonitor *monitor, return list; } -/* Find the active window in order to collect stats */ -void -get_active_app_properties (ShellAppMonitor *monitor, - char **wm_class, - char **title) +/** + * shell_app_monitor_get_running_app_ids: + * + * @monitor: An app monitor instance + * + * Returns: (element-type utf8) (transfer container): List of application desktop + * identifiers + */ +GList * +shell_app_monitor_get_running_app_ids (ShellAppMonitor *monitor) { + return g_hash_table_get_keys (monitor->running_appids); +} + +void +update_app_info (ShellAppMonitor *monitor) +{ + char *wm_class; ShellGlobal *global; + GHashTable *app_active_times = NULL; /* last active time for an application */ MetaScreen *screen; MetaDisplay *display; MetaWindow *active; + int activity; + guint32 timestamp; global = shell_global_get (); g_object_get (global, "screen", &screen, NULL); @@ -317,60 +576,14 @@ get_active_app_properties (ShellAppMonitor *monitor, active = meta_display_get_focus_window (display); - if (wm_class) - *wm_class = NULL; - if (title) - *title = NULL; - if (active == NULL) return; - *wm_class = g_strdup (meta_window_get_wm_class (active)); - g_object_get (active, "title", title, NULL); -} + wm_class = get_cleaned_wmclass_for_window (active); -void -update_app_info (ShellAppMonitor *monitor) -{ - char *wm_class; - char *title; - GHashTable *app_active_times = NULL; /* GTime spent per activity */ - static gboolean first_time = TRUE; - int activity; - guint32 timestamp; - int i; - - if (first_time) /* Generate match patterns once for all */ - { - first_time = FALSE; - for (i = 0; title_patterns[i].app_id; i++) - { - title_patterns[i].regex = g_regex_new (title_patterns[i].pattern, - 0, 0, NULL); - } - } - - get_active_app_properties (monitor, &wm_class, &title); - - /* Match window title patterns to identifiers for non-standard apps */ - if (title) - { - for (i = 0; title_patterns[i].app_id; i++) - { - if ( g_regex_match (title_patterns[i].regex, title, 0, NULL) ) - { - /* Set a pseudo WM class, handled like true ones */ - g_free (wm_class); - wm_class = g_strdup(title_patterns[i].app_id); - break; - } - } - g_free (title); - } - if (!wm_class) return; - + app_active_times = g_hash_table_lookup (monitor->apps_by_wm_class, wm_class); if (!app_active_times) { diff --git a/src/shell-app-monitor.h b/src/shell-app-monitor.h index f0cf8917e..65bf8b090 100644 --- a/src/shell-app-monitor.h +++ b/src/shell-app-monitor.h @@ -40,9 +40,10 @@ GSList *shell_app_monitor_get_most_used_apps (ShellAppMonitor *monitor, int activity, gint number); +guint shell_app_monitor_get_window_count (ShellAppMonitor *monitor, const char *appid); + /* Get whatever's running right now */ -GSList *shell_app_monitor_get_running_apps (ShellAppMonitor *monitor, - int activity); +GList *shell_app_monitor_get_running_app_ids (ShellAppMonitor *monitor); G_END_DECLS diff --git a/src/shell-global.c b/src/shell-global.c index 46d6052ba..a197aa26b 100644 --- a/src/shell-global.c +++ b/src/shell-global.c @@ -101,7 +101,7 @@ shell_global_get_property(GObject *object, g_value_set_object (value, mutter_plugin_get_overlay_group (global->plugin)); break; case PROP_SCREEN: - g_value_set_object (value, mutter_plugin_get_screen (global->plugin)); + g_value_set_object (value, shell_global_get_screen (global)); break; case PROP_SCREEN_WIDTH: { @@ -608,6 +608,17 @@ shell_global_set_stage_input_region (ShellGlobal *global, shell_global_set_stage_input_mode (global, global->input_mode); } +/** + * shell_global_get_screen: + * + * Return value: (transfer none): The default #MetaScreen + */ +MetaScreen * +shell_global_get_screen (ShellGlobal *global) +{ + return mutter_plugin_get_screen (global->plugin); +} + /** * shell_global_get_windows: * diff --git a/src/shell-global.h b/src/shell-global.h index 2192b75c2..0b641a53a 100644 --- a/src/shell-global.h +++ b/src/shell-global.h @@ -48,6 +48,8 @@ ClutterActor *shell_get_event_related(ClutterEvent *event); ShellGlobal *shell_global_get (void); +MetaScreen *shell_global_get_screen (ShellGlobal *global); + void shell_global_grab_dbus_service (ShellGlobal *global); void shell_global_start_task_panel (ShellGlobal *global);