aboutsummaryrefslogtreecommitdiff
path: root/libtopmenu-client/topmenu-monitor.c
diff options
context:
space:
mode:
Diffstat (limited to 'libtopmenu-client/topmenu-monitor.c')
-rw-r--r--libtopmenu-client/topmenu-monitor.c163
1 files changed, 163 insertions, 0 deletions
diff --git a/libtopmenu-client/topmenu-monitor.c b/libtopmenu-client/topmenu-monitor.c
new file mode 100644
index 0000000..abd7522
--- /dev/null
+++ b/libtopmenu-client/topmenu-monitor.c
@@ -0,0 +1,163 @@
+#include <X11/Xatom.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+
+#include "../global.h"
+
+#include "topmenu-monitor.h"
+
+struct _TopMenuMonitorPrivate
+{
+ GdkAtom atom_selection;
+ GtkClipboard *selection;
+ GdkWindow *cur_server;
+};
+
+enum {
+ PROP_0,
+ PROP_AVAILABLE,
+ N_PROPERTIES
+};
+
+G_DEFINE_TYPE(TopMenuMonitor, topmenu_monitor, G_TYPE_OBJECT)
+
+#define TOPMENU_MONITOR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), TOPMENU_TYPE_MONITOR, TopMenuMonitorPrivate))
+
+static GParamSpec *properties[N_PROPERTIES] = { NULL };
+
+static void topmenu_monitor_update(TopMenuMonitor *self);
+
+static void handle_clipboard_owner_change(GtkClipboard *clipboard, GdkEvent *event, TopMenuMonitor *self)
+{
+ topmenu_monitor_update(self);
+}
+
+static GdkFilterReturn handle_cur_server_event(GdkXEvent *xevent, GdkEvent *event, gpointer data)
+{
+ XEvent *e = (XEvent*)xevent;
+ if (e->type == DestroyNotify) {
+ g_debug("Current server has been destroyed");
+ TopMenuMonitor *self = TOPMENU_MONITOR(data);
+ if (self->priv->cur_server &&
+ GDK_WINDOW_XWINDOW(self->priv->cur_server) == e->xdestroywindow.window) {
+ topmenu_monitor_update(self);
+ }
+ }
+ return GDK_FILTER_CONTINUE;
+}
+
+static void topmenu_monitor_set_cur_server(TopMenuMonitor *self, GdkWindow *window)
+{
+ if (self->priv->cur_server == window) {
+ // Nothing to do
+ return;
+ }
+ g_debug("Setting current server to 0x%lx", GDK_WINDOW_XWINDOW(window));
+ if (self->priv->cur_server) {
+ gdk_window_remove_filter(window, handle_cur_server_event, self);
+ gdk_window_unref(self->priv->cur_server);
+ self->priv->cur_server = 0;
+ }
+ if (window) {
+ gdk_window_set_events(window, gdk_window_get_events(window) | GDK_STRUCTURE_MASK);
+ gdk_window_add_filter(window, handle_cur_server_event, self);
+ self->priv->cur_server = window;
+ }
+ if (self->priv->cur_server && !self->available) {
+ // Signal availability
+ self->available = TRUE;
+ g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_AVAILABLE]);
+ } else if (!self->priv->cur_server && self->available) {
+ // Signal no availability
+ self->available = FALSE;
+ g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_AVAILABLE]);
+ }
+}
+
+static void topmenu_monitor_update(TopMenuMonitor *self)
+{
+ GdkScreen *screen = gdk_screen_get_default();
+ GdkDisplay *display = gdk_screen_get_display(screen);
+
+ Display *xdpy = GDK_DISPLAY_XDISPLAY(display);
+ Atom atom = gdk_x11_atom_to_xatom_for_display(display, self->priv->atom_selection);
+
+ Window xwin = XGetSelectionOwner(xdpy, atom);
+
+ if (xwin) {
+ GdkWindow *window = gdk_x11_window_foreign_new_for_display(display, xwin);
+ topmenu_monitor_set_cur_server(self, window);
+ } else {
+ topmenu_monitor_set_cur_server(self, NULL);
+ }
+}
+
+static void topmenu_monitor_get_property(GObject *obj, guint property_id, GValue *value, GParamSpec *pspec)
+{
+ TopMenuMonitor *self = TOPMENU_MONITOR(obj);
+ switch (property_id) {
+ case PROP_AVAILABLE:
+ g_value_set_boolean(value, self->available);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
+ }
+}
+
+static void topmenu_monitor_dispose(GObject *obj)
+{
+ TopMenuMonitor *self = TOPMENU_MONITOR(obj);
+ if (self->priv->cur_server) {
+ gdk_window_remove_filter(self->priv->cur_server,
+ handle_cur_server_event, self);
+ gdk_window_unref(self->priv->cur_server);
+ self->priv->cur_server = 0;
+ }
+ self->priv->selection = NULL;
+ G_OBJECT_CLASS(topmenu_monitor_parent_class)->dispose(obj);
+}
+
+static void topmenu_monitor_class_init(TopMenuMonitorClass *klass)
+{
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+ obj_class->get_property = topmenu_monitor_get_property;
+ obj_class->dispose = topmenu_monitor_dispose;
+
+ properties[PROP_AVAILABLE] = g_param_spec_boolean("available",
+ "TopMenu's availability",
+ "Set to TRUE whether a TopMenu server is currently available",
+ FALSE,
+ G_PARAM_STATIC_STRINGS | G_PARAM_READABLE);
+ g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
+
+ g_type_class_add_private(klass, sizeof(TopMenuMonitorPrivate));
+}
+
+static void topmenu_monitor_init(TopMenuMonitor *self)
+{
+ self->priv = TOPMENU_MONITOR_GET_PRIVATE(self);
+ self->available = FALSE;
+
+ self->priv->atom_selection = gdk_atom_intern_static_string(ATOM_TOPMENU_SERVER_SELECTION);
+ self->priv->selection = gtk_clipboard_get(self->priv->atom_selection);
+ self->priv->cur_server = NULL;
+
+ g_signal_connect_object(self->priv->selection, "owner-change",
+ G_CALLBACK(handle_clipboard_owner_change), self, 0);
+
+ topmenu_monitor_update(self);
+}
+
+TopMenuMonitor * topmenu_monitor_get_instance()
+{
+ static TopMenuMonitor *instance = NULL;
+ if (!instance) {
+ instance = TOPMENU_MONITOR(g_object_new(TOPMENU_TYPE_MONITOR, NULL));
+ }
+ return instance;
+}
+
+gboolean topmenu_monitor_is_topmenu_available(TopMenuMonitor * self)
+{
+ return self->available;
+}