1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* Copyright © 2002 Red Hat, Inc.
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation, and that the name of Red Hat not be used in advertising or
9  * publicity pertaining to distribution of the software without specific,
10  * written prior permission.  Red Hat makes no representations about the
11  * suitability of this software for any purpose.  It is provided "as is"
12  * without express or implied warranty.
13  *
14  * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT
16  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
18  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
19  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20  *
21  * Author:  Owen Taylor, Red Hat, Inc.
22  */
23 
24 #include <config.h>
25 #include <gtk/gtk.h>
26 #include <gdk/gdkx.h>
27 
28 #include "drw-selection.h"
29 
30 struct _DrwSelection
31 {
32 	GdkWindow *owner_window;
33 	GtkWidget *invisible;
34 };
35 
36 #define SELECTION_NAME "_CODEFACTORY_DRWRIGHT"
37 
38 static GdkFilterReturn drw_selection_filter     (GdkXEvent   *xevent,
39 						 GdkEvent    *event,
40 						 gpointer     data);
41 static void            drw_selection_negotiate  (DrwSelection *drw_selection);
42 
43 static void
drw_selection_reset(DrwSelection * drw_selection)44 drw_selection_reset (DrwSelection *drw_selection)
45 {
46 	if (drw_selection->owner_window) {
47 		gdk_window_remove_filter (drw_selection->owner_window,
48 					  drw_selection_filter, drw_selection);
49 		g_object_unref (drw_selection->owner_window);
50 		drw_selection->owner_window = NULL;
51 	}
52 
53 	if (drw_selection->invisible) {
54 		gtk_widget_destroy (drw_selection->invisible);
55 		drw_selection->invisible = NULL;
56 	}
57 }
58 
59 static void
drw_selection_clear(GtkWidget * widget,GdkEventSelection * event,gpointer user_data)60 drw_selection_clear (GtkWidget         *widget,
61 		    GdkEventSelection *event,
62 		    gpointer           user_data)
63 {
64 	DrwSelection *drw_selection = user_data;
65 
66 	drw_selection_reset (drw_selection);
67 	drw_selection_negotiate (drw_selection);
68 }
69 
70 static gboolean
drw_selection_find_existing(DrwSelection * drw_selection)71 drw_selection_find_existing (DrwSelection *drw_selection)
72 {
73 	GdkDisplay *display;
74 	Window old;
75 	Display *xdisplay;
76 
77 	display = gdk_display_get_default ();
78 	xdisplay = GDK_DISPLAY_XDISPLAY(display);
79 
80 	gdk_x11_display_error_trap_push (display);
81 	old = XGetSelectionOwner (xdisplay,
82 				  gdk_x11_get_xatom_by_name (SELECTION_NAME));
83 	if (old) {
84 		XSelectInput (xdisplay, old, StructureNotifyMask);
85 		drw_selection->owner_window = gdk_x11_window_foreign_new_for_display (gdk_display_get_default (), old);
86 	}
87 	XSync (xdisplay, False);
88 
89 	if (gdk_x11_display_error_trap_pop (display) == 0 && drw_selection->owner_window) {
90 		gdk_window_add_filter (drw_selection->owner_window,
91 				       drw_selection_filter, drw_selection);
92 
93 		XUngrabServer (xdisplay);
94 
95 		return TRUE;
96 	} else {
97 		if (drw_selection->owner_window) {
98 			g_object_unref (drw_selection->owner_window);
99 			drw_selection->owner_window = NULL;
100 		}
101 
102 		return FALSE;
103 	}
104 }
105 
106 static gboolean
drw_selection_claim(DrwSelection * drw_selection)107 drw_selection_claim (DrwSelection *drw_selection)
108 {
109 	drw_selection->invisible = gtk_invisible_new ();
110 	g_signal_connect (drw_selection->invisible, "selection-clear-event",
111 			  G_CALLBACK (drw_selection_clear), drw_selection);
112 
113 
114 	if (gtk_selection_owner_set (drw_selection->invisible,
115 				     gdk_atom_intern (SELECTION_NAME, FALSE),
116 				     GDK_CURRENT_TIME)) {
117 		return TRUE;
118 	} else {
119 		drw_selection_reset (drw_selection);
120 		return FALSE;
121 	}
122 }
123 
124 static void
drw_selection_negotiate(DrwSelection * drw_selection)125 drw_selection_negotiate (DrwSelection *drw_selection)
126 {
127 	Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
128 	gboolean found = FALSE;
129 
130 	/* We don't need both the XGrabServer() and the loop here;
131 	 * the XGrabServer() should make sure that we only go through
132 	 * the loop once. It also works if you remove the XGrabServer()
133 	 * and just have the loop, but then the selection ownership
134 	 * can get transfered a bunch of times before things
135 	 * settle down.
136 	 */
137 	while (!found)
138 	{
139 		XGrabServer (xdisplay);
140 
141 		if (drw_selection_find_existing (drw_selection))
142 			found = TRUE;
143 		else if (drw_selection_claim (drw_selection))
144 			found = TRUE;
145 
146 		XUngrabServer (xdisplay);
147 	}
148 }
149 
150 static GdkFilterReturn
drw_selection_filter(GdkXEvent * xevent,GdkEvent * event,gpointer data)151 drw_selection_filter (GdkXEvent *xevent,
152 		     GdkEvent  *event,
153 		     gpointer   data)
154 {
155 	DrwSelection *drw_selection = data;
156 	XEvent *xev = (XEvent *)xevent;
157 
158 	if (xev->xany.type == DestroyNotify &&
159 	    xev->xdestroywindow.window == xev->xdestroywindow.event)
160 	{
161 		drw_selection_reset (drw_selection);
162 		drw_selection_negotiate (drw_selection);
163 
164 		return GDK_FILTER_REMOVE;
165 	}
166 
167 	return GDK_FILTER_CONTINUE;
168 }
169 
170 DrwSelection *
drw_selection_start(void)171 drw_selection_start (void)
172 {
173 	DrwSelection *drw_selection = g_new (DrwSelection, 1);
174 
175 	drw_selection->owner_window = NULL;
176 	drw_selection->invisible = NULL;
177 
178 	drw_selection_negotiate (drw_selection);
179 
180 	return drw_selection;
181 }
182 
183 void
drw_selection_stop(DrwSelection * drw_selection)184 drw_selection_stop (DrwSelection *drw_selection)
185 {
186 	drw_selection_reset (drw_selection);
187 	g_free (drw_selection);
188 }
189 
190 gboolean
drw_selection_is_master(DrwSelection * drw_selection)191 drw_selection_is_master (DrwSelection *drw_selection)
192 {
193 	return drw_selection->invisible != NULL;
194 }
195