[libwnck/wip/muktupavels/wnck-handle-2] util: move resource usage to WnckHandle



commit d4a4c86cfed070acf373273e6b8e506eedc910b3
Author: Alberts Muktupāvels <alberts muktupavels gmail com>
Date:   Tue May 11 23:44:07 2021 +0300

    util: move resource usage to WnckHandle

 libwnck/util.c                | 494 +--------------------------------------
 libwnck/wnck-handle-private.h |  10 +
 libwnck/wnck-handle.c         | 531 +++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 546 insertions(+), 489 deletions(-)
---
diff --git a/libwnck/util.c b/libwnck/util.c
index e908a1c..8e4856f 100644
--- a/libwnck/util.c
+++ b/libwnck/util.c
@@ -25,11 +25,9 @@
 #include "util.h"
 #include "xutils.h"
 #include "private.h"
+#include "wnck-handle-private.h"
 #include <gdk/gdkx.h>
 #include <string.h>
-#ifdef HAVE_XRES
-#include <X11/extensions/XRes.h>
-#endif
 
 /**
  * SECTION:resource
@@ -60,14 +58,6 @@
  * #WnckApplication.
  */
 
-typedef enum
-{
-  WNCK_EXT_UNKNOWN = 0,
-  WNCK_EXT_FOUND = 1,
-  WNCK_EXT_MISSING = 2
-} WnckExtStatus;
-
-
 #if 0
 /* useful for debugging */
 static void
@@ -103,38 +93,6 @@ _wnck_print_resource_usage (WnckResourceUsage *usage)
 }
 #endif
 
-static WnckExtStatus
-wnck_init_resource_usage (GdkDisplay *gdisplay)
-{
-  WnckExtStatus status;
-
-  status = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (gdisplay),
-                                               "wnck-xres-status"));
-
-  if (status == WNCK_EXT_UNKNOWN)
-    {
-#ifdef HAVE_XRES
-      Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdisplay);
-      int event, error;
-
-      if (!XResQueryExtension (xdisplay, &event, &error))
-        status = WNCK_EXT_MISSING;
-      else
-        status = WNCK_EXT_FOUND;
-#else
-      status = WNCK_EXT_MISSING;
-#endif
-
-      g_object_set_data (G_OBJECT (gdisplay),
-                         "wnck-xres-status",
-                         GINT_TO_POINTER (status));
-    }
-
-  g_assert (status != WNCK_EXT_UNKNOWN);
-
-  return status;
-}
-
 /**
  * wnck_xid_read_resource_usage:
  * @gdk_display: a <classname>GdkDisplay</classname>.
@@ -155,430 +113,12 @@ wnck_xid_read_resource_usage (GdkDisplay        *gdisplay,
                               gulong             xid,
                               WnckResourceUsage *usage)
 {
-  g_return_if_fail (usage != NULL);
-
-  memset (usage, '\0', sizeof (*usage));
-
-  if (wnck_init_resource_usage (gdisplay) == WNCK_EXT_MISSING)
-    return;
-
-#ifdef HAVE_XRES
- {
-   Display *xdisplay;
-   XResType *types;
-   int n_types;
-   unsigned long pixmap_bytes;
-   int i;
-   Atom pixmap_atom;
-   Atom window_atom;
-   Atom gc_atom;
-   Atom picture_atom;
-   Atom glyphset_atom;
-   Atom font_atom;
-   Atom colormap_entry_atom;
-   Atom passive_grab_atom;
-   Atom cursor_atom;
-
-   types = NULL;
-   n_types = 0;
-   pixmap_bytes = 0;
-
-  xdisplay = GDK_DISPLAY_XDISPLAY (gdisplay);
-
-  _wnck_error_trap_push (xdisplay);
-
-  XResQueryClientResources (xdisplay,
-                             xid, &n_types,
-                             &types);
-
-   XResQueryClientPixmapBytes (xdisplay,
-                               xid, &pixmap_bytes);
-   _wnck_error_trap_pop (xdisplay);
-
-   usage->pixmap_bytes = pixmap_bytes;
-
-   pixmap_atom = _wnck_atom_get ("PIXMAP");
-   window_atom = _wnck_atom_get ("WINDOW");
-   gc_atom = _wnck_atom_get ("GC");
-   font_atom = _wnck_atom_get ("FONT");
-   glyphset_atom = _wnck_atom_get ("GLYPHSET");
-   picture_atom = _wnck_atom_get ("PICTURE");
-   colormap_entry_atom = _wnck_atom_get ("COLORMAP ENTRY");
-   passive_grab_atom = _wnck_atom_get ("PASSIVE GRAB");
-   cursor_atom = _wnck_atom_get ("CURSOR");
-
-   i = 0;
-   while (i < n_types)
-     {
-       guint t = types[i].resource_type;
-
-       if (t == pixmap_atom)
-         usage->n_pixmaps += types[i].count;
-       else if (t == window_atom)
-         usage->n_windows += types[i].count;
-       else if (t == gc_atom)
-         usage->n_gcs += types[i].count;
-       else if (t == picture_atom)
-         usage->n_pictures += types[i].count;
-       else if (t == glyphset_atom)
-         usage->n_glyphsets += types[i].count;
-       else if (t == font_atom)
-         usage->n_fonts += types[i].count;
-       else if (t == colormap_entry_atom)
-         usage->n_colormap_entries += types[i].count;
-       else if (t == passive_grab_atom)
-         usage->n_passive_grabs += types[i].count;
-       else if (t == cursor_atom)
-         usage->n_cursors += types[i].count;
-       else
-         usage->n_other += types[i].count;
-
-       ++i;
-     }
-
-   XFree(types);
-
-   usage->total_bytes_estimate = usage->pixmap_bytes;
-
-   /* FIXME look in the X server source and come up with better
-    * answers here. Ideally we change XRes to return a number
-    * like this since it can do things like divide the cost of
-    * a shared resource among those sharing it.
-    */
-   usage->total_bytes_estimate += usage->n_windows * 24;
-   usage->total_bytes_estimate += usage->n_gcs * 24;
-   usage->total_bytes_estimate += usage->n_pictures * 24;
-   usage->total_bytes_estimate += usage->n_glyphsets * 24;
-   usage->total_bytes_estimate += usage->n_fonts * 1024;
-   usage->total_bytes_estimate += usage->n_colormap_entries * 24;
-   usage->total_bytes_estimate += usage->n_passive_grabs * 24;
-   usage->total_bytes_estimate += usage->n_cursors * 24;
-   usage->total_bytes_estimate += usage->n_other * 24;
- }
-#else /* HAVE_XRES */
-  g_assert_not_reached ();
-#endif /* HAVE_XRES */
-}
-
-#ifdef HAVE_XRES
-static void
-wnck_pid_read_resource_usage_free_hash (gpointer data)
-{
-  g_slice_free (gulong, data);
-}
-
-static guint
-wnck_gulong_hash (gconstpointer v)
-{
-  /* FIXME: this is obvioulsy wrong, but nearly 100% of the time, the gulong
-   * only contains guint values */
-  return *(const guint *) v;
-}
-
-static gboolean
-wnck_gulong_equal (gconstpointer a,
-                   gconstpointer b)
-{
-  return *((const gulong *) a) == *((const gulong *) b);
-}
-
-static gulong
-wnck_check_window_for_pid (Screen *screen,
-                           Window  win,
-                           XID     match_xid,
-                           XID     mask)
-{
-  if ((win & ~mask) == match_xid) {
-    return _wnck_get_pid (screen, win);
-  }
-
-  return 0;
-}
-
-static void
-wnck_find_pid_for_resource_r (Display *xdisplay,
-                              Screen  *screen,
-                              Window   win_top,
-                              XID      match_xid,
-                              XID      mask,
-                              gulong  *xid,
-                              gulong  *pid)
-{
-  Status   qtres;
-  int      err;
-  Window   dummy;
-  Window  *children;
-  guint    n_children;
-  guint    i;
-  gulong   found_pid = 0;
-
-  while (gtk_events_pending ())
-    gtk_main_iteration ();
-
-  found_pid = wnck_check_window_for_pid (screen, win_top, match_xid, mask);
-  if (found_pid != 0)
-    {
-      *xid = win_top;
-      *pid = found_pid;
-    }
-
-  _wnck_error_trap_push (xdisplay);
-  qtres = XQueryTree (xdisplay, win_top, &dummy, &dummy,
-                      &children, &n_children);
-  err = _wnck_error_trap_pop (xdisplay);
-
-  if (!qtres || err != Success)
-    return;
-
-  for (i = 0; i < n_children; i++)
-    {
-      wnck_find_pid_for_resource_r (xdisplay, screen, children[i],
-                                    match_xid, mask, xid, pid);
-
-      if (*pid != 0)
-       break;
-    }
-
-  if (children)
-    XFree ((char *)children);
+  _wnck_handle_read_resource_usage_xid (_wnck_get_handle (),
+                                        gdisplay,
+                                        xid,
+                                        usage);
 }
 
-struct xresclient_state
-{
-  XResClient *clients;
-  int         n_clients;
-  int         next;
-  Display    *xdisplay;
-  GHashTable *hashtable_pid;
-};
-
-static struct xresclient_state xres_state = { NULL, 0, -1, NULL, NULL };
-static guint       xres_idleid = 0;
-static GHashTable *xres_hashtable = NULL;
-static time_t      start_update = 0;
-static time_t      end_update = 0;
-static guint       xres_removeid = 0;
-
-static void
-wnck_pid_read_resource_usage_xres_state_free (gpointer data)
-{
-  struct xresclient_state *state;
-
-  state = (struct xresclient_state *) data;
-
-  if (state->clients)
-    XFree (state->clients);
-  state->clients = NULL;
-
-  state->n_clients = 0;
-  state->next = -1;
-  state->xdisplay = NULL;
-
-  if (state->hashtable_pid)
-    g_hash_table_destroy (state->hashtable_pid);
-  state->hashtable_pid = NULL;
-}
-
-static gboolean
-wnck_pid_read_resource_usage_fill_cache (struct xresclient_state *state)
-{
-  int    i;
-  gulong pid;
-  gulong xid;
-  XID    match_xid;
-
-  if (state->next >= state->n_clients)
-    {
-      if (xres_hashtable)
-        g_hash_table_destroy (xres_hashtable);
-      xres_hashtable = state->hashtable_pid;
-      state->hashtable_pid = NULL;
-
-      time (&end_update);
-
-      xres_idleid = 0;
-      return FALSE;
-    }
-
-  match_xid = (state->clients[state->next].resource_base &
-               ~state->clients[state->next].resource_mask);
-
-  pid = 0;
-  xid = 0;
-
-  for (i = 0; i < ScreenCount (state->xdisplay); i++)
-    {
-      Screen *screen;
-      Window  root;
-
-      screen = ScreenOfDisplay (state->xdisplay, i);
-      root = RootWindow (state->xdisplay, i);
-
-      if (root == None)
-        continue;
-
-      wnck_find_pid_for_resource_r (state->xdisplay, screen, root, match_xid,
-                                    state->clients[state->next].resource_mask,
-                                    &xid, &pid);
-
-      if (pid != 0 && xid != 0)
-        break;
-    }
-
-  if (pid != 0 && xid != 0)
-    {
-      gulong *key;
-      gulong *value;
-
-      key = g_slice_new (gulong);
-      value = g_slice_new (gulong);
-      *key = pid;
-      *value = xid;
-      g_hash_table_insert (state->hashtable_pid, key, value);
-    }
-
-  state->next++;
-
-  return TRUE;
-}
-
-static void
-wnck_pid_read_resource_usage_start_build_cache (GdkDisplay *gdisplay)
-{
-  Display *xdisplay;
-  int      err;
-
-  if (xres_idleid != 0)
-    return;
-
-  time (&start_update);
-
-  xdisplay = GDK_DISPLAY_XDISPLAY (gdisplay);
-
-  _wnck_error_trap_push (xdisplay);
-  XResQueryClients (xdisplay, &xres_state.n_clients, &xres_state.clients);
-  err = _wnck_error_trap_pop (xdisplay);
-
-  if (err != Success)
-    return;
-
-  xres_state.next = (xres_state.n_clients > 0) ? 0 : -1;
-  xres_state.xdisplay = xdisplay;
-  xres_state.hashtable_pid = g_hash_table_new_full (
-                                     wnck_gulong_hash,
-                                     wnck_gulong_equal,
-                                     wnck_pid_read_resource_usage_free_hash,
-                                     wnck_pid_read_resource_usage_free_hash);
-
-  xres_idleid = g_idle_add_full (
-                        G_PRIORITY_HIGH_IDLE,
-                        (GSourceFunc) wnck_pid_read_resource_usage_fill_cache,
-                        &xres_state, wnck_pid_read_resource_usage_xres_state_free);
-}
-
-static gboolean
-wnck_pid_read_resource_usage_destroy_hash_table (gpointer data)
-{
-  xres_removeid = 0;
-
-  if (xres_hashtable)
-    g_hash_table_destroy (xres_hashtable);
-
-  xres_hashtable = NULL;
-
-  return FALSE;
-}
-
-#define XRES_UPDATE_RATE_SEC 30
-static gboolean
-wnck_pid_read_resource_usage_from_cache (GdkDisplay        *gdisplay,
-                                         gulong             pid,
-                                         WnckResourceUsage *usage)
-{
-  gboolean  need_rebuild;
-  gulong   *xid_p;
-  int       cache_validity;
-
-  if (end_update == 0)
-    time (&end_update);
-
-  cache_validity = MAX (XRES_UPDATE_RATE_SEC, (end_update - start_update) * 2);
-
-  /* we rebuild the cache if it was never built or if it's old */
-  need_rebuild = (xres_hashtable == NULL ||
-                  (end_update < time (NULL) - cache_validity));
-
-  if (xres_hashtable)
-    {
-      /* clear the cache after quite some time, because it might not be used
-       * anymore */
-      if (xres_removeid != 0)
-        g_source_remove (xres_removeid);
-      xres_removeid = g_timeout_add_seconds (cache_validity * 2,
-                                             wnck_pid_read_resource_usage_destroy_hash_table,
-                                             NULL);
-    }
-
-  if (need_rebuild)
-    wnck_pid_read_resource_usage_start_build_cache (gdisplay);
-
-  if (xres_hashtable)
-    xid_p = g_hash_table_lookup (xres_hashtable, &pid);
-  else
-    xid_p = NULL;
-
-  if (xid_p)
-    {
-      wnck_xid_read_resource_usage (gdisplay, *xid_p, usage);
-      return TRUE;
-    }
-
-  return FALSE;
-}
-
-static void
-wnck_pid_read_resource_usage_no_cache (GdkDisplay        *gdisplay,
-                                       gulong             pid,
-                                       WnckResourceUsage *usage)
-{
-  Display *xdisplay;
-  int i;
-
-  xdisplay = GDK_DISPLAY_XDISPLAY (gdisplay);
-
-  i = 0;
-  while (i < ScreenCount (xdisplay))
-    {
-      WnckScreen *screen;
-      GList *windows;
-      GList *tmp;
-
-      screen = wnck_screen_get (i);
-
-      g_assert (screen != NULL);
-
-      windows = wnck_screen_get_windows (screen);
-      tmp = windows;
-      while (tmp != NULL)
-        {
-          if (wnck_window_get_pid (tmp->data) == (int) pid)
-            {
-              wnck_xid_read_resource_usage (gdisplay,
-                                            wnck_window_get_xid (tmp->data),
-                                            usage);
-
-              /* stop on first window found */
-              return;
-            }
-
-          tmp = tmp->next;
-        }
-
-      ++i;
-    }
-}
-#endif /* HAVE_XRES */
-
 /**
  * wnck_pid_read_resource_usage:
  * @gdk_display: a <classname>GdkDisplay</classname>.
@@ -608,19 +148,10 @@ wnck_pid_read_resource_usage (GdkDisplay        *gdisplay,
                               gulong             pid,
                               WnckResourceUsage *usage)
 {
-  g_return_if_fail (usage != NULL);
-
-  memset (usage, '\0', sizeof (*usage));
-
-  if (wnck_init_resource_usage (gdisplay) == WNCK_EXT_MISSING)
-    return;
-
-#ifdef HAVE_XRES
-  if (!wnck_pid_read_resource_usage_from_cache (gdisplay, pid, usage))
-    /* the cache might not be built, might be outdated or might not contain
-     * data for a new X client, so try to fallback to something else */
-    wnck_pid_read_resource_usage_no_cache (gdisplay, pid, usage);
-#endif /* HAVE_XRES */
+  _wnck_handle_read_resource_usage_pid (_wnck_get_handle (),
+                                        gdisplay,
+                                        pid,
+                                        usage);
 }
 
 static WnckClientType client_type = 0;
@@ -852,13 +383,6 @@ wnck_shutdown (void)
   _wnck_window_shutdown_all ();
 
   g_clear_object (&wnck_handle);
-
-#ifdef HAVE_XRES
-  if (xres_removeid != 0)
-    g_source_remove (xres_removeid);
-  xres_removeid = 0;
-  wnck_pid_read_resource_usage_destroy_hash_table (NULL);
-#endif
 }
 
 void
diff --git a/libwnck/wnck-handle-private.h b/libwnck/wnck-handle-private.h
index f277661..ef28f09 100644
--- a/libwnck/wnck-handle-private.h
+++ b/libwnck/wnck-handle-private.h
@@ -39,6 +39,16 @@ void            _wnck_handle_set_default_mini_icon_size (WnckHandle     *self,
 
 gsize           _wnck_handle_get_default_mini_icon_size (WnckHandle     *self);
 
+void            _wnck_handle_read_resource_usage_xid    (WnckHandle        *self,
+                                                         GdkDisplay        *gdk_display,
+                                                         gulong             xid,
+                                                         WnckResourceUsage *usage);
+
+void            _wnck_handle_read_resource_usage_pid    (WnckHandle        *self,
+                                                         GdkDisplay        *gdk_display,
+                                                         gulong             pid,
+                                                         WnckResourceUsage *usage);
+
 G_END_DECLS
 
 #endif
diff --git a/libwnck/wnck-handle.c b/libwnck/wnck-handle.c
index 3f39daa..06ae68d 100644
--- a/libwnck/wnck-handle.c
+++ b/libwnck/wnck-handle.c
@@ -20,6 +20,12 @@
 #include "config.h"
 #include "wnck-handle-private.h"
 
+#include <gdk/gdkx.h>
+
+#ifdef HAVE_XRES
+#include <X11/extensions/XRes.h>
+#endif
+
 #include "private.h"
 #include "screen.h"
 #include "window.h"
@@ -29,14 +35,39 @@
 #define WNCK_TYPE_HANDLE (wnck_handle_get_type ())
 G_DECLARE_FINAL_TYPE (WnckHandle, wnck_handle, WNCK, HANDLE, GObject)
 
+typedef struct
+{
+  WnckHandle *handle;
+
+  XResClient *clients;
+  int         n_clients;
+  int         next;
+  Display    *xdisplay;
+  GHashTable *hashtable_pid;
+} xresclient_state;
+
+typedef enum
+{
+  WNCK_EXT_UNKNOWN = 0,
+  WNCK_EXT_FOUND = 1,
+  WNCK_EXT_MISSING = 2
+} WnckExtStatus;
+
 struct _WnckHandle
 {
-  GObject        parent;
+  GObject           parent;
+
+  WnckClientType    client_type;
 
-  WnckClientType client_type;
+  gsize             default_icon_size;
+  gsize             default_mini_icon_size;
 
-  gsize          default_icon_size;
-  gsize          default_mini_icon_size;
+  xresclient_state  xres_state;
+  guint             xres_idleid;
+  GHashTable       *xres_hashtable;
+  time_t            start_update;
+  time_t            end_update;
+  guint             xres_removeid;
 };
 
 enum
@@ -52,6 +83,354 @@ static GParamSpec *handle_properties[LAST_PROP] = { NULL };
 
 G_DEFINE_TYPE (WnckHandle, wnck_handle, G_TYPE_OBJECT)
 
+static WnckExtStatus
+wnck_init_resource_usage (GdkDisplay *gdisplay)
+{
+  WnckExtStatus status;
+
+  status = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (gdisplay),
+                                               "wnck-xres-status"));
+
+  if (status == WNCK_EXT_UNKNOWN)
+    {
+#ifdef HAVE_XRES
+      Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdisplay);
+      int event, error;
+
+      if (!XResQueryExtension (xdisplay, &event, &error))
+        status = WNCK_EXT_MISSING;
+      else
+        status = WNCK_EXT_FOUND;
+#else
+      status = WNCK_EXT_MISSING;
+#endif
+
+      g_object_set_data (G_OBJECT (gdisplay),
+                         "wnck-xres-status",
+                         GINT_TO_POINTER (status));
+    }
+
+  g_assert (status != WNCK_EXT_UNKNOWN);
+
+  return status;
+}
+
+#ifdef HAVE_XRES
+static void
+wnck_pid_read_resource_usage_free_hash (gpointer data)
+{
+  g_slice_free (gulong, data);
+}
+
+static guint
+wnck_gulong_hash (gconstpointer v)
+{
+  /* FIXME: this is obvioulsy wrong, but nearly 100% of the time, the gulong
+   * only contains guint values */
+  return *(const guint *) v;
+}
+
+static gboolean
+wnck_gulong_equal (gconstpointer a,
+                   gconstpointer b)
+{
+  return *((const gulong *) a) == *((const gulong *) b);
+}
+
+static gulong
+wnck_check_window_for_pid (Screen *screen,
+                           Window  win,
+                           XID     match_xid,
+                           XID     mask)
+{
+  if ((win & ~mask) == match_xid) {
+    return _wnck_get_pid (screen, win);
+  }
+
+  return 0;
+}
+
+static void
+wnck_find_pid_for_resource_r (Display *xdisplay,
+                              Screen  *screen,
+                              Window   win_top,
+                              XID      match_xid,
+                              XID      mask,
+                              gulong  *xid,
+                              gulong  *pid)
+{
+  Status   qtres;
+  int      err;
+  Window   dummy;
+  Window  *children;
+  guint    n_children;
+  guint    i;
+  gulong   found_pid = 0;
+
+  while (gtk_events_pending ())
+    gtk_main_iteration ();
+
+  found_pid = wnck_check_window_for_pid (screen, win_top, match_xid, mask);
+  if (found_pid != 0)
+    {
+      *xid = win_top;
+      *pid = found_pid;
+    }
+
+  _wnck_error_trap_push (xdisplay);
+  qtres = XQueryTree (xdisplay, win_top, &dummy, &dummy,
+                      &children, &n_children);
+  err = _wnck_error_trap_pop (xdisplay);
+
+  if (!qtres || err != Success)
+    return;
+
+  for (i = 0; i < n_children; i++)
+    {
+      wnck_find_pid_for_resource_r (xdisplay, screen, children[i],
+                                    match_xid, mask, xid, pid);
+
+      if (*pid != 0)
+       break;
+    }
+
+  if (children)
+    XFree ((char *)children);
+}
+
+static void
+wnck_pid_read_resource_usage_xres_state_free (gpointer data)
+{
+  xresclient_state *state;
+
+  state = (xresclient_state *) data;
+
+  if (state->clients)
+    XFree (state->clients);
+  state->clients = NULL;
+
+  state->n_clients = 0;
+  state->next = -1;
+  state->xdisplay = NULL;
+
+  if (state->hashtable_pid)
+    g_hash_table_destroy (state->hashtable_pid);
+  state->hashtable_pid = NULL;
+}
+
+static gboolean
+wnck_pid_read_resource_usage_fill_cache (xresclient_state *state)
+{
+  WnckHandle *self;
+  int    i;
+  gulong pid;
+  gulong xid;
+  XID    match_xid;
+
+  self = state->handle;
+
+  if (state->next >= state->n_clients)
+    {
+      if (self->xres_hashtable)
+        g_hash_table_destroy (self->xres_hashtable);
+      self->xres_hashtable = state->hashtable_pid;
+      state->hashtable_pid = NULL;
+
+      time (&self->end_update);
+
+      self->xres_idleid = 0;
+      return FALSE;
+    }
+
+  match_xid = (state->clients[state->next].resource_base &
+               ~state->clients[state->next].resource_mask);
+
+  pid = 0;
+  xid = 0;
+
+  for (i = 0; i < ScreenCount (state->xdisplay); i++)
+    {
+      Screen *screen;
+      Window  root;
+
+      screen = ScreenOfDisplay (state->xdisplay, i);
+      root = RootWindow (state->xdisplay, i);
+
+      if (root == None)
+        continue;
+
+      wnck_find_pid_for_resource_r (state->xdisplay, screen, root, match_xid,
+                                    state->clients[state->next].resource_mask,
+                                    &xid, &pid);
+
+      if (pid != 0 && xid != 0)
+        break;
+    }
+
+  if (pid != 0 && xid != 0)
+    {
+      gulong *key;
+      gulong *value;
+
+      key = g_slice_new (gulong);
+      value = g_slice_new (gulong);
+      *key = pid;
+      *value = xid;
+      g_hash_table_insert (state->hashtable_pid, key, value);
+    }
+
+  state->next++;
+
+  return TRUE;
+}
+
+static void
+wnck_pid_read_resource_usage_start_build_cache (WnckHandle *self,
+                                                GdkDisplay *gdisplay)
+{
+  Display *xdisplay;
+  int      err;
+
+  if (self->xres_idleid != 0)
+    return;
+
+  time (&self->start_update);
+
+  xdisplay = GDK_DISPLAY_XDISPLAY (gdisplay);
+
+  _wnck_error_trap_push (xdisplay);
+  XResQueryClients (xdisplay,
+                    &self->xres_state.n_clients,
+                    &self->xres_state.clients);
+  err = _wnck_error_trap_pop (xdisplay);
+
+  if (err != Success)
+    return;
+
+  self->xres_state.next = (self->xres_state.n_clients > 0) ? 0 : -1;
+  self->xres_state.xdisplay = xdisplay;
+  self->xres_state.hashtable_pid = g_hash_table_new_full (wnck_gulong_hash,
+                                                          wnck_gulong_equal,
+                                                          wnck_pid_read_resource_usage_free_hash,
+                                                          wnck_pid_read_resource_usage_free_hash);
+
+  self->xres_idleid = g_idle_add_full (G_PRIORITY_HIGH_IDLE,
+                                       (GSourceFunc) wnck_pid_read_resource_usage_fill_cache,
+                                       &self->xres_state,
+                                       wnck_pid_read_resource_usage_xres_state_free);
+}
+
+static gboolean
+wnck_pid_read_resource_usage_destroy_hash_table (gpointer data)
+{
+  WnckHandle *self;
+
+  self = WNCK_HANDLE (data);
+
+  self->xres_removeid = 0;
+
+  if (self->xres_hashtable)
+    {
+      g_hash_table_destroy (self->xres_hashtable);
+      self->xres_hashtable = NULL;
+    }
+
+  return G_SOURCE_REMOVE;
+}
+
+#define XRES_UPDATE_RATE_SEC 30
+static gboolean
+wnck_pid_read_resource_usage_from_cache (WnckHandle        *self,
+                                         GdkDisplay        *gdisplay,
+                                         gulong             pid,
+                                         WnckResourceUsage *usage)
+{
+  gboolean  need_rebuild;
+  gulong   *xid_p;
+  int       cache_validity;
+
+  if (self->end_update == 0)
+    time (&self->end_update);
+
+  cache_validity = MAX (XRES_UPDATE_RATE_SEC, (self->end_update - self->start_update) * 2);
+
+  /* we rebuild the cache if it was never built or if it's old */
+  need_rebuild = (self->xres_hashtable == NULL ||
+                  (self->end_update < time (NULL) - cache_validity));
+
+  if (self->xres_hashtable)
+    {
+      /* clear the cache after quite some time, because it might not be used
+       * anymore */
+      if (self->xres_removeid != 0)
+        g_source_remove (self->xres_removeid);
+      self->xres_removeid = g_timeout_add_seconds (cache_validity * 2,
+                                                   wnck_pid_read_resource_usage_destroy_hash_table,
+                                                   self);
+    }
+
+  if (need_rebuild)
+    wnck_pid_read_resource_usage_start_build_cache (self, gdisplay);
+
+  if (self->xres_hashtable)
+    xid_p = g_hash_table_lookup (self->xres_hashtable, &pid);
+  else
+    xid_p = NULL;
+
+  if (xid_p)
+    {
+      _wnck_handle_read_resource_usage_xid (self, gdisplay, *xid_p, usage);
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static void
+wnck_pid_read_resource_usage_no_cache (WnckHandle        *self,
+                                       GdkDisplay        *gdisplay,
+                                       gulong             pid,
+                                       WnckResourceUsage *usage)
+{
+  Display *xdisplay;
+  int i;
+
+  xdisplay = GDK_DISPLAY_XDISPLAY (gdisplay);
+
+  i = 0;
+  while (i < ScreenCount (xdisplay))
+    {
+      WnckScreen *screen;
+      GList *windows;
+      GList *tmp;
+
+      screen = wnck_screen_get (i);
+
+      g_assert (screen != NULL);
+
+      windows = wnck_screen_get_windows (screen);
+      tmp = windows;
+      while (tmp != NULL)
+        {
+          if (wnck_window_get_pid (tmp->data) == (int) pid)
+            {
+              _wnck_handle_read_resource_usage_xid (self,
+                                                    gdisplay,
+                                                    wnck_window_get_xid (tmp->data),
+                                                    usage);
+
+              /* stop on first window found */
+              return;
+            }
+
+          tmp = tmp->next;
+        }
+
+      ++i;
+    }
+}
+#endif /* HAVE_XRES */
+
 static GdkFilterReturn
 filter_func (GdkXEvent *gdkxevent,
              GdkEvent  *event,
@@ -144,6 +523,16 @@ wnck_handle_finalize (GObject *object)
 
   gdk_window_remove_filter (NULL, filter_func, self);
 
+#ifdef HAVE_XRES
+  if (self->xres_removeid != 0)
+    {
+      g_source_remove (self->xres_removeid);
+      self->xres_removeid = 0;
+    }
+
+  wnck_pid_read_resource_usage_destroy_hash_table (self);
+#endif
+
   G_OBJECT_CLASS (wnck_handle_parent_class)->finalize (object);
 }
 
@@ -228,6 +617,8 @@ wnck_handle_init (WnckHandle *self)
   self->default_icon_size = WNCK_DEFAULT_ICON_SIZE;
   self->default_mini_icon_size = WNCK_DEFAULT_MINI_ICON_SIZE;
 
+  self->xres_state = (xresclient_state) { self, NULL, 0, -1, NULL, NULL };
+
   gdk_window_add_filter (NULL, filter_func, self);
 }
 
@@ -270,3 +661,135 @@ _wnck_handle_get_default_mini_icon_size (WnckHandle *self)
 {
   return self->default_mini_icon_size;
 }
+
+void
+_wnck_handle_read_resource_usage_xid (WnckHandle        *self,
+                                      GdkDisplay        *gdk_display,
+                                      gulong             xid,
+                                      WnckResourceUsage *usage)
+{
+  g_return_if_fail (usage != NULL);
+
+  memset (usage, '\0', sizeof (*usage));
+
+  if (wnck_init_resource_usage (gdk_display) == WNCK_EXT_MISSING)
+    return;
+
+#ifdef HAVE_XRES
+  {
+    Display *xdisplay;
+    XResType *types;
+    int n_types;
+    unsigned long pixmap_bytes;
+    int i;
+    Atom pixmap_atom;
+    Atom window_atom;
+    Atom gc_atom;
+    Atom picture_atom;
+    Atom glyphset_atom;
+    Atom font_atom;
+    Atom colormap_entry_atom;
+    Atom passive_grab_atom;
+    Atom cursor_atom;
+
+    types = NULL;
+    n_types = 0;
+    pixmap_bytes = 0;
+
+    xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display);
+
+    _wnck_error_trap_push (xdisplay);
+
+    XResQueryClientResources (xdisplay,
+                              xid, &n_types,
+                              &types);
+
+    XResQueryClientPixmapBytes (xdisplay,
+                                xid, &pixmap_bytes);
+    _wnck_error_trap_pop (xdisplay);
+
+    usage->pixmap_bytes = pixmap_bytes;
+
+    pixmap_atom = _wnck_atom_get ("PIXMAP");
+    window_atom = _wnck_atom_get ("WINDOW");
+    gc_atom = _wnck_atom_get ("GC");
+    font_atom = _wnck_atom_get ("FONT");
+    glyphset_atom = _wnck_atom_get ("GLYPHSET");
+    picture_atom = _wnck_atom_get ("PICTURE");
+    colormap_entry_atom = _wnck_atom_get ("COLORMAP ENTRY");
+    passive_grab_atom = _wnck_atom_get ("PASSIVE GRAB");
+    cursor_atom = _wnck_atom_get ("CURSOR");
+
+    i = 0;
+    while (i < n_types)
+      {
+        guint t = types[i].resource_type;
+
+        if (t == pixmap_atom)
+          usage->n_pixmaps += types[i].count;
+        else if (t == window_atom)
+          usage->n_windows += types[i].count;
+        else if (t == gc_atom)
+          usage->n_gcs += types[i].count;
+        else if (t == picture_atom)
+          usage->n_pictures += types[i].count;
+        else if (t == glyphset_atom)
+          usage->n_glyphsets += types[i].count;
+        else if (t == font_atom)
+          usage->n_fonts += types[i].count;
+        else if (t == colormap_entry_atom)
+          usage->n_colormap_entries += types[i].count;
+        else if (t == passive_grab_atom)
+          usage->n_passive_grabs += types[i].count;
+        else if (t == cursor_atom)
+          usage->n_cursors += types[i].count;
+        else
+          usage->n_other += types[i].count;
+
+        ++i;
+      }
+
+    XFree(types);
+
+    usage->total_bytes_estimate = usage->pixmap_bytes;
+
+    /* FIXME look in the X server source and come up with better
+     * answers here. Ideally we change XRes to return a number
+     * like this since it can do things like divide the cost of
+     * a shared resource among those sharing it.
+     */
+    usage->total_bytes_estimate += usage->n_windows * 24;
+    usage->total_bytes_estimate += usage->n_gcs * 24;
+    usage->total_bytes_estimate += usage->n_pictures * 24;
+    usage->total_bytes_estimate += usage->n_glyphsets * 24;
+    usage->total_bytes_estimate += usage->n_fonts * 1024;
+    usage->total_bytes_estimate += usage->n_colormap_entries * 24;
+    usage->total_bytes_estimate += usage->n_passive_grabs * 24;
+    usage->total_bytes_estimate += usage->n_cursors * 24;
+    usage->total_bytes_estimate += usage->n_other * 24;
+  }
+#else /* HAVE_XRES */
+  g_assert_not_reached ();
+#endif /* HAVE_XRES */
+}
+
+void
+_wnck_handle_read_resource_usage_pid (WnckHandle        *self,
+                                      GdkDisplay        *gdk_display,
+                                      gulong             pid,
+                                      WnckResourceUsage *usage)
+{
+  g_return_if_fail (usage != NULL);
+
+  memset (usage, '\0', sizeof (*usage));
+
+  if (wnck_init_resource_usage (gdk_display) == WNCK_EXT_MISSING)
+    return;
+
+#ifdef HAVE_XRES
+  if (!wnck_pid_read_resource_usage_from_cache (self, gdk_display, pid, usage))
+    /* the cache might not be built, might be outdated or might not contain
+     * data for a new X client, so try to fallback to something else */
+    wnck_pid_read_resource_usage_no_cache (self, gdk_display, pid, usage);
+#endif /* HAVE_XRES */
+}


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