[gnome-shell] ShellAppMonitor: create "applications" for unknown windows



commit a4bf54e4653d4df83067a7830e2b3627a2a9a9d8
Author: Colin Walters <walters verbum org>
Date:   Fri Aug 14 04:35:48 2009 -0400

    ShellAppMonitor: create "applications" for unknown windows
    
    Use MetaGroup for a window when looking up applications.  If
    we know the application for a TYPE_NORMAL window in the group,
    use that.
    
    However, we aren't always going to know the application for a window.  In
    that case, create a fake one.
    
    ShellAppInfo has a "transient" flag so we know not to write these
    fake apps to the usage file.
    
    Clean up the idle focus handler to better handle the case where
    no window is focused, and where we don't want to track the
    particular window.
    
    Update track_window to create the fake window.
    
    When a window goes away, we want to delete the usage.
    
    Rewrite shell_app_monitor_get_running_apps to be based
    on the window_to_app hash, because that's what has the pointer
    to ShellAppInfo*.  Before we were looking up all ids through
    ShellAppSystem, but that shouldn't be holding a ref to transients.
    
    Change the well display icon to be centered, since our icons for
    window apps aren't 48 pixels.

 js/ui/appDisplay.js     |   38 +++++-----
 src/shell-app-monitor.c |  201 ++++++++++++++++++++++++++++++++++++----------
 src/shell-app-monitor.h |    2 +-
 3 files changed, 177 insertions(+), 64 deletions(-)
---
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index bee50ce..f12c3c2 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -468,11 +468,12 @@ WellDisplayItem.prototype = {
         let draggable = DND.makeDraggable(this.actor);
 
         let iconBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
-                                    x_align: Big.BoxAlignment.CENTER });
+                                    x_align: Big.BoxAlignment.CENTER,
+                                    y_align: Big.BoxAlignment.CENTER });
         this._icon = appInfo.create_icon_texture(APP_ICON_SIZE);
         iconBox.append(this._icon, Big.BoxPackFlags.NONE);
 
-        this.actor.append(iconBox, Big.BoxPackFlags.NONE);
+        this.actor.append(iconBox, Big.BoxPackFlags.EXPAND);
 
         this._windows = Shell.AppMonitor.get_default().get_windows_for_app(appInfo.get_id());
 
@@ -615,20 +616,17 @@ WellGrid.prototype = {
         let y = box.y1;
         let columnIndex = 0;
         for (let i = 0; i < children.length; i++) {
-            let [childMinWidth, childMinHeight,
-                 childNaturalWidth, childNaturalHeight] = children[i].get_preferred_size();
+            let [childMinWidth, childNaturalWidth] = children[i].get_preferred_width(-1);
 
-            /* Center the item in its allocation */
+            /* Center the item in its allocation horizontally */
             let width = Math.min(itemWidth, childNaturalWidth);
-            let height = Math.min(itemHeight, childNaturalHeight);
             let horizSpacing = (itemWidth - width) / 2;
-            let vertSpacing = (itemHeight - height) / 2;
 
             let childBox = new Clutter.ActorBox();
             childBox.x1 = Math.floor(x + horizSpacing);
-            childBox.y1 = Math.floor(y + vertSpacing);
+            childBox.y1 = y;
             childBox.x2 = childBox.x1 + width;
-            childBox.y2 = childBox.y1 + height;
+            childBox.y2 = childBox.y1 + itemHeight;
             children[i].allocate(childBox, flags);
 
             let atSeparator = (i == this._separatorIndex - 1);
@@ -803,11 +801,10 @@ AppWell.prototype = {
         /* hardcode here pending some design about how exactly desktop contexts behave */
         let contextId = "";
 
-        let runningIds = this._appMonitor.get_running_app_ids(contextId).filter(function (e) {
-            return !(e in favoriteIdsHash);
+        let running = this._appMonitor.get_running_apps(contextId).filter(function (e) {
+            return !(e.get_id() in favoriteIdsHash);
         });
         let favorites = this._lookupApps(favoriteIds);
-        let running = this._lookupApps(runningIds);
 
         let displays = []
         this._addApps(favorites, true);
@@ -831,22 +828,25 @@ AppWell.prototype = {
     acceptDrop : function(source, actor, x, y, time) {
         let global = Shell.Global.get();
 
-        let id = null;
+        let appSystem = Shell.AppSystem.get_default();
+
+        let app = null;
         if (source instanceof WellDisplayItem) {
-            id = source.appInfo.get_id();
+            app = source.appInfo;
         } else if (source instanceof AppDisplayItem) {
-            id = source.getId();
+            app = appSystem.lookup_cached_app(source.getId());
         } else if (source instanceof Workspaces.WindowClone) {
             let appMonitor = Shell.AppMonitor.get_default();
-            let app = appMonitor.get_window_app(source.metaWindow);
-            id = app.get_id();
+            app = appMonitor.get_window_app(source.metaWindow);
         }
 
-        if (id == null) {
+        // Don't allow favoriting of transient apps
+        if (app == null || app.is_transient()) {
             return false;
         }
 
-        let appSystem = Shell.AppSystem.get_default();
+        let id = app.get_id();
+
         let favoriteIds = this._appSystem.get_favorites();
         let favoriteIdsObject = this._arrayValues(favoriteIds);
 
diff --git a/src/shell-app-monitor.c b/src/shell-app-monitor.c
index 08ce67e..3ee4acd 100644
--- a/src/shell-app-monitor.c
+++ b/src/shell-app-monitor.c
@@ -22,6 +22,7 @@
 
 #include "display.h"
 #include "window.h"
+#include "group.h"
 
 /* This file includes modified code from
  * desktop-data-engine/engine-dbus/hippo-application-monitor.c
@@ -140,6 +141,11 @@ G_DEFINE_TYPE (ShellAppMonitor, shell_app_monitor, G_TYPE_OBJECT);
 /* Represents an application record for a given context */
 struct AppUsage
 {
+  /* Whether the application we're tracking is "transient", see
+   * shell_app_info_is_transient.
+   */
+  gboolean transient;
+
   gdouble score; /* Based on the number of times we'e seen the app and normalized */
   long last_seen; /* Used to clear old apps we've only seen a few times */
 
@@ -317,24 +323,39 @@ get_cleaned_wmclass_for_window (MetaWindow  *window)
   return wmclass;
 }
 
+static gboolean
+window_is_tracked (MetaWindow *window)
+{
+  return !meta_window_is_override_redirect (window);
+}
+
+static ShellAppInfo *
+create_transient_app_for_window (MetaWindow *window)
+{
+  return shell_app_system_create_from_window (shell_app_system_get_default (), window);
+}
+
 /**
- * get_app_for_window:
+ * get_app_for_window_direct:
  *
- * Returns the application associated with a window, or %NULL if
- * we're unable to determine one.
+ * Looks only at the given window, and attempts to determine
+ * an application based on WM_CLASS.  If that fails, then
+ * a "transient" application is created.
+ *
+ * Return value: (transfer full): A newly-referenced #ShellAppInfo
  */
 static ShellAppInfo *
-get_app_for_window (MetaWindow     *window)
+get_app_for_window_direct (MetaWindow  *window)
 {
-  char *wmclass;
-  char *with_desktop;
   ShellAppInfo *result;
   ShellAppSystem *appsys;
+  char *wmclass;
+  char *with_desktop;
 
   wmclass = get_cleaned_wmclass_for_window (window);
 
   if (!wmclass)
-    return NULL;
+    return create_transient_app_for_window (window);
 
   with_desktop = g_strjoin (NULL, wmclass, ".desktop", NULL);
   g_free (wmclass);
@@ -343,9 +364,63 @@ get_app_for_window (MetaWindow     *window)
   result = shell_app_system_lookup_heuristic_basename (appsys, with_desktop);
   g_free (with_desktop);
 
+  if (result == NULL)
+    result = create_transient_app_for_window (window);
   return result;
 }
 
+/**
+ * get_app_for_window:
+ *
+ * Determines the application associated with a window, using
+ * all available information such as the window's MetaGroup,
+ * and what we know about other windows.
+ */
+static ShellAppInfo *
+get_app_for_window (ShellAppMonitor    *monitor,
+                    MetaWindow         *window)
+{
+  ShellAppInfo *result;
+  MetaWindow *source_window;
+  GSList *group_windows;
+  MetaGroup *group;
+  GSList *iter;
+
+  group = meta_window_get_group (window);
+  if (group == NULL)
+    group_windows = g_slist_prepend (NULL, window);
+  else
+    group_windows = meta_group_list_windows (group);
+
+  source_window = window;
+
+  result = NULL;
+  /* Try finding a window in the group of type NORMAL; if we
+   * succeed, use that as our source. */
+  for (iter = group_windows; iter; iter = iter->next)
+    {
+      MetaWindow *group_window = iter->data;
+
+      if (meta_window_get_window_type (group_window) != META_WINDOW_NORMAL)
+        continue;
+
+       source_window = group_window;
+       result = g_hash_table_lookup (monitor->window_to_app, group_window);
+       if (result)
+         break;
+    }
+
+  g_slist_free (group_windows);
+
+  if (result != NULL)
+    {
+      shell_app_info_ref (result);
+      return result;
+    }
+
+  return get_app_for_window_direct (source_window);
+}
+
 static const char *
 get_window_context (MetaWindow *window)
 {
@@ -410,11 +485,18 @@ get_active_window (ShellAppMonitor *monitor)
 {
   MetaScreen *screen;
   MetaDisplay *display;
+  MetaWindow *window;
+
   screen = shell_global_get_screen (shell_global_get ());
   display = meta_screen_get_display (screen);
-  return meta_display_get_focus_window (display);
+  window = meta_display_get_focus_window (display);
+
+  if (window != NULL && window_is_tracked (window))
+    return window;
+  return NULL;
 }
 
+
 typedef struct {
   gboolean in_context;
   GHashTableIter context_iter;
@@ -504,13 +586,6 @@ increment_usage_for_window_at_time (ShellAppMonitor *self,
   guint usage_count;
 
   usage = get_app_usage_from_window (self, window);
-  if (usage == NULL)
-    {
-      /* This could in theory happen if we lost the app tracking, i.e.
-       * the window changed WM_CLASS.  In that case, time for a punt.
-       */
-      return;
-    }
 
   usage->last_seen = time;
 
@@ -533,30 +608,50 @@ increment_usage_for_window (ShellAppMonitor *self,
   increment_usage_for_window_at_time (self, window, curtime);
 }
 
+/**
+ * reset_usage:
+ *
+ * For non-transient applications, just reset the "seen sequence".
+ *
+ * For transient ones, we don't want to keep an AppUsage around, so
+ * remove it entirely.
+ */
+static void
+reset_usage (ShellAppMonitor *self,
+             const char      *context,
+             const char      *appid,
+             AppUsage        *usage)
+{
+  if (!usage->transient)
+    {
+      usage->initially_seen_sequence = 0;
+    }
+  else
+    {
+      GHashTable *usages;
+      usages = g_hash_table_lookup (self->app_usages_for_context, context);
+      g_hash_table_remove (usages, appid);
+    }
+}
+
 static void
 track_window (ShellAppMonitor *self,
               MetaWindow      *window)
 {
   ShellAppInfo *app;
   AppUsage *usage;
-  MetaWindow *transient_for;
 
-  /* Just ignore anything that isn't NORMAL */
-  if (meta_window_get_window_type (window) != META_WINDOW_NORMAL)
+  if (!window_is_tracked (window))
     return;
 
-  /* Also ignore transients */
-  transient_for = meta_window_get_transient_for (window);
-  if (transient_for != NULL)
-    return;
-
-  app = get_app_for_window (window);
+  app = get_app_for_window (self, window);
   if (!app)
     return;
 
   g_hash_table_insert (self->window_to_app, window, app);
 
   usage = get_app_usage_from_window (self, window);
+  usage->transient = shell_app_info_is_transient (app);
 
   /* Keep track of the number of windows open for this app, when it
    * switches between 0 and 1 we emit an app-added signal.
@@ -590,6 +685,7 @@ shell_app_monitor_on_window_removed (MetaWorkspace   *workspace,
   ShellAppMonitor *self = SHELL_APP_MONITOR (user_data);
   ShellAppInfo *app;
   AppUsage *usage;
+  const char *context;
 
   app = g_hash_table_lookup (self->window_to_app, window);
   if (!app)
@@ -597,6 +693,7 @@ shell_app_monitor_on_window_removed (MetaWorkspace   *workspace,
 
   shell_app_info_ref (app);
 
+  context = get_window_context (window);
   usage = get_app_usage_from_window (self, window);
 
   if (window == self->watched_window)
@@ -610,8 +707,8 @@ shell_app_monitor_on_window_removed (MetaWorkspace   *workspace,
 
   if (usage->window_count == 0)
     {
-      usage->initially_seen_sequence = 0;
       g_signal_emit (self, signals[APP_REMOVED], 0, app);
+      reset_usage (self, context, shell_app_info_get_id (app), usage);
     }
 
   shell_app_info_unref (app);
@@ -870,7 +967,7 @@ shell_app_monitor_get_most_used_apps (ShellAppMonitor *monitor,
  * @monitor: An app monitor instance
  * @metawin: A #MetaWindow
  *
- * Returns: Desktop file id associated with window
+ * Returns: Application associated with window
  */
 ShellAppInfo *
 shell_app_monitor_get_window_app (ShellAppMonitor *monitor,
@@ -900,8 +997,10 @@ sort_apps_by_open_sequence (gconstpointer a,
                             gpointer datap)
 {
   AppOpenSequenceSortData *data = datap;
-  const char *id_a = a;
-  const char *id_b = b;
+  ShellAppInfo *app_a = (ShellAppInfo*)a;
+  ShellAppInfo *app_b = (ShellAppInfo*)b;
+  const char *id_a = shell_app_info_get_id (app_a);
+  const char *id_b = shell_app_info_get_id (app_b);
   AppUsage *usage_a;
   AppUsage *usage_b;
 
@@ -915,38 +1014,48 @@ sort_apps_by_open_sequence (gconstpointer a,
 }
 
 /**
- * shell_app_monitor_get_running_app_ids:
+ * shell_app_monitor_get_running_apps:
  * @monitor: An app monitor instance
  * @context: Activity identifier
  *
  * Returns the set of applications which currently have at least one open
  * window in the given context.
  *
- * Returns: (element-type utf8) (transfer container): List of application desktop
- *     identifiers
+ * Returns: (element-type ShellAppInfo) (transfer container): Active applications
  */
 GSList *
-shell_app_monitor_get_running_app_ids (ShellAppMonitor *monitor,
-                                       const char      *context)
+shell_app_monitor_get_running_apps (ShellAppMonitor *monitor,
+                                    const char      *context)
 {
-  UsageIterator iter;
-  const char *cur_context;
-  const char *id;
-  AppUsage *usage;
+  GHashTableIter iter;
+  gpointer key, value;
   GSList *ret;
   AppOpenSequenceSortData data;
+  GHashTable *unique_apps;
 
-  usage_iterator_init (monitor, &iter);
+  unique_apps = g_hash_table_new (g_str_hash, g_str_equal);
+
+  g_hash_table_iter_init (&iter, monitor->window_to_app);
 
   ret = NULL;
-  while (usage_iterator_next (monitor, &iter, &cur_context, &id, &usage))
+  while (g_hash_table_iter_next (&iter, &key, &value))
     {
-      if (strcmp (cur_context, context) != 0)
+      MetaWindow *window = key;
+      ShellAppInfo *app = value;
+      const char *id;
+
+      if (strcmp (get_window_context (window), context) != 0)
+        continue;
+
+      id = shell_app_info_get_id (app);
+
+      if (g_hash_table_lookup (unique_apps, id))
         continue;
+      g_hash_table_insert (unique_apps, (gpointer)id, (gpointer)id);
 
-      if (usage->window_count > 0)
-        ret = g_slist_prepend (ret, (char*)id);
+      ret = g_slist_prepend (ret, app);
     }
+  g_hash_table_destroy (unique_apps);
 
   data.self = monitor;
   data.context_id = context;
@@ -959,7 +1068,8 @@ idle_handle_focus_change (gpointer data)
   ShellAppMonitor *monitor = data;
   long curtime = get_time ();
 
-  increment_usage_for_window (monitor, monitor->watched_window);
+  if (monitor->watched_window != NULL)
+    increment_usage_for_window (monitor, monitor->watched_window);
 
   monitor->watched_window = get_active_window (monitor);
   monitor->watch_start_time = curtime;
@@ -1122,7 +1232,6 @@ idle_save_application_usage (gpointer data)
   GDataOutputStream *data_output;
   GError *error = NULL;
 
-
   monitor->save_id = 0;
 
   /* Parent directory is already created by shell-global */
@@ -1146,6 +1255,10 @@ idle_save_application_usage (gpointer data)
   current_context = NULL;
   while (usage_iterator_next (monitor, &iter, &context, &id, &usage))
     {
+      /* Don't save the fake apps we create for windows */
+      if (usage->transient)
+        continue;
+
       if (context != current_context)
         {
           if (current_context != NULL)
diff --git a/src/shell-app-monitor.h b/src/shell-app-monitor.h
index 7858d0a..a56bd5e 100644
--- a/src/shell-app-monitor.h
+++ b/src/shell-app-monitor.h
@@ -45,7 +45,7 @@ GList *shell_app_monitor_get_most_used_apps (ShellAppMonitor *monitor,
 GSList *shell_app_monitor_get_windows_for_app (ShellAppMonitor *monitor, const char *appid);
 
 /* Get whatever's running right now */
-GSList *shell_app_monitor_get_running_app_ids (ShellAppMonitor *monitor, const char *context);
+GSList *shell_app_monitor_get_running_apps (ShellAppMonitor *monitor, const char *context);
 
 GSList *shell_app_monitor_get_startup_sequences (ShellAppMonitor *monitor);
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]