From c5bd78a28513a5614ffc8411d202f2f8ee29295b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alberts=20Muktup=C4=81vels?= Date: Thu, 13 May 2021 23:20:12 +0300 Subject: util: move resource usage to its own file --- libwnck/meson.build | 2 + libwnck/util.c | 523 +------------------------------ libwnck/wnck-resource-usage-private.h | 37 +++ libwnck/wnck-resource-usage.c | 559 ++++++++++++++++++++++++++++++++++ 4 files changed, 602 insertions(+), 519 deletions(-) create mode 100644 libwnck/wnck-resource-usage-private.h create mode 100644 libwnck/wnck-resource-usage.c 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 #include -#ifdef HAVE_XRES -#include -#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 GdkDisplay. @@ -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 GdkDisplay. @@ -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 . + */ + +#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 . + */ + +#include "config.h" +#include "wnck-resource-usage-private.h" + +#include +#ifdef HAVE_XRES +#include +#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 +} -- cgit v1.2.1