1 | /* GDK - The GIMP Drawing Kit |
2 | * gdkdisplay-x11.c |
3 | * |
4 | * Copyright 2001 Sun Microsystems Inc. |
5 | * Copyright (C) 2004 Nokia Corporation |
6 | * |
7 | * Erwann Chenede <erwann.chenede@sun.com> |
8 | * |
9 | * This library is free software; you can redistribute it and/or |
10 | * modify it under the terms of the GNU Library General Public |
11 | * License as published by the Free Software Foundation; either |
12 | * version 2 of the License, or (at your option) any later version. |
13 | * |
14 | * This library is distributed in the hope that it will be useful, |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
17 | * Library General Public License for more details. |
18 | * |
19 | * You should have received a copy of the GNU Library General Public |
20 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
21 | */ |
22 | |
23 | #include "config.h" |
24 | |
25 | #define VK_USE_PLATFORM_XLIB_KHR |
26 | |
27 | #include "gdkdisplay-x11.h" |
28 | #include "gdkdisplayprivate.h" |
29 | |
30 | #include "gdkasync.h" |
31 | #include "gdkdisplay.h" |
32 | #include "gdkeventsprivate.h" |
33 | #include "gdkeventsource.h" |
34 | #include "gdkeventtranslator.h" |
35 | #include "gdkframeclockprivate.h" |
36 | #include "gdkdeviceprivate.h" |
37 | #include "gdksurfaceprivate.h" |
38 | #include "gdkkeysprivate.h" |
39 | #include "gdkmarshalers.h" |
40 | #include "xsettings-client.h" |
41 | |
42 | #include "gdkcairocontext-x11.h" |
43 | #include "gdkclipboard-x11.h" |
44 | #include "gdkglcontext-x11.h" |
45 | #include "gdkkeys-x11.h" |
46 | #include "gdkprivate-x11.h" |
47 | #include "gdkscreen-x11.h" |
48 | #include "gdkvulkancontext-x11.h" |
49 | |
50 | #include "gdk-private.h" |
51 | |
52 | #include <glib.h> |
53 | #include <glib/gprintf.h> |
54 | #include <stdlib.h> |
55 | #include <string.h> |
56 | #include <errno.h> |
57 | #include <unistd.h> |
58 | |
59 | #include <epoxy/egl.h> |
60 | |
61 | #include <X11/Xatom.h> |
62 | #include <X11/Xlibint.h> |
63 | |
64 | #ifdef HAVE_XKB |
65 | #include <X11/XKBlib.h> |
66 | #endif |
67 | |
68 | #ifdef HAVE_XFIXES |
69 | #include <X11/extensions/Xfixes.h> |
70 | #endif |
71 | |
72 | #include <X11/extensions/shape.h> |
73 | |
74 | #ifdef HAVE_RANDR |
75 | #include <X11/extensions/Xrandr.h> |
76 | #endif |
77 | |
78 | enum { |
79 | XEVENT, |
80 | LAST_SIGNAL |
81 | }; |
82 | |
83 | typedef struct _GdkErrorTrap GdkErrorTrap; |
84 | |
85 | struct _GdkErrorTrap |
86 | { |
87 | /* Next sequence when trap was pushed, i.e. first sequence to |
88 | * ignore |
89 | */ |
90 | gulong start_sequence; |
91 | |
92 | /* Next sequence when trap was popped, i.e. first sequence |
93 | * to not ignore. 0 if trap is still active. |
94 | */ |
95 | gulong end_sequence; |
96 | |
97 | /* Most recent error code within the sequence */ |
98 | int error_code; |
99 | }; |
100 | |
101 | static void gdk_x11_display_dispose (GObject *object); |
102 | static void gdk_x11_display_finalize (GObject *object); |
103 | |
104 | static void gdk_x11_display_event_translator_init (GdkEventTranslatorIface *iface); |
105 | |
106 | static GdkEvent * gdk_x11_display_translate_event (GdkEventTranslator *translator, |
107 | GdkDisplay *display, |
108 | const XEvent *xevent); |
109 | |
110 | static void gdk_internal_connection_watch (Display *display, |
111 | XPointer arg, |
112 | int fd, |
113 | gboolean opening, |
114 | XPointer *watch_data); |
115 | |
116 | typedef struct _GdkEventTypeX11 GdkEventTypeX11; |
117 | |
118 | struct _GdkEventTypeX11 |
119 | { |
120 | int base; |
121 | int n_events; |
122 | }; |
123 | |
124 | /* Note that we never *directly* use WM_LOCALE_NAME, WM_PROTOCOLS, |
125 | * but including them here has the side-effect of getting them |
126 | * into the internal Xlib cache |
127 | */ |
128 | static const char *const precache_atoms[] = { |
129 | "UTF8_STRING" , |
130 | "WM_CLIENT_LEADER" , |
131 | "WM_DELETE_WINDOW" , |
132 | "WM_ICON_NAME" , |
133 | "WM_LOCALE_NAME" , |
134 | "WM_NAME" , |
135 | "WM_PROTOCOLS" , |
136 | "WM_TAKE_FOCUS" , |
137 | "WM_WINDOW_ROLE" , |
138 | "WM_STATE" , |
139 | "_NET_ACTIVE_WINDOW" , |
140 | "_NET_CURRENT_DESKTOP" , |
141 | "_NET_FRAME_EXTENTS" , |
142 | "_NET_STARTUP_ID" , |
143 | "_NET_WM_CM_S0" , |
144 | "_NET_WM_DESKTOP" , |
145 | "_NET_WM_ICON" , |
146 | "_NET_WM_ICON_NAME" , |
147 | "_NET_WM_NAME" , |
148 | "_NET_WM_PID" , |
149 | "_NET_WM_PING" , |
150 | "_NET_WM_STATE" , |
151 | "_NET_WM_STATE_ABOVE" , |
152 | "_NET_WM_STATE_BELOW" , |
153 | "_NET_WM_STATE_FULLSCREEN" , |
154 | "_NET_WM_STATE_HIDDEN" , |
155 | "_NET_WM_STATE_MODAL" , |
156 | "_NET_WM_STATE_MAXIMIZED_VERT" , |
157 | "_NET_WM_STATE_MAXIMIZED_HORZ" , |
158 | "_NET_WM_STATE_SKIP_TASKBAR" , |
159 | "_NET_WM_STATE_SKIP_PAGER" , |
160 | "_NET_WM_STATE_STICKY" , |
161 | "_NET_WM_SYNC_REQUEST" , |
162 | "_NET_WM_SYNC_REQUEST_COUNTER" , |
163 | "_NET_WM_WINDOW_TYPE" , |
164 | "_NET_WM_WINDOW_TYPE_COMBO" , |
165 | "_NET_WM_WINDOW_TYPE_DIALOG" , |
166 | "_NET_WM_WINDOW_TYPE_DND" , |
167 | "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU" , |
168 | "_NET_WM_WINDOW_TYPE_MENU" , |
169 | "_NET_WM_WINDOW_TYPE_NORMAL" , |
170 | "_NET_WM_WINDOW_TYPE_POPUP_MENU" , |
171 | "_NET_WM_WINDOW_TYPE_TOOLTIP" , |
172 | "_NET_WM_WINDOW_TYPE_UTILITY" , |
173 | "_NET_WM_USER_TIME" , |
174 | "_NET_WM_USER_TIME_WINDOW" , |
175 | "_NET_VIRTUAL_ROOTS" , |
176 | "GDK_SELECTION" , |
177 | "_NET_WM_STATE_FOCUSED" , |
178 | "GDK_VISUALS" , |
179 | "XdndAware" , |
180 | "XdndProxy" , |
181 | "XdndActionAsk" , |
182 | "XdndActionCopy" , |
183 | "XdndActionLink" , |
184 | "XdndActionList" , |
185 | "XdndActionMove" , |
186 | "XdndActionPrivate" , |
187 | "XdndDrop" , |
188 | "XdndEnter" , |
189 | "XdndFinished" , |
190 | "XdndLeave" , |
191 | "XdndPosition" , |
192 | "XdndSelection" , |
193 | "XdndStatus" , |
194 | "XdndTypeList" |
195 | }; |
196 | |
197 | static char *gdk_sm_client_id; |
198 | |
199 | static guint signals[LAST_SIGNAL] = { 0 }; |
200 | |
201 | G_DEFINE_TYPE_WITH_CODE (GdkX11Display, gdk_x11_display, GDK_TYPE_DISPLAY, |
202 | G_IMPLEMENT_INTERFACE (GDK_TYPE_EVENT_TRANSLATOR, |
203 | gdk_x11_display_event_translator_init)) |
204 | |
205 | static void |
206 | gdk_x11_display_init (GdkX11Display *self) |
207 | { |
208 | self->monitors = g_list_store_new (GDK_TYPE_MONITOR); |
209 | } |
210 | |
211 | static void |
212 | gdk_x11_display_event_translator_init (GdkEventTranslatorIface *iface) |
213 | { |
214 | iface->translate_event = gdk_x11_display_translate_event; |
215 | } |
216 | |
217 | #define ANY_EDGE_TILED (GDK_TOPLEVEL_STATE_LEFT_TILED | \ |
218 | GDK_TOPLEVEL_STATE_RIGHT_TILED | \ |
219 | GDK_TOPLEVEL_STATE_TOP_TILED | \ |
220 | GDK_TOPLEVEL_STATE_BOTTOM_TILED) |
221 | |
222 | static void |
223 | do_edge_constraint_state_check (GdkSurface *surface, |
224 | GdkToplevelState old_state, |
225 | GdkToplevelState *set, |
226 | GdkToplevelState *unset) |
227 | { |
228 | GdkToplevelX11 *toplevel = _gdk_x11_surface_get_toplevel (window: surface); |
229 | GdkToplevelState local_set, local_unset; |
230 | guint edge_constraints; |
231 | |
232 | local_set = *set; |
233 | local_unset = *unset; |
234 | edge_constraints = toplevel->edge_constraints; |
235 | |
236 | /* If the WM doesn't support _GTK_EDGE_CONSTRAINTS, rely on the fallback |
237 | * implementation. If it supports _GTK_EDGE_CONSTRAINTS, arrange for |
238 | * GDK_TOPLEVEL_STATE_TILED to be set if any edge is tiled, and cleared |
239 | * if no edge is tiled. |
240 | */ |
241 | if (!gdk_x11_surface_supports_edge_constraints (surface)) |
242 | { |
243 | /* FIXME: we rely on implementation details of mutter here: |
244 | * mutter only tiles horizontally, and sets maxvert when it does |
245 | * and if it tiles, it always affects all edges |
246 | */ |
247 | if (old_state & GDK_TOPLEVEL_STATE_TILED) |
248 | { |
249 | if (!toplevel->have_maxvert) |
250 | local_unset |= GDK_TOPLEVEL_STATE_TILED; |
251 | } |
252 | else |
253 | { |
254 | if (toplevel->have_maxvert && !toplevel->have_maxhorz) |
255 | local_set |= GDK_TOPLEVEL_STATE_TILED; |
256 | } |
257 | } |
258 | else |
259 | { |
260 | if (old_state & GDK_TOPLEVEL_STATE_TILED) |
261 | { |
262 | if (!(edge_constraints & ANY_EDGE_TILED)) |
263 | local_unset |= GDK_TOPLEVEL_STATE_TILED; |
264 | } |
265 | else |
266 | { |
267 | if (edge_constraints & ANY_EDGE_TILED) |
268 | local_set |= GDK_TOPLEVEL_STATE_TILED; |
269 | } |
270 | } |
271 | |
272 | /* Top edge */ |
273 | if (old_state & GDK_TOPLEVEL_STATE_TOP_TILED) |
274 | { |
275 | if ((edge_constraints & GDK_TOPLEVEL_STATE_TOP_TILED) == 0) |
276 | local_unset |= GDK_TOPLEVEL_STATE_TOP_TILED; |
277 | } |
278 | else |
279 | { |
280 | if (edge_constraints & GDK_TOPLEVEL_STATE_TOP_TILED) |
281 | local_set |= GDK_TOPLEVEL_STATE_TOP_TILED; |
282 | } |
283 | |
284 | if (old_state & GDK_TOPLEVEL_STATE_TOP_RESIZABLE) |
285 | { |
286 | if ((edge_constraints & GDK_TOPLEVEL_STATE_TOP_RESIZABLE) == 0) |
287 | local_unset |= GDK_TOPLEVEL_STATE_TOP_RESIZABLE; |
288 | } |
289 | else |
290 | { |
291 | if (edge_constraints & GDK_TOPLEVEL_STATE_TOP_RESIZABLE) |
292 | local_set |= GDK_TOPLEVEL_STATE_TOP_RESIZABLE; |
293 | } |
294 | |
295 | /* Right edge */ |
296 | if (old_state & GDK_TOPLEVEL_STATE_RIGHT_TILED) |
297 | { |
298 | if ((edge_constraints & GDK_TOPLEVEL_STATE_RIGHT_TILED) == 0) |
299 | local_unset |= GDK_TOPLEVEL_STATE_RIGHT_TILED; |
300 | } |
301 | else |
302 | { |
303 | if (edge_constraints & GDK_TOPLEVEL_STATE_RIGHT_TILED) |
304 | local_set |= GDK_TOPLEVEL_STATE_RIGHT_TILED; |
305 | } |
306 | |
307 | if (old_state & GDK_TOPLEVEL_STATE_RIGHT_RESIZABLE) |
308 | { |
309 | if ((edge_constraints & GDK_TOPLEVEL_STATE_RIGHT_RESIZABLE) == 0) |
310 | local_unset |= GDK_TOPLEVEL_STATE_RIGHT_RESIZABLE; |
311 | } |
312 | else |
313 | { |
314 | if (edge_constraints & GDK_TOPLEVEL_STATE_RIGHT_RESIZABLE) |
315 | local_set |= GDK_TOPLEVEL_STATE_RIGHT_RESIZABLE; |
316 | } |
317 | |
318 | /* Bottom edge */ |
319 | if (old_state & GDK_TOPLEVEL_STATE_BOTTOM_TILED) |
320 | { |
321 | if ((edge_constraints & GDK_TOPLEVEL_STATE_BOTTOM_TILED) == 0) |
322 | local_unset |= GDK_TOPLEVEL_STATE_BOTTOM_TILED; |
323 | } |
324 | else |
325 | { |
326 | if (edge_constraints & GDK_TOPLEVEL_STATE_BOTTOM_TILED) |
327 | local_set |= GDK_TOPLEVEL_STATE_BOTTOM_TILED; |
328 | } |
329 | |
330 | if (old_state & GDK_TOPLEVEL_STATE_BOTTOM_RESIZABLE) |
331 | { |
332 | if ((edge_constraints & GDK_TOPLEVEL_STATE_BOTTOM_RESIZABLE) == 0) |
333 | local_unset |= GDK_TOPLEVEL_STATE_BOTTOM_RESIZABLE; |
334 | } |
335 | else |
336 | { |
337 | if (edge_constraints & GDK_TOPLEVEL_STATE_BOTTOM_RESIZABLE) |
338 | local_set |= GDK_TOPLEVEL_STATE_BOTTOM_RESIZABLE; |
339 | } |
340 | |
341 | /* Left edge */ |
342 | if (old_state & GDK_TOPLEVEL_STATE_LEFT_TILED) |
343 | { |
344 | if ((edge_constraints & GDK_TOPLEVEL_STATE_LEFT_TILED) == 0) |
345 | local_unset |= GDK_TOPLEVEL_STATE_LEFT_TILED; |
346 | } |
347 | else |
348 | { |
349 | if (edge_constraints & GDK_TOPLEVEL_STATE_LEFT_TILED) |
350 | local_set |= GDK_TOPLEVEL_STATE_LEFT_TILED; |
351 | } |
352 | |
353 | if (old_state & GDK_TOPLEVEL_STATE_LEFT_RESIZABLE) |
354 | { |
355 | if ((edge_constraints & GDK_TOPLEVEL_STATE_LEFT_RESIZABLE) == 0) |
356 | local_unset |= GDK_TOPLEVEL_STATE_LEFT_RESIZABLE; |
357 | } |
358 | else |
359 | { |
360 | if (edge_constraints & GDK_TOPLEVEL_STATE_LEFT_RESIZABLE) |
361 | local_set |= GDK_TOPLEVEL_STATE_LEFT_RESIZABLE; |
362 | } |
363 | |
364 | *set = local_set; |
365 | *unset = local_unset; |
366 | } |
367 | |
368 | static void |
369 | do_net_wm_state_changes (GdkSurface *surface) |
370 | { |
371 | GdkToplevelX11 *toplevel = _gdk_x11_surface_get_toplevel (window: surface); |
372 | GdkToplevelState old_state, set, unset; |
373 | |
374 | if (GDK_SURFACE_DESTROYED (surface) || |
375 | !GDK_IS_TOPLEVEL (ptr: surface)) |
376 | return; |
377 | |
378 | old_state = gdk_toplevel_get_state (toplevel: GDK_TOPLEVEL (ptr: surface)); |
379 | |
380 | set = unset = 0; |
381 | |
382 | if (old_state & GDK_TOPLEVEL_STATE_FULLSCREEN) |
383 | { |
384 | if (!toplevel->have_fullscreen) |
385 | unset |= GDK_TOPLEVEL_STATE_FULLSCREEN; |
386 | } |
387 | else |
388 | { |
389 | if (toplevel->have_fullscreen) |
390 | set |= GDK_TOPLEVEL_STATE_FULLSCREEN; |
391 | } |
392 | |
393 | /* Our "maximized" means both vertical and horizontal; if only one, |
394 | * we don't expose that via GDK |
395 | */ |
396 | if (old_state & GDK_TOPLEVEL_STATE_MAXIMIZED) |
397 | { |
398 | if (!(toplevel->have_maxvert && toplevel->have_maxhorz)) |
399 | unset |= GDK_TOPLEVEL_STATE_MAXIMIZED; |
400 | } |
401 | else |
402 | { |
403 | if (toplevel->have_maxvert && toplevel->have_maxhorz) |
404 | set |= GDK_TOPLEVEL_STATE_MAXIMIZED; |
405 | } |
406 | |
407 | if (old_state & GDK_TOPLEVEL_STATE_FOCUSED) |
408 | { |
409 | if (!toplevel->have_focused) |
410 | unset |= GDK_TOPLEVEL_STATE_FOCUSED; |
411 | } |
412 | else |
413 | { |
414 | if (toplevel->have_focused) |
415 | set |= GDK_TOPLEVEL_STATE_FOCUSED; |
416 | } |
417 | |
418 | if (old_state & GDK_TOPLEVEL_STATE_MINIMIZED) |
419 | { |
420 | if (!toplevel->have_hidden) |
421 | unset |= GDK_TOPLEVEL_STATE_MINIMIZED; |
422 | } |
423 | else |
424 | { |
425 | if (toplevel->have_hidden) |
426 | set |= GDK_TOPLEVEL_STATE_MINIMIZED; |
427 | } |
428 | |
429 | /* Update edge constraints and tiling */ |
430 | do_edge_constraint_state_check (surface, old_state, set: &set, unset: &unset); |
431 | |
432 | gdk_synthesize_surface_state (surface, unset_flags: unset, set_flags: set); |
433 | } |
434 | |
435 | static void |
436 | gdk_check_wm_desktop_changed (GdkSurface *surface) |
437 | { |
438 | GdkToplevelX11 *toplevel = _gdk_x11_surface_get_toplevel (window: surface); |
439 | GdkDisplay *display = GDK_SURFACE_DISPLAY (surface); |
440 | |
441 | Atom type; |
442 | int format; |
443 | gulong nitems; |
444 | gulong bytes_after; |
445 | guchar *data; |
446 | gulong *desktop; |
447 | |
448 | type = None; |
449 | gdk_x11_display_error_trap_push (display); |
450 | XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), |
451 | GDK_SURFACE_XID (surface), |
452 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_DESKTOP" ), |
453 | 0, G_MAXLONG, False, XA_CARDINAL, &type, |
454 | &format, &nitems, |
455 | &bytes_after, &data); |
456 | gdk_x11_display_error_trap_pop_ignored (display); |
457 | |
458 | if (type != None) |
459 | { |
460 | desktop = (gulong *)data; |
461 | toplevel->on_all_desktops = ((*desktop & 0xFFFFFFFF) == 0xFFFFFFFF); |
462 | XFree (desktop); |
463 | } |
464 | else |
465 | toplevel->on_all_desktops = FALSE; |
466 | |
467 | do_net_wm_state_changes (surface); |
468 | } |
469 | |
470 | static void |
471 | gdk_check_wm_state_changed (GdkSurface *surface) |
472 | { |
473 | GdkToplevelX11 *toplevel = _gdk_x11_surface_get_toplevel (window: surface); |
474 | GdkDisplay *display = GDK_SURFACE_DISPLAY (surface); |
475 | GdkX11Screen *screen = GDK_SURFACE_SCREEN (surface); |
476 | |
477 | Atom type; |
478 | int format; |
479 | gulong nitems; |
480 | gulong bytes_after; |
481 | guchar *data; |
482 | Atom *atoms = NULL; |
483 | gulong i; |
484 | |
485 | toplevel->have_maxvert = FALSE; |
486 | toplevel->have_maxhorz = FALSE; |
487 | toplevel->have_fullscreen = FALSE; |
488 | toplevel->have_focused = FALSE; |
489 | toplevel->have_hidden = FALSE; |
490 | |
491 | type = None; |
492 | gdk_x11_display_error_trap_push (display); |
493 | XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XID (surface), |
494 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_STATE" ), |
495 | 0, G_MAXLONG, False, XA_ATOM, &type, &format, &nitems, |
496 | &bytes_after, &data); |
497 | gdk_x11_display_error_trap_pop_ignored (display); |
498 | |
499 | if (type != None) |
500 | { |
501 | Atom maxvert_atom = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_STATE_MAXIMIZED_VERT" ); |
502 | Atom maxhorz_atom = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_STATE_MAXIMIZED_HORZ" ); |
503 | Atom fullscreen_atom = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_STATE_FULLSCREEN" ); |
504 | Atom focused_atom = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_STATE_FOCUSED" ); |
505 | Atom hidden_atom = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_STATE_HIDDEN" ); |
506 | |
507 | atoms = (Atom *)data; |
508 | |
509 | i = 0; |
510 | while (i < nitems) |
511 | { |
512 | if (atoms[i] == maxvert_atom) |
513 | toplevel->have_maxvert = TRUE; |
514 | else if (atoms[i] == maxhorz_atom) |
515 | toplevel->have_maxhorz = TRUE; |
516 | else if (atoms[i] == fullscreen_atom) |
517 | toplevel->have_fullscreen = TRUE; |
518 | else if (atoms[i] == focused_atom) |
519 | toplevel->have_focused = TRUE; |
520 | else if (atoms[i] == hidden_atom) |
521 | toplevel->have_hidden = TRUE; |
522 | |
523 | ++i; |
524 | } |
525 | |
526 | XFree (atoms); |
527 | } |
528 | |
529 | if (!gdk_x11_screen_supports_net_wm_hint (screen, |
530 | property_name: g_intern_static_string (string: "_NET_WM_STATE_FOCUSED" ))) |
531 | toplevel->have_focused = TRUE; |
532 | |
533 | do_net_wm_state_changes (surface); |
534 | } |
535 | |
536 | static void |
537 | gdk_check_edge_constraints_changed (GdkSurface *surface) |
538 | { |
539 | GdkToplevelX11 *toplevel = _gdk_x11_surface_get_toplevel (window: surface); |
540 | GdkDisplay *display = GDK_SURFACE_DISPLAY (surface); |
541 | |
542 | Atom type; |
543 | int format; |
544 | gulong nitems; |
545 | gulong bytes_after; |
546 | guchar *data; |
547 | gulong *constraints; |
548 | |
549 | type = None; |
550 | gdk_x11_display_error_trap_push (display); |
551 | XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), |
552 | GDK_SURFACE_XID (surface), |
553 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_GTK_EDGE_CONSTRAINTS" ), |
554 | 0, G_MAXLONG, False, XA_CARDINAL, &type, |
555 | &format, &nitems, |
556 | &bytes_after, &data); |
557 | gdk_x11_display_error_trap_pop_ignored (display); |
558 | |
559 | if (type != None) |
560 | { |
561 | constraints = (gulong *)data; |
562 | |
563 | /* The GDK enum for these states does not begin at zero so, to avoid |
564 | * messing around with shifts, just make the passed value and GDK's |
565 | * enum values match by shifting to the first tiled state. |
566 | */ |
567 | toplevel->edge_constraints = constraints[0] << 8; |
568 | |
569 | XFree (constraints); |
570 | } |
571 | else |
572 | { |
573 | toplevel->edge_constraints = 0; |
574 | } |
575 | |
576 | do_net_wm_state_changes (surface); |
577 | } |
578 | |
579 | static Window |
580 | get_event_xwindow (const XEvent *xevent) |
581 | { |
582 | Window xwindow; |
583 | |
584 | switch (xevent->type) |
585 | { |
586 | case DestroyNotify: |
587 | xwindow = xevent->xdestroywindow.window; |
588 | break; |
589 | case UnmapNotify: |
590 | xwindow = xevent->xunmap.window; |
591 | break; |
592 | case MapNotify: |
593 | xwindow = xevent->xmap.window; |
594 | break; |
595 | case ConfigureNotify: |
596 | xwindow = xevent->xconfigure.window; |
597 | break; |
598 | case ReparentNotify: |
599 | xwindow = xevent->xreparent.window; |
600 | break; |
601 | case GravityNotify: |
602 | xwindow = xevent->xgravity.window; |
603 | break; |
604 | case CirculateNotify: |
605 | xwindow = xevent->xcirculate.window; |
606 | break; |
607 | default: |
608 | xwindow = xevent->xany.window; |
609 | } |
610 | |
611 | return xwindow; |
612 | } |
613 | |
614 | static GdkEvent * |
615 | gdk_x11_display_translate_event (GdkEventTranslator *translator, |
616 | GdkDisplay *display, |
617 | const XEvent *xevent) |
618 | { |
619 | Window xwindow; |
620 | GdkSurface *surface; |
621 | GdkX11Surface *surface_impl = NULL; |
622 | GdkX11Screen *x11_screen = NULL; |
623 | GdkToplevelX11 *toplevel = NULL; |
624 | GdkX11Display *display_x11 = GDK_X11_DISPLAY (display); |
625 | GdkEvent *event; |
626 | |
627 | event = NULL; |
628 | |
629 | xwindow = get_event_xwindow (xevent); |
630 | if (xwindow != xevent->xany.window) |
631 | return NULL; |
632 | |
633 | surface = gdk_x11_surface_lookup_for_display (display, window: xwindow); |
634 | if (surface) |
635 | { |
636 | /* We may receive events such as NoExpose/GraphicsExpose |
637 | * and ShmCompletion for pixmaps |
638 | */ |
639 | if (!GDK_IS_SURFACE (surface)) |
640 | return NULL; |
641 | |
642 | x11_screen = GDK_SURFACE_SCREEN (surface); |
643 | toplevel = _gdk_x11_surface_get_toplevel (window: surface); |
644 | surface_impl = GDK_X11_SURFACE (surface); |
645 | |
646 | g_object_ref (surface); |
647 | } |
648 | |
649 | if (surface && GDK_SURFACE_DESTROYED (surface)) |
650 | { |
651 | if (xevent->type != DestroyNotify) |
652 | goto done; |
653 | } |
654 | |
655 | if (xevent->type == DestroyNotify) |
656 | { |
657 | x11_screen = GDK_X11_DISPLAY (display)->screen; |
658 | |
659 | if (x11_screen->wmspec_check_window == xevent->xdestroywindow.window) |
660 | { |
661 | x11_screen->wmspec_check_window = None; |
662 | x11_screen->last_wmspec_check_time = 0; |
663 | g_free (mem: x11_screen->window_manager_name); |
664 | x11_screen->window_manager_name = g_strdup (str: "unknown" ); |
665 | |
666 | /* careful, reentrancy */ |
667 | _gdk_x11_screen_window_manager_changed (screen: x11_screen); |
668 | |
669 | goto done; |
670 | } |
671 | } |
672 | |
673 | /* We do a "manual" conversion of the XEvent to a |
674 | * GdkEvent. The structures are mostly the same so |
675 | * the conversion is fairly straightforward. We also |
676 | * optionally print debugging info regarding events |
677 | * received. |
678 | */ |
679 | |
680 | switch (xevent->type) |
681 | { |
682 | case KeymapNotify: |
683 | GDK_DISPLAY_NOTE (display, EVENTS, g_message ("keymap notify" )); |
684 | |
685 | /* Not currently handled */ |
686 | break; |
687 | |
688 | case Expose: |
689 | GDK_DISPLAY_NOTE (display, EVENTS, |
690 | g_message ("expose:\t\twindow: %ld %d x,y: %d %d w,h: %d %d" , |
691 | xevent->xexpose.window, xevent->xexpose.count, |
692 | xevent->xexpose.x, xevent->xexpose.y, |
693 | xevent->xexpose.width, xevent->xexpose.height)); |
694 | |
695 | if (surface == NULL) |
696 | break; |
697 | |
698 | { |
699 | GdkRectangle expose_rect; |
700 | int x2, y2; |
701 | |
702 | expose_rect.x = xevent->xexpose.x / surface_impl->surface_scale; |
703 | expose_rect.y = xevent->xexpose.y / surface_impl->surface_scale; |
704 | |
705 | x2 = (xevent->xexpose.x + xevent->xexpose.width + surface_impl->surface_scale -1) / surface_impl->surface_scale; |
706 | expose_rect.width = x2 - expose_rect.x; |
707 | |
708 | y2 = (xevent->xexpose.y + xevent->xexpose.height + surface_impl->surface_scale -1) / surface_impl->surface_scale; |
709 | expose_rect.height = y2 - expose_rect.y; |
710 | |
711 | gdk_surface_invalidate_rect (surface, rect: &expose_rect); |
712 | } |
713 | |
714 | break; |
715 | |
716 | case GraphicsExpose: |
717 | { |
718 | GdkRectangle expose_rect; |
719 | int x2, y2; |
720 | |
721 | GDK_DISPLAY_NOTE (display, EVENTS, |
722 | g_message ("graphics expose:\tdrawable: %ld" , |
723 | xevent->xgraphicsexpose.drawable)); |
724 | |
725 | if (surface == NULL) |
726 | break; |
727 | |
728 | expose_rect.x = xevent->xgraphicsexpose.x / surface_impl->surface_scale; |
729 | expose_rect.y = xevent->xgraphicsexpose.y / surface_impl->surface_scale; |
730 | |
731 | x2 = (xevent->xgraphicsexpose.x + xevent->xgraphicsexpose.width + surface_impl->surface_scale -1) / surface_impl->surface_scale; |
732 | expose_rect.width = x2 - expose_rect.x; |
733 | |
734 | y2 = (xevent->xgraphicsexpose.y + xevent->xgraphicsexpose.height + surface_impl->surface_scale -1) / surface_impl->surface_scale; |
735 | expose_rect.height = y2 - expose_rect.y; |
736 | |
737 | gdk_surface_invalidate_rect (surface, rect: &expose_rect); |
738 | } |
739 | break; |
740 | |
741 | case VisibilityNotify: |
742 | #ifdef G_ENABLE_DEBUG |
743 | if (GDK_DISPLAY_DEBUG_CHECK (display, EVENTS)) |
744 | switch (xevent->xvisibility.state) |
745 | { |
746 | case VisibilityFullyObscured: |
747 | g_message ("visibility notify:\twindow: %ld none" , |
748 | xevent->xvisibility.window); |
749 | break; |
750 | case VisibilityPartiallyObscured: |
751 | g_message ("visibility notify:\twindow: %ld partial" , |
752 | xevent->xvisibility.window); |
753 | break; |
754 | case VisibilityUnobscured: |
755 | g_message ("visibility notify:\twindow: %ld full" , |
756 | xevent->xvisibility.window); |
757 | break; |
758 | default: |
759 | break; |
760 | } |
761 | #endif /* G_ENABLE_DEBUG */ |
762 | /* not handled */ |
763 | break; |
764 | |
765 | case CreateNotify: |
766 | GDK_DISPLAY_NOTE (display, EVENTS, |
767 | g_message ("create notify:\twindow: %ld x,y: %d %d w,h: %d %d b-w: %d parent: %ld ovr: %d" , |
768 | xevent->xcreatewindow.window, |
769 | xevent->xcreatewindow.x, |
770 | xevent->xcreatewindow.y, |
771 | xevent->xcreatewindow.width, |
772 | xevent->xcreatewindow.height, |
773 | xevent->xcreatewindow.border_width, |
774 | xevent->xcreatewindow.parent, |
775 | xevent->xcreatewindow.override_redirect)); |
776 | /* not really handled */ |
777 | break; |
778 | |
779 | case DestroyNotify: |
780 | GDK_DISPLAY_NOTE (display, EVENTS, |
781 | g_message ("destroy notify:\twindow: %ld" , |
782 | xevent->xdestroywindow.window)); |
783 | |
784 | if (surface) |
785 | event = gdk_delete_event_new (surface); |
786 | |
787 | if (surface && GDK_SURFACE_XID (surface) != x11_screen->xroot_window) |
788 | gdk_surface_destroy_notify (surface); |
789 | |
790 | break; |
791 | |
792 | case UnmapNotify: |
793 | GDK_DISPLAY_NOTE (display, EVENTS, |
794 | g_message ("unmap notify:\t\twindow: %ld" , |
795 | xevent->xmap.window)); |
796 | |
797 | if (surface) |
798 | { |
799 | /* If the WM supports the _NET_WM_STATE_HIDDEN hint, we do not want to |
800 | * interpret UnmapNotify events as implying iconic state. |
801 | * http://bugzilla.gnome.org/show_bug.cgi?id=590726. |
802 | */ |
803 | if (x11_screen && |
804 | !gdk_x11_screen_supports_net_wm_hint (screen: x11_screen, |
805 | property_name: g_intern_static_string (string: "_NET_WM_STATE_HIDDEN" ))) |
806 | { |
807 | /* If we are shown (not withdrawn) and get an unmap, it means we were |
808 | * iconified in the X sense. If we are withdrawn, and get an unmap, it |
809 | * means we hid the window ourselves, so we will have already flipped |
810 | * the minimized bit off. |
811 | */ |
812 | if (GDK_SURFACE_IS_MAPPED (surface)) |
813 | gdk_synthesize_surface_state (surface, |
814 | unset_flags: 0, |
815 | set_flags: GDK_TOPLEVEL_STATE_MINIMIZED); |
816 | } |
817 | |
818 | if (surface_impl->toplevel && |
819 | surface_impl->toplevel->frame_pending) |
820 | { |
821 | surface_impl->toplevel->frame_pending = FALSE; |
822 | gdk_surface_thaw_updates (surface); |
823 | } |
824 | |
825 | if (toplevel) |
826 | gdk_surface_freeze_updates (surface); |
827 | |
828 | _gdk_x11_surface_grab_check_unmap (window: surface, serial: xevent->xany.serial); |
829 | |
830 | gdk_profiler_add_markf (GDK_PROFILER_CURRENT_TIME, 0, "unmapped window" , "0x%lx" , GDK_SURFACE_XID (surface)); |
831 | } |
832 | |
833 | break; |
834 | |
835 | case MapNotify: |
836 | GDK_DISPLAY_NOTE (display, EVENTS, |
837 | g_message ("map notify:\t\twindow: %ld" , |
838 | xevent->xmap.window)); |
839 | |
840 | if (surface) |
841 | { |
842 | /* Unset minimized if it was set */ |
843 | if (surface->state & GDK_TOPLEVEL_STATE_MINIMIZED) |
844 | gdk_synthesize_surface_state (surface, |
845 | unset_flags: GDK_TOPLEVEL_STATE_MINIMIZED, |
846 | set_flags: 0); |
847 | |
848 | if (toplevel) |
849 | gdk_surface_thaw_updates (surface); |
850 | |
851 | if (GDK_PROFILER_IS_RUNNING) |
852 | { |
853 | gdk_profiler_end_markf (surface_impl->map_time, "mapped window" , "0x%lx" , GDK_SURFACE_XID (surface)); |
854 | surface_impl->map_time = 0; |
855 | } |
856 | } |
857 | |
858 | break; |
859 | |
860 | case ReparentNotify: |
861 | GDK_DISPLAY_NOTE (display, EVENTS, |
862 | g_message ("reparent notify:\twindow: %ld x,y: %d %d parent: %ld ovr: %d" , |
863 | xevent->xreparent.window, |
864 | xevent->xreparent.x, |
865 | xevent->xreparent.y, |
866 | xevent->xreparent.parent, |
867 | xevent->xreparent.override_redirect)); |
868 | |
869 | /* Not currently handled */ |
870 | break; |
871 | |
872 | case ConfigureNotify: |
873 | GDK_DISPLAY_NOTE (display, EVENTS, |
874 | g_message ("configure notify:\twindow: %ld x,y: %d %d w,h: %d %d b-w: %d above: %ld ovr: %d%s" , |
875 | xevent->xconfigure.window, |
876 | xevent->xconfigure.x, |
877 | xevent->xconfigure.y, |
878 | xevent->xconfigure.width, |
879 | xevent->xconfigure.height, |
880 | xevent->xconfigure.border_width, |
881 | xevent->xconfigure.above, |
882 | xevent->xconfigure.override_redirect, |
883 | !surface ? " (discarding)" : "" )); |
884 | if (_gdk_x11_display_is_root_window (display, xroot_window: xevent->xconfigure.window)) |
885 | { |
886 | _gdk_x11_screen_size_changed (screen: x11_screen, event: xevent); |
887 | } |
888 | |
889 | #ifdef HAVE_XSYNC |
890 | if (toplevel && display_x11->use_sync && toplevel->pending_counter_value != 0) |
891 | { |
892 | toplevel->configure_counter_value = toplevel->pending_counter_value; |
893 | toplevel->configure_counter_value_is_extended = toplevel->pending_counter_value_is_extended; |
894 | toplevel->pending_counter_value = 0; |
895 | } |
896 | #endif |
897 | |
898 | if (surface && |
899 | xevent->xconfigure.event == xevent->xconfigure.window) |
900 | { |
901 | int x, y; |
902 | int configured_width; |
903 | int configured_height; |
904 | int new_abs_x, new_abs_y; |
905 | |
906 | configured_width = |
907 | (xevent->xconfigure.width + surface_impl->surface_scale - 1) / |
908 | surface_impl->surface_scale; |
909 | configured_height = |
910 | (xevent->xconfigure.height + surface_impl->surface_scale - 1) / |
911 | surface_impl->surface_scale; |
912 | |
913 | if (!xevent->xconfigure.send_event && |
914 | !xevent->xconfigure.override_redirect && |
915 | !GDK_SURFACE_DESTROYED (surface)) |
916 | { |
917 | int tx = 0; |
918 | int ty = 0; |
919 | Window child_window = 0; |
920 | |
921 | x = y = 0; |
922 | gdk_x11_display_error_trap_push (display); |
923 | if (XTranslateCoordinates (GDK_SURFACE_XDISPLAY (surface), |
924 | GDK_SURFACE_XID (surface), |
925 | x11_screen->xroot_window, |
926 | 0, 0, |
927 | &tx, &ty, |
928 | &child_window)) |
929 | { |
930 | x = tx / surface_impl->surface_scale; |
931 | y = ty / surface_impl->surface_scale; |
932 | } |
933 | gdk_x11_display_error_trap_pop_ignored (display); |
934 | } |
935 | else |
936 | { |
937 | x = xevent->xconfigure.x / surface_impl->surface_scale; |
938 | y = xevent->xconfigure.y / surface_impl->surface_scale; |
939 | } |
940 | |
941 | new_abs_x = x; |
942 | new_abs_y = y; |
943 | |
944 | surface_impl->abs_x = new_abs_x; |
945 | surface_impl->abs_y = new_abs_y; |
946 | |
947 | if (surface->parent) |
948 | { |
949 | GdkX11Surface *parent_impl = |
950 | GDK_X11_SURFACE (surface->parent); |
951 | |
952 | surface->x = new_abs_x - parent_impl->abs_x; |
953 | surface->y = new_abs_y - parent_impl->abs_y; |
954 | } |
955 | |
956 | if (surface_impl->unscaled_width != xevent->xconfigure.width || |
957 | surface_impl->unscaled_height != xevent->xconfigure.height) |
958 | { |
959 | surface_impl->unscaled_width = xevent->xconfigure.width; |
960 | surface_impl->unscaled_height = xevent->xconfigure.height; |
961 | |
962 | surface_impl->next_layout.configured_width = configured_width; |
963 | surface_impl->next_layout.configured_height = configured_height; |
964 | surface_impl->next_layout.surface_geometry_dirty = TRUE; |
965 | surface_impl->next_layout.configure_pending = TRUE; |
966 | gdk_surface_request_layout (surface); |
967 | } |
968 | |
969 | if (surface->resize_count >= 1) |
970 | { |
971 | surface->resize_count -= 1; |
972 | |
973 | if (surface->resize_count == 0) |
974 | _gdk_x11_moveresize_configure_done (display, window: surface); |
975 | } |
976 | |
977 | gdk_x11_surface_update_popups (surface); |
978 | gdk_x11_surface_enter_leave_monitors (surface); |
979 | } |
980 | break; |
981 | |
982 | case PropertyNotify: |
983 | GDK_DISPLAY_NOTE (display, EVENTS, |
984 | g_message ("property notify:\twindow: %ld, atom(%ld): %s%s%s" , |
985 | xevent->xproperty.window, |
986 | xevent->xproperty.atom, |
987 | "\"" , |
988 | gdk_x11_get_xatom_name_for_display (display, xevent->xproperty.atom), |
989 | "\"" )); |
990 | |
991 | if (surface == NULL) |
992 | break; |
993 | |
994 | /* We compare with the serial of the last time we mapped the |
995 | * window to avoid refetching properties that we set ourselves |
996 | */ |
997 | if (toplevel && |
998 | xevent->xproperty.serial >= toplevel->map_serial) |
999 | { |
1000 | if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_STATE" )) |
1001 | gdk_check_wm_state_changed (surface); |
1002 | |
1003 | if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_DESKTOP" )) |
1004 | gdk_check_wm_desktop_changed (surface); |
1005 | |
1006 | if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_GTK_EDGE_CONSTRAINTS" )) |
1007 | gdk_check_edge_constraints_changed (surface); |
1008 | } |
1009 | break; |
1010 | |
1011 | case ColormapNotify: |
1012 | GDK_DISPLAY_NOTE (display, EVENTS, |
1013 | g_message ("colormap notify:\twindow: %ld" , |
1014 | xevent->xcolormap.window)); |
1015 | |
1016 | /* Not currently handled */ |
1017 | break; |
1018 | |
1019 | case ClientMessage: |
1020 | GDK_DISPLAY_NOTE (display, EVENTS, |
1021 | g_message ("client message:\twindow: %ld" , |
1022 | xevent->xclient.window)); |
1023 | |
1024 | /* Not currently handled */ |
1025 | break; |
1026 | |
1027 | case MappingNotify: |
1028 | GDK_DISPLAY_NOTE (display, EVENTS, |
1029 | g_message ("mapping notify" )); |
1030 | |
1031 | /* Let XLib know that there is a new keyboard mapping. |
1032 | */ |
1033 | XRefreshKeyboardMapping ((XMappingEvent *) xevent); |
1034 | _gdk_x11_keymap_keys_changed (display); |
1035 | break; |
1036 | |
1037 | default: |
1038 | #ifdef HAVE_RANDR |
1039 | if (xevent->type - display_x11->xrandr_event_base == RRScreenChangeNotify || |
1040 | xevent->type - display_x11->xrandr_event_base == RRNotify) |
1041 | { |
1042 | if (display_x11->screen) |
1043 | _gdk_x11_screen_size_changed (screen: display_x11->screen, event: xevent); |
1044 | } |
1045 | else |
1046 | #endif |
1047 | #ifdef HAVE_XKB |
1048 | if (xevent->type == display_x11->xkb_event_type) |
1049 | { |
1050 | XkbEvent *xkb_event = (XkbEvent *) xevent; |
1051 | |
1052 | switch (xkb_event->any.xkb_type) |
1053 | { |
1054 | case XkbNewKeyboardNotify: |
1055 | case XkbMapNotify: |
1056 | _gdk_x11_keymap_keys_changed (display); |
1057 | break; |
1058 | |
1059 | case XkbStateNotify: |
1060 | _gdk_x11_keymap_state_changed (display, event: xevent); |
1061 | break; |
1062 | default: |
1063 | break; |
1064 | } |
1065 | } |
1066 | #endif |
1067 | } |
1068 | |
1069 | done: |
1070 | if (surface) |
1071 | g_object_unref (object: surface); |
1072 | |
1073 | return event; |
1074 | } |
1075 | |
1076 | static GdkFrameTimings * |
1077 | find_frame_timings (GdkFrameClock *clock, |
1078 | guint64 serial) |
1079 | { |
1080 | gint64 start_frame, end_frame, i; |
1081 | |
1082 | start_frame = gdk_frame_clock_get_history_start (frame_clock: clock); |
1083 | end_frame = gdk_frame_clock_get_frame_counter (frame_clock: clock); |
1084 | for (i = end_frame; i >= start_frame; i--) |
1085 | { |
1086 | GdkFrameTimings *timings = gdk_frame_clock_get_timings (frame_clock: clock, frame_counter: i); |
1087 | |
1088 | if (timings->cookie == serial) |
1089 | return timings; |
1090 | } |
1091 | |
1092 | return NULL; |
1093 | } |
1094 | |
1095 | /* _NET_WM_FRAME_DRAWN and _NET_WM_FRAME_TIMINGS messages represent time |
1096 | * as a "high resolution server time" - this is the server time interpolated |
1097 | * to microsecond resolution. The advantage of this time representation |
1098 | * is that if X server is running on the same computer as a client, and |
1099 | * the Xserver uses 'clock_gettime(CLOCK_MONOTONIC, ...)' for the server |
1100 | * time, the client can detect this, and all such clients will share a |
1101 | * a time representation with high accuracy. If there is not a common |
1102 | * time source, then the time synchronization will be less accurate. |
1103 | */ |
1104 | static gint64 |
1105 | server_time_to_monotonic_time (GdkX11Display *display_x11, |
1106 | gint64 server_time) |
1107 | { |
1108 | if (display_x11->server_time_query_time == 0 || |
1109 | (!display_x11->server_time_is_monotonic_time && |
1110 | server_time > display_x11->server_time_query_time + 10*1000*1000)) /* 10 seconds */ |
1111 | { |
1112 | gint64 current_server_time = gdk_x11_get_server_time (surface: display_x11->leader_gdk_surface); |
1113 | gint64 current_server_time_usec = (gint64)current_server_time * 1000; |
1114 | gint64 current_monotonic_time = g_get_monotonic_time (); |
1115 | display_x11->server_time_query_time = current_monotonic_time; |
1116 | |
1117 | /* If the server time is within a second of the monotonic time, |
1118 | * we assume that they are identical. This seems like a big margin, |
1119 | * but we want to be as robust as possible even if the system |
1120 | * is under load and our processing of the server response is |
1121 | * delayed. |
1122 | */ |
1123 | if (current_server_time_usec > current_monotonic_time - 1000*1000 && |
1124 | current_server_time_usec < current_monotonic_time + 1000*1000) |
1125 | display_x11->server_time_is_monotonic_time = TRUE; |
1126 | |
1127 | display_x11->server_time_offset = current_server_time_usec - current_monotonic_time; |
1128 | } |
1129 | |
1130 | if (display_x11->server_time_is_monotonic_time) |
1131 | return server_time; |
1132 | else |
1133 | return server_time - display_x11->server_time_offset; |
1134 | } |
1135 | |
1136 | GdkFilterReturn |
1137 | _gdk_wm_protocols_filter (const XEvent *xevent, |
1138 | GdkSurface *win, |
1139 | GdkEvent **event, |
1140 | gpointer data) |
1141 | { |
1142 | GdkDisplay *display; |
1143 | Atom atom; |
1144 | |
1145 | if (!GDK_IS_X11_SURFACE (win) || GDK_SURFACE_DESTROYED (win)) |
1146 | return GDK_FILTER_CONTINUE; |
1147 | |
1148 | if (xevent->type != ClientMessage) |
1149 | return GDK_FILTER_CONTINUE; |
1150 | |
1151 | display = GDK_SURFACE_DISPLAY (win); |
1152 | |
1153 | /* This isn't actually WM_PROTOCOLS because that wouldn't leave enough space |
1154 | * in the message for everything that gets stuffed in */ |
1155 | if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_FRAME_DRAWN" )) |
1156 | { |
1157 | GdkX11Surface *surface_impl; |
1158 | surface_impl = GDK_X11_SURFACE (win); |
1159 | if (surface_impl->toplevel) |
1160 | { |
1161 | guint32 d0 = xevent->xclient.data.l[0]; |
1162 | guint32 d1 = xevent->xclient.data.l[1]; |
1163 | guint32 d2 = xevent->xclient.data.l[2]; |
1164 | guint32 d3 = xevent->xclient.data.l[3]; |
1165 | |
1166 | guint64 serial = ((guint64)d1 << 32) | d0; |
1167 | gint64 frame_drawn_time = server_time_to_monotonic_time (GDK_X11_DISPLAY (display), server_time: ((guint64)d3 << 32) | d2); |
1168 | gint64 refresh_interval, presentation_time; |
1169 | |
1170 | GdkFrameClock *clock = gdk_surface_get_frame_clock (surface: win); |
1171 | GdkFrameTimings *timings = find_frame_timings (clock, serial); |
1172 | |
1173 | if (timings) |
1174 | timings->drawn_time = frame_drawn_time; |
1175 | |
1176 | if (!surface_impl->toplevel->frame_still_painting && surface_impl->toplevel->frame_pending) |
1177 | { |
1178 | surface_impl->toplevel->frame_pending = FALSE; |
1179 | gdk_surface_thaw_updates (surface: win); |
1180 | } |
1181 | |
1182 | gdk_frame_clock_get_refresh_info (frame_clock: clock, |
1183 | base_time: frame_drawn_time, |
1184 | refresh_interval_return: &refresh_interval, |
1185 | presentation_time_return: &presentation_time); |
1186 | if (presentation_time != 0) |
1187 | surface_impl->toplevel->throttled_presentation_time = presentation_time + refresh_interval; |
1188 | } |
1189 | |
1190 | return GDK_FILTER_REMOVE; |
1191 | } |
1192 | |
1193 | if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_FRAME_TIMINGS" )) |
1194 | { |
1195 | GdkX11Surface *surface_impl; |
1196 | surface_impl = GDK_X11_SURFACE (win); |
1197 | if (surface_impl->toplevel) |
1198 | { |
1199 | guint32 d0 = xevent->xclient.data.l[0]; |
1200 | guint32 d1 = xevent->xclient.data.l[1]; |
1201 | guint32 d2 = xevent->xclient.data.l[2]; |
1202 | guint32 d3 = xevent->xclient.data.l[3]; |
1203 | |
1204 | guint64 serial = ((guint64)d1 << 32) | d0; |
1205 | |
1206 | GdkFrameClock *clock = gdk_surface_get_frame_clock (surface: win); |
1207 | GdkFrameTimings *timings = find_frame_timings (clock, serial); |
1208 | |
1209 | if (timings) |
1210 | { |
1211 | gint32 presentation_time_offset = (gint32)d2; |
1212 | gint32 refresh_interval = d3; |
1213 | |
1214 | if (timings->drawn_time && presentation_time_offset) |
1215 | timings->presentation_time = timings->drawn_time + presentation_time_offset; |
1216 | |
1217 | if (refresh_interval) |
1218 | timings->refresh_interval = refresh_interval; |
1219 | |
1220 | timings->complete = TRUE; |
1221 | #ifdef G_ENABLE_DEBUG |
1222 | if (GDK_DISPLAY_DEBUG_CHECK (display, FRAMES)) |
1223 | _gdk_frame_clock_debug_print_timings (clock, timings); |
1224 | |
1225 | if (GDK_PROFILER_IS_RUNNING) |
1226 | _gdk_frame_clock_add_timings_to_profiler (frame_clock: clock, timings); |
1227 | #endif /* G_ENABLE_DEBUG */ |
1228 | } |
1229 | } |
1230 | } |
1231 | |
1232 | if (xevent->xclient.message_type != gdk_x11_get_xatom_by_name_for_display (display, atom_name: "WM_PROTOCOLS" )) |
1233 | return GDK_FILTER_CONTINUE; |
1234 | |
1235 | atom = (Atom) xevent->xclient.data.l[0]; |
1236 | |
1237 | if (atom == gdk_x11_get_xatom_by_name_for_display (display, atom_name: "WM_DELETE_WINDOW" )) |
1238 | { |
1239 | /* The delete window request specifies a window |
1240 | * to delete. We don't actually destroy the |
1241 | * window because "it is only a request". (The |
1242 | * window might contain vital data that the |
1243 | * program does not want destroyed). Instead |
1244 | * the event is passed along to the program, |
1245 | * which should then destroy the window. |
1246 | */ |
1247 | GDK_DISPLAY_NOTE (display, EVENTS, |
1248 | g_message ("delete window:\t\twindow: %ld" , |
1249 | xevent->xclient.window)); |
1250 | |
1251 | *event = gdk_delete_event_new (surface: win); |
1252 | |
1253 | gdk_x11_surface_set_user_time (surface: win, timestamp: xevent->xclient.data.l[1]); |
1254 | |
1255 | return GDK_FILTER_TRANSLATE; |
1256 | } |
1257 | else if (atom == gdk_x11_get_xatom_by_name_for_display (display, atom_name: "WM_TAKE_FOCUS" )) |
1258 | { |
1259 | GdkToplevelX11 *toplevel = _gdk_x11_surface_get_toplevel (window: win); |
1260 | |
1261 | /* There is no way of knowing reliably whether we are viewable; |
1262 | * so trap errors asynchronously around the XSetInputFocus call |
1263 | */ |
1264 | if (toplevel) |
1265 | { |
1266 | gdk_x11_display_error_trap_push (display); |
1267 | XSetInputFocus (GDK_DISPLAY_XDISPLAY (display), |
1268 | toplevel->focus_window, |
1269 | RevertToParent, |
1270 | xevent->xclient.data.l[1]); |
1271 | gdk_x11_display_error_trap_pop_ignored (display); |
1272 | } |
1273 | |
1274 | return GDK_FILTER_REMOVE; |
1275 | } |
1276 | else if (atom == gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_PING" ) && |
1277 | !_gdk_x11_display_is_root_window (display, xroot_window: xevent->xclient.window)) |
1278 | { |
1279 | XClientMessageEvent xclient = xevent->xclient; |
1280 | |
1281 | xclient.window = GDK_SURFACE_XROOTWIN (win); |
1282 | XSendEvent (GDK_SURFACE_XDISPLAY (win), |
1283 | xclient.window, |
1284 | False, |
1285 | SubstructureRedirectMask | SubstructureNotifyMask, (XEvent *)&xclient); |
1286 | |
1287 | return GDK_FILTER_REMOVE; |
1288 | } |
1289 | else if (atom == gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_SYNC_REQUEST" ) && |
1290 | GDK_X11_DISPLAY (display)->use_sync) |
1291 | { |
1292 | GdkToplevelX11 *toplevel = _gdk_x11_surface_get_toplevel (window: win); |
1293 | if (toplevel) |
1294 | { |
1295 | #ifdef HAVE_XSYNC |
1296 | toplevel->pending_counter_value = xevent->xclient.data.l[2] + ((gint64)xevent->xclient.data.l[3] << 32); |
1297 | toplevel->pending_counter_value_is_extended = xevent->xclient.data.l[4] != 0; |
1298 | #endif |
1299 | } |
1300 | return GDK_FILTER_REMOVE; |
1301 | } |
1302 | |
1303 | return GDK_FILTER_CONTINUE; |
1304 | } |
1305 | |
1306 | static void |
1307 | gdk_event_init (GdkDisplay *display) |
1308 | { |
1309 | GdkX11Display *display_x11; |
1310 | |
1311 | display_x11 = GDK_X11_DISPLAY (display); |
1312 | display_x11->event_source = gdk_x11_event_source_new (display); |
1313 | |
1314 | gdk_x11_event_source_add_translator (source: (GdkEventSource *) display_x11->event_source, |
1315 | GDK_EVENT_TRANSLATOR (display)); |
1316 | |
1317 | gdk_x11_event_source_add_translator (source: (GdkEventSource *) display_x11->event_source, |
1318 | GDK_EVENT_TRANSLATOR (display_x11->device_manager)); |
1319 | } |
1320 | |
1321 | static void |
1322 | set_sm_client_id (GdkDisplay *display, |
1323 | const char *sm_client_id) |
1324 | { |
1325 | GdkX11Display *display_x11 = GDK_X11_DISPLAY (display); |
1326 | |
1327 | if (gdk_display_is_closed (display)) |
1328 | return; |
1329 | |
1330 | if (sm_client_id && strcmp (s1: sm_client_id, s2: "" )) |
1331 | XChangeProperty (display_x11->xdisplay, display_x11->leader_window, |
1332 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: "SM_CLIENT_ID" ), |
1333 | XA_STRING, 8, PropModeReplace, (guchar *)sm_client_id, |
1334 | strlen (s: sm_client_id)); |
1335 | else |
1336 | XDeleteProperty (display_x11->xdisplay, display_x11->leader_window, |
1337 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: "SM_CLIENT_ID" )); |
1338 | } |
1339 | |
1340 | static void |
1341 | gdk_x11_display_query_default_visual (GdkX11Display *self, |
1342 | Visual **out_visual, |
1343 | int *out_depth) |
1344 | { |
1345 | XVisualInfo template, *visinfo; |
1346 | int n_visuals; |
1347 | Display *dpy; |
1348 | |
1349 | dpy = gdk_x11_display_get_xdisplay (GDK_DISPLAY (self)); |
1350 | |
1351 | template.screen = self->screen->screen_num; |
1352 | template.depth = 32; |
1353 | template.red_mask = 0xff0000; |
1354 | template.green_mask = 0x00ff00; |
1355 | template.blue_mask = 0x0000ff; |
1356 | |
1357 | visinfo = XGetVisualInfo (dpy, |
1358 | VisualScreenMask | VisualDepthMask |
1359 | | VisualRedMaskMask | VisualGreenMaskMask | VisualBlueMaskMask, |
1360 | &template, |
1361 | &n_visuals); |
1362 | if (visinfo != NULL) |
1363 | { |
1364 | *out_visual = visinfo[0].visual; |
1365 | *out_depth = visinfo[0].depth; |
1366 | XFree (visinfo); |
1367 | return; |
1368 | } |
1369 | |
1370 | *out_visual = DefaultVisual (dpy, self->screen->screen_num); |
1371 | *out_depth = DefaultDepth (dpy, self->screen->screen_num); |
1372 | } |
1373 | |
1374 | static void |
1375 | gdk_x11_display_init_leader_surface (GdkX11Display *self) |
1376 | { |
1377 | GdkDisplay *display = GDK_DISPLAY (self); |
1378 | Display *xdisplay = gdk_x11_display_get_xdisplay (display); |
1379 | |
1380 | self->window_colormap = XCreateColormap (xdisplay, |
1381 | DefaultRootWindow (xdisplay), |
1382 | self->window_visual, |
1383 | AllocNone); |
1384 | gdk_display_set_rgba (display, rgba: self->window_depth == 32); |
1385 | |
1386 | /* We need to initialize events after we have the screen |
1387 | * structures in places |
1388 | */ |
1389 | _gdk_x11_xsettings_init (GDK_X11_SCREEN (self->screen)); |
1390 | |
1391 | self->device_manager = _gdk_x11_device_manager_new (display); |
1392 | |
1393 | gdk_event_init (display); |
1394 | |
1395 | self->leader_gdk_surface = |
1396 | _gdk_x11_display_create_surface (display, |
1397 | surface_type: GDK_SURFACE_TEMP, |
1398 | NULL, |
1399 | x: -100, y: -100, width: 1, height: 1); |
1400 | |
1401 | (_gdk_x11_surface_get_toplevel (window: self->leader_gdk_surface))->is_leader = TRUE; |
1402 | self->leader_window = GDK_SURFACE_XID (self->leader_gdk_surface); |
1403 | self->leader_window_title_set = FALSE; |
1404 | } |
1405 | |
1406 | /** |
1407 | * gdk_x11_display_open: |
1408 | * @display_name: (nullable): name of the X display. |
1409 | * See the XOpenDisplay() for details. |
1410 | * |
1411 | * Tries to open a new display to the X server given by |
1412 | * @display_name. If opening the display fails, %NULL is |
1413 | * returned. |
1414 | * |
1415 | * Returns: (nullable) (transfer full): The new display |
1416 | */ |
1417 | GdkDisplay * |
1418 | gdk_x11_display_open (const char *display_name) |
1419 | { |
1420 | Display *xdisplay; |
1421 | GdkDisplay *display; |
1422 | GdkX11Display *display_x11; |
1423 | int argc; |
1424 | char *argv[1]; |
1425 | XClassHint *class_hint; |
1426 | int ignore; |
1427 | int maj, min; |
1428 | char *cm_name; |
1429 | |
1430 | XInitThreads (); |
1431 | |
1432 | xdisplay = XOpenDisplay (display_name); |
1433 | if (!xdisplay) |
1434 | return NULL; |
1435 | |
1436 | display = g_object_new (GDK_TYPE_X11_DISPLAY, NULL); |
1437 | display_x11 = GDK_X11_DISPLAY (display); |
1438 | |
1439 | display_x11->xdisplay = xdisplay; |
1440 | |
1441 | /* Set up handlers for Xlib internal connections */ |
1442 | XAddConnectionWatch (xdisplay, gdk_internal_connection_watch, NULL); |
1443 | |
1444 | _gdk_x11_precache_atoms (display, atom_names: precache_atoms, G_N_ELEMENTS (precache_atoms)); |
1445 | |
1446 | /* RandR must be initialized before we initialize the screens */ |
1447 | display_x11->have_randr12 = FALSE; |
1448 | display_x11->have_randr13 = FALSE; |
1449 | display_x11->have_randr15 = FALSE; |
1450 | #ifdef HAVE_RANDR |
1451 | if (XRRQueryExtension (dpy: display_x11->xdisplay, |
1452 | event_base_return: &display_x11->xrandr_event_base, error_base_return: &ignore)) |
1453 | { |
1454 | int major, minor; |
1455 | |
1456 | XRRQueryVersion (dpy: display_x11->xdisplay, major_version_return: &major, minor_version_return: &minor); |
1457 | |
1458 | if ((major == 1 && minor >= 2) || major > 1) { |
1459 | display_x11->have_randr12 = TRUE; |
1460 | if (minor >= 3 || major > 1) |
1461 | display_x11->have_randr13 = TRUE; |
1462 | #ifdef HAVE_RANDR15 |
1463 | if (minor >= 5 || major > 1) |
1464 | display_x11->have_randr15 = TRUE; |
1465 | #endif |
1466 | } |
1467 | } |
1468 | #endif |
1469 | |
1470 | /* initialize the display's screens */ |
1471 | display_x11->screen = _gdk_x11_screen_new (display, DefaultScreen (display_x11->xdisplay)); |
1472 | |
1473 | /* If GL is available we want to pick better default/rgba visuals, |
1474 | * as we care about GLX details such as alpha/depth/stencil depth, |
1475 | * stereo and double buffering |
1476 | * |
1477 | * Note that this also sets up the leader surface while creating the inital |
1478 | * GL context. |
1479 | */ |
1480 | if (!gdk_display_prepare_gl (self: display, NULL)) |
1481 | { |
1482 | gdk_x11_display_query_default_visual (self: display_x11, out_visual: &display_x11->window_visual, out_depth: &display_x11->window_depth); |
1483 | gdk_x11_display_init_leader_surface (self: display_x11); |
1484 | } |
1485 | |
1486 | #ifdef HAVE_XFIXES |
1487 | if (XFixesQueryExtension (dpy: display_x11->xdisplay, |
1488 | event_base_return: &display_x11->xfixes_event_base, |
1489 | error_base_return: &ignore)) |
1490 | { |
1491 | display_x11->have_xfixes = TRUE; |
1492 | } |
1493 | else |
1494 | #endif |
1495 | display_x11->have_xfixes = FALSE; |
1496 | |
1497 | display_x11->have_shapes = FALSE; |
1498 | display_x11->have_input_shapes = FALSE; |
1499 | |
1500 | if (XShapeQueryExtension (GDK_DISPLAY_XDISPLAY (display), &display_x11->shape_event_base, &ignore)) |
1501 | { |
1502 | display_x11->have_shapes = TRUE; |
1503 | #ifdef ShapeInput |
1504 | if (XShapeQueryVersion (GDK_DISPLAY_XDISPLAY (display), &maj, &min)) |
1505 | display_x11->have_input_shapes = (maj == 1 && min >= 1); |
1506 | #endif |
1507 | } |
1508 | |
1509 | gdk_display_set_input_shapes (display, input_shapes: display_x11->have_input_shapes); |
1510 | |
1511 | display_x11->trusted_client = TRUE; |
1512 | { |
1513 | Window root, child; |
1514 | int rootx, rooty, winx, winy; |
1515 | unsigned int xmask; |
1516 | |
1517 | gdk_x11_display_error_trap_push (display); |
1518 | XQueryPointer (display_x11->xdisplay, |
1519 | GDK_X11_SCREEN (display_x11->screen)->xroot_window, |
1520 | &root, &child, &rootx, &rooty, &winx, &winy, &xmask); |
1521 | if (G_UNLIKELY (gdk_x11_display_error_trap_pop (display) == BadWindow)) |
1522 | { |
1523 | g_warning ("Connection to display %s appears to be untrusted. Pointer and keyboard grabs and inter-client communication may not work as expected." , gdk_display_get_name (display)); |
1524 | display_x11->trusted_client = FALSE; |
1525 | } |
1526 | } |
1527 | |
1528 | if (g_getenv (variable: "GDK_SYNCHRONIZE" )) |
1529 | XSynchronize (display_x11->xdisplay, True); |
1530 | |
1531 | class_hint = XAllocClassHint(); |
1532 | class_hint->res_name = (char *) g_get_prgname (); |
1533 | class_hint->res_class = (char *) g_get_prgname (); |
1534 | |
1535 | /* XmbSetWMProperties sets the RESOURCE_NAME environment variable |
1536 | * from argv[0], so we just synthesize an argument array here. |
1537 | */ |
1538 | argc = 1; |
1539 | argv[0] = (char *) g_get_prgname (); |
1540 | |
1541 | XmbSetWMProperties (display_x11->xdisplay, |
1542 | display_x11->leader_window, |
1543 | NULL, NULL, argv, argc, NULL, NULL, |
1544 | class_hint); |
1545 | XFree (class_hint); |
1546 | |
1547 | if (gdk_sm_client_id) |
1548 | set_sm_client_id (display, sm_client_id: gdk_sm_client_id); |
1549 | |
1550 | if (!gdk_running_in_sandbox ()) |
1551 | { |
1552 | /* if sandboxed, we're likely in a pid namespace and would only confuse the wm with this */ |
1553 | long pid = getpid (); |
1554 | XChangeProperty (display_x11->xdisplay, |
1555 | display_x11->leader_window, |
1556 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_PID" ), |
1557 | XA_CARDINAL, 32, PropModeReplace, (guchar *) & pid, 1); |
1558 | } |
1559 | |
1560 | /* We don't yet know a valid time. */ |
1561 | display_x11->user_time = 0; |
1562 | |
1563 | #ifdef HAVE_XKB |
1564 | { |
1565 | int xkb_major = XkbMajorVersion; |
1566 | int xkb_minor = XkbMinorVersion; |
1567 | if (XkbLibraryVersion (&xkb_major, &xkb_minor)) |
1568 | { |
1569 | xkb_major = XkbMajorVersion; |
1570 | xkb_minor = XkbMinorVersion; |
1571 | |
1572 | if (XkbQueryExtension (display_x11->xdisplay, |
1573 | NULL, &display_x11->xkb_event_type, NULL, |
1574 | &xkb_major, &xkb_minor)) |
1575 | { |
1576 | Bool detectable_autorepeat_supported; |
1577 | |
1578 | display_x11->use_xkb = TRUE; |
1579 | |
1580 | XkbSelectEvents (display_x11->xdisplay, |
1581 | XkbUseCoreKbd, |
1582 | XkbNewKeyboardNotifyMask | XkbMapNotifyMask | XkbStateNotifyMask, |
1583 | XkbNewKeyboardNotifyMask | XkbMapNotifyMask | XkbStateNotifyMask); |
1584 | |
1585 | /* keep this in sync with _gdk_x11_keymap_state_changed() */ |
1586 | XkbSelectEventDetails (display_x11->xdisplay, |
1587 | XkbUseCoreKbd, XkbStateNotify, |
1588 | XkbAllStateComponentsMask, |
1589 | XkbModifierStateMask|XkbGroupStateMask); |
1590 | |
1591 | XkbSetDetectableAutoRepeat (display_x11->xdisplay, |
1592 | True, |
1593 | &detectable_autorepeat_supported); |
1594 | |
1595 | GDK_NOTE (MISC, g_message ("Detectable autorepeat %s." , |
1596 | detectable_autorepeat_supported ? |
1597 | "supported" : "not supported" )); |
1598 | |
1599 | display_x11->have_xkb_autorepeat = detectable_autorepeat_supported; |
1600 | } |
1601 | } |
1602 | } |
1603 | #endif |
1604 | |
1605 | display_x11->use_sync = FALSE; |
1606 | #ifdef HAVE_XSYNC |
1607 | { |
1608 | int major, minor; |
1609 | int error_base, event_base; |
1610 | |
1611 | if (XSyncQueryExtension (display_x11->xdisplay, |
1612 | &event_base, &error_base) && |
1613 | XSyncInitialize (display_x11->xdisplay, |
1614 | &major, &minor)) |
1615 | display_x11->use_sync = TRUE; |
1616 | } |
1617 | #endif |
1618 | |
1619 | #ifdef HAVE_XDAMAGE |
1620 | display_x11->have_damage = FALSE; |
1621 | if (XDamageQueryExtension (dpy: display_x11->xdisplay, |
1622 | event_base_return: &display_x11->damage_event_base, |
1623 | error_base_return: &display_x11->damage_error_base)) |
1624 | display_x11->have_damage = TRUE; |
1625 | #endif |
1626 | |
1627 | display->clipboard = gdk_x11_clipboard_new (display, selection: "CLIPBOARD" ); |
1628 | display->primary_clipboard = gdk_x11_clipboard_new (display, selection: "PRIMARY" ); |
1629 | |
1630 | /* |
1631 | * It is important that we first request the selection |
1632 | * notification, and then setup the initial state of |
1633 | * is_composited to avoid a race condition here. |
1634 | */ |
1635 | cm_name = g_strdup_printf (format: "_NET_WM_CM_S%d" , DefaultScreen (GDK_DISPLAY_XDISPLAY (display))); |
1636 | gdk_x11_display_request_selection_notification (display, selection: cm_name); |
1637 | gdk_display_set_composited (GDK_DISPLAY (display), |
1638 | composited: XGetSelectionOwner (GDK_DISPLAY_XDISPLAY (display), |
1639 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: cm_name)) != None); |
1640 | g_free (mem: cm_name); |
1641 | |
1642 | gdk_display_emit_opened (display); |
1643 | |
1644 | return display; |
1645 | } |
1646 | |
1647 | /** |
1648 | * gdk_x11_display_set_program_class: |
1649 | * @display: a `GdkDisplay` |
1650 | * @program_class: a string |
1651 | * |
1652 | * Sets the program class. |
1653 | * |
1654 | * The X11 backend uses the program class to set the class name part |
1655 | * of the `WM_CLASS` property on toplevel windows; see the ICCCM. |
1656 | */ |
1657 | void |
1658 | gdk_x11_display_set_program_class (GdkDisplay *display, |
1659 | const char *program_class) |
1660 | { |
1661 | GdkX11Display *display_x11 = GDK_X11_DISPLAY (display); |
1662 | XClassHint *class_hint; |
1663 | |
1664 | g_free (mem: display_x11->program_class); |
1665 | display_x11->program_class = g_strdup (str: program_class); |
1666 | |
1667 | class_hint = XAllocClassHint(); |
1668 | class_hint->res_name = (char *) g_get_prgname (); |
1669 | class_hint->res_class = (char *) program_class; |
1670 | XSetClassHint (display_x11->xdisplay, display_x11->leader_window, class_hint); |
1671 | XFree (class_hint); |
1672 | } |
1673 | |
1674 | /* |
1675 | * XLib internal connection handling |
1676 | */ |
1677 | typedef struct _GdkInternalConnection GdkInternalConnection; |
1678 | |
1679 | struct _GdkInternalConnection |
1680 | { |
1681 | int fd; |
1682 | GSource *source; |
1683 | Display *display; |
1684 | }; |
1685 | |
1686 | static gboolean |
1687 | process_internal_connection (GIOChannel *gioc, |
1688 | GIOCondition cond, |
1689 | gpointer data) |
1690 | { |
1691 | GdkInternalConnection *connection = (GdkInternalConnection *)data; |
1692 | |
1693 | XProcessInternalConnection ((Display*)connection->display, connection->fd); |
1694 | |
1695 | return TRUE; |
1696 | } |
1697 | |
1698 | static gulong |
1699 | gdk_x11_display_get_next_serial (GdkDisplay *display) |
1700 | { |
1701 | return NextRequest (GDK_DISPLAY_XDISPLAY (display)); |
1702 | } |
1703 | |
1704 | |
1705 | static GdkInternalConnection * |
1706 | gdk_add_connection_handler (Display *display, |
1707 | guint fd) |
1708 | { |
1709 | GIOChannel *io_channel; |
1710 | GdkInternalConnection *connection; |
1711 | |
1712 | connection = g_new (GdkInternalConnection, 1); |
1713 | |
1714 | connection->fd = fd; |
1715 | connection->display = display; |
1716 | |
1717 | io_channel = g_io_channel_unix_new (fd); |
1718 | |
1719 | connection->source = g_io_create_watch (channel: io_channel, condition: G_IO_IN); |
1720 | g_source_set_callback (source: connection->source, |
1721 | func: (GSourceFunc)process_internal_connection, data: connection, NULL); |
1722 | g_source_attach (source: connection->source, NULL); |
1723 | |
1724 | g_io_channel_unref (channel: io_channel); |
1725 | |
1726 | return connection; |
1727 | } |
1728 | |
1729 | static void |
1730 | gdk_remove_connection_handler (GdkInternalConnection *connection) |
1731 | { |
1732 | g_source_destroy (source: connection->source); |
1733 | g_free (mem: connection); |
1734 | } |
1735 | |
1736 | static void |
1737 | gdk_internal_connection_watch (Display *display, |
1738 | XPointer arg, |
1739 | int fd, |
1740 | gboolean opening, |
1741 | XPointer *watch_data) |
1742 | { |
1743 | if (opening) |
1744 | *watch_data = (XPointer)gdk_add_connection_handler (display, fd); |
1745 | else |
1746 | gdk_remove_connection_handler (connection: (GdkInternalConnection *)*watch_data); |
1747 | } |
1748 | |
1749 | static const char * |
1750 | gdk_x11_display_get_name (GdkDisplay *display) |
1751 | { |
1752 | return (char *) DisplayString (GDK_X11_DISPLAY (display)->xdisplay); |
1753 | } |
1754 | |
1755 | gboolean |
1756 | _gdk_x11_display_is_root_window (GdkDisplay *display, |
1757 | Window xroot_window) |
1758 | { |
1759 | GdkX11Display *display_x11; |
1760 | |
1761 | display_x11 = GDK_X11_DISPLAY (display); |
1762 | |
1763 | return GDK_SCREEN_XROOTWIN (display_x11->screen) == xroot_window; |
1764 | } |
1765 | |
1766 | struct XPointerUngrabInfo { |
1767 | GdkDisplay *display; |
1768 | guint32 time; |
1769 | }; |
1770 | |
1771 | static void |
1772 | device_grab_update_callback (GdkDisplay *display, |
1773 | gpointer data, |
1774 | gulong serial) |
1775 | { |
1776 | GdkDevice *device = data; |
1777 | |
1778 | _gdk_display_device_grab_update (display, device, current_serial: serial); |
1779 | } |
1780 | |
1781 | #define XSERVER_TIME_IS_LATER(time1, time2) \ |
1782 | ( (( time1 > time2 ) && ( time1 - time2 < ((guint32)-1)/2 )) || \ |
1783 | (( time1 < time2 ) && ( time2 - time1 > ((guint32)-1)/2 )) \ |
1784 | ) |
1785 | |
1786 | void |
1787 | _gdk_x11_display_update_grab_info (GdkDisplay *display, |
1788 | GdkDevice *device, |
1789 | int status) |
1790 | { |
1791 | if (status == GrabSuccess) |
1792 | _gdk_x11_roundtrip_async (display, callback: device_grab_update_callback, data: device); |
1793 | } |
1794 | |
1795 | void |
1796 | _gdk_x11_display_update_grab_info_ungrab (GdkDisplay *display, |
1797 | GdkDevice *device, |
1798 | guint32 time, |
1799 | gulong serial) |
1800 | { |
1801 | GdkDeviceGrabInfo *grab; |
1802 | |
1803 | XFlush (GDK_DISPLAY_XDISPLAY (display)); |
1804 | |
1805 | grab = _gdk_display_get_last_device_grab (display, device); |
1806 | if (grab && |
1807 | (time == GDK_CURRENT_TIME || |
1808 | grab->time == GDK_CURRENT_TIME || |
1809 | !XSERVER_TIME_IS_LATER (grab->time, time))) |
1810 | { |
1811 | grab->serial_end = serial; |
1812 | _gdk_x11_roundtrip_async (display, callback: device_grab_update_callback, data: device); |
1813 | } |
1814 | } |
1815 | |
1816 | static void |
1817 | gdk_x11_display_beep (GdkDisplay *display) |
1818 | { |
1819 | if (!GDK_X11_DISPLAY (display)->trusted_client) |
1820 | return; |
1821 | |
1822 | #ifdef HAVE_XKB |
1823 | XkbBell (GDK_DISPLAY_XDISPLAY (display), None, 0, None); |
1824 | #else |
1825 | XBell (GDK_DISPLAY_XDISPLAY (display), 0); |
1826 | #endif |
1827 | } |
1828 | |
1829 | static void |
1830 | gdk_x11_display_sync (GdkDisplay *display) |
1831 | { |
1832 | XSync (GDK_DISPLAY_XDISPLAY (display), False); |
1833 | } |
1834 | |
1835 | static void |
1836 | gdk_x11_display_flush (GdkDisplay *display) |
1837 | { |
1838 | if (!display->closed) |
1839 | XFlush (GDK_DISPLAY_XDISPLAY (display)); |
1840 | } |
1841 | |
1842 | static gboolean |
1843 | gdk_x11_display_has_pending (GdkDisplay *display) |
1844 | { |
1845 | return XPending (GDK_DISPLAY_XDISPLAY (display)); |
1846 | } |
1847 | |
1848 | /** |
1849 | * gdk_x11_display_get_default_group: |
1850 | * @display: (type GdkX11Display): a `GdkDisplay` |
1851 | * |
1852 | * Returns the default group leader surface for all toplevel surfaces |
1853 | * on @display. This surface is implicitly created by GDK. |
1854 | * See gdk_x11_surface_set_group(). |
1855 | * |
1856 | * Returns: (transfer none): The default group leader surface |
1857 | * for @display |
1858 | */ |
1859 | GdkSurface * |
1860 | gdk_x11_display_get_default_group (GdkDisplay *display) |
1861 | { |
1862 | g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL); |
1863 | |
1864 | return GDK_X11_DISPLAY (display)->leader_gdk_surface; |
1865 | } |
1866 | |
1867 | /** |
1868 | * gdk_x11_display_grab: |
1869 | * @display: (type GdkX11Display): a `GdkDisplay` |
1870 | * |
1871 | * Call XGrabServer() on @display. |
1872 | * To ungrab the display again, use gdk_x11_display_ungrab(). |
1873 | * |
1874 | * gdk_x11_display_grab()/gdk_x11_display_ungrab() calls can be nested. |
1875 | **/ |
1876 | void |
1877 | gdk_x11_display_grab (GdkDisplay *display) |
1878 | { |
1879 | GdkX11Display *display_x11; |
1880 | |
1881 | g_return_if_fail (GDK_IS_DISPLAY (display)); |
1882 | |
1883 | display_x11 = GDK_X11_DISPLAY (display); |
1884 | |
1885 | if (display_x11->grab_count == 0) |
1886 | XGrabServer (display_x11->xdisplay); |
1887 | display_x11->grab_count++; |
1888 | } |
1889 | |
1890 | /** |
1891 | * gdk_x11_display_ungrab: |
1892 | * @display: (type GdkX11Display): a `GdkDisplay` |
1893 | * |
1894 | * Ungrab @display after it has been grabbed with |
1895 | * gdk_x11_display_grab(). |
1896 | **/ |
1897 | void |
1898 | gdk_x11_display_ungrab (GdkDisplay *display) |
1899 | { |
1900 | GdkX11Display *display_x11; |
1901 | |
1902 | g_return_if_fail (GDK_IS_DISPLAY (display)); |
1903 | |
1904 | display_x11 = GDK_X11_DISPLAY (display);; |
1905 | g_return_if_fail (display_x11->grab_count > 0); |
1906 | |
1907 | display_x11->grab_count--; |
1908 | if (display_x11->grab_count == 0) |
1909 | { |
1910 | XUngrabServer (display_x11->xdisplay); |
1911 | XFlush (display_x11->xdisplay); |
1912 | } |
1913 | } |
1914 | |
1915 | static void |
1916 | gdk_x11_display_dispose (GObject *object) |
1917 | { |
1918 | GdkX11Display *display_x11 = GDK_X11_DISPLAY (object); |
1919 | |
1920 | if (display_x11->event_source) |
1921 | { |
1922 | g_source_destroy (source: display_x11->event_source); |
1923 | g_source_unref (source: display_x11->event_source); |
1924 | display_x11->event_source = NULL; |
1925 | } |
1926 | |
1927 | G_OBJECT_CLASS (gdk_x11_display_parent_class)->dispose (object); |
1928 | } |
1929 | |
1930 | static void |
1931 | gdk_x11_display_finalize (GObject *object) |
1932 | { |
1933 | GdkX11Display *display_x11 = GDK_X11_DISPLAY (object); |
1934 | |
1935 | /* Keymap */ |
1936 | if (display_x11->keymap) |
1937 | g_object_unref (object: display_x11->keymap); |
1938 | |
1939 | _gdk_x11_cursor_display_finalize (GDK_DISPLAY (display_x11)); |
1940 | |
1941 | /* Get rid of pending streams */ |
1942 | g_slist_free_full (list: display_x11->streams, free_func: g_object_unref); |
1943 | |
1944 | /* Atom Hashtable */ |
1945 | g_hash_table_destroy (hash_table: display_x11->atom_from_string); |
1946 | g_hash_table_destroy (hash_table: display_x11->atom_to_string); |
1947 | |
1948 | /* Leader Window */ |
1949 | XDestroyWindow (display_x11->xdisplay, display_x11->leader_window); |
1950 | |
1951 | /* Free all GdkX11Screens */ |
1952 | g_object_unref (object: display_x11->screen); |
1953 | |
1954 | g_list_store_remove_all (store: display_x11->monitors); |
1955 | g_object_unref (object: display_x11->monitors); |
1956 | |
1957 | g_free (mem: display_x11->startup_notification_id); |
1958 | |
1959 | /* X ID hashtable */ |
1960 | g_hash_table_destroy (hash_table: display_x11->xid_ht); |
1961 | |
1962 | XCloseDisplay (display_x11->xdisplay); |
1963 | |
1964 | g_clear_error (err: &display_x11->gl_error); |
1965 | |
1966 | /* error traps */ |
1967 | while (display_x11->error_traps != NULL) |
1968 | { |
1969 | GdkErrorTrap *trap = display_x11->error_traps->data; |
1970 | |
1971 | display_x11->error_traps = |
1972 | g_slist_delete_link (list: display_x11->error_traps, |
1973 | link_: display_x11->error_traps); |
1974 | |
1975 | if (trap->end_sequence == 0) |
1976 | g_warning ("Display finalized with an unpopped error trap" ); |
1977 | |
1978 | g_slice_free (GdkErrorTrap, trap); |
1979 | } |
1980 | |
1981 | g_free (mem: display_x11->program_class); |
1982 | |
1983 | G_OBJECT_CLASS (gdk_x11_display_parent_class)->finalize (object); |
1984 | } |
1985 | |
1986 | /** |
1987 | * gdk_x11_lookup_xdisplay: |
1988 | * @xdisplay: a pointer to an X Display |
1989 | * |
1990 | * Find the `GdkDisplay` corresponding to @xdisplay, if any exists. |
1991 | * |
1992 | * Returns: (transfer none) (type GdkX11Display): the `GdkDisplay`, if found, otherwise %NULL. |
1993 | **/ |
1994 | GdkDisplay * |
1995 | gdk_x11_lookup_xdisplay (Display *xdisplay) |
1996 | { |
1997 | GSList *list, *l; |
1998 | GdkDisplay *display; |
1999 | |
2000 | display = NULL; |
2001 | |
2002 | list = gdk_display_manager_list_displays (manager: gdk_display_manager_get ()); |
2003 | |
2004 | for (l = list; l; l = l->next) |
2005 | { |
2006 | if (GDK_IS_X11_DISPLAY (l->data) && |
2007 | GDK_DISPLAY_XDISPLAY (l->data) == xdisplay) |
2008 | { |
2009 | display = l->data; |
2010 | break; |
2011 | } |
2012 | } |
2013 | |
2014 | g_slist_free (list); |
2015 | |
2016 | return display; |
2017 | } |
2018 | |
2019 | /** |
2020 | * gdk_x11_display_get_xdisplay: |
2021 | * @display: (type GdkX11Display): a `GdkDisplay` |
2022 | * |
2023 | * Returns the X display of a `GdkDisplay`. |
2024 | * |
2025 | * Returns: (transfer none): an X display |
2026 | */ |
2027 | Display * |
2028 | gdk_x11_display_get_xdisplay (GdkDisplay *display) |
2029 | { |
2030 | g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL); |
2031 | |
2032 | return GDK_X11_DISPLAY (display)->xdisplay; |
2033 | } |
2034 | |
2035 | /** |
2036 | * gdk_x11_display_get_xscreen: |
2037 | * @display: (type GdkX11Display): a `GdkDisplay` |
2038 | * |
2039 | * Returns the X Screen used by `GdkDisplay`. |
2040 | * |
2041 | * Returns: (transfer none): an X Screen |
2042 | */ |
2043 | Screen * |
2044 | gdk_x11_display_get_xscreen (GdkDisplay *display) |
2045 | { |
2046 | g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL); |
2047 | |
2048 | return GDK_X11_SCREEN (GDK_X11_DISPLAY (display)->screen)->xscreen; |
2049 | } |
2050 | |
2051 | /** |
2052 | * gdk_x11_display_get_xrootwindow: |
2053 | * @display: (type GdkX11Display): a `GdkDisplay` |
2054 | * |
2055 | * Returns the root X window used by `GdkDisplay`. |
2056 | * |
2057 | * Returns: an X Window |
2058 | */ |
2059 | Window |
2060 | gdk_x11_display_get_xrootwindow (GdkDisplay *display) |
2061 | { |
2062 | g_return_val_if_fail (GDK_IS_DISPLAY (display), None); |
2063 | |
2064 | return GDK_SCREEN_XROOTWIN (GDK_X11_DISPLAY (display)->screen); |
2065 | } |
2066 | |
2067 | static void |
2068 | gdk_x11_display_make_default (GdkDisplay *display) |
2069 | { |
2070 | GdkX11Display *display_x11 = GDK_X11_DISPLAY (display); |
2071 | const char *startup_id; |
2072 | |
2073 | g_free (mem: display_x11->startup_notification_id); |
2074 | display_x11->startup_notification_id = NULL; |
2075 | |
2076 | startup_id = gdk_get_startup_notification_id (); |
2077 | if (startup_id) |
2078 | gdk_x11_display_set_startup_notification_id (display, startup_id); |
2079 | } |
2080 | |
2081 | static void |
2082 | broadcast_xmessage (GdkDisplay *display, |
2083 | const char *message_type, |
2084 | const char *message_type_begin, |
2085 | const char *message) |
2086 | { |
2087 | Display *xdisplay = GDK_DISPLAY_XDISPLAY (display); |
2088 | Window xroot_window = GDK_DISPLAY_XROOTWIN (display); |
2089 | |
2090 | Atom type_atom; |
2091 | Atom type_atom_begin; |
2092 | Window xwindow; |
2093 | |
2094 | if (!G_LIKELY (GDK_X11_DISPLAY (display)->trusted_client)) |
2095 | return; |
2096 | |
2097 | { |
2098 | XSetWindowAttributes attrs; |
2099 | |
2100 | attrs.override_redirect = True; |
2101 | attrs.event_mask = PropertyChangeMask | StructureNotifyMask; |
2102 | |
2103 | xwindow = |
2104 | XCreateWindow (xdisplay, |
2105 | xroot_window, |
2106 | -100, -100, 1, 1, |
2107 | 0, |
2108 | CopyFromParent, |
2109 | CopyFromParent, |
2110 | (Visual *)CopyFromParent, |
2111 | CWOverrideRedirect | CWEventMask, |
2112 | &attrs); |
2113 | } |
2114 | |
2115 | type_atom = gdk_x11_get_xatom_by_name_for_display (display, atom_name: message_type); |
2116 | type_atom_begin = gdk_x11_get_xatom_by_name_for_display (display, atom_name: message_type_begin); |
2117 | |
2118 | { |
2119 | XClientMessageEvent xclient; |
2120 | const char *src; |
2121 | const char *src_end; |
2122 | char *dest; |
2123 | char *dest_end; |
2124 | |
2125 | memset(s: &xclient, c: 0, n: sizeof (xclient)); |
2126 | xclient.type = ClientMessage; |
2127 | xclient.message_type = type_atom_begin; |
2128 | xclient.display =xdisplay; |
2129 | xclient.window = xwindow; |
2130 | xclient.format = 8; |
2131 | |
2132 | src = message; |
2133 | src_end = message + strlen (s: message) + 1; /* +1 to include nul byte */ |
2134 | |
2135 | while (src != src_end) |
2136 | { |
2137 | dest = &xclient.data.b[0]; |
2138 | dest_end = dest + 20; |
2139 | |
2140 | while (dest != dest_end && |
2141 | src != src_end) |
2142 | { |
2143 | *dest = *src; |
2144 | ++dest; |
2145 | ++src; |
2146 | } |
2147 | |
2148 | while (dest != dest_end) |
2149 | { |
2150 | *dest = 0; |
2151 | ++dest; |
2152 | } |
2153 | |
2154 | XSendEvent (xdisplay, |
2155 | xroot_window, |
2156 | False, |
2157 | PropertyChangeMask, |
2158 | (XEvent *)&xclient); |
2159 | |
2160 | xclient.message_type = type_atom; |
2161 | } |
2162 | } |
2163 | |
2164 | XDestroyWindow (xdisplay, xwindow); |
2165 | XFlush (xdisplay); |
2166 | } |
2167 | |
2168 | /** |
2169 | * gdk_x11_display_broadcast_startup_message: |
2170 | * @display: (type GdkX11Display): a `GdkDisplay` |
2171 | * @message_type: startup notification message type ("new", "change", |
2172 | * or "remove") |
2173 | * @...: a list of key/value pairs (as strings), terminated by a |
2174 | * %NULL key. (A %NULL value for a key will cause that key to be |
2175 | * skipped in the output.) |
2176 | * |
2177 | * Sends a startup notification message of type @message_type to |
2178 | * @display. |
2179 | * |
2180 | * This is a convenience function for use by code that implements the |
2181 | * freedesktop startup notification specification. Applications should |
2182 | * not normally need to call it directly. See the |
2183 | * [Startup Notification Protocol specification](http://standards.freedesktop.org/startup-notification-spec/startup-notification-latest.txt) |
2184 | * for definitions of the message types and keys that can be used. |
2185 | **/ |
2186 | void |
2187 | gdk_x11_display_broadcast_startup_message (GdkDisplay *display, |
2188 | const char *message_type, |
2189 | ...) |
2190 | { |
2191 | GString *message; |
2192 | va_list ap; |
2193 | const char *key, *value, *p; |
2194 | |
2195 | message = g_string_new (init: message_type); |
2196 | g_string_append_c (message, ':'); |
2197 | |
2198 | va_start (ap, message_type); |
2199 | while ((key = va_arg (ap, const char *))) |
2200 | { |
2201 | value = va_arg (ap, const char *); |
2202 | if (!value) |
2203 | continue; |
2204 | |
2205 | g_string_append_printf (string: message, format: " %s=\"" , key); |
2206 | for (p = value; *p; p++) |
2207 | { |
2208 | switch (*p) |
2209 | { |
2210 | case ' ': |
2211 | case '"': |
2212 | case '\\': |
2213 | g_string_append_c (message, '\\'); |
2214 | break; |
2215 | default: |
2216 | break; |
2217 | } |
2218 | |
2219 | g_string_append_c (message, *p); |
2220 | } |
2221 | g_string_append_c (message, '\"'); |
2222 | } |
2223 | va_end (ap); |
2224 | |
2225 | broadcast_xmessage (display, |
2226 | message_type: "_NET_STARTUP_INFO" , |
2227 | message_type_begin: "_NET_STARTUP_INFO_BEGIN" , |
2228 | message: message->str); |
2229 | |
2230 | g_string_free (string: message, TRUE); |
2231 | } |
2232 | |
2233 | static void |
2234 | gdk_x11_display_notify_startup_complete (GdkDisplay *display, |
2235 | const char *startup_id) |
2236 | { |
2237 | char *free_this = NULL; |
2238 | |
2239 | if (startup_id == NULL) |
2240 | { |
2241 | GdkX11Display *display_x11 = GDK_X11_DISPLAY (display); |
2242 | |
2243 | startup_id = free_this = display_x11->startup_notification_id; |
2244 | display_x11->startup_notification_id = NULL; |
2245 | |
2246 | if (startup_id == NULL) |
2247 | return; |
2248 | } |
2249 | |
2250 | gdk_x11_display_broadcast_startup_message (display, message_type: "remove" , |
2251 | "ID" , startup_id, |
2252 | NULL); |
2253 | |
2254 | g_free (mem: free_this); |
2255 | } |
2256 | |
2257 | gboolean |
2258 | gdk_x11_display_request_selection_notification (GdkDisplay *display, |
2259 | const char *selection) |
2260 | |
2261 | { |
2262 | #ifdef HAVE_XFIXES |
2263 | GdkX11Display *display_x11 = GDK_X11_DISPLAY (display); |
2264 | Atom atom; |
2265 | |
2266 | if (display_x11->have_xfixes) |
2267 | { |
2268 | atom = gdk_x11_get_xatom_by_name_for_display (display, atom_name: selection); |
2269 | XFixesSelectSelectionInput (dpy: display_x11->xdisplay, |
2270 | win: display_x11->leader_window, |
2271 | selection: atom, |
2272 | XFixesSetSelectionOwnerNotifyMask | |
2273 | XFixesSelectionWindowDestroyNotifyMask | |
2274 | XFixesSelectionClientCloseNotifyMask); |
2275 | return TRUE; |
2276 | } |
2277 | else |
2278 | #endif |
2279 | return FALSE; |
2280 | } |
2281 | |
2282 | /** |
2283 | * gdk_x11_display_get_user_time: |
2284 | * @display: (type GdkX11Display): a `GdkDisplay` |
2285 | * |
2286 | * Returns the timestamp of the last user interaction on |
2287 | * @display. The timestamp is taken from events caused |
2288 | * by user interaction such as key presses or pointer |
2289 | * movements. See gdk_x11_surface_set_user_time(). |
2290 | * |
2291 | * Returns: the timestamp of the last user interaction |
2292 | */ |
2293 | guint32 |
2294 | gdk_x11_display_get_user_time (GdkDisplay *display) |
2295 | { |
2296 | return GDK_X11_DISPLAY (display)->user_time; |
2297 | } |
2298 | |
2299 | /** |
2300 | * gdk_x11_display_get_startup_notification_id: |
2301 | * @display: (type GdkX11Display): a `GdkDisplay` |
2302 | * |
2303 | * Gets the startup notification ID for a display. |
2304 | * |
2305 | * Returns: the startup notification ID for @display |
2306 | */ |
2307 | const char * |
2308 | gdk_x11_display_get_startup_notification_id (GdkDisplay *display) |
2309 | { |
2310 | return GDK_X11_DISPLAY (display)->startup_notification_id; |
2311 | } |
2312 | |
2313 | /** |
2314 | * gdk_x11_display_set_startup_notification_id: |
2315 | * @display: (type GdkX11Display): a `GdkDisplay` |
2316 | * @startup_id: the startup notification ID (must be valid utf8) |
2317 | * |
2318 | * Sets the startup notification ID for a display. |
2319 | * |
2320 | * This is usually taken from the value of the DESKTOP_STARTUP_ID |
2321 | * environment variable, but in some cases (such as the application not |
2322 | * being launched using exec()) it can come from other sources. |
2323 | * |
2324 | * If the ID contains the string "_TIME" then the portion following that |
2325 | * string is taken to be the X11 timestamp of the event that triggered |
2326 | * the application to be launched and the GDK current event time is set |
2327 | * accordingly. |
2328 | * |
2329 | * The startup ID is also what is used to signal that the startup is |
2330 | * complete (for example, when opening a window or when calling |
2331 | * gdk_display_notify_startup_complete()). |
2332 | **/ |
2333 | void |
2334 | gdk_x11_display_set_startup_notification_id (GdkDisplay *display, |
2335 | const char *startup_id) |
2336 | { |
2337 | GdkX11Display *display_x11; |
2338 | char *time_str; |
2339 | |
2340 | display_x11 = GDK_X11_DISPLAY (display); |
2341 | |
2342 | g_free (mem: display_x11->startup_notification_id); |
2343 | display_x11->startup_notification_id = g_strdup (str: startup_id); |
2344 | |
2345 | if (startup_id != NULL) |
2346 | { |
2347 | /* Find the launch time from the startup_id, if it's there. Newer spec |
2348 | * states that the startup_id is of the form <unique>_TIME<timestamp> |
2349 | */ |
2350 | time_str = g_strrstr (haystack: startup_id, needle: "_TIME" ); |
2351 | if (time_str != NULL) |
2352 | { |
2353 | gulong retval; |
2354 | char *end; |
2355 | errno = 0; |
2356 | |
2357 | /* Skip past the "_TIME" part */ |
2358 | time_str += 5; |
2359 | |
2360 | retval = strtoul (nptr: time_str, endptr: &end, base: 0); |
2361 | if (end != time_str && errno == 0) |
2362 | display_x11->user_time = retval; |
2363 | } |
2364 | else |
2365 | display_x11->user_time = 0; |
2366 | |
2367 | /* Set the startup id on the leader window so it |
2368 | * applies to all windows we create on this display |
2369 | */ |
2370 | XChangeProperty (display_x11->xdisplay, |
2371 | display_x11->leader_window, |
2372 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_STARTUP_ID" ), |
2373 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: "UTF8_STRING" ), 8, |
2374 | PropModeReplace, |
2375 | (guchar *)startup_id, strlen (s: startup_id)); |
2376 | } |
2377 | else |
2378 | { |
2379 | XDeleteProperty (display_x11->xdisplay, display_x11->leader_window, |
2380 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_STARTUP_ID" )); |
2381 | display_x11->user_time = 0; |
2382 | } |
2383 | } |
2384 | |
2385 | /* look up the extension name for a given major opcode. grubs around in |
2386 | * xlib to do it since a) it’s already cached there b) XQueryExtension |
2387 | * emits protocol so we can’t use it in an error handler. |
2388 | */ |
2389 | static const char * |
2390 | _gdk_x11_decode_request_code(Display *dpy, int code) |
2391 | { |
2392 | _XExtension *ext; |
2393 | |
2394 | if (code < 128) |
2395 | return "core protocol" ; |
2396 | |
2397 | for (ext = dpy->ext_procs; ext; ext = ext->next) |
2398 | { |
2399 | if (ext->codes.major_opcode == code) |
2400 | return ext->name; |
2401 | } |
2402 | |
2403 | return "unknown" ; |
2404 | } |
2405 | |
2406 | /* compare X sequence numbers handling wraparound */ |
2407 | #define SEQUENCE_COMPARE(a,op,b) (((long) (a) - (long) (b)) op 0) |
2408 | |
2409 | /* delivers an error event from the error handler in gdkmain-x11.c */ |
2410 | void |
2411 | _gdk_x11_display_error_event (GdkDisplay *display, |
2412 | XErrorEvent *error) |
2413 | { |
2414 | GdkX11Display *display_x11; |
2415 | GSList *tmp_list; |
2416 | gboolean ignore; |
2417 | |
2418 | display_x11 = GDK_X11_DISPLAY (display); |
2419 | |
2420 | ignore = FALSE; |
2421 | for (tmp_list = display_x11->error_traps; |
2422 | tmp_list != NULL; |
2423 | tmp_list = tmp_list->next) |
2424 | { |
2425 | GdkErrorTrap *trap; |
2426 | |
2427 | trap = tmp_list->data; |
2428 | |
2429 | if (SEQUENCE_COMPARE (trap->start_sequence, <=, error->serial) && |
2430 | (trap->end_sequence == 0 || |
2431 | SEQUENCE_COMPARE (trap->end_sequence, >, error->serial))) |
2432 | { |
2433 | ignore = TRUE; |
2434 | trap->error_code = error->error_code; |
2435 | break; /* only innermost trap gets the error code */ |
2436 | } |
2437 | } |
2438 | |
2439 | if (!ignore) |
2440 | { |
2441 | char buf[64]; |
2442 | char *msg; |
2443 | |
2444 | XGetErrorText (display_x11->xdisplay, error->error_code, buf, 63); |
2445 | |
2446 | msg = |
2447 | g_strdup_printf (format: "The program '%s' received an X Window System error.\n" |
2448 | "This probably reflects a bug in the program.\n" |
2449 | "The error was '%s'.\n" |
2450 | " (Details: serial %ld error_code %d request_code %d (%s) minor_code %d)\n" |
2451 | " (Note to programmers: normally, X errors are reported asynchronously;\n" |
2452 | " that is, you will receive the error a while after causing it.\n" |
2453 | " To debug your program, run it with the GDK_SYNCHRONIZE environment\n" |
2454 | " variable to change this behavior. You can then get a meaningful\n" |
2455 | " backtrace from your debugger if you break on the gdk_x_error() function.)" , |
2456 | g_get_prgname (), |
2457 | buf, |
2458 | error->serial, |
2459 | error->error_code, |
2460 | error->request_code, |
2461 | _gdk_x11_decode_request_code(dpy: display_x11->xdisplay, |
2462 | code: error->request_code), |
2463 | error->minor_code); |
2464 | |
2465 | #ifdef G_ENABLE_DEBUG |
2466 | g_error ("%s" , msg); |
2467 | #else /* !G_ENABLE_DEBUG */ |
2468 | g_warning ("%s" , msg); |
2469 | |
2470 | _exit (1); |
2471 | #endif /* G_ENABLE_DEBUG */ |
2472 | } |
2473 | } |
2474 | |
2475 | static void |
2476 | delete_outdated_error_traps (GdkX11Display *display_x11) |
2477 | { |
2478 | GSList *tmp_list; |
2479 | gulong processed_sequence; |
2480 | |
2481 | processed_sequence = XLastKnownRequestProcessed (display_x11->xdisplay); |
2482 | |
2483 | tmp_list = display_x11->error_traps; |
2484 | while (tmp_list != NULL) |
2485 | { |
2486 | GdkErrorTrap *trap = tmp_list->data; |
2487 | |
2488 | if (trap->end_sequence != 0 && |
2489 | SEQUENCE_COMPARE (trap->end_sequence, <=, processed_sequence)) |
2490 | { |
2491 | GSList *free_me = tmp_list; |
2492 | |
2493 | tmp_list = tmp_list->next; |
2494 | display_x11->error_traps = |
2495 | g_slist_delete_link (list: display_x11->error_traps, link_: free_me); |
2496 | g_slice_free (GdkErrorTrap, trap); |
2497 | } |
2498 | else |
2499 | { |
2500 | tmp_list = tmp_list->next; |
2501 | } |
2502 | } |
2503 | } |
2504 | |
2505 | /** |
2506 | * gdk_x11_display_error_trap_push: |
2507 | * @display: (type GdkX11Display): a `GdkDisplay` |
2508 | * |
2509 | * Begins a range of X requests on @display for which X error events |
2510 | * will be ignored. Unignored errors (when no trap is pushed) will abort |
2511 | * the application. Use gdk_x11_display_error_trap_pop() or |
2512 | * gdk_x11_display_error_trap_pop_ignored()to lift a trap pushed |
2513 | * with this function. |
2514 | */ |
2515 | void |
2516 | gdk_x11_display_error_trap_push (GdkDisplay *display) |
2517 | { |
2518 | GdkX11Display *display_x11; |
2519 | GdkErrorTrap *trap; |
2520 | |
2521 | display_x11 = GDK_X11_DISPLAY (display); |
2522 | |
2523 | delete_outdated_error_traps (display_x11); |
2524 | |
2525 | /* set up the Xlib callback to tell us about errors */ |
2526 | _gdk_x11_error_handler_push (); |
2527 | |
2528 | trap = g_slice_new0 (GdkErrorTrap); |
2529 | |
2530 | trap->start_sequence = XNextRequest (display_x11->xdisplay); |
2531 | trap->error_code = Success; |
2532 | |
2533 | display_x11->error_traps = |
2534 | g_slist_prepend (list: display_x11->error_traps, data: trap); |
2535 | } |
2536 | |
2537 | static int |
2538 | gdk_x11_display_error_trap_pop_internal (GdkDisplay *display, |
2539 | gboolean need_code) |
2540 | { |
2541 | GdkX11Display *display_x11; |
2542 | GdkErrorTrap *trap; |
2543 | GSList *tmp_list; |
2544 | int result; |
2545 | |
2546 | display_x11 = GDK_X11_DISPLAY (display); |
2547 | |
2548 | g_return_val_if_fail (display_x11->error_traps != NULL, Success); |
2549 | |
2550 | /* Find the first trap that hasn't been popped already */ |
2551 | trap = NULL; /* quiet gcc */ |
2552 | for (tmp_list = display_x11->error_traps; |
2553 | tmp_list != NULL; |
2554 | tmp_list = tmp_list->next) |
2555 | { |
2556 | trap = tmp_list->data; |
2557 | |
2558 | if (trap->end_sequence == 0) |
2559 | break; |
2560 | } |
2561 | |
2562 | if (trap == NULL) |
2563 | { |
2564 | g_critical ("gdk_x11_display_error_trap_pop() called without gdk_x11_display_error_trap_push()" ); |
2565 | return Success; |
2566 | } |
2567 | g_assert (trap->end_sequence == 0); |
2568 | |
2569 | /* May need to sync to fill in trap->error_code if we care about |
2570 | * getting an error code. |
2571 | */ |
2572 | if (need_code) |
2573 | { |
2574 | gulong processed_sequence; |
2575 | gulong next_sequence; |
2576 | |
2577 | next_sequence = XNextRequest (display_x11->xdisplay); |
2578 | processed_sequence = XLastKnownRequestProcessed (display_x11->xdisplay); |
2579 | |
2580 | /* If our last request was already processed, there is no point |
2581 | * in syncing. i.e. if last request was a round trip (or even if |
2582 | * we got an event with the serial of a non-round-trip) |
2583 | */ |
2584 | if ((next_sequence - 1) != processed_sequence) |
2585 | { |
2586 | XSync (display_x11->xdisplay, False); |
2587 | } |
2588 | |
2589 | result = trap->error_code; |
2590 | } |
2591 | else |
2592 | { |
2593 | result = Success; |
2594 | } |
2595 | |
2596 | /* record end of trap, giving us a range of |
2597 | * error sequences we'll ignore. |
2598 | */ |
2599 | trap->end_sequence = XNextRequest (display_x11->xdisplay); |
2600 | |
2601 | /* remove the Xlib callback */ |
2602 | _gdk_x11_error_handler_pop (); |
2603 | |
2604 | /* we may already be outdated */ |
2605 | delete_outdated_error_traps (display_x11); |
2606 | |
2607 | return result; |
2608 | } |
2609 | |
2610 | /** |
2611 | * gdk_x11_display_set_surface_scale: |
2612 | * @display: (type GdkX11Display): the display |
2613 | * @scale: The new scale value |
2614 | * |
2615 | * Forces a specific window scale for all windows on this display, |
2616 | * instead of using the default or user configured scale. This |
2617 | * is can be used to disable scaling support by setting @scale to |
2618 | * 1, or to programmatically set the window scale. |
2619 | * |
2620 | * Once the scale is set by this call it will not change in response |
2621 | * to later user configuration changes. |
2622 | */ |
2623 | void |
2624 | gdk_x11_display_set_surface_scale (GdkDisplay *display, |
2625 | int scale) |
2626 | { |
2627 | GdkX11Screen *x11_screen; |
2628 | gboolean need_reread_settings = FALSE; |
2629 | |
2630 | g_return_if_fail (GDK_IS_X11_DISPLAY (display)); |
2631 | |
2632 | scale = MAX (scale, 1); |
2633 | |
2634 | x11_screen = GDK_X11_SCREEN (GDK_X11_DISPLAY (display)->screen); |
2635 | |
2636 | if (!x11_screen->fixed_surface_scale) |
2637 | { |
2638 | x11_screen->fixed_surface_scale = TRUE; |
2639 | |
2640 | /* We treat screens with a window scale set differently when |
2641 | * reading xsettings, so we need to reread |
2642 | */ |
2643 | need_reread_settings = TRUE; |
2644 | } |
2645 | |
2646 | _gdk_x11_screen_set_surface_scale (x11_screen, scale); |
2647 | |
2648 | if (need_reread_settings) |
2649 | _gdk_x11_settings_force_reread (x11_screen); |
2650 | } |
2651 | |
2652 | |
2653 | /** |
2654 | * gdk_x11_display_error_trap_pop: |
2655 | * @display: (type GdkX11Display): the display |
2656 | * |
2657 | * Pops the error trap pushed by gdk_x11_display_error_trap_push(). |
2658 | * Will XSync() if necessary and will always block until |
2659 | * the error is known to have occurred or not occurred, |
2660 | * so the error code can be returned. |
2661 | * |
2662 | * If you don’t need to use the return value, |
2663 | * gdk_x11_display_error_trap_pop_ignored() would be more efficient. |
2664 | * |
2665 | * Returns: X error code or 0 on success |
2666 | */ |
2667 | int |
2668 | gdk_x11_display_error_trap_pop (GdkDisplay *display) |
2669 | { |
2670 | g_return_val_if_fail (GDK_IS_X11_DISPLAY (display), Success); |
2671 | |
2672 | return gdk_x11_display_error_trap_pop_internal (display, TRUE); |
2673 | } |
2674 | |
2675 | /** |
2676 | * gdk_x11_display_error_trap_pop_ignored: |
2677 | * @display: (type GdkX11Display): the display |
2678 | * |
2679 | * Pops the error trap pushed by gdk_x11_display_error_trap_push(). |
2680 | * Does not block to see if an error occurred; merely records the |
2681 | * range of requests to ignore errors for, and ignores those errors |
2682 | * if they arrive asynchronously. |
2683 | */ |
2684 | void |
2685 | gdk_x11_display_error_trap_pop_ignored (GdkDisplay *display) |
2686 | { |
2687 | g_return_if_fail (GDK_IS_X11_DISPLAY (display)); |
2688 | |
2689 | gdk_x11_display_error_trap_pop_internal (display, FALSE); |
2690 | } |
2691 | |
2692 | /** |
2693 | * gdk_x11_set_sm_client_id: |
2694 | * @sm_client_id: (nullable): the client id assigned by the session manager |
2695 | * when the connection was opened, or %NULL to remove the property. |
2696 | * |
2697 | * Sets the `SM_CLIENT_ID` property on the application’s leader window so that |
2698 | * the window manager can save the application’s state using the X11R6 ICCCM |
2699 | * session management protocol. |
2700 | * |
2701 | * See the X Session Management Library documentation for more information on |
2702 | * session management and the Inter-Client Communication Conventions Manual |
2703 | */ |
2704 | void |
2705 | gdk_x11_set_sm_client_id (const char *sm_client_id) |
2706 | { |
2707 | GSList *displays, *l; |
2708 | |
2709 | g_free (mem: gdk_sm_client_id); |
2710 | gdk_sm_client_id = g_strdup (str: sm_client_id); |
2711 | |
2712 | displays = gdk_display_manager_list_displays (manager: gdk_display_manager_get ()); |
2713 | for (l = displays; l; l = l->next) |
2714 | { |
2715 | if (GDK_IS_X11_DISPLAY (l->data)) |
2716 | set_sm_client_id (display: l->data, sm_client_id); |
2717 | } |
2718 | |
2719 | g_slist_free (list: displays); |
2720 | } |
2721 | |
2722 | gsize |
2723 | gdk_x11_display_get_max_request_size (GdkDisplay *display) |
2724 | { |
2725 | Display *xdisplay = GDK_DISPLAY_XDISPLAY (display); |
2726 | gsize size; |
2727 | |
2728 | size = XExtendedMaxRequestSize (xdisplay); |
2729 | if (size <= 0) |
2730 | size = XMaxRequestSize (xdisplay); |
2731 | |
2732 | size = MIN (262144, size - 100); |
2733 | return size; |
2734 | } |
2735 | |
2736 | /** |
2737 | * gdk_x11_display_get_screen: |
2738 | * @display: (type GdkX11Display): a `GdkX11Display` |
2739 | * |
2740 | * Retrieves the `GdkX11Screen` of the @display. |
2741 | * |
2742 | * Returns: (transfer none): the `GdkX11Screen` |
2743 | */ |
2744 | GdkX11Screen * |
2745 | gdk_x11_display_get_screen (GdkDisplay *display) |
2746 | { |
2747 | return GDK_X11_DISPLAY (display)->screen; |
2748 | } |
2749 | |
2750 | static GdkKeymap * |
2751 | gdk_x11_display_get_keymap (GdkDisplay *display) |
2752 | { |
2753 | GdkX11Display *display_x11; |
2754 | g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL); |
2755 | display_x11 = GDK_X11_DISPLAY (display); |
2756 | |
2757 | if (!display_x11->keymap) |
2758 | { |
2759 | display_x11->keymap = g_object_new (GDK_TYPE_X11_KEYMAP, NULL); |
2760 | display_x11->keymap->display = display; /* beware of ref cycle */ |
2761 | } |
2762 | |
2763 | return display_x11->keymap; |
2764 | } |
2765 | |
2766 | static GdkSeat * |
2767 | gdk_x11_display_get_default_seat (GdkDisplay *display) |
2768 | { |
2769 | GList *seats, *l; |
2770 | int device_id; |
2771 | gboolean result = FALSE; |
2772 | |
2773 | seats = gdk_display_list_seats (display); |
2774 | |
2775 | gdk_x11_display_error_trap_push (display); |
2776 | result = XIGetClientPointer (GDK_DISPLAY_XDISPLAY (display), |
2777 | None, deviceid: &device_id); |
2778 | gdk_x11_display_error_trap_pop_ignored (display); |
2779 | |
2780 | for (l = seats; l; l = l->next) |
2781 | { |
2782 | GdkDevice *pointer; |
2783 | |
2784 | pointer = gdk_seat_get_pointer (seat: l->data); |
2785 | |
2786 | if (gdk_x11_device_get_id (device: pointer) == device_id || !result) |
2787 | { |
2788 | GdkSeat *seat = l->data; |
2789 | g_list_free (list: seats); |
2790 | |
2791 | return seat; |
2792 | } |
2793 | } |
2794 | |
2795 | g_list_free (list: seats); |
2796 | |
2797 | return NULL; |
2798 | } |
2799 | |
2800 | static GListModel * |
2801 | gdk_x11_display_get_monitors (GdkDisplay *display) |
2802 | { |
2803 | GdkX11Display *self = GDK_X11_DISPLAY (display); |
2804 | |
2805 | return G_LIST_MODEL (ptr: self->monitors); |
2806 | } |
2807 | |
2808 | /** |
2809 | * gdk_x11_display_get_primary_monitor: |
2810 | * @display: (type GdkX11Display): a `GdkDisplay` |
2811 | * |
2812 | * Gets the primary monitor for the display. |
2813 | * |
2814 | * The primary monitor is considered the monitor where the “main desktop” |
2815 | * lives. While normal application surfaces typically allow the window |
2816 | * manager to place the surfaces, specialized desktop applications |
2817 | * such as panels should place themselves on the primary monitor. |
2818 | * |
2819 | * If no monitor is the designated primary monitor, any monitor |
2820 | * (usually the first) may be returned. |
2821 | * |
2822 | * Returns: (transfer none): the primary monitor, or any monitor if no |
2823 | * primary monitor is configured by the user |
2824 | */ |
2825 | GdkMonitor * |
2826 | gdk_x11_display_get_primary_monitor (GdkDisplay *display) |
2827 | { |
2828 | GdkX11Display *self = GDK_X11_DISPLAY (display); |
2829 | GdkMonitor *monitor; |
2830 | |
2831 | monitor = g_list_model_get_item (list: G_LIST_MODEL (ptr: self->monitors), position: self->primary_monitor); |
2832 | if (monitor == NULL) |
2833 | monitor = g_list_model_get_item (list: G_LIST_MODEL (ptr: self->monitors), position: 0); |
2834 | |
2835 | /* because g_list_model_get_item() returns a ref */ |
2836 | if (monitor) |
2837 | g_object_unref (object: monitor); |
2838 | |
2839 | return monitor; |
2840 | } |
2841 | |
2842 | int |
2843 | gdk_x11_display_get_window_depth (GdkX11Display *display) |
2844 | { |
2845 | return display->window_depth; |
2846 | } |
2847 | |
2848 | Visual * |
2849 | gdk_x11_display_get_window_visual (GdkX11Display *display) |
2850 | { |
2851 | return display->window_visual; |
2852 | } |
2853 | |
2854 | Colormap |
2855 | gdk_x11_display_get_window_colormap (GdkX11Display *display) |
2856 | { |
2857 | return display->window_colormap; |
2858 | } |
2859 | |
2860 | static gboolean |
2861 | gdk_x11_display_get_setting (GdkDisplay *display, |
2862 | const char *name, |
2863 | GValue *value) |
2864 | { |
2865 | return gdk_x11_screen_get_setting (GDK_X11_DISPLAY (display)->screen, name, value); |
2866 | } |
2867 | |
2868 | GList * |
2869 | gdk_x11_display_get_toplevel_windows (GdkDisplay *display) |
2870 | { |
2871 | return GDK_X11_DISPLAY (display)->toplevels; |
2872 | } |
2873 | |
2874 | static gboolean |
2875 | gdk_boolean_handled_accumulator (GSignalInvocationHint *ihint, |
2876 | GValue *return_accu, |
2877 | const GValue *handler_return, |
2878 | gpointer dummy) |
2879 | { |
2880 | gboolean continue_emission; |
2881 | gboolean signal_handled; |
2882 | |
2883 | signal_handled = g_value_get_boolean (value: handler_return); |
2884 | g_value_set_boolean (value: return_accu, v_boolean: signal_handled); |
2885 | continue_emission = !signal_handled; |
2886 | |
2887 | return continue_emission; |
2888 | } |
2889 | |
2890 | static XVisualInfo * |
2891 | gdk_x11_display_get_visual_info_for_visual (GdkX11Display *self, |
2892 | VisualID visualid) |
2893 | { |
2894 | XVisualInfo template, *visinfo; |
2895 | int nvisuals; |
2896 | |
2897 | template.screen = self->screen->screen_num; |
2898 | template.visualid = visualid; |
2899 | |
2900 | visinfo = XGetVisualInfo (gdk_x11_display_get_xdisplay (GDK_DISPLAY (self)), |
2901 | VisualScreenMask | VisualIDMask, |
2902 | &template, |
2903 | &nvisuals); |
2904 | g_warn_if_fail (nvisuals == 1); |
2905 | |
2906 | return visinfo; |
2907 | } |
2908 | |
2909 | static gboolean |
2910 | visual_is_rgba (XVisualInfo *visinfo) |
2911 | { |
2912 | return |
2913 | visinfo->depth == 32 && |
2914 | visinfo->visual->red_mask == 0xff0000 && |
2915 | visinfo->visual->green_mask == 0x00ff00 && |
2916 | visinfo->visual->blue_mask == 0x0000ff; |
2917 | } |
2918 | |
2919 | static guint |
2920 | gdk_x11_display_rate_egl_config (GdkDisplay *display, |
2921 | gpointer egl_display, |
2922 | gpointer config) |
2923 | { |
2924 | GdkX11Display *self = GDK_X11_DISPLAY (display); |
2925 | XVisualInfo *visinfo; |
2926 | guint distance; |
2927 | int visualid; |
2928 | |
2929 | if (!eglGetConfigAttrib (egl_display, config, EGL_NATIVE_VISUAL_ID, &visualid)) |
2930 | return G_MAXUINT; |
2931 | |
2932 | visinfo = gdk_x11_display_get_visual_info_for_visual (self, visualid); |
2933 | if (visinfo == NULL) |
2934 | return G_MAXUINT; |
2935 | |
2936 | distance = GDK_DISPLAY_CLASS (gdk_x11_display_parent_class)->rate_egl_config (display, egl_display, config); |
2937 | |
2938 | if (!visual_is_rgba (visinfo)) |
2939 | distance += 0x100; |
2940 | |
2941 | XFree (visinfo); |
2942 | |
2943 | return distance; |
2944 | } |
2945 | |
2946 | static gboolean |
2947 | gdk_x11_display_init_gl_backend (GdkX11Display *self, |
2948 | Visual **out_visual, |
2949 | int *out_depth, |
2950 | GError **error) |
2951 | { |
2952 | GdkDisplay *display = GDK_DISPLAY (self); |
2953 | Display *dpy = gdk_x11_display_get_xdisplay (GDK_DISPLAY (self)); |
2954 | EGLDisplay egl_display; |
2955 | XVisualInfo *visinfo; |
2956 | int visualid; |
2957 | |
2958 | /* No env vars set, do the regular GL initialization. |
2959 | * |
2960 | * We try EGL first, but are very picky about what we accept. |
2961 | * If that fails, we try to go with GLX instead. |
2962 | * And if that also fails, we try EGL again, but this time accept anything. |
2963 | * |
2964 | * The idea here is that EGL is the preferred method going forward, but GLX is |
2965 | * the tried and tested method that we know works. So if we detect issues with |
2966 | * EGL, we want to avoid using it in favor of GLX. |
2967 | */ |
2968 | |
2969 | if (!gdk_display_init_egl (display, EGL_PLATFORM_X11_KHR, native_display: dpy, FALSE, error)) |
2970 | { |
2971 | g_clear_error (err: error); |
2972 | |
2973 | if (gdk_x11_display_init_glx (display_x11: self, out_visual, out_depth, error)) |
2974 | return TRUE; |
2975 | |
2976 | g_clear_error (err: error); |
2977 | if (!gdk_display_init_egl (display, EGL_PLATFORM_X11_KHR, native_display: dpy, TRUE, error)) |
2978 | return FALSE; |
2979 | } |
2980 | |
2981 | if (!eglGetConfigAttrib (gdk_display_get_egl_display (display), |
2982 | gdk_display_get_egl_config (display), |
2983 | EGL_NATIVE_VISUAL_ID, |
2984 | &visualid)) |
2985 | { |
2986 | /* We guarantee this when rating configs */ |
2987 | g_assert_not_reached (); |
2988 | } |
2989 | visinfo = gdk_x11_display_get_visual_info_for_visual (self, visualid); |
2990 | g_assert (visinfo); |
2991 | *out_visual = visinfo->visual; |
2992 | *out_depth = visinfo->depth; |
2993 | |
2994 | egl_display = gdk_display_get_egl_display (display); |
2995 | |
2996 | self->egl_version = epoxy_egl_version (dpy: egl_display); |
2997 | |
2998 | return TRUE; |
2999 | } |
3000 | |
3001 | static GdkGLContext * |
3002 | gdk_x11_display_init_gl (GdkDisplay *display, |
3003 | GError **error) |
3004 | { |
3005 | GdkX11Display *self = GDK_X11_DISPLAY (display); |
3006 | |
3007 | if (!gdk_x11_display_init_gl_backend (self, out_visual: &self->window_visual, out_depth: &self->window_depth, error)) |
3008 | return FALSE; |
3009 | |
3010 | gdk_x11_display_init_leader_surface (self); |
3011 | |
3012 | if (self->glx_config != NULL) |
3013 | return g_object_new (GDK_TYPE_X11_GL_CONTEXT_GLX, first_property_name: "surface" , self->leader_gdk_surface, NULL); |
3014 | else if (gdk_display_get_egl_display (display)) |
3015 | return g_object_new (GDK_TYPE_X11_GL_CONTEXT_EGL, first_property_name: "surface" , self->leader_gdk_surface, NULL); |
3016 | else |
3017 | g_return_val_if_reached (NULL); |
3018 | } |
3019 | |
3020 | static void |
3021 | gdk_x11_display_class_init (GdkX11DisplayClass * class) |
3022 | { |
3023 | GObjectClass *object_class = G_OBJECT_CLASS (class); |
3024 | GdkDisplayClass *display_class = GDK_DISPLAY_CLASS (class); |
3025 | |
3026 | object_class->dispose = gdk_x11_display_dispose; |
3027 | object_class->finalize = gdk_x11_display_finalize; |
3028 | |
3029 | display_class->cairo_context_type = GDK_TYPE_X11_CAIRO_CONTEXT; |
3030 | #ifdef GDK_RENDERING_VULKAN |
3031 | display_class->vk_context_type = GDK_TYPE_X11_VULKAN_CONTEXT; |
3032 | display_class->vk_extension_name = VK_KHR_XLIB_SURFACE_EXTENSION_NAME; |
3033 | #endif |
3034 | |
3035 | display_class->get_name = gdk_x11_display_get_name; |
3036 | display_class->beep = gdk_x11_display_beep; |
3037 | display_class->sync = gdk_x11_display_sync; |
3038 | display_class->flush = gdk_x11_display_flush; |
3039 | display_class->make_default = gdk_x11_display_make_default; |
3040 | display_class->has_pending = gdk_x11_display_has_pending; |
3041 | display_class->queue_events = _gdk_x11_display_queue_events; |
3042 | display_class->get_app_launch_context = _gdk_x11_display_get_app_launch_context; |
3043 | |
3044 | display_class->get_next_serial = gdk_x11_display_get_next_serial; |
3045 | display_class->get_startup_notification_id = gdk_x11_display_get_startup_notification_id; |
3046 | display_class->notify_startup_complete = gdk_x11_display_notify_startup_complete; |
3047 | display_class->create_surface = _gdk_x11_display_create_surface; |
3048 | display_class->get_keymap = gdk_x11_display_get_keymap; |
3049 | |
3050 | display_class->init_gl = gdk_x11_display_init_gl; |
3051 | display_class->rate_egl_config = gdk_x11_display_rate_egl_config; |
3052 | |
3053 | display_class->get_default_seat = gdk_x11_display_get_default_seat; |
3054 | |
3055 | display_class->get_monitors = gdk_x11_display_get_monitors; |
3056 | display_class->get_setting = gdk_x11_display_get_setting; |
3057 | display_class->set_cursor_theme = gdk_x11_display_set_cursor_theme; |
3058 | |
3059 | class->xevent = gdk_event_source_xevent; |
3060 | |
3061 | /** |
3062 | * GdkX11Display::xevent: |
3063 | * @display: (type GdkX11Display): the object on which the signal is emitted |
3064 | * @xevent: a pointer to the XEvent to process |
3065 | * |
3066 | * The ::xevent signal is a low level signal that is emitted |
3067 | * whenever an XEvent has been received. |
3068 | * |
3069 | * When handlers to this signal return %TRUE, no other handlers will be |
3070 | * invoked. In particular, the default handler for this function is |
3071 | * GDK's own event handling mechanism, so by returning %TRUE for an event |
3072 | * that GDK expects to translate, you may break GDK and/or GTK+ in |
3073 | * interesting ways. You have been warned. |
3074 | * |
3075 | * If you want this signal handler to queue a `GdkEvent`, you can use |
3076 | * gdk_display_put_event(). |
3077 | * |
3078 | * If you are interested in X GenericEvents, bear in mind that |
3079 | * XGetEventData() has been already called on the event, and |
3080 | * XFreeEventData() will be called afterwards. |
3081 | * |
3082 | * Returns: %TRUE to stop other handlers from being invoked for the event. |
3083 | * %FALSE to propagate the event further. |
3084 | */ |
3085 | signals[XEVENT] = |
3086 | g_signal_new (signal_name: g_intern_static_string (string: "xevent" ), |
3087 | G_OBJECT_CLASS_TYPE (object_class), |
3088 | signal_flags: G_SIGNAL_RUN_LAST, |
3089 | G_STRUCT_OFFSET (GdkX11DisplayClass, xevent), |
3090 | accumulator: gdk_boolean_handled_accumulator, NULL, |
3091 | c_marshaller: _gdk_marshal_BOOLEAN__POINTER, |
3092 | G_TYPE_BOOLEAN, n_params: 1, G_TYPE_POINTER); |
3093 | |
3094 | _gdk_x11_surfaceing_init (); |
3095 | } |
3096 | |