summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlberts Muktupāvels <alberts.muktupavels@gmail.com>2021-05-13 23:20:12 +0300
committerMarco Trevisan <mail@3v1n0.net>2022-02-17 13:38:05 +0000
commitc5bd78a28513a5614ffc8411d202f2f8ee29295b (patch)
tree28222fcd3bb49725e452bb7361dff47aa7297cbf
parent5681d7d1ba277df1ac45f987457ec2775e6a5ba6 (diff)
downloadlibwnck-c5bd78a28513a5614ffc8411d202f2f8ee29295b.tar.gz
util: move resource usage to its own file
-rw-r--r--libwnck/meson.build2
-rw-r--r--libwnck/util.c523
-rw-r--r--libwnck/wnck-resource-usage-private.h37
-rw-r--r--libwnck/wnck-resource-usage.c559
4 files changed, 602 insertions, 519 deletions
diff --git a/libwnck/meson.build b/libwnck/meson.build
index aa60e83..9f714d0 100644
--- a/libwnck/meson.build
+++ b/libwnck/meson.build
@@ -63,6 +63,8 @@ sources = [
'wnck-icon-cache.c',
'wnck-image-menu-item-private.h',
'wnck-image-menu-item.c',
+ 'wnck-resource-usage-private.h',
+ 'wnck-resource-usage.c',
'workspace.c',
'xutils.c',
]
diff --git a/libwnck/util.c b/libwnck/util.c
index 3760bc3..fa06c34 100644
--- a/libwnck/util.c
+++ b/libwnck/util.c
@@ -25,11 +25,9 @@
#include "util.h"
#include "xutils.h"
#include "private.h"
+#include "wnck-resource-usage-private.h"
#include <gdk/gdkx.h>
#include <string.h>
-#ifdef HAVE_XRES
-#include <X11/extensions/XRes.h>
-#endif
/**
* SECTION:resource
@@ -60,81 +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
-_wnck_print_resource_usage (WnckResourceUsage *usage)
-{
- if (!usage)
- return;
-
- g_print ("\twindows : %d\n"
- "\tGCs : %d\n"
- "\tfonts : %d\n"
- "\tpixmaps : %d\n"
- "\tpictures : %d\n"
- "\tglyphsets : %d\n"
- "\tcolormaps : %d\n"
- "\tpassive grabs : %d\n"
- "\tcursors : %d\n"
- "\tunknowns : %d\n"
- "\tpixmap bytes : %ld\n"
- "\ttotal bytes : ~%ld\n",
- usage->n_windows,
- usage->n_gcs,
- usage->n_fonts,
- usage->n_pixmaps,
- usage->n_pictures,
- usage->n_glyphsets,
- usage->n_colormap_entries,
- usage->n_passive_grabs,
- usage->n_cursors,
- usage->n_other,
- usage->pixmap_bytes,
- usage->total_bytes_estimate);
-}
-#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 +78,9 @@ 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 */
+ _wnck_read_resource_usage_xid (gdisplay, xid, usage);
}
-#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);
-}
-
-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 +110,7 @@ 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_read_resource_usage_pid (gdisplay, pid, usage);
}
static WnckClientType client_type = 0;
@@ -835,12 +325,7 @@ wnck_shutdown (void)
{
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
+ _wnck_read_resources_shutdown_all ();
}
void
diff --git a/libwnck/wnck-resource-usage-private.h b/libwnck/wnck-resource-usage-private.h
new file mode 100644
index 0000000..7470e8a
--- /dev/null
+++ b/libwnck/wnck-resource-usage-private.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2021 Alberts Muktupāvels
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef WNCK_RESOURCE_USAGE_PRIVATE_H
+#define WNCK_RESOURCE_USAGE_PRIVATE_H
+
+#include "util.h"
+
+G_BEGIN_DECLS
+
+void _wnck_read_resource_usage_xid (GdkDisplay *gdisplay,
+ gulong xid,
+ WnckResourceUsage *usage);
+
+void _wnck_read_resource_usage_pid (GdkDisplay *gdisplay,
+ gulong pid,
+ WnckResourceUsage *usage);
+
+void _wnck_read_resources_shutdown_all (void);
+
+G_END_DECLS
+
+#endif
diff --git a/libwnck/wnck-resource-usage.c b/libwnck/wnck-resource-usage.c
new file mode 100644
index 0000000..b413c53
--- /dev/null
+++ b/libwnck/wnck-resource-usage.c
@@ -0,0 +1,559 @@
+/*
+ * Copyright (C) 2001 Havoc Pennington
+ * Copyright (C) 2006-2007 Vincent Untz
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include "wnck-resource-usage-private.h"
+
+#include <gdk/gdkx.h>
+#ifdef HAVE_XRES
+#include <X11/extensions/XRes.h>
+#endif
+
+#include "screen.h"
+#include "window.h"
+#include "xutils.h"
+
+typedef enum
+{
+ WNCK_EXT_UNKNOWN = 0,
+ WNCK_EXT_FOUND = 1,
+ WNCK_EXT_MISSING = 2
+} WnckExtStatus;
+
+#if 0
+/* useful for debugging */
+static void
+_wnck_print_resource_usage (WnckResourceUsage *usage)
+{
+ if (!usage)
+ return;
+
+ g_print ("\twindows : %d\n"
+ "\tGCs : %d\n"
+ "\tfonts : %d\n"
+ "\tpixmaps : %d\n"
+ "\tpictures : %d\n"
+ "\tglyphsets : %d\n"
+ "\tcolormaps : %d\n"
+ "\tpassive grabs : %d\n"
+ "\tcursors : %d\n"
+ "\tunknowns : %d\n"
+ "\tpixmap bytes : %ld\n"
+ "\ttotal bytes : ~%ld\n",
+ usage->n_windows,
+ usage->n_gcs,
+ usage->n_fonts,
+ usage->n_pixmaps,
+ usage->n_pictures,
+ usage->n_glyphsets,
+ usage->n_colormap_entries,
+ usage->n_passive_grabs,
+ usage->n_cursors,
+ usage->n_other,
+ usage->pixmap_bytes,
+ usage->total_bytes_estimate);
+}
+#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;
+}
+
+#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);
+}
+
+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 */
+
+void
+_wnck_read_resource_usage_xid (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 */
+}
+
+void
+_wnck_read_resource_usage_pid (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 */
+}
+
+void
+_wnck_read_resources_shutdown_all (void)
+{
+#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
+}