/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* Copyright © 2002 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Red Hat not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Red Hat makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Owen Taylor, Red Hat, Inc. */ #include #include #include #include "drw-selection.h" struct _DrwSelection { GdkWindow *owner_window; GtkWidget *invisible; }; #define SELECTION_NAME "_CODEFACTORY_DRWRIGHT" static GdkFilterReturn drw_selection_filter (GdkXEvent *xevent, GdkEvent *event, gpointer data); static void drw_selection_negotiate (DrwSelection *drw_selection); static void drw_selection_reset (DrwSelection *drw_selection) { if (drw_selection->owner_window) { gdk_window_remove_filter (drw_selection->owner_window, drw_selection_filter, drw_selection); g_object_unref (drw_selection->owner_window); drw_selection->owner_window = NULL; } if (drw_selection->invisible) { gtk_widget_destroy (drw_selection->invisible); drw_selection->invisible = NULL; } } static void drw_selection_clear (GtkWidget *widget, GdkEventSelection *event, gpointer user_data) { DrwSelection *drw_selection = user_data; drw_selection_reset (drw_selection); drw_selection_negotiate (drw_selection); } static gboolean drw_selection_find_existing (DrwSelection *drw_selection) { GdkDisplay *display; Window old; Display *xdisplay; display = gdk_display_get_default (); xdisplay = GDK_DISPLAY_XDISPLAY(display); gdk_x11_display_error_trap_push (display); old = XGetSelectionOwner (xdisplay, gdk_x11_get_xatom_by_name (SELECTION_NAME)); if (old) { XSelectInput (xdisplay, old, StructureNotifyMask); drw_selection->owner_window = gdk_x11_window_foreign_new_for_display (gdk_display_get_default (), old); } XSync (xdisplay, False); if (gdk_x11_display_error_trap_pop (display) == 0 && drw_selection->owner_window) { gdk_window_add_filter (drw_selection->owner_window, drw_selection_filter, drw_selection); XUngrabServer (xdisplay); return TRUE; } else { if (drw_selection->owner_window) { g_object_unref (drw_selection->owner_window); drw_selection->owner_window = NULL; } return FALSE; } } static gboolean drw_selection_claim (DrwSelection *drw_selection) { drw_selection->invisible = gtk_invisible_new (); g_signal_connect (drw_selection->invisible, "selection-clear-event", G_CALLBACK (drw_selection_clear), drw_selection); if (gtk_selection_owner_set (drw_selection->invisible, gdk_atom_intern (SELECTION_NAME, FALSE), GDK_CURRENT_TIME)) { return TRUE; } else { drw_selection_reset (drw_selection); return FALSE; } } static void drw_selection_negotiate (DrwSelection *drw_selection) { Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); gboolean found = FALSE; /* We don't need both the XGrabServer() and the loop here; * the XGrabServer() should make sure that we only go through * the loop once. It also works if you remove the XGrabServer() * and just have the loop, but then the selection ownership * can get transfered a bunch of times before things * settle down. */ while (!found) { XGrabServer (xdisplay); if (drw_selection_find_existing (drw_selection)) found = TRUE; else if (drw_selection_claim (drw_selection)) found = TRUE; XUngrabServer (xdisplay); } } static GdkFilterReturn drw_selection_filter (GdkXEvent *xevent, GdkEvent *event, gpointer data) { DrwSelection *drw_selection = data; XEvent *xev = (XEvent *)xevent; if (xev->xany.type == DestroyNotify && xev->xdestroywindow.window == xev->xdestroywindow.event) { drw_selection_reset (drw_selection); drw_selection_negotiate (drw_selection); return GDK_FILTER_REMOVE; } return GDK_FILTER_CONTINUE; } DrwSelection * drw_selection_start (void) { DrwSelection *drw_selection = g_new (DrwSelection, 1); drw_selection->owner_window = NULL; drw_selection->invisible = NULL; drw_selection_negotiate (drw_selection); return drw_selection; } void drw_selection_stop (DrwSelection *drw_selection) { drw_selection_reset (drw_selection); g_free (drw_selection); } gboolean drw_selection_is_master (DrwSelection *drw_selection) { return drw_selection->invisible != NULL; }