[gnome-flashback] desktop: add GfWorkareaWatcher



commit 6d744c6142d6ecf8b5f79acf76566bdaae905e09
Author: Alberts Muktupāvels <alberts muktupavels gmail com>
Date:   Wed Jan 22 15:46:52 2020 +0200

    desktop: add GfWorkareaWatcher
    
    GTK does not track workarea changes. Add new object that will emit
    changed signal when WM changes workarea property.
    
    This also adds support for new unmerged _NET_WORKAREAS_Dn property:
    https://gitlab.freedesktop.org/xdg/xdg-specs/merge_requests/22

 gnome-flashback/libdesktop/Makefile.am           |   2 +
 gnome-flashback/libdesktop/gf-icon-view.c        |  66 +----
 gnome-flashback/libdesktop/gf-workarea-watcher.c | 319 +++++++++++++++++++++++
 gnome-flashback/libdesktop/gf-workarea-watcher.h |  33 +++
 4 files changed, 366 insertions(+), 54 deletions(-)
---
diff --git a/gnome-flashback/libdesktop/Makefile.am b/gnome-flashback/libdesktop/Makefile.am
index 4433b49..eee2f20 100644
--- a/gnome-flashback/libdesktop/Makefile.am
+++ b/gnome-flashback/libdesktop/Makefile.am
@@ -48,6 +48,8 @@ libdesktop_la_SOURCES = \
        gf-thumbnail-factory.h \
        gf-utils.c \
        gf-utils.h \
+       gf-workarea-watcher.c \
+       gf-workarea-watcher.h \
        $(BUILT_SOURCES) \
        $(NULL)
 
diff --git a/gnome-flashback/libdesktop/gf-icon-view.c b/gnome-flashback/libdesktop/gf-icon-view.c
index 75bd21b..11f4749 100644
--- a/gnome-flashback/libdesktop/gf-icon-view.c
+++ b/gnome-flashback/libdesktop/gf-icon-view.c
@@ -18,7 +18,6 @@
 #include "config.h"
 #include "gf-icon-view.h"
 
-#include <gdk/gdkx.h>
 #include <glib/gi18n.h>
 
 #include "dbus/gf-file-manager-gen.h"
@@ -32,6 +31,7 @@
 #include "gf-monitor-view.h"
 #include "gf-trash-icon.h"
 #include "gf-utils.h"
+#include "gf-workarea-watcher.h"
 
 typedef struct
 {
@@ -44,6 +44,8 @@ struct _GfIconView
 {
   GtkEventBox         parent;
 
+  GfWorkareaWatcher  *workarea_watcher;
+
   GfThumbnailFactory *thumbnail_factory;
 
   GtkGesture         *multi_press;
@@ -1723,7 +1725,8 @@ find_monitor_view_by_monitor (GfIconView *self,
 }
 
 static void
-workarea_changed (GfIconView *self)
+workarea_watcher_changed_cb (GfWorkareaWatcher *workarea_watcher,
+                             GfIconView        *self)
 {
   GList *children;
   GList *l;
@@ -1923,55 +1926,6 @@ show_trash_changed_cb (GSettings  *settings,
     }
 }
 
-static GdkFilterReturn
-filter_func (GdkXEvent *xevent,
-             GdkEvent  *event,
-             gpointer   user_data)
-{
-  XEvent *x;
-  GdkAtom atom;
-
-  x = (XEvent *) xevent;
-
-  if (x->type != PropertyNotify)
-    return GDK_FILTER_CONTINUE;
-
-  atom = gdk_atom_intern_static_string ("_NET_WORKAREA");
-  if (x->xproperty.atom == gdk_x11_atom_to_xatom (atom))
-    workarea_changed (GF_ICON_VIEW (user_data));
-
-  return GDK_FILTER_CONTINUE;
-}
-
-static void
-remove_event_filter (GfIconView *self)
-{
-  GdkScreen *screen;
-  GdkWindow *root;
-
-  screen = gtk_widget_get_screen (GTK_WIDGET (self));
-  root = gdk_screen_get_root_window (screen);
-
-  gdk_window_remove_filter (root, filter_func, self);
-}
-
-static void
-add_event_filter (GfIconView *self)
-{
-  GdkScreen *screen;
-  GdkWindow *root;
-  GdkEventMask event_mask;
-
-  screen = gtk_widget_get_screen (GTK_WIDGET (self));
-  root = gdk_screen_get_root_window (screen);
-
-  event_mask = gdk_window_get_events (root);
-  event_mask |= GDK_PROPERTY_NOTIFY;
-
-  gdk_window_add_filter (root, filter_func, self);
-  gdk_window_set_events (root, event_mask);
-}
-
 static char **
 get_selected_uris (GfIconView *self)
 {
@@ -2407,6 +2361,8 @@ gf_icon_view_dispose (GObject *object)
 
   self = GF_ICON_VIEW (object);
 
+  g_clear_object (&self->workarea_watcher);
+
   g_clear_object (&self->thumbnail_factory);
 
   g_clear_object (&self->multi_press);
@@ -2450,8 +2406,6 @@ gf_icon_view_finalize (GObject *object)
 
   g_clear_pointer (&self->dummy_icon, gtk_widget_unparent);
 
-  remove_event_filter (self);
-
   G_OBJECT_CLASS (gf_icon_view_parent_class)->finalize (object);
 }
 
@@ -2798,7 +2752,11 @@ gf_icon_view_init (GfIconView *self)
   g_signal_connect (self, "toggle", G_CALLBACK (toggle_cb), NULL);
   g_signal_connect (self, "move", G_CALLBACK (move_cb), NULL);
 
-  add_event_filter (self);
+  self->workarea_watcher = gf_workarea_watcher_new ();
+
+  g_signal_connect (self->workarea_watcher, "changed",
+                    G_CALLBACK (workarea_watcher_changed_cb),
+                    self);
 
   self->thumbnail_factory = gf_thumbnail_factory_new ();
 
diff --git a/gnome-flashback/libdesktop/gf-workarea-watcher.c 
b/gnome-flashback/libdesktop/gf-workarea-watcher.c
new file mode 100644
index 0000000..6dd6170
--- /dev/null
+++ b/gnome-flashback/libdesktop/gf-workarea-watcher.c
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2020 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include "gf-workarea-watcher.h"
+
+#include <gdk/gdkx.h>
+#include <X11/Xatom.h>
+
+struct _GfWorkareaWatcher
+{
+  GObject  parent;
+
+  Atom     net_supported;
+  Atom     net_current_desktop;
+  Atom     net_workarea;
+  Atom     net_workareas;
+
+  gboolean net_workareas_supported;
+  Atom     net_workareas_dn;
+
+  guint    changed_id;
+};
+
+enum
+{
+  CHANGED,
+
+  LAST_SIGNAL
+};
+
+static guint workarea_watcher_signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (GfWorkareaWatcher, gf_workarea_watcher, G_TYPE_OBJECT)
+
+static gboolean
+emit_changed_cb (gpointer user_data)
+{
+  GfWorkareaWatcher *self;
+
+  self = GF_WORKAREA_WATCHER (user_data);
+
+  g_signal_emit (self, workarea_watcher_signals[CHANGED], 0);
+  self->changed_id = 0;
+
+  return G_SOURCE_REMOVE;
+}
+
+static void
+emit_changed_idle (GfWorkareaWatcher *self)
+{
+  if (self->changed_id != 0)
+    return;
+
+  self->changed_id = g_idle_add (emit_changed_cb, self);
+
+  g_source_set_name_by_id (self->changed_id,
+                           "[gnome-flashback] emit_changed_cb");
+}
+
+static void
+update_net_workareas_supported (GfWorkareaWatcher *self,
+                                gboolean           emit)
+{
+  gboolean changed;
+  gboolean net_workareas_supported;
+  Atom net_workareas_dn;
+  GdkDisplay *display;
+  Display *xdisplay;
+  int result;
+  Atom actual_type;
+  int actual_format;
+  unsigned long n_items;
+  unsigned long bytes_after;
+  unsigned char *prop;
+
+  changed = FALSE;
+  net_workareas_supported = FALSE;
+  net_workareas_dn = None;
+
+  display = gdk_display_get_default ();
+  xdisplay = gdk_x11_display_get_xdisplay (display);
+  prop = NULL;
+
+  gdk_x11_display_error_trap_push (display);
+
+  result = XGetWindowProperty (xdisplay,
+                               XDefaultRootWindow (xdisplay),
+                               self->net_supported,
+                               0,
+                               G_MAXLONG,
+                               False,
+                               XA_ATOM,
+                               &actual_type,
+                               &actual_format,
+                               &n_items,
+                               &bytes_after,
+                               &prop);
+
+  gdk_x11_display_error_trap_pop_ignored (display);
+
+  if (result == Success &&
+      actual_type == XA_ATOM)
+    {
+      Atom *atoms;
+      unsigned int i;
+
+      atoms = (Atom *) prop;
+
+      for (i = 0; i < n_items; i++)
+        {
+          if (atoms[i] == self->net_workareas)
+            {
+              net_workareas_supported = TRUE;
+              break;
+            }
+        }
+    }
+
+  XFree (prop);
+  prop = NULL;
+
+  if (net_workareas_supported)
+    {
+      int current_desktop;
+
+      current_desktop = -1;
+
+      gdk_x11_display_error_trap_push (display);
+
+      result = XGetWindowProperty (xdisplay,
+                                   XDefaultRootWindow (xdisplay),
+                                   self->net_current_desktop,
+                                   0,
+                                   G_MAXLONG,
+                                   False,
+                                   XA_CARDINAL,
+                                   &actual_type,
+                                   &actual_format,
+                                   &n_items,
+                                   &bytes_after,
+                                   &prop);
+
+      gdk_x11_display_error_trap_pop_ignored (display);
+
+      if (result == Success &&
+          actual_type == XA_CARDINAL &&
+          actual_format == 32 &&
+          n_items != 0)
+        {
+          current_desktop = ((long *) prop)[0];
+        }
+
+      XFree (prop);
+      prop = NULL;
+
+      if (current_desktop != -1)
+        {
+          char *atom_name;
+
+          atom_name = g_strdup_printf ("_NET_WORKAREAS_D%d", current_desktop);
+          net_workareas_dn = XInternAtom (xdisplay, atom_name, False);
+          g_free (atom_name);
+        }
+    }
+
+  if (self->net_workareas_supported != net_workareas_supported)
+    {
+      self->net_workareas_supported = net_workareas_supported;
+      changed = TRUE;
+    }
+
+  if (self->net_workareas_dn != net_workareas_dn)
+    {
+      self->net_workareas_dn = net_workareas_dn;
+      changed = TRUE;
+    }
+
+  if (changed && emit)
+    emit_changed_idle (self);
+}
+
+static GdkFilterReturn
+filter_func (GdkXEvent *xevent,
+             GdkEvent  *event,
+             gpointer   user_data)
+{
+  XEvent *x;
+  GfWorkareaWatcher *self;
+
+  x = (XEvent *) xevent;
+
+  if (x->type != PropertyNotify)
+    return GDK_FILTER_CONTINUE;
+
+  self = GF_WORKAREA_WATCHER (user_data);
+
+  if (self->net_workareas_supported &&
+      x->xproperty.atom == self->net_workareas_dn)
+    {
+      emit_changed_idle (self);
+    }
+  else if (!self->net_workareas_supported &&
+           x->xproperty.atom == self->net_workarea)
+    {
+      emit_changed_idle (self);
+    }
+  else if (x->xproperty.atom == self->net_current_desktop ||
+           x->xproperty.atom == self->net_supported)
+    {
+      update_net_workareas_supported (self, TRUE);
+    }
+
+  return GDK_FILTER_CONTINUE;
+}
+
+static void
+gf_workarea_watcher_finalize (GObject *object)
+{
+  GfWorkareaWatcher *self;
+  GdkDisplay *display;
+  GdkScreen *screen;
+  GdkWindow *root;
+
+  self = GF_WORKAREA_WATCHER (object);
+
+  display = gdk_display_get_default ();
+  screen = gdk_display_get_default_screen (display);
+  root = gdk_screen_get_root_window (screen);
+
+  gdk_window_remove_filter (root, filter_func, self);
+
+  if (self->changed_id != 0)
+    {
+      g_source_remove (self->changed_id);
+      self->changed_id = 0;
+    }
+
+  G_OBJECT_CLASS (gf_workarea_watcher_parent_class)->finalize (object);
+}
+
+static void
+install_signals (void)
+{
+  workarea_watcher_signals[CHANGED] =
+    g_signal_new ("changed",
+                  GF_TYPE_WORKAREA_WATCHER,
+                  G_SIGNAL_RUN_LAST,
+                  0,
+                  NULL,
+                  NULL,
+                  NULL,
+                  G_TYPE_NONE,
+                  0);
+}
+
+static void
+gf_workarea_watcher_class_init (GfWorkareaWatcherClass *self_class)
+{
+  GObjectClass *object_class;
+
+  object_class = G_OBJECT_CLASS (self_class);
+
+  object_class->finalize = gf_workarea_watcher_finalize;
+
+  install_signals ();
+}
+
+static void
+gf_workarea_watcher_init (GfWorkareaWatcher *self)
+{
+  GdkDisplay *display;
+  Display *xdisplay;
+  GdkScreen *screen;
+  GdkWindow *root;
+  GdkEventMask event_mask;
+
+  display = gdk_display_get_default ();
+  xdisplay = gdk_x11_display_get_xdisplay (display);
+
+  self->net_supported = XInternAtom (xdisplay, "_NET_SUPPORTED", False);
+  self->net_current_desktop = XInternAtom (xdisplay, "_NET_CURRENT_DESKTOP", False);
+  self->net_workarea = XInternAtom (xdisplay, "_NET_WORKAREA", False);
+  self->net_workareas = XInternAtom (xdisplay, "_NET_WORKAREAS", False);
+
+  self->net_workareas_supported = FALSE;
+  self->net_workareas_dn = None;
+
+  screen = gdk_display_get_default_screen (display);
+  root = gdk_screen_get_root_window (screen);
+
+  event_mask = gdk_window_get_events (root);
+  event_mask |= GDK_PROPERTY_NOTIFY;
+
+  gdk_window_add_filter (root, filter_func, self);
+  gdk_window_set_events (root, event_mask);
+
+  update_net_workareas_supported (self, FALSE);
+}
+
+GfWorkareaWatcher *
+gf_workarea_watcher_new (void)
+{
+  return g_object_new (GF_TYPE_WORKAREA_WATCHER, NULL);
+}
diff --git a/gnome-flashback/libdesktop/gf-workarea-watcher.h 
b/gnome-flashback/libdesktop/gf-workarea-watcher.h
new file mode 100644
index 0000000..688ad28
--- /dev/null
+++ b/gnome-flashback/libdesktop/gf-workarea-watcher.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GF_WORKAREA_WATCHER_H
+#define GF_WORKAREA_WATCHER_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GF_TYPE_WORKAREA_WATCHER (gf_workarea_watcher_get_type ())
+G_DECLARE_FINAL_TYPE (GfWorkareaWatcher, gf_workarea_watcher,
+                      GF, WORKAREA_WATCHER, GObject)
+
+GfWorkareaWatcher *gf_workarea_watcher_new (void);
+
+G_END_DECLS
+
+#endif


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