[gnome-flashback] desktop: add GfWorkareaWatcher
- From: Alberts Muktupāvels <muktupavels src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-flashback] desktop: add GfWorkareaWatcher
- Date: Wed, 22 Jan 2020 18:08:04 +0000 (UTC)
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]