/* * Copyright 2014 Javier S. Pedro * * This file is part of TopMenu. * * TopMenu is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * TopMenu 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with TopMenu. If not, see . */ #include #include #include #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) { TopMenuMonitor *self = TOPMENU_MONITOR(data); if (self->priv->cur_server && GDK_WINDOW_XID(self->priv->cur_server) == e->xdestroywindow.window) { g_debug("Current server has been destroyed"); 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; } if (self->priv->cur_server) { g_debug("Removing current server"); gdk_window_remove_filter(window, handle_cur_server_event, self); g_object_unref(self->priv->cur_server); self->priv->cur_server = 0; } if (window) { g_debug("Setting current server to 0x%lx", GDK_WINDOW_XID(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); g_object_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; }