summaryrefslogtreecommitdiffstats
path: root/panels/keyboard/wm-common.c
diff options
context:
space:
mode:
Diffstat (limited to 'panels/keyboard/wm-common.c')
-rw-r--r--panels/keyboard/wm-common.c261
1 files changed, 261 insertions, 0 deletions
diff --git a/panels/keyboard/wm-common.c b/panels/keyboard/wm-common.c
new file mode 100644
index 0000000..0cddb10
--- /dev/null
+++ b/panels/keyboard/wm-common.c
@@ -0,0 +1,261 @@
+#include <X11/Xatom.h>
+#include <gdk/gdkx.h>
+#include <gdk/gdk.h>
+#include <string.h>
+#include <glib.h>
+#include <glib-object.h>
+#include "wm-common.h"
+
+typedef struct _WMCallbackData
+{
+ GFunc func;
+ gpointer data;
+} WMCallbackData;
+
+/* Our WM Window */
+static Window wm_window = None;
+
+/*
+ * These push/pop implementations are based on the GDK versions, except that they
+ * use only non-deprecated API.
+ */
+
+static GPtrArray*
+push_error_traps (void)
+{
+ GdkDisplayManager *manager;
+ g_autoptr(GPtrArray) trapped_displays = NULL;
+ g_autoptr(GSList) displays = NULL;
+ GSList *l;
+
+ manager = gdk_display_manager_get ();
+ displays = gdk_display_manager_list_displays (manager);
+ trapped_displays = g_ptr_array_new ();
+
+ for (l = displays; l != NULL; l = l->next)
+ {
+ GdkDisplay *display = l->data;
+
+#ifdef GDK_WINDOWING_X11
+ if (GDK_IS_X11_DISPLAY (display))
+ {
+ gdk_x11_display_error_trap_push (display);
+ g_ptr_array_add (trapped_displays, display);
+ }
+#endif
+ }
+
+ return g_steal_pointer (&trapped_displays);
+}
+
+static gint
+pop_error_traps (GPtrArray *displays)
+{
+ guint i;
+ gint result;
+
+ result = 0;
+
+ for (i = 0; displays && i < displays->len; i++)
+ {
+ GdkDisplay *display;
+ gint code = 0;
+
+ display = g_ptr_array_index (displays, i);
+
+#ifdef GDK_WINDOWING_X11
+ code = gdk_x11_display_error_trap_pop (display);
+#endif
+
+ if (code != 0)
+ result = code;
+ }
+
+ return result;
+}
+
+static char *
+wm_common_get_window_manager_property (Atom atom)
+{
+ g_autoptr(GPtrArray) trapped_displays = NULL;
+ Atom utf8_string, type;
+ int result;
+ char *retval;
+ int format;
+ gulong nitems;
+ gulong bytes_after;
+ gchar *val;
+
+ if (wm_window == None)
+ return NULL;
+
+ utf8_string = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "UTF8_STRING", False);
+
+ trapped_displays = push_error_traps ();
+
+ val = NULL;
+ result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
+ wm_window,
+ atom,
+ 0, G_MAXLONG,
+ False, utf8_string,
+ &type, &format, &nitems,
+ &bytes_after, (guchar **) &val);
+
+ if (pop_error_traps (trapped_displays) ||
+ result != Success ||
+ type != utf8_string ||
+ format != 8 ||
+ nitems == 0 ||
+ !g_utf8_validate (val, nitems, NULL))
+ {
+ retval = NULL;
+ }
+ else
+ {
+ retval = g_strndup (val, nitems);
+ }
+
+ g_clear_pointer (&val, XFree);
+
+ return retval;
+}
+static gchar*
+wm_common_get_current_window_manager (void)
+{
+ Atom atom = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "_NET_WM_NAME", False);
+ char *result;
+
+ result = wm_common_get_window_manager_property (atom);
+ if (result)
+ return result;
+ else
+ return g_strdup (WM_COMMON_UNKNOWN);
+}
+
+GStrv
+wm_common_get_current_keybindings (void)
+{
+ g_autofree gchar* keybindings = NULL;
+ g_auto(GStrv) results = NULL;
+ Atom keybindings_atom;
+
+ keybindings_atom = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "_GNOME_WM_KEYBINDINGS", False);
+ keybindings = wm_common_get_window_manager_property (keybindings_atom);
+
+ if (keybindings)
+ {
+ GStrv p;
+
+ results = g_strsplit (keybindings, ",", -1);
+
+ for (p = results; p && *p; p++)
+ g_strstrip (*p);
+ }
+ else
+ {
+ g_autofree gchar *wm_name = NULL;
+ Atom wm_atom;
+ gchar *to_copy[2] = { NULL, NULL };
+
+ wm_atom = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "_NET_WM_NAME", False);
+ wm_name = wm_common_get_window_manager_property (wm_atom);
+
+ to_copy[0] = wm_name ? wm_name : WM_COMMON_UNKNOWN;
+
+ results = g_strdupv (to_copy);
+ }
+
+ return g_steal_pointer (&results);
+}
+
+static void
+update_wm_window (void)
+{
+ g_autoptr(GPtrArray) trapped_displays = NULL;
+ Window *xwindow;
+ Atom type;
+ gint format;
+ gulong nitems;
+ gulong bytes_after;
+
+ XGetWindowProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), GDK_ROOT_WINDOW (),
+ XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "_NET_SUPPORTING_WM_CHECK", False),
+ 0, G_MAXLONG, False, XA_WINDOW, &type, &format,
+ &nitems, &bytes_after, (guchar **) &xwindow);
+
+ if (type != XA_WINDOW)
+ {
+ wm_window = None;
+ return;
+ }
+
+ trapped_displays = push_error_traps ();
+
+ XSelectInput (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), *xwindow, StructureNotifyMask | PropertyChangeMask);
+ XSync (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), False);
+
+ if (pop_error_traps (trapped_displays))
+ {
+ XFree (xwindow);
+ wm_window = None;
+ return;
+ }
+
+ wm_window = *xwindow;
+ XFree (xwindow);
+}
+
+static GdkFilterReturn
+wm_window_event_filter (GdkXEvent *xev,
+ GdkEvent *event,
+ gpointer data)
+{
+ WMCallbackData *ncb_data = (WMCallbackData*) data;
+ XEvent *xevent = (XEvent *)xev;
+
+ if ((xevent->type == DestroyNotify &&
+ wm_window != None && xevent->xany.window == wm_window) ||
+ (xevent->type == PropertyNotify &&
+ xevent->xany.window == GDK_ROOT_WINDOW () &&
+ xevent->xproperty.atom == (XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "_NET_SUPPORTING_WM_CHECK", False))) ||
+ (xevent->type == PropertyNotify &&
+ wm_window != None && xevent->xany.window == wm_window &&
+ xevent->xproperty.atom == (XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "_NET_WM_NAME", False))))
+ {
+ update_wm_window ();
+ (* ncb_data->func) ((gpointer) wm_common_get_current_window_manager (), ncb_data->data);
+ }
+
+ return GDK_FILTER_CONTINUE;
+}
+
+gpointer
+wm_common_register_window_manager_change (GFunc func,
+ gpointer data)
+{
+ WMCallbackData *ncb_data;
+
+ ncb_data = g_new0 (WMCallbackData, 1);
+
+ ncb_data->func = func;
+ ncb_data->data = data;
+
+ gdk_window_add_filter (NULL, wm_window_event_filter, ncb_data);
+
+ update_wm_window ();
+
+ XSelectInput (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), GDK_ROOT_WINDOW (), PropertyChangeMask);
+ XSync (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), False);
+
+ return ncb_data;
+}
+
+void
+wm_common_unregister_window_manager_change (gpointer id)
+{
+ g_return_if_fail (id != NULL);
+
+ gdk_window_remove_filter (NULL, wm_window_event_filter, id);
+ g_free (id);
+}