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