[libwnck/wip/muktupavels/wnck-handle: 4/8] screen: move screen handling to WnckHandle




commit 860a11efde60def01c189131cba211a97f56d788
Author: Alberts Muktupāvels <alberts muktupavels gmail com>
Date:   Mon Aug 19 21:41:48 2019 +0300

    screen: move screen handling to WnckHandle
    
    Deprecate old functions and update libwnck to use new functions.

 libwnck/pager.c               |  26 +-
 libwnck/pager.h               |   3 +
 libwnck/private.h             |  10 +-
 libwnck/screen.c              | 112 +------
 libwnck/screen.h              |   5 +
 libwnck/selector.c            |  26 +-
 libwnck/selector.h            |  10 +-
 libwnck/tasklist.c            |  50 ++-
 libwnck/tasklist.h            |   3 +
 libwnck/util.c                | 548 +------------------------------
 libwnck/util.h                |   2 +
 libwnck/window.c              |   5 +-
 libwnck/wnck-handle-private.h |   9 +-
 libwnck/wnck-handle.c         | 738 +++++++++++++++++++++++++++++++++++++++++-
 libwnck/wnck-handle.h         |  29 +-
 15 files changed, 898 insertions(+), 678 deletions(-)
---
diff --git a/libwnck/pager.c b/libwnck/pager.c
index c769d62..dad5307 100644
--- a/libwnck/pager.c
+++ b/libwnck/pager.c
@@ -61,6 +61,8 @@
 
 struct _WnckPagerPrivate
 {
+  WnckHandle *handle;
+
   WnckScreen *screen;
 
   int n_rows; /* really columns for vertical orientation */
@@ -296,7 +298,8 @@ _wnck_pager_set_screen (WnckPager *pager)
     return;
 
   gdkscreen = gtk_widget_get_screen (GTK_WIDGET (pager));
-  pager->priv->screen = wnck_screen_get (gdk_x11_screen_get_screen_number (gdkscreen));
+  pager->priv->screen = wnck_handle_get_screen (pager->priv->handle,
+                                                gdk_x11_screen_get_screen_number (gdkscreen));
 
   if (!wnck_pager_set_layout_hint (pager))
     {
@@ -2194,6 +2197,27 @@ wnck_pager_new (void)
   WnckPager *pager;
 
   pager = g_object_new (WNCK_TYPE_PAGER, NULL);
+  pager->priv->handle = _wnck_get_handle ();
+
+  return GTK_WIDGET (pager);
+}
+
+/**
+ * wnck_pager_new_with_handle:
+ * @handle: a #WnckHandle
+ *
+ * Creates a new #WnckPager. The #WnckPager will show the #WnckWorkspace of the
+ * #WnckScreen it is on.
+ *
+ * Returns: a newly created #WnckPager.
+ */
+GtkWidget *
+wnck_pager_new_with_handle (WnckHandle *handle)
+{
+  WnckPager *pager;
+
+  pager = g_object_new (WNCK_TYPE_PAGER, NULL);
+  pager->priv->handle = handle;
 
   return GTK_WIDGET (pager);
 }
diff --git a/libwnck/pager.h b/libwnck/pager.h
index c67f2fa..084aeb4 100644
--- a/libwnck/pager.h
+++ b/libwnck/pager.h
@@ -102,8 +102,11 @@ typedef enum {
 
 GType      wnck_pager_get_type           (void) G_GNUC_CONST;
 
+G_DEPRECATED_FOR(wnck_pager_new_with_handle)
 GtkWidget* wnck_pager_new                (void);
 
+GtkWidget* wnck_pager_new_with_handle    (WnckHandle           *handle);
+
 gboolean   wnck_pager_set_orientation    (WnckPager            *pager,
                                           GtkOrientation        orientation);
 gboolean   wnck_pager_set_n_rows         (WnckPager            *pager,
diff --git a/libwnck/private.h b/libwnck/private.h
index 8404d8f..887455b 100644
--- a/libwnck/private.h
+++ b/libwnck/private.h
@@ -108,8 +108,6 @@ void _wnck_screen_change_workspace_name (WnckScreen *screen,
                                          int         number,
                                          const char *name);
 
-void _wnck_screen_shutdown_all          (void);
-
 gboolean _wnck_workspace_set_geometry (WnckWorkspace *space, int w, int h);
 gboolean _wnck_workspace_set_viewport (WnckWorkspace *space, int x, int y);
 
@@ -118,6 +116,12 @@ Display *_wnck_get_default_display (void);
 
 #define WNCK_SCREEN_XSCREEN(screen) (_wnck_screen_get_xscreen (screen))
 
+void       _wnck_screen_construct      (WnckScreen *screen,
+                                        WnckHandle *handle,
+                                        Display    *display,
+                                        int         number);
+
+Window     _wnck_screen_get_xroot      (WnckScreen *screen);
 Screen    *_wnck_screen_get_xscreen    (WnckScreen *screen);
 GdkScreen *_wnck_screen_get_gdk_screen (WnckScreen *screen);
 
@@ -125,8 +129,6 @@ GdkScreen *_wnck_screen_get_gdk_screen (WnckScreen *screen);
 SnDisplay* _wnck_screen_get_sn_display (WnckScreen *screen);
 #endif
 
-WnckScreen* _wnck_screen_get_existing (int number);
-
 void           _wnck_pager_activate_workspace   (WnckWorkspace *wspace,
                                                  guint32        timestamp);
 int            _wnck_pager_get_n_workspaces     (WnckPager     *pager);
diff --git a/libwnck/screen.c b/libwnck/screen.c
index e6364eb..f12e065 100644
--- a/libwnck/screen.c
+++ b/libwnck/screen.c
@@ -65,8 +65,6 @@
 #define _NET_WM_BOTTOMRIGHT 2
 #define _NET_WM_BOTTOMLEFT  3
 
-static WnckScreen** screens = NULL;
-
 struct _WnckScreenPrivate
 {
   WnckHandle *handle;
@@ -500,8 +498,6 @@ wnck_screen_finalize (GObject *object)
   g_free (screen->priv->wm_name);
   screen->priv->wm_name = NULL;
 
-  screens[screen->priv->number] = NULL;
-
 #ifdef HAVE_STARTUP_NOTIFICATION
   sn_display_unref (screen->priv->sn_display);
   screen->priv->sn_display = NULL;
@@ -526,12 +522,13 @@ sn_error_trap_pop (SnDisplay *display,
 }
 #endif /* HAVE_STARTUP_NOTIFICATION */
 
-static void
-wnck_screen_construct (Display    *display,
-                       WnckScreen *screen,
-                       int         number)
+void
+_wnck_screen_construct (WnckScreen *screen,
+                        WnckHandle *handle,
+                        Display    *display,
+                        int         number)
 {
-  screen->priv->handle = _wnck_get_handle ();
+  screen->priv->handle = handle;
 
   /* Create the initial state of the screen. */
   screen->priv->xroot = RootWindow (display, number);
@@ -578,44 +575,7 @@ wnck_screen_construct (Display    *display,
 WnckScreen*
 wnck_screen_get (int index)
 {
-  Display *display;
-
-  display = _wnck_get_default_display ();
-
-  g_return_val_if_fail (display != NULL, NULL);
-
-  if (index >= ScreenCount (display))
-    return NULL;
-
-  if (screens == NULL)
-    {
-      screens = g_new0 (WnckScreen*, ScreenCount (display));
-    }
-
-  if (screens[index] == NULL)
-    {
-      screens[index] = g_object_new (WNCK_TYPE_SCREEN, NULL);
-
-      wnck_screen_construct (display, screens[index], index);
-    }
-
-  return screens[index];
-}
-
-WnckScreen*
-_wnck_screen_get_existing (int number)
-{
-  Display *display;
-
-  display = _wnck_get_default_display ();
-
-  g_return_val_if_fail (display != NULL, NULL);
-  g_return_val_if_fail (number < ScreenCount (display), NULL);
-
-  if (screens != NULL)
-    return screens[number];
-  else
-    return NULL;
+  return wnck_handle_get_screen (_wnck_get_handle (), index);
 }
 
 /**
@@ -630,15 +590,7 @@ _wnck_screen_get_existing (int number)
 WnckScreen*
 wnck_screen_get_default (void)
 {
-  int default_screen;
-  Display *default_display = _wnck_get_default_display ();
-
-  if (default_display == NULL)
-    return NULL;
-
-  default_screen = DefaultScreen (default_display);
-
-  return wnck_screen_get (default_screen);
+  return wnck_handle_get_default_screen (_wnck_get_handle ());
 }
 
 /**
@@ -658,24 +610,7 @@ wnck_screen_get_default (void)
 WnckScreen*
 wnck_screen_get_for_root (gulong root_window_id)
 {
-  int i;
-  Display *display;
-
-  if (screens == NULL)
-    return NULL;
-
-  i = 0;
-  display = _wnck_get_default_display ();
-
-  while (i < ScreenCount (display))
-    {
-      if (screens[i] != NULL && screens[i]->priv->xroot == root_window_id)
-        return screens[i];
-
-      ++i;
-    }
-
-  return NULL;
+  return wnck_handle_get_screen_for_root (_wnck_get_handle (), root_window_id);
 }
 
 /**
@@ -2502,6 +2437,12 @@ wnck_screen_get_height (WnckScreen *screen)
   return HeightOfScreen (screen->priv->xscreen);
 }
 
+Window
+_wnck_screen_get_xroot (WnckScreen *screen)
+{
+  return screen->priv->xroot;
+}
+
 Screen *
 _wnck_screen_get_xscreen (WnckScreen *screen)
 {
@@ -2732,26 +2673,3 @@ _wnck_screen_change_workspace_name (WnckScreen *screen,
 
   g_free (names);
 }
-
-void
-_wnck_screen_shutdown_all (void)
-{
-  int i;
-  Display *display;
-
-  if (screens == NULL)
-    return;
-
-  display = _wnck_get_default_display ();
-
-  for (i = 0; i < ScreenCount (display); ++i)
-    {
-      if (screens[i] != NULL) {
-        g_object_unref (screens[i]);
-        screens[i] = NULL;
-      }
-    }
-
-  g_free (screens);
-  screens = NULL;
-}
diff --git a/libwnck/screen.h b/libwnck/screen.h
index 153cf5f..9fca17b 100644
--- a/libwnck/screen.h
+++ b/libwnck/screen.h
@@ -194,8 +194,13 @@ typedef enum
 
 GType wnck_screen_get_type (void) G_GNUC_CONST;
 
+G_DEPRECATED_FOR(wnck_handle_get_default_screen)
 WnckScreen*    wnck_screen_get_default              (void);
+
+G_DEPRECATED_FOR(wnck_handle_get_screen)
 WnckScreen*    wnck_screen_get                      (int         index);
+
+G_DEPRECATED_FOR(wnck_handle_get_screen_for_root)
 WnckScreen*    wnck_screen_get_for_root             (gulong      root_window_id);
 
 WnckHandle*    wnck_screen_get_handle               (WnckScreen *screen);
diff --git a/libwnck/selector.c b/libwnck/selector.c
index b975b27..ea686d1 100644
--- a/libwnck/selector.c
+++ b/libwnck/selector.c
@@ -52,6 +52,8 @@
  */
 
 struct _WnckSelectorPrivate {
+  WnckHandle *handle;
+
   GtkWidget  *image;
   WnckWindow *icon_window;
 
@@ -118,7 +120,8 @@ wnck_selector_get_screen (WnckSelector *selector)
 
   screen = gtk_widget_get_screen (GTK_WIDGET (selector));
 
-  return wnck_screen_get (gdk_x11_screen_get_screen_number (screen));
+  return wnck_handle_get_screen (selector->priv->handle,
+                                 gdk_x11_screen_get_screen_number (screen));
 }
 
 static GdkPixbuf *
@@ -1273,6 +1276,27 @@ wnck_selector_new (void)
   WnckSelector *selector;
 
   selector = g_object_new (WNCK_TYPE_SELECTOR, NULL);
+  selector->priv->handle = _wnck_get_handle ();
+
+  return GTK_WIDGET (selector);
+}
+
+/**
+ * wnck_selector_new_with_handle:
+ * @handle: a #WnckHandle
+ *
+ * Creates a new #WnckSelector. The #WnckSelector will list #WnckWindow of the
+ * #WnckScreen it is on.
+ *
+ * Returns: a newly created #WnckSelector.
+ */
+GtkWidget *
+wnck_selector_new_with_handle (WnckHandle *handle)
+{
+  WnckSelector *selector;
+
+  selector = g_object_new (WNCK_TYPE_SELECTOR, NULL);
+  selector->priv->handle = handle;
 
   return GTK_WIDGET (selector);
 }
diff --git a/libwnck/selector.h b/libwnck/selector.h
index 4783162..c793f42 100644
--- a/libwnck/selector.h
+++ b/libwnck/selector.h
@@ -27,6 +27,7 @@
 #define WNCK_SELECTOR_H
 
 #include <gtk/gtk.h>
+#include <libwnck/wnck-handle.h>
 
 G_BEGIN_DECLS
 #define WNCK_TYPE_SELECTOR              (wnck_selector_get_type ())
@@ -62,8 +63,13 @@ struct _WnckSelectorClass
   void (* pad4) (void);
 };
 
-GtkWidget *wnck_selector_new      (void);
-GType      wnck_selector_get_type (void) G_GNUC_CONST;
+
+G_DEPRECATED_FOR(wnck_selector_new_with_handle)
+GtkWidget *wnck_selector_new             (void);
+
+GtkWidget *wnck_selector_new_with_handle (WnckHandle *handle);
+
+GType      wnck_selector_get_type        (void) G_GNUC_CONST;
 
 G_END_DECLS
 
diff --git a/libwnck/tasklist.c b/libwnck/tasklist.c
index 4166d76..d298597 100644
--- a/libwnck/tasklist.c
+++ b/libwnck/tasklist.c
@@ -189,6 +189,8 @@ typedef struct _skipped_window
 
 struct _WnckTasklistPrivate
 {
+  WnckHandle *handle;
+
   WnckScreen *screen;
 
   WnckTask *active_task; /* NULL if active window not in tasklist */
@@ -1816,12 +1818,9 @@ get_preferred_size (WnckTasklist   *self,
 
   if (orientation == GTK_ORIENTATION_HORIZONTAL)
     {
-      WnckHandle *handle;
       int min_button_width;
 
-      handle = wnck_screen_get_handle (self->priv->screen);
-
-      get_minimum_button_size (handle, &min_button_width, NULL);
+      get_minimum_button_size (self->priv->handle, &min_button_width, NULL);
 
       if (self->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
         {
@@ -2160,7 +2159,8 @@ wnck_tasklist_realize (GtkWidget *widget)
   tasklist = WNCK_TASKLIST (widget);
 
   gdkscreen = gtk_widget_get_screen (widget);
-  tasklist->priv->screen = wnck_screen_get (gdk_x11_screen_get_screen_number (gdkscreen));
+  tasklist->priv->screen = wnck_handle_get_screen (tasklist->priv->handle,
+                                                   gdk_x11_screen_get_screen_number (gdkscreen));
   g_assert (tasklist->priv->screen != NULL);
 
 #ifdef HAVE_STARTUP_NOTIFICATION
@@ -2555,10 +2555,31 @@ wnck_tasklist_new (void)
   WnckTasklist *tasklist;
 
   tasklist = g_object_new (WNCK_TYPE_TASKLIST, NULL);
+  tasklist->priv->handle = _wnck_get_handle ();
 
   return GTK_WIDGET (tasklist);
 }
 
+/**
+ * wnck_tasklist_new_with_handle:
+ * @handle: a #WnckHandle
+ *
+ * Creates a new #WnckTasklist. The #WnckTasklist will list #WnckWindow of the
+ * #WnckScreen it is on.
+ *
+ * Returns: a newly created #WnckTasklist.
+ */
+GtkWidget *
+wnck_tasklist_new_with_handle (WnckHandle *handle)
+{
+  WnckTasklist *self;
+
+  self = g_object_new (WNCK_TYPE_TASKLIST, NULL);
+  self->priv->handle = handle;
+
+  return GTK_WIDGET (self);
+}
+
 static void
 wnck_tasklist_free_tasks (WnckTasklist *tasklist)
 {
@@ -3683,17 +3704,15 @@ wnck_task_scale_icon (WnckHandle *handle,
 static GdkPixbuf *
 wnck_task_get_icon (WnckTask *task)
 {
-  WnckHandle *handle;
   WnckWindowState state;
   GdkPixbuf *pixbuf;
 
-  handle = wnck_screen_get_handle (task->tasklist->priv->screen);
   pixbuf = NULL;
 
   switch (task->type)
     {
     case WNCK_TASK_CLASS_GROUP:
-      pixbuf = wnck_task_scale_icon (handle,
+      pixbuf = wnck_task_scale_icon (task->tasklist->priv->handle,
                                      wnck_class_group_get_mini_icon (task->class_group),
                                      FALSE);
       break;
@@ -3701,7 +3720,7 @@ wnck_task_get_icon (WnckTask *task)
     case WNCK_TASK_WINDOW:
       state = wnck_window_get_state (task->window);
 
-      pixbuf =  wnck_task_scale_icon (handle,
+      pixbuf =  wnck_task_scale_icon (task->tasklist->priv->handle,
                                       wnck_window_get_mini_icon (task->window),
                                       state & WNCK_WINDOW_STATE_MINIMIZED);
       break;
@@ -3718,7 +3737,7 @@ wnck_task_get_icon (WnckTask *task)
               gsize mini_icon_size;
               GdkPixbuf *loaded;
 
-              mini_icon_size = _wnck_handle_get_default_mini_icon_size (handle);
+              mini_icon_size = _wnck_handle_get_default_mini_icon_size (task->tasklist->priv->handle);
 
               loaded =  (* task->tasklist->priv->icon_loader) (icon,
                                                                mini_icon_size,
@@ -3727,7 +3746,7 @@ wnck_task_get_icon (WnckTask *task)
 
               if (loaded != NULL)
                 {
-                  pixbuf = wnck_task_scale_icon (handle, loaded, FALSE);
+                  pixbuf = wnck_task_scale_icon (task->tasklist->priv->handle, loaded, FALSE);
                   g_object_unref (G_OBJECT (loaded));
                 }
             }
@@ -3737,7 +3756,7 @@ wnck_task_get_icon (WnckTask *task)
         {
           gsize mini_icon_size;
 
-          mini_icon_size = _wnck_handle_get_default_mini_icon_size (handle);
+          mini_icon_size = _wnck_handle_get_default_mini_icon_size (task->tasklist->priv->handle);
 
           _wnck_get_fallback_icons (NULL, 0, &pixbuf, mini_icon_size);
         }
@@ -3933,6 +3952,7 @@ static gboolean
 wnck_task_motion_timeout (gpointer data)
 {
   WnckWorkspace *ws;
+  WnckScreen *screen;
   WnckTask *task = WNCK_TASK (data);
 
   task->button_activate = 0;
@@ -3942,7 +3962,9 @@ wnck_task_motion_timeout (gpointer data)
    * There should only be *one* activate call.
    */
   ws = wnck_window_get_workspace (task->window);
-  if (ws && ws != wnck_screen_get_active_workspace (wnck_screen_get_default ()))
+  screen = wnck_handle_get_default_screen (task->tasklist->priv->handle);
+
+  if (ws && ws != wnck_screen_get_active_workspace (screen))
   {
     wnck_workspace_activate (ws, task->dnd_timestamp);
   }
@@ -4281,7 +4303,7 @@ wnck_task_create_widgets (WnckTask *task, GtkReliefStyle relief)
 
   task->button = wnck_button_new ();
   wnck_button_set_handle (WNCK_BUTTON (task->button),
-                          wnck_screen_get_handle (task->tasklist->priv->screen));
+                          task->tasklist->priv->handle);
 
   gtk_button_set_relief (GTK_BUTTON (task->button), relief);
 
diff --git a/libwnck/tasklist.h b/libwnck/tasklist.h
index 0659f9d..5407d34 100644
--- a/libwnck/tasklist.h
+++ b/libwnck/tasklist.h
@@ -87,8 +87,11 @@ typedef enum {
 
 GType wnck_tasklist_get_type (void) G_GNUC_CONST;
 
+G_DEPRECATED_FOR(wnck_tasklist_new_with_handle)
 GtkWidget *wnck_tasklist_new (void);
 
+GtkWidget *wnck_tasklist_new_with_handle (WnckHandle *handle);
+
 G_DEPRECATED
 const int *wnck_tasklist_get_size_hint_list (WnckTasklist  *tasklist,
                                              int           *n_elements);
diff --git a/libwnck/util.c b/libwnck/util.c
index 51e75ba..cd742f4 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,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>.
@@ -151,434 +74,13 @@ wnck_init_resource_usage (GdkDisplay *gdisplay)
  * Since: 2.6
  */
 void
-wnck_xid_read_resource_usage (GdkDisplay        *gdisplay,
+wnck_xid_read_resource_usage (GdkDisplay        *gdk_display,
                               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);
-}
-
-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;
+  wnck_handle_read_resource_usage_xid (_wnck_get_handle (), gdk_display, xid, usage);
 }
 
-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>.
@@ -604,23 +106,11 @@ wnck_pid_read_resource_usage_no_cache (GdkDisplay        *gdisplay,
  * Since: 2.6
  */
 void
-wnck_pid_read_resource_usage (GdkDisplay        *gdisplay,
+wnck_pid_read_resource_usage (GdkDisplay        *gdk_display,
                               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 (), gdk_display, pid, usage);
 }
 
 static WnckClientType client_type = 0;
@@ -708,7 +198,8 @@ wnck_set_default_mini_icon_size (gsize size)
   wnck_handle_set_default_mini_icon_size (_wnck_get_handle (), size);
 
   default_screen = DefaultScreen (_wnck_get_default_display ());
-  screen = _wnck_screen_get_existing (default_screen);
+  screen = _wnck_handle_get_existing_screen (_wnck_get_handle (),
+                                             default_screen);
 
   if (WNCK_IS_SCREEN (screen))
     {
@@ -815,32 +306,7 @@ _wnck_get_default_display (void)
 void
 wnck_shutdown (void)
 {
-  /* Warning: this is hacky :-)
-   *
-   * Shutting down all WnckScreen objects will automatically unreference (and
-   * finalize) all WnckWindow objects, but not the WnckClassGroup and
-   * WnckApplication objects.
-   * Therefore we need to manually shut down all WnckClassGroup and
-   * WnckApplication objects first, since they reference the WnckScreen they're
-   * on.
-   * On the other side, shutting down the WnckScreen objects will results in
-   * all WnckWindow objects getting unreferenced and finalized, and must
-   * actually be done before shutting down global WnckWindow structures
-   * (because the WnckScreen has a list of WnckWindow that will get mis-used
-   * otherwise). */
-  _wnck_class_group_shutdown_all ();
-  _wnck_application_shutdown_all ();
-  _wnck_screen_shutdown_all ();
-  _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/util.h b/libwnck/util.h
index 91a4e08..5e70127 100644
--- a/libwnck/util.h
+++ b/libwnck/util.h
@@ -114,10 +114,12 @@ void wnck_set_default_mini_icon_size (gsize size);
 
 void wnck_shutdown        (void);
 
+G_DEPRECATED_FOR(wnck_handle_read_resource_usage_xid)
 void wnck_xid_read_resource_usage (GdkDisplay        *gdk_display,
                                    gulong             xid,
                                    WnckResourceUsage *usage);
 
+G_DEPRECATED_FOR(wnck_handle_read_resource_usage_pid)
 void wnck_pid_read_resource_usage (GdkDisplay        *gdk_display,
                                    gulong             pid,
                                    WnckResourceUsage *usage);
diff --git a/libwnck/window.c b/libwnck/window.c
index 294f556..3098339 100644
--- a/libwnck/window.c
+++ b/libwnck/window.c
@@ -3105,9 +3105,12 @@ update_transient_for (WnckWindow *window)
                         &parent) &&
       parent != window->priv->xwindow)
     {
+      WnckHandle *handle;
+
       window->priv->transient_for = parent;
 
-      if (wnck_screen_get_for_root (window->priv->transient_for) != NULL)
+      handle = wnck_screen_get_handle (window->priv->screen);
+      if (wnck_handle_get_screen_for_root (handle, window->priv->transient_for) != NULL)
         window->priv->transient_for_root = TRUE;
       else
         window->priv->transient_for_root = FALSE;
diff --git a/libwnck/wnck-handle-private.h b/libwnck/wnck-handle-private.h
index f77b4ba..c791560 100644
--- a/libwnck/wnck-handle-private.h
+++ b/libwnck/wnck-handle-private.h
@@ -23,11 +23,14 @@
 
 G_BEGIN_DECLS
 
-WnckClientType  _wnck_handle_get_client_type            (WnckHandle     *self);
+WnckClientType  _wnck_handle_get_client_type            (WnckHandle *self);
 
-gsize           _wnck_handle_get_default_icon_size      (WnckHandle     *self);
+WnckScreen     *_wnck_handle_get_existing_screen        (WnckHandle *self,
+                                                         int         number);
 
-gsize           _wnck_handle_get_default_mini_icon_size (WnckHandle     *self);
+gsize           _wnck_handle_get_default_icon_size      (WnckHandle *self);
+
+gsize           _wnck_handle_get_default_mini_icon_size (WnckHandle *self);
 
 G_END_DECLS
 
diff --git a/libwnck/wnck-handle.c b/libwnck/wnck-handle.c
index 69438ae..9a7f565 100644
--- a/libwnck/wnck-handle.c
+++ b/libwnck/wnck-handle.c
@@ -1,5 +1,7 @@
 /*
  * Copyright (C) 2001 Havoc Pennington
+ * Copyright (C) 2003 Kim Woelders
+ * Copyright (C) 2003 Red Hat, Inc.
  * Copyright (C) 2005-2007 Vincent Untz
  * Copyright (C) 2021 Alberts Muktupāvels
  *
@@ -27,20 +29,53 @@
 #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"
 #include "wnck-enum-types.h"
 #include "xutils.h"
 
+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;
+
+  WnckScreen       **screens;
 
-  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
@@ -56,24 +91,373 @@ 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_handle_get_screen (self, 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,
              gpointer   data)
 {
+  WnckHandle *self;
   XEvent *xevent = gdkxevent;
 #ifdef HAVE_STARTUP_NOTIFICATION
   int i;
   Display *display;
 #endif /* HAVE_STARTUP_NOTIFICATION */
 
+  self = WNCK_HANDLE (data);
+
   switch (xevent->type)
     {
     case PropertyNotify:
       {
         WnckScreen *screen;
 
-        screen = wnck_screen_get_for_root (xevent->xany.window);
+        screen = wnck_handle_get_screen_for_root (self, xevent->xany.window);
+
         if (screen != NULL)
           {
             _wnck_screen_process_property_notify (screen, xevent);
@@ -122,11 +506,12 @@ filter_func (GdkXEvent *gdkxevent,
 
       while (i < ScreenCount (display))
         {
-          WnckScreen *s;
+          WnckScreen *screen;
 
-          s = _wnck_screen_get_existing (i);
-          if (s != NULL)
-            sn_display_process_event (_wnck_screen_get_sn_display (s),
+          screen = _wnck_handle_get_existing_screen (self, i);
+
+          if (screen != NULL)
+            sn_display_process_event (_wnck_screen_get_sn_display (screen),
                                       xevent);
 
           ++i;
@@ -150,6 +535,44 @@ wnck_handle_finalize (GObject *object)
 
   gdk_window_remove_filter (NULL, filter_func, self);
 
+  /* Warning: this is hacky :-)
+   *
+   * Shutting down all WnckScreen objects will automatically unreference (and
+   * finalize) all WnckWindow objects, but not the WnckClassGroup and
+   * WnckApplication objects.
+   * Therefore we need to manually shut down all WnckClassGroup and
+   * WnckApplication objects first, since they reference the WnckScreen they're
+   * on.
+   * On the other side, shutting down the WnckScreen objects will results in
+   * all WnckWindow objects getting unreferenced and finalized, and must
+   * actually be done before shutting down global WnckWindow structures
+   * (because the WnckScreen has a list of WnckWindow that will get mis-used
+   * otherwise). */
+  _wnck_class_group_shutdown_all ();
+  _wnck_application_shutdown_all ();
+  _wnck_window_shutdown_all ();
+
+  if (self->screens != NULL)
+    {
+      Display *display;
+      int i;
+
+      for (i = 0; i < ScreenCount (display); ++i)
+        g_clear_object (&self->screens[i]);
+
+      g_clear_pointer (&self->screens, g_free);
+    }
+
+#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);
 }
 
@@ -234,6 +657,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);
 }
 
@@ -259,6 +684,130 @@ _wnck_handle_get_client_type (WnckHandle *self)
   return self->client_type;
 }
 
+/**
+ * wnck_handle_get_default_screen:
+ * @self: a #WnckHandle
+ *
+ * Gets the default #WnckScreen on the default display.
+ *
+ * Returns: (transfer none) (nullable): the default #WnckScreen. The
+ * returned #WnckScreen is owned by #WnckHandle and must not be referenced
+ * or unreferenced. This can return %NULL if not on X11.
+ */
+WnckScreen *
+wnck_handle_get_default_screen (WnckHandle *self)
+{
+  Display *display;
+
+  g_return_val_if_fail (WNCK_IS_HANDLE (self), NULL);
+
+  display = _wnck_get_default_display ();
+  if (display == NULL)
+    return NULL;
+
+  return wnck_handle_get_screen (self, DefaultScreen (display));
+}
+
+/**
+ * wnck_handle_get_screen:
+ * @self: a #WnckHandle
+ * @index: screen number, starting from 0.
+ *
+ * Gets the #WnckScreen for a given screen on the default display.
+ *
+ * Returns: (transfer none): the #WnckScreen for screen @index, or %NULL
+ * if no such screen exists. The returned #WnckScreen is owned by #WnckHandle
+ * and must not be referenced or unreferenced.
+ */
+WnckScreen *
+wnck_handle_get_screen (WnckHandle *self,
+                        int         index)
+{
+  Display *display;
+
+  display = _wnck_get_default_display ();
+
+  g_return_val_if_fail (WNCK_IS_HANDLE (self), NULL);
+  g_return_val_if_fail (display != NULL, NULL);
+
+  if (index >= ScreenCount (display))
+    return NULL;
+
+  if (self->screens == NULL)
+    self->screens = g_new0 (WnckScreen*, ScreenCount (display));
+
+  if (self->screens[index] == NULL)
+    {
+      self->screens[index] = g_object_new (WNCK_TYPE_SCREEN, NULL);
+
+      _wnck_screen_construct (self->screens[index], self, display, index);
+    }
+
+  return self->screens[index];
+}
+
+/**
+ * wnck_handle_get_screen_for_root:
+ * @self: a #WnckHandle
+ * @root_window_id: an X window ID.
+ *
+ * Gets the #WnckScreen for the root window at @root_window_id, or
+ * %NULL if no #WnckScreen exists for this root window.
+ *
+ * This function does not work if wnck_handle_get_screen() was not called
+ * for the sought #WnckScreen before, and returns %NULL.
+ *
+ * Returns: (transfer none): the #WnckScreen for the root window at
+ * @root_window_id, or %NULL. The returned #WnckScreen is owned by
+ * #WnckHandle and must not be referenced or unreferenced.
+ */
+WnckScreen *
+wnck_handle_get_screen_for_root (WnckHandle *self,
+                                 gulong      root_window_id)
+{
+  Display *display;
+  int i;
+
+  g_return_val_if_fail (WNCK_IS_HANDLE (self), NULL);
+
+  if (self->screens == NULL)
+    return NULL;
+
+  display = _wnck_get_default_display ();
+  i = 0;
+
+  while (i < ScreenCount (display))
+    {
+      WnckScreen *screen;
+
+      screen = self->screens[i];
+      if (screen != NULL && _wnck_screen_get_xroot (screen) == root_window_id)
+        return screen;
+
+      ++i;
+    }
+
+  return NULL;
+}
+
+WnckScreen *
+_wnck_handle_get_existing_screen (WnckHandle *self,
+                                  int         number)
+{
+  Display *display;
+
+  display = _wnck_get_default_display ();
+
+  g_return_val_if_fail (WNCK_IS_HANDLE (self), NULL);
+  g_return_val_if_fail (display != NULL, NULL);
+  g_return_val_if_fail (number < ScreenCount (display), NULL);
+
+  if (self->screens != NULL)
+    return self->screens[number];
+
+  return NULL;
+}
+
 /**
  * wnck_handle_set_default_icon_size:
  * @self: a #WnckHandle
@@ -308,3 +857,174 @@ _wnck_handle_get_default_mini_icon_size (WnckHandle *self)
 
   return self->default_mini_icon_size;
 }
+
+/**
+ * wnck_handle_read_resource_usage_xid:
+ * @self: a #WnckHandle
+ * @gdk_display: a <classname>GdkDisplay</classname>.
+ * @xid: an X window ID.
+ * @usage: return location for the X resource usage of the application owning
+ * the X window ID @xid.
+ *
+ * Looks for the X resource usage of the application owning the X window ID
+ * @xid on display @gdk_display. If no resource usage can be found, then all
+ * fields of @usage are set to 0.
+ *
+ * To properly work, this function requires the XRes extension on the X server.
+ */
+void
+wnck_handle_read_resource_usage_xid (WnckHandle        *self,
+                                     GdkDisplay        *gdk_display,
+                                     gulong             xid,
+                                     WnckResourceUsage *usage)
+{
+  g_return_if_fail (WNCK_IS_HANDLE (self));
+  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 */
+}
+
+/**
+ * wnck_handle_read_resource_usage_pid:
+ * @self: a #WnckHandle
+ * @gdk_display: a <classname>GdkDisplay</classname>.
+ * @pid: a process ID.
+ * @usage: return location for the X resource usage of the application with
+ * process ID @pid.
+ *
+ * Looks for the X resource usage of the application with process ID @pid on
+ * display @gdk_display. If no resource usage can be found, then all fields of
+ * @usage are set to 0.
+ *
+ * In order to find the resource usage of an application that does not have an
+ * X window visible to libwnck (panel applets do not have any toplevel windows,
+ * for example), wnck_pid_read_resource_usage() walks through the whole tree of
+ * X windows. Since this walk is expensive in CPU, a cache is created. This
+ * cache is updated in the background. This means there is a non-null
+ * probability that no resource usage will be found for an application, even if
+ * it is an X client. If this happens, calling wnck_pid_read_resource_usage()
+ * again after a few seconds should work.
+ *
+ * To properly work, this function requires the XRes extension on the X server.
+ */
+void
+wnck_handle_read_resource_usage_pid (WnckHandle        *self,
+                                     GdkDisplay        *gdk_display,
+                                     gulong             pid,
+                                     WnckResourceUsage *usage)
+{
+  g_return_if_fail (WNCK_IS_HANDLE (self));
+  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 */
+}
diff --git a/libwnck/wnck-handle.h b/libwnck/wnck-handle.h
index 1f7aae1..0c76ab1 100644
--- a/libwnck/wnck-handle.h
+++ b/libwnck/wnck-handle.h
@@ -23,6 +23,7 @@
 #define WNCK_HANDLE_H
 
 #include <glib-object.h>
+#include <libwnck/screen.h>
 #include <libwnck/util.h>
 
 G_BEGIN_DECLS
@@ -30,13 +31,31 @@ G_BEGIN_DECLS
 #define WNCK_TYPE_HANDLE (wnck_handle_get_type ())
 G_DECLARE_FINAL_TYPE (WnckHandle, wnck_handle, WNCK, HANDLE, GObject)
 
-WnckHandle *wnck_handle_new                        (WnckClientType  client_type);
+WnckHandle *wnck_handle_new                        (WnckClientType     client_type);
 
-void        wnck_handle_set_default_icon_size      (WnckHandle     *self,
-                                                    gsize           icon_size);
+WnckScreen *wnck_handle_get_default_screen         (WnckHandle        *self);
 
-void        wnck_handle_set_default_mini_icon_size (WnckHandle     *self,
-                                                    gsize           icon_size);
+WnckScreen *wnck_handle_get_screen                 (WnckHandle        *self,
+                                                    int                index);
+
+WnckScreen *wnck_handle_get_screen_for_root        (WnckHandle        *self,
+                                                    gulong             root_window_id);
+
+void        wnck_handle_set_default_icon_size      (WnckHandle        *self,
+                                                    gsize              icon_size);
+
+void        wnck_handle_set_default_mini_icon_size (WnckHandle        *self,
+                                                    gsize              icon_size);
+
+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
 


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