1 | /* GDK - The GIMP Drawing Kit |
2 | * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Lesser General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Lesser General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Lesser General Public |
15 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
16 | */ |
17 | |
18 | /* |
19 | * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS |
20 | * file for a list of people on the GTK+ Team. See the ChangeLog |
21 | * files for a list of changes. These files are distributed with |
22 | * GTK+ at ftp://ftp.gtk.org/pub/gtk/. |
23 | */ |
24 | |
25 | #include "config.h" |
26 | |
27 | #include "gdkdeviceprivate.h" |
28 | #include "gdkintl.h" |
29 | #include "gdkasync.h" |
30 | #include "gdkdisplay-x11.h" |
31 | #include "gdkprivate-x11.h" |
32 | |
33 | #include <glib/gprintf.h> |
34 | #include <stdlib.h> |
35 | #include <string.h> |
36 | #include <unistd.h> |
37 | #include <limits.h> |
38 | #include <errno.h> |
39 | |
40 | #include <X11/Xatom.h> |
41 | #include <X11/Xlib.h> |
42 | #include <X11/Xutil.h> |
43 | |
44 | #ifdef HAVE_XKB |
45 | #include <X11/XKBlib.h> |
46 | #endif |
47 | |
48 | /* non-GDK previous error handler */ |
49 | typedef int (*GdkXErrorHandler) (Display *, XErrorEvent *); |
50 | static GdkXErrorHandler _gdk_old_error_handler; |
51 | /* number of times we've pushed the GDK error handler */ |
52 | static int _gdk_error_handler_push_count = 0; |
53 | |
54 | /* |
55 | * Private function declarations |
56 | */ |
57 | |
58 | static int gdk_x_error (Display *display, |
59 | XErrorEvent *error); |
60 | static int gdk_x_io_error (Display *display); |
61 | |
62 | void |
63 | _gdk_x11_surfaceing_init (void) |
64 | { |
65 | XSetErrorHandler (gdk_x_error); |
66 | XSetIOErrorHandler (gdk_x_io_error); |
67 | } |
68 | |
69 | /* |
70 | * _gdk_x11_surface_grab_check_unmap: |
71 | * @surface: a `GdkSurface` |
72 | * @serial: serial from Unmap event (or from NextRequest(display) |
73 | * if the unmap is being done by this client.) |
74 | * |
75 | * Checks to see if an unmap request or event causes the current |
76 | * grab surface to become not viewable, and if so, clear the |
77 | * the pointer we keep to it. |
78 | **/ |
79 | void |
80 | _gdk_x11_surface_grab_check_unmap (GdkSurface *surface, |
81 | gulong serial) |
82 | { |
83 | GdkDisplay *display = gdk_surface_get_display (surface); |
84 | GdkSeat *seat; |
85 | GList *devices, *d; |
86 | |
87 | seat = gdk_display_get_default_seat (display); |
88 | |
89 | devices = gdk_seat_get_devices (seat, capabilities: GDK_SEAT_CAPABILITY_ALL); |
90 | devices = g_list_prepend (list: devices, data: gdk_seat_get_keyboard (seat)); |
91 | devices = g_list_prepend (list: devices, data: gdk_seat_get_pointer (seat)); |
92 | |
93 | /* End all grabs on the newly hidden surface */ |
94 | for (d = devices; d; d = d->next) |
95 | _gdk_display_end_device_grab (display, device: d->data, serial, if_child: surface, TRUE); |
96 | |
97 | g_list_free (list: devices); |
98 | } |
99 | |
100 | /* |
101 | * _gdk_x11_surface_grab_check_destroy: |
102 | * @surface: a `GdkSurface` |
103 | * |
104 | * Checks to see if surface is the current grab surface, and if |
105 | * so, clear the current grab surface. |
106 | **/ |
107 | void |
108 | _gdk_x11_surface_grab_check_destroy (GdkSurface *surface) |
109 | { |
110 | GdkDisplay *display = gdk_surface_get_display (surface); |
111 | GdkSeat *seat; |
112 | GdkDeviceGrabInfo *grab; |
113 | GList *devices, *d; |
114 | |
115 | seat = gdk_display_get_default_seat (display); |
116 | |
117 | devices = gdk_seat_get_devices (seat, capabilities: GDK_SEAT_CAPABILITY_ALL); |
118 | devices = g_list_prepend (list: devices, data: gdk_seat_get_keyboard (seat)); |
119 | devices = g_list_prepend (list: devices, data: gdk_seat_get_pointer (seat)); |
120 | |
121 | for (d = devices; d; d = d->next) |
122 | { |
123 | /* Make sure there is no lasting grab in this native surface */ |
124 | grab = _gdk_display_get_last_device_grab (display, device: d->data); |
125 | |
126 | if (grab && grab->surface == surface) |
127 | { |
128 | /* We don't know the actual serial to end, but it |
129 | doesn't really matter as this only happens |
130 | after we get told of the destroy from the |
131 | server so we know its ended in the server, |
132 | just make sure its ended. */ |
133 | grab->serial_end = grab->serial_start; |
134 | grab->implicit_ungrab = TRUE; |
135 | } |
136 | } |
137 | |
138 | g_list_free (list: devices); |
139 | } |
140 | |
141 | /* |
142 | *-------------------------------------------------------------- |
143 | * gdk_x_io_error |
144 | * |
145 | * The X I/O error handling routine. |
146 | * |
147 | * Arguments: |
148 | * "display" is the X display the error originated from. |
149 | * |
150 | * Results: |
151 | * An X I/O error basically means we lost our connection |
152 | * to the X server. There is not much we can do to |
153 | * continue, so simply print an error message and exit. |
154 | * |
155 | * Side effects: |
156 | * |
157 | *-------------------------------------------------------------- |
158 | */ |
159 | |
160 | static int |
161 | gdk_x_io_error (Display *display) |
162 | { |
163 | /* This is basically modelled after the code in XLib. We need |
164 | * an explicit error handler here, so we can disable our atexit() |
165 | * which would otherwise cause a nice segfault. |
166 | * We g_debug() instead of g_warning(), because g_warning() |
167 | * could possibly be redirected to the log |
168 | */ |
169 | g_debug ("%s: Fatal IO error %d (%s) on X server %s.\n" , |
170 | g_get_prgname (), |
171 | errno, g_strerror (errno), |
172 | display ? DisplayString (display) : "" ); |
173 | |
174 | _exit (status: 1); |
175 | } |
176 | |
177 | /* X error handler. Keep the name the same because people are used to |
178 | * breaking on it in the debugger. |
179 | */ |
180 | static int |
181 | gdk_x_error (Display *xdisplay, |
182 | XErrorEvent *error) |
183 | { |
184 | if (error->error_code) |
185 | { |
186 | GdkDisplay *error_display; |
187 | GdkDisplayManager *manager; |
188 | GSList *displays; |
189 | |
190 | /* Figure out which GdkDisplay if any got the error. */ |
191 | error_display = NULL; |
192 | manager = gdk_display_manager_get (); |
193 | displays = gdk_display_manager_list_displays (manager); |
194 | while (displays != NULL) |
195 | { |
196 | GdkX11Display *gdk_display = displays->data; |
197 | |
198 | if (GDK_IS_X11_DISPLAY (gdk_display) && |
199 | xdisplay == gdk_display->xdisplay) |
200 | { |
201 | error_display = GDK_DISPLAY (gdk_display); |
202 | g_slist_free (list: displays); |
203 | displays = NULL; |
204 | } |
205 | else |
206 | { |
207 | displays = g_slist_delete_link (list: displays, link_: displays); |
208 | } |
209 | } |
210 | |
211 | if (error_display == NULL) |
212 | { |
213 | /* Error on an X display not opened by GDK. Ignore. */ |
214 | |
215 | return 0; |
216 | } |
217 | else |
218 | { |
219 | _gdk_x11_display_error_event (display: error_display, error); |
220 | } |
221 | } |
222 | |
223 | return 0; |
224 | } |
225 | |
226 | void |
227 | _gdk_x11_error_handler_push (void) |
228 | { |
229 | GdkXErrorHandler previous; |
230 | |
231 | previous = XSetErrorHandler (gdk_x_error); |
232 | |
233 | if (_gdk_error_handler_push_count > 0) |
234 | { |
235 | if (previous != gdk_x_error) |
236 | g_warning ("XSetErrorHandler() called with a GDK error trap pushed. Don't do that." ); |
237 | } |
238 | else |
239 | { |
240 | _gdk_old_error_handler = previous; |
241 | } |
242 | |
243 | _gdk_error_handler_push_count += 1; |
244 | } |
245 | |
246 | void |
247 | _gdk_x11_error_handler_pop (void) |
248 | { |
249 | g_return_if_fail (_gdk_error_handler_push_count > 0); |
250 | |
251 | _gdk_error_handler_push_count -= 1; |
252 | |
253 | if (_gdk_error_handler_push_count == 0) |
254 | { |
255 | XSetErrorHandler (_gdk_old_error_handler); |
256 | _gdk_old_error_handler = NULL; |
257 | } |
258 | } |
259 | |
260 | int |
261 | _gdk_x11_display_send_xevent (GdkDisplay *display, |
262 | Window window, |
263 | gboolean propagate, |
264 | glong event_mask, |
265 | XEvent *event_send) |
266 | { |
267 | gboolean result; |
268 | |
269 | if (gdk_display_is_closed (display)) |
270 | return FALSE; |
271 | |
272 | gdk_x11_display_error_trap_push (display); |
273 | result = XSendEvent (GDK_DISPLAY_XDISPLAY (display), window, |
274 | propagate, event_mask, event_send); |
275 | XSync (GDK_DISPLAY_XDISPLAY (display), False); |
276 | |
277 | if (gdk_x11_display_error_trap_pop (display)) |
278 | return FALSE; |
279 | |
280 | return result; |
281 | } |
282 | |
283 | void |
284 | _gdk_x11_region_get_xrectangles (const cairo_region_t *region, |
285 | int x_offset, |
286 | int y_offset, |
287 | int scale, |
288 | XRectangle **rects, |
289 | int *n_rects) |
290 | { |
291 | XRectangle *rectangles; |
292 | cairo_rectangle_int_t box; |
293 | int i, n; |
294 | |
295 | n = cairo_region_num_rectangles (region); |
296 | rectangles = g_new (XRectangle, n); |
297 | |
298 | for (i = 0; i < n; i++) |
299 | { |
300 | cairo_region_get_rectangle (region, nth: i, rectangle: &box); |
301 | rectangles[i].x = CLAMP ((box.x + x_offset) * scale, G_MINSHORT, G_MAXSHORT); |
302 | rectangles[i].y = CLAMP ((box.y + y_offset) * scale, G_MINSHORT, G_MAXSHORT); |
303 | rectangles[i].width = CLAMP (box.width * scale, G_MINSHORT, G_MAXSHORT); |
304 | rectangles[i].height = CLAMP (box.height * scale, G_MINSHORT, G_MAXSHORT); |
305 | } |
306 | |
307 | *n_rects = n; |
308 | *rects = rectangles; |
309 | } |
310 | |