1 | /* GDK - The GIMP Drawing Kit |
2 | * Copyright (C) 1995-2007 Peter Mattis, Spencer Kimball, |
3 | * Josh MacDonald, Ryan Lortie |
4 | * |
5 | * This library is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU Lesser General Public |
7 | * License as published by the Free Software Foundation; either |
8 | * version 2 of the License, or (at your option) any later version. |
9 | * |
10 | * This library is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * Lesser General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU Lesser General Public |
16 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
17 | */ |
18 | |
19 | /* |
20 | * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS |
21 | * file for a list of people on the GTK+ Team. See the ChangeLog |
22 | * files for a list of changes. These files are distributed with |
23 | * GTK+ at ftp://ftp.gtk.org/pub/gtk/. |
24 | */ |
25 | |
26 | #include "config.h" |
27 | |
28 | #include "gdksurface-x11.h" |
29 | |
30 | #include "gdksurfaceprivate.h" |
31 | #include "gdkpopupprivate.h" |
32 | #include "gdktoplevelprivate.h" |
33 | #include "gdkdragsurfaceprivate.h" |
34 | #include "gdkdeviceprivate.h" |
35 | #include "gdkdevice-xi2-private.h" |
36 | #include "gdkframeclockidleprivate.h" |
37 | #include "gdkasync.h" |
38 | #include "gdkeventsource.h" |
39 | #include "gdkdisplay-x11.h" |
40 | #include "gdkglcontext-x11.h" |
41 | #include "gdkprivate-x11.h" |
42 | #include "gdktextureprivate.h" |
43 | |
44 | #include "gdkseatprivate.h" |
45 | #include "gdk-private.h" |
46 | |
47 | #include <graphene.h> |
48 | #include <stdlib.h> |
49 | #include <stdio.h> |
50 | #include <string.h> |
51 | #include <netinet/in.h> |
52 | #include <unistd.h> |
53 | |
54 | #include <cairo-xlib.h> |
55 | |
56 | #include "MwmUtil.h" |
57 | |
58 | #include <X11/Xlib.h> |
59 | #include <X11/Xutil.h> |
60 | #include <X11/Xatom.h> |
61 | |
62 | #include <X11/extensions/shape.h> |
63 | |
64 | #ifdef HAVE_XKB |
65 | #include <X11/XKBlib.h> |
66 | #endif |
67 | |
68 | const int _gdk_x11_event_mask_table[21] = |
69 | { |
70 | ExposureMask, |
71 | PointerMotionMask, |
72 | PointerMotionHintMask, |
73 | ButtonMotionMask, |
74 | Button1MotionMask, |
75 | Button2MotionMask, |
76 | Button3MotionMask, |
77 | ButtonPressMask, |
78 | ButtonReleaseMask, |
79 | KeyPressMask, |
80 | KeyReleaseMask, |
81 | EnterWindowMask, |
82 | LeaveWindowMask, |
83 | FocusChangeMask, |
84 | StructureNotifyMask, |
85 | PropertyChangeMask, |
86 | VisibilityChangeMask, |
87 | 0, /* PROXIMITY_IN */ |
88 | 0, /* PROXIMTY_OUT */ |
89 | SubstructureNotifyMask, |
90 | ButtonPressMask /* SCROLL; on X mouse wheel events is treated as mouse button 4/5 */ |
91 | }; |
92 | |
93 | typedef struct { |
94 | GdkX11Surface parent_instance; |
95 | } GdkX11Toplevel; |
96 | |
97 | typedef struct { |
98 | GdkX11SurfaceClass parent_class; |
99 | } GdkX11ToplevelClass; |
100 | |
101 | const int _gdk_x11_event_mask_table_size = G_N_ELEMENTS (_gdk_x11_event_mask_table); |
102 | |
103 | /* Forward declarations */ |
104 | static void gdk_x11_surface_apply_fullscreen_mode (GdkSurface *surface); |
105 | static gboolean gdk_surface_icon_name_set (GdkSurface *surface); |
106 | static void set_wm_name (GdkDisplay *display, |
107 | Window xwindow, |
108 | const char *name); |
109 | static void move_to_current_desktop (GdkSurface *surface); |
110 | static void gdk_x11_toplevel_state_callback (GdkSurface *surface); |
111 | static gboolean gdk_x11_toplevel_event_callback (GdkSurface *surface, |
112 | GdkEvent *gdk_event); |
113 | |
114 | static void gdk_x11_surface_toplevel_resize (GdkSurface *surface, |
115 | int width, |
116 | int height); |
117 | |
118 | static void gdk_x11_surface_set_geometry_hints (GdkSurface *surface, |
119 | const GdkGeometry *geometry, |
120 | GdkSurfaceHints geom_mask); |
121 | |
122 | /* Return whether time1 is considered later than time2 as far as xserver |
123 | * time is concerned. Accounts for wraparound. |
124 | */ |
125 | #define XSERVER_TIME_IS_LATER(time1, time2) \ |
126 | ( (( time1 > time2 ) && ( time1 - time2 < ((guint32)-1)/2 )) || \ |
127 | (( time1 < time2 ) && ( time2 - time1 > ((guint32)-1)/2 )) \ |
128 | ) |
129 | |
130 | G_DEFINE_TYPE (GdkX11Surface, gdk_x11_surface, GDK_TYPE_SURFACE) |
131 | |
132 | GType gdk_x11_toplevel_get_type (void) G_GNUC_CONST; |
133 | GType gdk_x11_popup_get_type (void) G_GNUC_CONST; |
134 | GType gdk_x11_drag_surface_get_type (void) G_GNUC_CONST; |
135 | |
136 | #define GDK_TYPE_X11_TOPLEVEL (gdk_x11_toplevel_get_type ()) |
137 | #define (gdk_x11_popup_get_type ()) |
138 | #define GDK_TYPE_X11_DRAG_SURFACE (gdk_x11_drag_surface_get_type ()) |
139 | |
140 | static void |
141 | gdk_x11_surface_init (GdkX11Surface *impl) |
142 | { |
143 | impl->surface_scale = 1; |
144 | impl->frame_sync_enabled = TRUE; |
145 | impl->surface_is_on_monitor = NULL; |
146 | } |
147 | |
148 | GdkToplevelX11 * |
149 | _gdk_x11_surface_get_toplevel (GdkSurface *surface) |
150 | { |
151 | GdkX11Surface *impl; |
152 | |
153 | g_assert (GDK_IS_SURFACE (surface)); |
154 | |
155 | impl = GDK_X11_SURFACE (surface); |
156 | |
157 | if (!impl->toplevel) |
158 | { |
159 | impl->toplevel = g_new0 (GdkToplevelX11, 1); |
160 | impl->toplevel->have_focused = FALSE; |
161 | g_signal_connect (surface, "notify::state" , |
162 | G_CALLBACK (gdk_x11_toplevel_state_callback), |
163 | NULL); |
164 | g_signal_connect (surface, "event" , |
165 | G_CALLBACK (gdk_x11_toplevel_event_callback), |
166 | NULL); |
167 | } |
168 | |
169 | return impl->toplevel; |
170 | } |
171 | |
172 | /* |
173 | * gdk_x11_surface_update_size: |
174 | * @self: a `GdkX11Surface` |
175 | * @width: the new width of the surface |
176 | * @height: the new height of the surface |
177 | * @scale: the new scale of the surface |
178 | * |
179 | * Updates the state of the surface (in particular the drawable's |
180 | * cairo surface) when its size has changed. |
181 | * |
182 | * Returns: %TRUE if the surface was updated, %FALSE if no updates |
183 | * where necessary |
184 | */ |
185 | static gboolean |
186 | gdk_x11_surface_update_size (GdkX11Surface *self, |
187 | int width, |
188 | int height, |
189 | int scale) |
190 | { |
191 | GdkSurface *surface = GDK_SURFACE (self); |
192 | |
193 | if (surface->width == width && |
194 | surface->height == height && |
195 | self->surface_scale == scale) |
196 | return FALSE; |
197 | |
198 | surface->width = width; |
199 | surface->height = height; |
200 | self->surface_scale = scale; |
201 | |
202 | _gdk_surface_update_size (surface); |
203 | |
204 | if (self->cairo_surface) |
205 | { |
206 | cairo_xlib_surface_set_size (surface: self->cairo_surface, |
207 | width: self->unscaled_width, height: self->unscaled_height); |
208 | cairo_surface_set_device_scale (surface: self->cairo_surface, x_scale: scale, y_scale: scale); |
209 | } |
210 | |
211 | return TRUE; |
212 | } |
213 | |
214 | static void |
215 | update_shadow_size (GdkSurface *surface, |
216 | int shadow_left, |
217 | int shadow_right, |
218 | int shadow_top, |
219 | int shadow_bottom) |
220 | { |
221 | GdkX11Surface *impl = GDK_X11_SURFACE (surface); |
222 | Atom frame_extents; |
223 | gulong data[4]; |
224 | |
225 | if (impl->shadow_left == shadow_left && |
226 | impl->shadow_right == shadow_right && |
227 | impl->shadow_top == shadow_top && |
228 | impl->shadow_bottom == shadow_bottom) |
229 | return; |
230 | |
231 | impl->shadow_left = shadow_left; |
232 | impl->shadow_right = shadow_right; |
233 | impl->shadow_top = shadow_top; |
234 | impl->shadow_bottom = shadow_bottom; |
235 | |
236 | data[0] = shadow_left * impl->surface_scale; |
237 | data[1] = shadow_right * impl->surface_scale; |
238 | data[2] = shadow_top * impl->surface_scale; |
239 | data[3] = shadow_bottom * impl->surface_scale; |
240 | |
241 | frame_extents = gdk_x11_get_xatom_by_name_for_display (display: gdk_surface_get_display (surface), |
242 | atom_name: "_GTK_FRAME_EXTENTS" ); |
243 | XChangeProperty (GDK_SURFACE_XDISPLAY (surface), |
244 | GDK_SURFACE_XID (surface), |
245 | frame_extents, XA_CARDINAL, |
246 | 32, PropModeReplace, |
247 | (guchar *) &data, 4); |
248 | } |
249 | |
250 | #define UPDATE_GEOMETRY TRUE |
251 | #define DONT_UPDATE_GEOMETRY FALSE |
252 | |
253 | static gboolean |
254 | compute_toplevel_size (GdkSurface *surface, |
255 | gboolean update_geometry, |
256 | int *width, |
257 | int *height) |
258 | { |
259 | GdkX11Surface *impl = GDK_X11_SURFACE (surface); |
260 | GdkDisplay *display = gdk_surface_get_display (surface); |
261 | GdkMonitor *monitor; |
262 | GdkToplevelSize size; |
263 | int bounds_width, bounds_height; |
264 | |
265 | monitor = gdk_display_get_monitor_at_surface (display, surface); |
266 | if (monitor) |
267 | { |
268 | GdkRectangle workarea; |
269 | |
270 | gdk_x11_monitor_get_workarea (monitor, workarea: &workarea); |
271 | bounds_width = workarea.width; |
272 | bounds_height = workarea.height; |
273 | } |
274 | else |
275 | { |
276 | bounds_width = G_MAXINT; |
277 | bounds_height = G_MAXINT; |
278 | } |
279 | |
280 | gdk_toplevel_size_init (size: &size, bounds_width, bounds_height); |
281 | gdk_toplevel_notify_compute_size (toplevel: GDK_TOPLEVEL (ptr: surface), size: &size); |
282 | |
283 | if (size.shadow.is_valid) |
284 | { |
285 | update_shadow_size (surface, |
286 | shadow_left: size.shadow.left, |
287 | shadow_right: size.shadow.right, |
288 | shadow_top: size.shadow.top, |
289 | shadow_bottom: size.shadow.bottom); |
290 | } |
291 | |
292 | if (update_geometry) |
293 | { |
294 | GdkGeometry geometry; |
295 | GdkSurfaceHints mask; |
296 | |
297 | if (!impl->toplevel_layout || gdk_toplevel_layout_get_resizable (layout: impl->toplevel_layout)) |
298 | { |
299 | geometry.min_width = size.min_width; |
300 | geometry.min_height = size.min_height; |
301 | mask = GDK_HINT_MIN_SIZE; |
302 | } |
303 | else |
304 | { |
305 | geometry.max_width = geometry.min_width = size.width; |
306 | geometry.max_height = geometry.min_height = size.height; |
307 | mask = GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE; |
308 | } |
309 | gdk_x11_surface_set_geometry_hints (surface, geometry: &geometry, geom_mask: mask); |
310 | } |
311 | |
312 | if (!(surface->state & (GDK_TOPLEVEL_STATE_FULLSCREEN | |
313 | GDK_TOPLEVEL_STATE_MAXIMIZED | |
314 | GDK_TOPLEVEL_STATE_TILED | |
315 | GDK_TOPLEVEL_STATE_TOP_TILED | |
316 | GDK_TOPLEVEL_STATE_RIGHT_TILED | |
317 | GDK_TOPLEVEL_STATE_BOTTOM_TILED | |
318 | GDK_TOPLEVEL_STATE_LEFT_TILED | |
319 | GDK_TOPLEVEL_STATE_MINIMIZED)) && |
320 | (!impl->next_layout.configure_pending || surface->resize_count > 0)) |
321 | { |
322 | GdkToplevelX11 *toplevel = _gdk_x11_surface_get_toplevel (surface); |
323 | GdkGeometry geometry; |
324 | GdkSurfaceHints mask; |
325 | |
326 | geometry = toplevel->last_geometry_hints; |
327 | mask = toplevel->last_geometry_hints_mask; |
328 | gdk_surface_constrain_size (geometry: &geometry, flags: mask, |
329 | width: size.width, height: size.height, |
330 | new_width: &size.width, new_height: &size.height); |
331 | if ((impl->last_computed_width != size.width || |
332 | impl->last_computed_height != size.height) && |
333 | (impl->next_layout.configured_width != size.width || |
334 | impl->next_layout.configured_height != size.height)) |
335 | { |
336 | *width = size.width; |
337 | *height = size.height; |
338 | impl->last_computed_width = size.width; |
339 | impl->last_computed_height = size.height; |
340 | |
341 | return TRUE; |
342 | } |
343 | } |
344 | |
345 | return FALSE; |
346 | } |
347 | |
348 | static gboolean |
349 | compute_size_idle (gpointer user_data) |
350 | { |
351 | GdkSurface *surface = user_data; |
352 | GdkX11Surface *impl = GDK_X11_SURFACE (surface); |
353 | int width, height; |
354 | |
355 | impl->compute_size_source_id = 0; |
356 | if (compute_toplevel_size (surface, UPDATE_GEOMETRY, width: &width, height: &height)) |
357 | gdk_x11_surface_toplevel_resize (surface, width, height); |
358 | |
359 | return G_SOURCE_REMOVE; |
360 | } |
361 | |
362 | static void |
363 | gdk_x11_surface_request_layout (GdkSurface *surface) |
364 | { |
365 | GdkX11Surface *impl = GDK_X11_SURFACE (surface); |
366 | |
367 | if (!impl->compute_size_source_id && |
368 | GDK_IS_TOPLEVEL (ptr: surface)) |
369 | { |
370 | impl->compute_size_source_id = g_idle_add_full (G_PRIORITY_HIGH - 10, |
371 | function: compute_size_idle, |
372 | data: surface, |
373 | NULL); |
374 | } |
375 | } |
376 | |
377 | static gboolean |
378 | gdk_x11_surface_compute_size (GdkSurface *surface) |
379 | { |
380 | GdkX11Surface *impl = GDK_X11_SURFACE (surface); |
381 | |
382 | if (GDK_IS_TOPLEVEL (ptr: surface)) |
383 | { |
384 | int width, height; |
385 | |
386 | if (compute_toplevel_size (surface, UPDATE_GEOMETRY, width: &width, height: &height)) |
387 | gdk_x11_surface_toplevel_resize (surface, width, height); |
388 | |
389 | if (surface->resize_count == 0) |
390 | { |
391 | gdk_x11_surface_update_size (self: impl, |
392 | width: impl->next_layout.configured_width, |
393 | height: impl->next_layout.configured_height, |
394 | scale: impl->surface_scale); |
395 | } |
396 | |
397 | impl->next_layout.surface_geometry_dirty = FALSE; |
398 | impl->next_layout.configure_pending = FALSE; |
399 | } |
400 | else |
401 | { |
402 | gdk_x11_surface_update_size (self: impl, |
403 | width: impl->next_layout.configured_width, |
404 | height: impl->next_layout.configured_height, |
405 | scale: impl->surface_scale); |
406 | |
407 | impl->next_layout.surface_geometry_dirty = FALSE; |
408 | } |
409 | |
410 | return surface->resize_count > 0; |
411 | } |
412 | |
413 | gboolean |
414 | gdk_x11_surface_supports_edge_constraints (GdkSurface *surface) |
415 | { |
416 | return gdk_x11_screen_supports_net_wm_hint (GDK_SURFACE_SCREEN (surface), |
417 | property_name: g_intern_static_string (string: "_GTK_EDGE_CONSTRAINTS" )); |
418 | } |
419 | |
420 | static void |
421 | set_sync_counter(Display *display, |
422 | XSyncCounter counter, |
423 | gint64 value) |
424 | { |
425 | XSyncValue sync_value; |
426 | |
427 | XSyncIntsToValue (&sync_value, |
428 | value & G_GINT64_CONSTANT(0xFFFFFFFF), |
429 | value >> 32); |
430 | XSyncSetCounter (display, counter, sync_value); |
431 | } |
432 | |
433 | void |
434 | gdk_x11_surface_pre_damage (GdkSurface *surface) |
435 | { |
436 | GdkX11Surface *impl; |
437 | |
438 | impl = GDK_X11_SURFACE (surface); |
439 | |
440 | if (impl->toplevel->in_frame && |
441 | impl->toplevel->current_counter_value % 2 == 0) |
442 | { |
443 | impl->toplevel->current_counter_value += 1; |
444 | set_sync_counter (GDK_SURFACE_XDISPLAY (surface), |
445 | counter: impl->toplevel->extended_update_counter, |
446 | value: impl->toplevel->current_counter_value); |
447 | } |
448 | } |
449 | |
450 | static void |
451 | on_surface_changed (void *data) |
452 | { |
453 | GdkSurface *surface = data; |
454 | GdkX11Surface *impl = GDK_X11_SURFACE (surface); |
455 | |
456 | if (impl->tracking_damage) |
457 | gdk_x11_surface_pre_damage (surface); |
458 | } |
459 | |
460 | /* We want to know when cairo drawing causes damage to the window, |
461 | * so we engage in the _NET_WM_FRAME_DRAWN protocol with the |
462 | * window only when there actually is drawing. To do that we use |
463 | * a technique (hack) suggested by Uli Schlachter - if we set |
464 | * a dummy "mime data" on the cairo surface (this facility is |
465 | * used to attach JPEG data to an imager), then cairo will flush |
466 | * and remove the mime data before making any changes to the window. |
467 | */ |
468 | |
469 | static void |
470 | hook_surface_changed (GdkSurface *surface) |
471 | { |
472 | GdkX11Surface *impl = GDK_X11_SURFACE (surface); |
473 | |
474 | if (impl->cairo_surface) |
475 | { |
476 | cairo_surface_set_mime_data (surface: impl->cairo_surface, |
477 | mime_type: "x-gdk/change-notify" , |
478 | data: (unsigned char *)"X" , |
479 | length: 1, |
480 | destroy: on_surface_changed, |
481 | closure: surface); |
482 | impl->tracking_damage = 1; |
483 | } |
484 | } |
485 | |
486 | static void |
487 | unhook_surface_changed (GdkSurface *surface) |
488 | { |
489 | GdkX11Surface *impl = GDK_X11_SURFACE (surface); |
490 | |
491 | if (impl->cairo_surface) |
492 | { |
493 | impl->tracking_damage = 0; |
494 | cairo_surface_set_mime_data (surface: impl->cairo_surface, |
495 | mime_type: "x-gdk/change-notify" , |
496 | NULL, length: 0, |
497 | NULL, NULL); |
498 | } |
499 | } |
500 | |
501 | static void |
502 | gdk_x11_surface_predict_presentation_time (GdkSurface *surface) |
503 | { |
504 | GdkX11Surface *impl = GDK_X11_SURFACE (surface); |
505 | GdkFrameClock *clock; |
506 | GdkFrameTimings *timings; |
507 | gint64 presentation_time; |
508 | gint64 refresh_interval; |
509 | |
510 | clock = gdk_surface_get_frame_clock (surface); |
511 | |
512 | timings = gdk_frame_clock_get_current_timings (frame_clock: clock); |
513 | |
514 | gdk_frame_clock_get_refresh_info (frame_clock: clock, |
515 | base_time: timings->frame_time, |
516 | refresh_interval_return: &refresh_interval, presentation_time_return: &presentation_time); |
517 | |
518 | if (presentation_time != 0) |
519 | { |
520 | if (timings->slept_before) |
521 | { |
522 | presentation_time += refresh_interval; |
523 | } |
524 | else |
525 | { |
526 | if (presentation_time < timings->frame_time + refresh_interval / 2) |
527 | presentation_time += refresh_interval; |
528 | } |
529 | } |
530 | else |
531 | { |
532 | if (timings->slept_before) |
533 | presentation_time = timings->frame_time + refresh_interval + refresh_interval / 2; |
534 | else |
535 | presentation_time = timings->frame_time + refresh_interval; |
536 | } |
537 | |
538 | if (presentation_time < impl->toplevel->throttled_presentation_time) |
539 | presentation_time = impl->toplevel->throttled_presentation_time; |
540 | |
541 | timings->predicted_presentation_time = presentation_time; |
542 | } |
543 | |
544 | static void |
545 | gdk_x11_surface_begin_frame (GdkSurface *surface, |
546 | gboolean force_frame) |
547 | { |
548 | GdkX11Surface *impl; |
549 | |
550 | g_return_if_fail (GDK_IS_SURFACE (surface)); |
551 | |
552 | impl = GDK_X11_SURFACE (surface); |
553 | |
554 | if (impl->toplevel->extended_update_counter == None) |
555 | return; |
556 | |
557 | impl->toplevel->in_frame = TRUE; |
558 | |
559 | if (impl->toplevel->configure_counter_value != 0 && |
560 | impl->toplevel->configure_counter_value_is_extended) |
561 | { |
562 | impl->toplevel->current_counter_value = impl->toplevel->configure_counter_value; |
563 | if ((impl->toplevel->current_counter_value % 2) == 1) |
564 | impl->toplevel->current_counter_value += 1; |
565 | |
566 | impl->toplevel->configure_counter_value = 0; |
567 | |
568 | gdk_x11_surface_pre_damage (surface); |
569 | } |
570 | else if (force_frame) |
571 | { |
572 | /* When mapping the surface, we really want to freeze the |
573 | rendering of the surface by the compositor until we've |
574 | actually painted something into the surface's buffer. */ |
575 | gdk_x11_surface_pre_damage (surface); |
576 | } |
577 | else |
578 | { |
579 | hook_surface_changed (surface); |
580 | } |
581 | } |
582 | |
583 | gboolean |
584 | _gdk_x11_surface_syncs_frames (GdkSurface *surface) |
585 | { |
586 | GdkX11Surface *impl = GDK_X11_SURFACE (surface); |
587 | |
588 | /* disabled client side */ |
589 | if (!impl->frame_sync_enabled) |
590 | return FALSE; |
591 | |
592 | /* disabled compositor side */ |
593 | if (!gdk_x11_screen_supports_net_wm_hint (GDK_SURFACE_SCREEN (surface), |
594 | property_name: g_intern_static_string (string: "_NET_WM_FRAME_DRAWN" ))) |
595 | return FALSE; |
596 | |
597 | return TRUE; |
598 | } |
599 | |
600 | static void |
601 | sync_counter_for_end_frame (GdkSurface *surface) |
602 | { |
603 | GdkX11Surface *impl = GDK_X11_SURFACE (surface); |
604 | |
605 | g_assert (!impl->toplevel->in_frame); |
606 | g_assert (impl->toplevel->extended_update_counter != None); |
607 | g_assert ((impl->toplevel->current_counter_value % 2) == 0); |
608 | |
609 | set_sync_counter (GDK_SURFACE_XDISPLAY (surface), |
610 | counter: impl->toplevel->extended_update_counter, |
611 | value: impl->toplevel->current_counter_value); |
612 | } |
613 | |
614 | static void |
615 | maybe_sync_counter_for_end_frame (GdkSurface *surface) |
616 | { |
617 | GdkX11Surface *impl = GDK_X11_SURFACE (surface); |
618 | gboolean frame_sync_negotiated = _gdk_x11_surface_syncs_frames (surface); |
619 | gboolean frame_done_painting; |
620 | |
621 | #ifdef HAVE_XDAMAGE |
622 | frame_done_painting = !impl->toplevel->frame_still_painting && frame_sync_negotiated; |
623 | #else |
624 | frame_done_painting = !impl->toplevel->frame_pending; |
625 | #endif |
626 | |
627 | if (!impl->toplevel->frame_pending) |
628 | { |
629 | if (!frame_sync_negotiated || frame_done_painting) |
630 | sync_counter_for_end_frame (surface); |
631 | } |
632 | else |
633 | { |
634 | if (frame_done_painting) |
635 | sync_counter_for_end_frame (surface); |
636 | } |
637 | } |
638 | |
639 | #ifdef HAVE_XDAMAGE |
640 | void |
641 | _gdk_x11_surface_set_frame_still_painting (GdkSurface *surface, |
642 | gboolean painting) |
643 | { |
644 | GdkX11Surface *impl = GDK_X11_SURFACE (surface); |
645 | |
646 | if (impl->toplevel->frame_still_painting == painting) |
647 | return; |
648 | |
649 | impl->toplevel->frame_still_painting = painting; |
650 | |
651 | if (!impl->toplevel->frame_still_painting) |
652 | maybe_sync_counter_for_end_frame (surface); |
653 | } |
654 | #endif |
655 | |
656 | static void |
657 | gdk_x11_surface_end_frame (GdkSurface *surface) |
658 | { |
659 | GdkFrameClock *clock; |
660 | GdkFrameTimings *timings; |
661 | GdkX11Surface *impl; |
662 | |
663 | g_return_if_fail (GDK_IS_SURFACE (surface)); |
664 | |
665 | impl = GDK_X11_SURFACE (surface); |
666 | |
667 | if (impl->toplevel->extended_update_counter == None || |
668 | !impl->toplevel->in_frame) |
669 | return; |
670 | |
671 | clock = gdk_surface_get_frame_clock (surface); |
672 | timings = gdk_frame_clock_get_current_timings (frame_clock: clock); |
673 | |
674 | /* Make sure we request timing updates even if nothing was damaged. |
675 | * We want the frame clock to be accurate. */ |
676 | gdk_x11_surface_pre_damage (surface); |
677 | |
678 | impl->toplevel->in_frame = FALSE; |
679 | |
680 | if (impl->toplevel->current_counter_value % 2 == 1) |
681 | { |
682 | if (GDK_DISPLAY_DEBUG_CHECK (gdk_surface_get_display (surface), FRAMES)) |
683 | { |
684 | XImage *image = XGetImage (GDK_SURFACE_XDISPLAY (surface), |
685 | GDK_SURFACE_XID (surface), |
686 | 0, 0, 1, 1, |
687 | (1 << 24) - 1, |
688 | ZPixmap); |
689 | XDestroyImage (image); |
690 | } |
691 | |
692 | /* An increment of 3 means that the frame was not drawn as fast as possible, |
693 | * but rather at a particular time. This can trigger different handling from |
694 | * the compositor. |
695 | */ |
696 | if (timings->slept_before) |
697 | impl->toplevel->current_counter_value += 3; |
698 | else |
699 | impl->toplevel->current_counter_value += 1; |
700 | |
701 | maybe_sync_counter_for_end_frame (surface); |
702 | |
703 | if (_gdk_x11_surface_syncs_frames (surface)) |
704 | { |
705 | impl->toplevel->frame_pending = TRUE; |
706 | gdk_surface_freeze_updates (surface); |
707 | timings->cookie = impl->toplevel->current_counter_value; |
708 | } |
709 | } |
710 | |
711 | unhook_surface_changed (surface); |
712 | |
713 | if (impl->toplevel->configure_counter_value != 0 && |
714 | !impl->toplevel->configure_counter_value_is_extended) |
715 | { |
716 | set_sync_counter (GDK_SURFACE_XDISPLAY (surface), |
717 | counter: impl->toplevel->update_counter, |
718 | value: impl->toplevel->configure_counter_value); |
719 | |
720 | impl->toplevel->configure_counter_value = 0; |
721 | } |
722 | |
723 | if (!impl->toplevel->frame_pending) |
724 | timings->complete = TRUE; |
725 | } |
726 | |
727 | /***************************************************** |
728 | * X11 specific implementations of generic functions * |
729 | *****************************************************/ |
730 | |
731 | static void |
732 | gdk_x11_surface_finalize (GObject *object) |
733 | { |
734 | GdkX11Surface *impl; |
735 | |
736 | g_return_if_fail (GDK_IS_X11_SURFACE (object)); |
737 | |
738 | impl = GDK_X11_SURFACE (object); |
739 | |
740 | if (impl->toplevel->in_frame) |
741 | unhook_surface_changed (GDK_SURFACE (impl)); |
742 | |
743 | g_signal_handlers_disconnect_by_func (GDK_SURFACE (impl), |
744 | gdk_x11_toplevel_state_callback, |
745 | NULL); |
746 | g_signal_handlers_disconnect_by_func (GDK_SURFACE (impl), |
747 | gdk_x11_toplevel_event_callback, |
748 | NULL); |
749 | |
750 | _gdk_x11_surface_grab_check_destroy (GDK_SURFACE (impl)); |
751 | |
752 | if (!GDK_SURFACE_DESTROYED (impl)) |
753 | { |
754 | GdkDisplay *display = GDK_SURFACE_DISPLAY (GDK_SURFACE (impl)); |
755 | |
756 | _gdk_x11_display_remove_window (display, xid: impl->xid); |
757 | if (impl->toplevel && impl->toplevel->focus_window) |
758 | _gdk_x11_display_remove_window (display, xid: impl->toplevel->focus_window); |
759 | } |
760 | |
761 | g_clear_pointer (&impl->surface_is_on_monitor, g_list_free); |
762 | g_clear_handle_id (&impl->compute_size_source_id, g_source_remove); |
763 | g_clear_pointer (&impl->toplevel_layout, gdk_toplevel_layout_unref); |
764 | |
765 | g_free (mem: impl->toplevel); |
766 | |
767 | if (impl->cursor) |
768 | g_object_unref (object: impl->cursor); |
769 | |
770 | G_OBJECT_CLASS (gdk_x11_surface_parent_class)->finalize (object); |
771 | } |
772 | |
773 | typedef struct { |
774 | GdkDisplay *display; |
775 | Pixmap pixmap; |
776 | } FreePixmapData; |
777 | |
778 | static void |
779 | free_pixmap (gpointer datap) |
780 | { |
781 | FreePixmapData *data = datap; |
782 | |
783 | if (!gdk_display_is_closed (display: data->display)) |
784 | { |
785 | XFreePixmap (GDK_DISPLAY_XDISPLAY (data->display), |
786 | data->pixmap); |
787 | } |
788 | |
789 | g_object_unref (object: data->display); |
790 | g_slice_free (FreePixmapData, data); |
791 | } |
792 | |
793 | static void |
794 | attach_free_pixmap_handler (cairo_surface_t *surface, |
795 | GdkDisplay *display, |
796 | Pixmap pixmap) |
797 | { |
798 | static const cairo_user_data_key_t key; |
799 | FreePixmapData *data; |
800 | |
801 | data = g_slice_new (FreePixmapData); |
802 | data->display = g_object_ref (display); |
803 | data->pixmap = pixmap; |
804 | |
805 | cairo_surface_set_user_data (surface, key: &key, user_data: data, destroy: free_pixmap); |
806 | } |
807 | |
808 | /* Cairo does not guarantee we get an xlib surface if we call |
809 | * cairo_surface_create_similar(). In some cases however, we must use a |
810 | * pixmap or bitmap in the X11 API. |
811 | * These functions ensure an Xlib surface. |
812 | */ |
813 | cairo_surface_t * |
814 | _gdk_x11_display_create_bitmap_surface (GdkDisplay *display, |
815 | int width, |
816 | int height) |
817 | { |
818 | cairo_surface_t *surface; |
819 | Pixmap pixmap; |
820 | |
821 | pixmap = XCreatePixmap (GDK_DISPLAY_XDISPLAY (display), |
822 | GDK_SCREEN_XROOTWIN (GDK_X11_DISPLAY (display)->screen), |
823 | width, height, 1); |
824 | surface = cairo_xlib_surface_create_for_bitmap (GDK_DISPLAY_XDISPLAY (display), |
825 | bitmap: pixmap, |
826 | GDK_X11_SCREEN (GDK_X11_DISPLAY (display)->screen)->xscreen, |
827 | width, height); |
828 | attach_free_pixmap_handler (surface, display, pixmap); |
829 | |
830 | return surface; |
831 | } |
832 | |
833 | /* Create a surface backed with a pixmap without alpha on the same screen as surface */ |
834 | static cairo_surface_t * |
835 | gdk_x11_surface_create_pixmap_surface (GdkSurface *surface, |
836 | int width, |
837 | int height) |
838 | { |
839 | GdkDisplay *display; |
840 | Display *dpy; |
841 | cairo_surface_t *cairo_surface; |
842 | Pixmap pixmap; |
843 | |
844 | display = gdk_surface_get_display (surface); |
845 | dpy = GDK_DISPLAY_XDISPLAY (display); |
846 | |
847 | pixmap = XCreatePixmap (dpy, |
848 | GDK_SURFACE_XID (surface), |
849 | width, height, |
850 | DefaultDepth (dpy, DefaultScreen (dpy))); |
851 | cairo_surface = cairo_xlib_surface_create (dpy, |
852 | drawable: pixmap, |
853 | DefaultVisual (dpy, DefaultScreen (dpy)), |
854 | width, height); |
855 | attach_free_pixmap_handler (surface: cairo_surface, display, pixmap); |
856 | |
857 | return cairo_surface; |
858 | } |
859 | |
860 | static void |
861 | set_wm_protocols (GdkSurface *surface) |
862 | { |
863 | GdkDisplay *display = gdk_surface_get_display (surface); |
864 | Atom protocols[4]; |
865 | int n = 0; |
866 | |
867 | protocols[n++] = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "WM_DELETE_WINDOW" ); |
868 | protocols[n++] = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "WM_TAKE_FOCUS" ); |
869 | protocols[n++] = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_PING" ); |
870 | |
871 | #ifdef HAVE_XSYNC |
872 | if (GDK_X11_DISPLAY (display)->use_sync) |
873 | protocols[n++] = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_SYNC_REQUEST" ); |
874 | #endif |
875 | |
876 | XSetWMProtocols (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XID (surface), protocols, n); |
877 | } |
878 | |
879 | static const char * |
880 | get_default_title (void) |
881 | { |
882 | const char *title; |
883 | |
884 | title = g_get_application_name (); |
885 | if (!title) |
886 | title = g_get_prgname (); |
887 | if (!title) |
888 | title = "" ; |
889 | |
890 | return title; |
891 | } |
892 | |
893 | static void |
894 | check_leader_window_title (GdkDisplay *display) |
895 | { |
896 | GdkX11Display *display_x11 = GDK_X11_DISPLAY (display); |
897 | |
898 | if (display_x11->leader_window && !display_x11->leader_window_title_set) |
899 | { |
900 | set_wm_name (display, |
901 | xwindow: display_x11->leader_window, |
902 | name: get_default_title ()); |
903 | |
904 | display_x11->leader_window_title_set = TRUE; |
905 | } |
906 | } |
907 | |
908 | static Window |
909 | create_focus_window (GdkDisplay *display, |
910 | XID parent) |
911 | { |
912 | GdkX11Display *display_x11; |
913 | GdkEventMask event_mask; |
914 | Display *xdisplay; |
915 | Window focus_window; |
916 | XSetWindowAttributes attrs; |
917 | |
918 | xdisplay = GDK_DISPLAY_XDISPLAY (display); |
919 | display_x11 = GDK_X11_DISPLAY (display); |
920 | |
921 | focus_window = XCreateWindow (xdisplay, parent, |
922 | -1, -1, 1, 1, 0, |
923 | 0, /* depth */ |
924 | InputOnly, |
925 | CopyFromParent, |
926 | 0, &attrs); |
927 | |
928 | event_mask = (GDK_KEY_PRESS_MASK | |
929 | GDK_KEY_RELEASE_MASK | |
930 | GDK_FOCUS_CHANGE_MASK); |
931 | |
932 | gdk_x11_event_source_select_events (source: (GdkEventSource *) display_x11->event_source, |
933 | window: focus_window, |
934 | event_mask, extra_x_mask: 0); |
935 | |
936 | XMapWindow (xdisplay, focus_window); |
937 | |
938 | return focus_window; |
939 | } |
940 | |
941 | static void |
942 | ensure_sync_counter (GdkSurface *surface) |
943 | { |
944 | #ifdef HAVE_XSYNC |
945 | if (!GDK_SURFACE_DESTROYED (surface)) |
946 | { |
947 | GdkDisplay *display = GDK_SURFACE_DISPLAY (surface); |
948 | GdkToplevelX11 *toplevel = _gdk_x11_surface_get_toplevel (surface); |
949 | |
950 | if (toplevel && |
951 | toplevel->update_counter == None && |
952 | GDK_X11_DISPLAY (display)->use_sync) |
953 | { |
954 | Display *xdisplay = GDK_DISPLAY_XDISPLAY (display); |
955 | XSyncValue value; |
956 | Atom atom; |
957 | XID counters[2]; |
958 | |
959 | XSyncIntToValue (&value, 0); |
960 | |
961 | toplevel->update_counter = XSyncCreateCounter (xdisplay, value); |
962 | toplevel->extended_update_counter = XSyncCreateCounter (xdisplay, value); |
963 | |
964 | atom = gdk_x11_get_xatom_by_name_for_display (display, |
965 | atom_name: "_NET_WM_SYNC_REQUEST_COUNTER" ); |
966 | |
967 | counters[0] = toplevel->update_counter; |
968 | counters[1] = toplevel->extended_update_counter; |
969 | XChangeProperty (xdisplay, GDK_SURFACE_XID (surface), |
970 | atom, XA_CARDINAL, |
971 | 32, PropModeReplace, |
972 | (guchar *)counters, 2); |
973 | |
974 | toplevel->current_counter_value = 0; |
975 | } |
976 | } |
977 | #endif |
978 | } |
979 | |
980 | static void |
981 | setup_toplevel_window (GdkSurface *surface, |
982 | GdkX11Screen *x11_screen) |
983 | { |
984 | GdkToplevelX11 *toplevel = _gdk_x11_surface_get_toplevel (surface); |
985 | GdkX11Surface *impl = GDK_X11_SURFACE (surface); |
986 | GdkDisplay *display = gdk_surface_get_display (surface); |
987 | Display *xdisplay = GDK_SURFACE_XDISPLAY (surface); |
988 | XID xid = GDK_SURFACE_XID (surface); |
989 | XSizeHints size_hints; |
990 | Window leader_window; |
991 | |
992 | set_wm_protocols (surface); |
993 | |
994 | /* The focus surface is off the visible area, and serves to receive key |
995 | * press events so they don't get sent to child surfaces. |
996 | */ |
997 | toplevel->focus_window = create_focus_window (display, parent: xid); |
998 | _gdk_x11_display_add_window (display: x11_screen->display, |
999 | xid: &toplevel->focus_window, |
1000 | window: surface); |
1001 | |
1002 | check_leader_window_title (display: x11_screen->display); |
1003 | |
1004 | /* FIXME: Is there any point in doing this? Do any WM's pay |
1005 | * attention to PSize, and even if they do, is this the |
1006 | * correct value??? |
1007 | */ |
1008 | size_hints.flags = PSize; |
1009 | size_hints.width = surface->width * impl->surface_scale; |
1010 | size_hints.height = surface->height * impl->surface_scale; |
1011 | |
1012 | XSetWMNormalHints (xdisplay, xid, &size_hints); |
1013 | |
1014 | /* This will set WM_CLIENT_MACHINE and WM_LOCALE_NAME */ |
1015 | XSetWMProperties (xdisplay, xid, NULL, NULL, NULL, 0, NULL, NULL, NULL); |
1016 | |
1017 | if (!gdk_running_in_sandbox ()) |
1018 | { |
1019 | /* if sandboxed, we're likely in a pid namespace and would only confuse the wm with this */ |
1020 | long pid = getpid (); |
1021 | XChangeProperty (xdisplay, xid, |
1022 | gdk_x11_get_xatom_by_name_for_display (display: x11_screen->display, atom_name: "_NET_WM_PID" ), |
1023 | XA_CARDINAL, 32, |
1024 | PropModeReplace, |
1025 | (guchar *)&pid, 1); |
1026 | } |
1027 | |
1028 | leader_window = GDK_X11_DISPLAY (x11_screen->display)->leader_window; |
1029 | if (!leader_window) |
1030 | leader_window = xid; |
1031 | XChangeProperty (xdisplay, xid, |
1032 | gdk_x11_get_xatom_by_name_for_display (display: x11_screen->display, atom_name: "WM_CLIENT_LEADER" ), |
1033 | XA_WINDOW, 32, PropModeReplace, |
1034 | (guchar *) &leader_window, 1); |
1035 | |
1036 | if (toplevel->focus_window != None) |
1037 | XChangeProperty (xdisplay, xid, |
1038 | gdk_x11_get_xatom_by_name_for_display (display: x11_screen->display, atom_name: "_NET_WM_USER_TIME_WINDOW" ), |
1039 | XA_WINDOW, 32, PropModeReplace, |
1040 | (guchar *) &toplevel->focus_window, 1); |
1041 | |
1042 | if (GDK_X11_DISPLAY (x11_screen->display)->user_time != 0) |
1043 | gdk_x11_surface_set_user_time (surface, GDK_X11_DISPLAY (x11_screen->display)->user_time); |
1044 | |
1045 | ensure_sync_counter (surface); |
1046 | |
1047 | /* Start off in a frozen state - we'll finish this when we first paint */ |
1048 | gdk_x11_surface_begin_frame (surface, TRUE); |
1049 | } |
1050 | |
1051 | static void |
1052 | on_frame_clock_before_paint (GdkFrameClock *clock, |
1053 | GdkSurface *surface) |
1054 | { |
1055 | if (surface->update_freeze_count > 0) |
1056 | return; |
1057 | |
1058 | gdk_x11_surface_predict_presentation_time (surface); |
1059 | gdk_x11_surface_begin_frame (surface, FALSE); |
1060 | } |
1061 | |
1062 | static void |
1063 | on_frame_clock_after_update (GdkFrameClock *clock, |
1064 | GdkSurface *surface) |
1065 | { |
1066 | GdkX11Surface *impl = GDK_X11_SURFACE (surface); |
1067 | |
1068 | if (impl->compute_size_source_id) |
1069 | { |
1070 | g_clear_handle_id (&impl->compute_size_source_id, g_source_remove); |
1071 | compute_size_idle (user_data: surface); |
1072 | } |
1073 | } |
1074 | |
1075 | static void |
1076 | on_frame_clock_after_paint (GdkFrameClock *clock, |
1077 | GdkSurface *surface) |
1078 | { |
1079 | if (surface->update_freeze_count > 0) |
1080 | return; |
1081 | |
1082 | gdk_x11_surface_end_frame (surface); |
1083 | } |
1084 | |
1085 | static void |
1086 | connect_frame_clock (GdkSurface *surface) |
1087 | { |
1088 | GdkX11Surface *impl; |
1089 | |
1090 | impl = GDK_X11_SURFACE (surface); |
1091 | if (!impl->frame_clock_connected) |
1092 | { |
1093 | GdkFrameClock *frame_clock = gdk_surface_get_frame_clock (surface); |
1094 | |
1095 | g_signal_connect (frame_clock, "before-paint" , |
1096 | G_CALLBACK (on_frame_clock_before_paint), surface); |
1097 | g_signal_connect_after (frame_clock, "update" , |
1098 | G_CALLBACK (on_frame_clock_after_update), surface); |
1099 | g_signal_connect (frame_clock, "after-paint" , |
1100 | G_CALLBACK (on_frame_clock_after_paint), surface); |
1101 | |
1102 | impl->frame_clock_connected = TRUE; |
1103 | } |
1104 | } |
1105 | |
1106 | static void |
1107 | disconnect_frame_clock (GdkSurface *surface) |
1108 | { |
1109 | GdkX11Surface *impl; |
1110 | |
1111 | impl = GDK_X11_SURFACE (surface); |
1112 | if (impl->frame_clock_connected) |
1113 | { |
1114 | GdkFrameClock *frame_clock = gdk_surface_get_frame_clock (surface); |
1115 | |
1116 | g_signal_handlers_disconnect_by_func (frame_clock, |
1117 | on_frame_clock_before_paint, surface); |
1118 | g_signal_handlers_disconnect_by_func (frame_clock, |
1119 | on_frame_clock_after_update, surface); |
1120 | g_signal_handlers_disconnect_by_func (frame_clock, |
1121 | on_frame_clock_after_paint, surface); |
1122 | |
1123 | impl->frame_clock_connected = FALSE; |
1124 | } |
1125 | } |
1126 | |
1127 | typedef enum |
1128 | { |
1129 | GDK_SURFACE_TYPE_HINT_NORMAL, |
1130 | GDK_SURFACE_TYPE_HINT_DIALOG, |
1131 | , /* Torn off menu */ |
1132 | GDK_SURFACE_TYPE_HINT_TOOLBAR, |
1133 | GDK_SURFACE_TYPE_HINT_SPLASHSCREEN, |
1134 | GDK_SURFACE_TYPE_HINT_UTILITY, |
1135 | GDK_SURFACE_TYPE_HINT_DOCK, |
1136 | GDK_SURFACE_TYPE_HINT_DESKTOP, |
1137 | , /* A drop down menu (from a menubar) */ |
1138 | , /* A popup menu (from right-click) */ |
1139 | GDK_SURFACE_TYPE_HINT_TOOLTIP, |
1140 | GDK_SURFACE_TYPE_HINT_NOTIFICATION, |
1141 | GDK_SURFACE_TYPE_HINT_COMBO, |
1142 | GDK_SURFACE_TYPE_HINT_DND |
1143 | } GdkSurfaceTypeHint; |
1144 | |
1145 | static void gdk_x11_surface_set_title (GdkSurface *surface, |
1146 | const char *title); |
1147 | static void gdk_x11_surface_set_type_hint (GdkSurface *surface, |
1148 | GdkSurfaceTypeHint hint); |
1149 | |
1150 | GdkSurface * |
1151 | _gdk_x11_display_create_surface (GdkDisplay *display, |
1152 | GdkSurfaceType surface_type, |
1153 | GdkSurface *parent, |
1154 | int x, |
1155 | int y, |
1156 | int width, |
1157 | int height) |
1158 | { |
1159 | GdkSurface *surface; |
1160 | GdkFrameClock *frame_clock; |
1161 | GdkX11Surface *impl; |
1162 | GdkX11Screen *x11_screen; |
1163 | GdkX11Display *display_x11; |
1164 | |
1165 | Window xparent; |
1166 | Display *xdisplay; |
1167 | |
1168 | XSetWindowAttributes xattributes; |
1169 | long xattributes_mask; |
1170 | XClassHint *class_hint; |
1171 | |
1172 | int abs_x; |
1173 | int abs_y; |
1174 | |
1175 | display_x11 = GDK_X11_DISPLAY (display); |
1176 | x11_screen = GDK_X11_SCREEN (display_x11->screen); |
1177 | xparent = GDK_SCREEN_XROOTWIN (x11_screen); |
1178 | |
1179 | if (parent) |
1180 | frame_clock = g_object_ref (gdk_surface_get_frame_clock (parent)); |
1181 | else |
1182 | frame_clock = _gdk_frame_clock_idle_new (); |
1183 | |
1184 | switch (surface_type) |
1185 | { |
1186 | case GDK_SURFACE_TOPLEVEL: |
1187 | surface = g_object_new (GDK_TYPE_X11_TOPLEVEL, |
1188 | first_property_name: "display" , display, |
1189 | "frame-clock" , frame_clock, |
1190 | NULL); |
1191 | break; |
1192 | case GDK_SURFACE_POPUP: |
1193 | surface = g_object_new (GDK_TYPE_X11_POPUP, |
1194 | first_property_name: "parent" , parent, |
1195 | "display" , display, |
1196 | "frame-clock" , frame_clock, |
1197 | NULL); |
1198 | break; |
1199 | case GDK_SURFACE_TEMP: |
1200 | surface = g_object_new (GDK_TYPE_X11_DRAG_SURFACE, |
1201 | first_property_name: "display" , display, |
1202 | "frame-clock" , frame_clock, |
1203 | NULL); |
1204 | break; |
1205 | default: |
1206 | g_assert_not_reached (); |
1207 | break; |
1208 | } |
1209 | |
1210 | g_object_unref (object: frame_clock); |
1211 | |
1212 | surface->x = x; |
1213 | surface->y = y; |
1214 | surface->width = width; |
1215 | surface->height = height; |
1216 | |
1217 | impl = GDK_X11_SURFACE (surface); |
1218 | impl->surface_scale = x11_screen->surface_scale; |
1219 | |
1220 | xdisplay = x11_screen->xdisplay; |
1221 | |
1222 | xattributes_mask = 0; |
1223 | |
1224 | impl->override_redirect = FALSE; |
1225 | |
1226 | xattributes.background_pixmap = None; |
1227 | xattributes_mask |= CWBackPixmap; |
1228 | |
1229 | xattributes.border_pixel = BlackPixel (xdisplay, x11_screen->screen_num); |
1230 | xattributes_mask |= CWBorderPixel; |
1231 | |
1232 | xattributes.bit_gravity = NorthWestGravity; |
1233 | xattributes_mask |= CWBitGravity; |
1234 | |
1235 | xattributes.colormap = gdk_x11_display_get_window_colormap (display: display_x11); |
1236 | xattributes_mask |= CWColormap; |
1237 | |
1238 | if (surface_type == GDK_SURFACE_TEMP || |
1239 | surface_type == GDK_SURFACE_POPUP) |
1240 | { |
1241 | xattributes.save_under = True; |
1242 | xattributes.override_redirect = True; |
1243 | xattributes.cursor = None; |
1244 | xattributes_mask |= CWSaveUnder | CWOverrideRedirect; |
1245 | |
1246 | impl->override_redirect = TRUE; |
1247 | } |
1248 | |
1249 | if (surface->width * impl->surface_scale > 32767 || |
1250 | surface->height * impl->surface_scale > 32767) |
1251 | { |
1252 | g_warning ("Native Windows wider or taller than 32767 pixels are not supported" ); |
1253 | |
1254 | if (surface->width * impl->surface_scale > 32767) |
1255 | surface->width = 32767 / impl->surface_scale; |
1256 | if (surface->height * impl->surface_scale > 32767) |
1257 | surface->height = 32767 / impl->surface_scale; |
1258 | } |
1259 | |
1260 | impl->unscaled_width = surface->width * impl->surface_scale; |
1261 | impl->unscaled_height = surface->height * impl->surface_scale; |
1262 | |
1263 | abs_x = 0; |
1264 | abs_y = 0; |
1265 | |
1266 | impl->xid = XCreateWindow (xdisplay, xparent, |
1267 | (surface->x + abs_x) * impl->surface_scale, |
1268 | (surface->y + abs_y) * impl->surface_scale, |
1269 | MAX (1, surface->width * impl->surface_scale), |
1270 | MAX (1, surface->height * impl->surface_scale), |
1271 | 0, |
1272 | gdk_x11_display_get_window_depth (display: display_x11), |
1273 | InputOutput, |
1274 | gdk_x11_display_get_window_visual (display: display_x11), |
1275 | xattributes_mask, &xattributes); |
1276 | |
1277 | g_object_ref (surface); |
1278 | _gdk_x11_display_add_window (display: x11_screen->display, xid: &impl->xid, window: surface); |
1279 | |
1280 | gdk_surface_set_egl_native_window (self: surface, native_window: (void *) impl->xid); |
1281 | |
1282 | gdk_x11_surface_set_title (surface, title: get_default_title ()); |
1283 | if (surface_type == GDK_SURFACE_TOPLEVEL) |
1284 | gdk_x11_surface_set_type_hint (surface, hint: GDK_SURFACE_TYPE_HINT_NORMAL); |
1285 | else if (surface_type == GDK_SURFACE_POPUP) |
1286 | gdk_x11_surface_set_type_hint (surface, hint: GDK_SURFACE_TYPE_HINT_MENU); |
1287 | |
1288 | class_hint = XAllocClassHint (); |
1289 | class_hint->res_name = (char *) g_get_prgname (); |
1290 | if (display_x11->program_class) |
1291 | class_hint->res_class = (char *) display_x11->program_class; |
1292 | else |
1293 | class_hint->res_class = class_hint->res_name; |
1294 | XSetClassHint (xdisplay, impl->xid, class_hint); |
1295 | XFree (class_hint); |
1296 | |
1297 | setup_toplevel_window (surface, x11_screen); |
1298 | |
1299 | gdk_x11_event_source_select_events (source: (GdkEventSource *) display_x11->event_source, |
1300 | GDK_SURFACE_XID (surface), event_mask: GDK_ALL_EVENTS_MASK, |
1301 | StructureNotifyMask | PropertyChangeMask); |
1302 | |
1303 | _gdk_x11_surface_register_dnd (window: surface); |
1304 | |
1305 | connect_frame_clock (surface); |
1306 | |
1307 | gdk_surface_freeze_updates (surface); |
1308 | |
1309 | return surface; |
1310 | } |
1311 | |
1312 | static void |
1313 | gdk_toplevel_x11_free_contents (GdkDisplay *display, |
1314 | GdkToplevelX11 *toplevel) |
1315 | { |
1316 | if (toplevel->icon_pixmap) |
1317 | { |
1318 | cairo_surface_destroy (surface: toplevel->icon_pixmap); |
1319 | toplevel->icon_pixmap = NULL; |
1320 | } |
1321 | if (toplevel->icon_mask) |
1322 | { |
1323 | cairo_surface_destroy (surface: toplevel->icon_mask); |
1324 | toplevel->icon_mask = NULL; |
1325 | } |
1326 | if (toplevel->group_leader) |
1327 | { |
1328 | g_object_unref (object: toplevel->group_leader); |
1329 | toplevel->group_leader = NULL; |
1330 | } |
1331 | #ifdef HAVE_XSYNC |
1332 | if (toplevel->update_counter != None) |
1333 | { |
1334 | XSyncDestroyCounter (GDK_DISPLAY_XDISPLAY (display), |
1335 | toplevel->update_counter); |
1336 | XSyncDestroyCounter (GDK_DISPLAY_XDISPLAY (display), |
1337 | toplevel->extended_update_counter); |
1338 | toplevel->update_counter = None; |
1339 | toplevel->extended_update_counter = None; |
1340 | |
1341 | toplevel->current_counter_value = 0; |
1342 | } |
1343 | #endif |
1344 | } |
1345 | |
1346 | static void |
1347 | gdk_x11_surface_destroy (GdkSurface *surface, |
1348 | gboolean foreign_destroy) |
1349 | { |
1350 | GdkX11Surface *impl = GDK_X11_SURFACE (surface); |
1351 | GdkToplevelX11 *toplevel; |
1352 | |
1353 | g_return_if_fail (GDK_IS_SURFACE (surface)); |
1354 | |
1355 | toplevel = _gdk_x11_surface_get_toplevel (surface); |
1356 | if (toplevel) |
1357 | gdk_toplevel_x11_free_contents (GDK_SURFACE_DISPLAY (surface), toplevel); |
1358 | |
1359 | unhook_surface_changed (surface); |
1360 | disconnect_frame_clock (surface); |
1361 | |
1362 | if (impl->cairo_surface) |
1363 | { |
1364 | cairo_surface_finish (surface: impl->cairo_surface); |
1365 | cairo_surface_destroy (surface: impl->cairo_surface); |
1366 | impl->cairo_surface = NULL; |
1367 | } |
1368 | |
1369 | if (!foreign_destroy) |
1370 | { |
1371 | gdk_surface_set_egl_native_window (self: surface, NULL); |
1372 | gdk_x11_surface_destroy_glx_drawable (self: impl); |
1373 | |
1374 | XDestroyWindow (GDK_SURFACE_XDISPLAY (surface), GDK_SURFACE_XID (surface)); |
1375 | } |
1376 | } |
1377 | |
1378 | /* This function is called when the XWindow is really gone. |
1379 | */ |
1380 | static void |
1381 | gdk_x11_surface_destroy_notify (GdkSurface *surface) |
1382 | { |
1383 | GdkX11Surface *surface_impl; |
1384 | |
1385 | surface_impl = GDK_X11_SURFACE (surface); |
1386 | |
1387 | if (!GDK_SURFACE_DESTROYED (surface)) |
1388 | { |
1389 | g_warning ("GdkSurface %#lx unexpectedly destroyed" , GDK_SURFACE_XID (surface)); |
1390 | |
1391 | _gdk_surface_destroy (surface, TRUE); |
1392 | } |
1393 | |
1394 | _gdk_x11_display_remove_window (GDK_SURFACE_DISPLAY (surface), GDK_SURFACE_XID (surface)); |
1395 | if (surface_impl->toplevel && surface_impl->toplevel->focus_window) |
1396 | _gdk_x11_display_remove_window (GDK_SURFACE_DISPLAY (surface), xid: surface_impl->toplevel->focus_window); |
1397 | |
1398 | _gdk_x11_surface_grab_check_destroy (window: surface); |
1399 | |
1400 | g_object_unref (object: surface); |
1401 | } |
1402 | |
1403 | static void |
1404 | update_wm_hints (GdkSurface *surface, |
1405 | gboolean force) |
1406 | { |
1407 | GdkToplevelX11 *toplevel = _gdk_x11_surface_get_toplevel (surface); |
1408 | GdkDisplay *display = GDK_SURFACE_DISPLAY (surface); |
1409 | XWMHints wm_hints; |
1410 | |
1411 | if (!force && |
1412 | !toplevel->is_leader && |
1413 | !GDK_SURFACE_IS_MAPPED (surface)) |
1414 | return; |
1415 | |
1416 | wm_hints.flags = StateHint | InputHint; |
1417 | wm_hints.input = True; |
1418 | wm_hints.initial_state = NormalState; |
1419 | |
1420 | if (surface->state & GDK_TOPLEVEL_STATE_MINIMIZED) |
1421 | { |
1422 | wm_hints.flags |= StateHint; |
1423 | wm_hints.initial_state = IconicState; |
1424 | } |
1425 | |
1426 | if (toplevel->icon_pixmap) |
1427 | { |
1428 | wm_hints.flags |= IconPixmapHint; |
1429 | wm_hints.icon_pixmap = cairo_xlib_surface_get_drawable (surface: toplevel->icon_pixmap); |
1430 | } |
1431 | |
1432 | if (toplevel->icon_mask) |
1433 | { |
1434 | wm_hints.flags |= IconMaskHint; |
1435 | wm_hints.icon_mask = cairo_xlib_surface_get_drawable (surface: toplevel->icon_mask); |
1436 | } |
1437 | |
1438 | wm_hints.flags |= WindowGroupHint; |
1439 | if (toplevel->group_leader && !GDK_SURFACE_DESTROYED (toplevel->group_leader)) |
1440 | { |
1441 | wm_hints.flags |= WindowGroupHint; |
1442 | wm_hints.window_group = GDK_SURFACE_XID (toplevel->group_leader); |
1443 | } |
1444 | else |
1445 | wm_hints.window_group = GDK_X11_DISPLAY (display)->leader_window; |
1446 | |
1447 | if (toplevel->urgency_hint) |
1448 | wm_hints.flags |= XUrgencyHint; |
1449 | |
1450 | XSetWMHints (GDK_SURFACE_XDISPLAY (surface), |
1451 | GDK_SURFACE_XID (surface), |
1452 | &wm_hints); |
1453 | } |
1454 | |
1455 | static void |
1456 | set_initial_hints (GdkSurface *surface) |
1457 | { |
1458 | GdkDisplay *display = GDK_SURFACE_DISPLAY (surface); |
1459 | Display *xdisplay = GDK_DISPLAY_XDISPLAY (display); |
1460 | Window xwindow = GDK_SURFACE_XID (surface); |
1461 | GdkToplevelX11 *toplevel; |
1462 | Atom atoms[9]; |
1463 | int i; |
1464 | |
1465 | toplevel = _gdk_x11_surface_get_toplevel (surface); |
1466 | |
1467 | if (!toplevel) |
1468 | return; |
1469 | |
1470 | update_wm_hints (surface, TRUE); |
1471 | |
1472 | /* We set the spec hints regardless of whether the spec is supported, |
1473 | * since it can't hurt and it's kind of expensive to check whether |
1474 | * it's supported. |
1475 | */ |
1476 | |
1477 | i = 0; |
1478 | |
1479 | if (surface->state & GDK_TOPLEVEL_STATE_MAXIMIZED) |
1480 | { |
1481 | atoms[i] = gdk_x11_get_xatom_by_name_for_display (display, |
1482 | atom_name: "_NET_WM_STATE_MAXIMIZED_VERT" ); |
1483 | ++i; |
1484 | atoms[i] = gdk_x11_get_xatom_by_name_for_display (display, |
1485 | atom_name: "_NET_WM_STATE_MAXIMIZED_HORZ" ); |
1486 | ++i; |
1487 | toplevel->have_maxhorz = toplevel->have_maxvert = TRUE; |
1488 | } |
1489 | |
1490 | if (surface->state & GDK_TOPLEVEL_STATE_ABOVE) |
1491 | { |
1492 | atoms[i] = gdk_x11_get_xatom_by_name_for_display (display, |
1493 | atom_name: "_NET_WM_STATE_ABOVE" ); |
1494 | ++i; |
1495 | } |
1496 | |
1497 | if (surface->state & GDK_TOPLEVEL_STATE_BELOW) |
1498 | { |
1499 | atoms[i] = gdk_x11_get_xatom_by_name_for_display (display, |
1500 | atom_name: "_NET_WM_STATE_BELOW" ); |
1501 | ++i; |
1502 | } |
1503 | |
1504 | if (surface->state & GDK_TOPLEVEL_STATE_FULLSCREEN) |
1505 | { |
1506 | atoms[i] = gdk_x11_get_xatom_by_name_for_display (display, |
1507 | atom_name: "_NET_WM_STATE_FULLSCREEN" ); |
1508 | ++i; |
1509 | toplevel->have_fullscreen = TRUE; |
1510 | } |
1511 | |
1512 | if (surface->modal_hint) |
1513 | { |
1514 | atoms[i] = gdk_x11_get_xatom_by_name_for_display (display, |
1515 | atom_name: "_NET_WM_STATE_MODAL" ); |
1516 | ++i; |
1517 | } |
1518 | |
1519 | if (toplevel->skip_taskbar_hint) |
1520 | { |
1521 | atoms[i] = gdk_x11_get_xatom_by_name_for_display (display, |
1522 | atom_name: "_NET_WM_STATE_SKIP_TASKBAR" ); |
1523 | ++i; |
1524 | } |
1525 | |
1526 | if (toplevel->skip_pager_hint) |
1527 | { |
1528 | atoms[i] = gdk_x11_get_xatom_by_name_for_display (display, |
1529 | atom_name: "_NET_WM_STATE_SKIP_PAGER" ); |
1530 | ++i; |
1531 | } |
1532 | |
1533 | if (surface->state & GDK_TOPLEVEL_STATE_MINIMIZED) |
1534 | { |
1535 | atoms[i] = gdk_x11_get_xatom_by_name_for_display (display, |
1536 | atom_name: "_NET_WM_STATE_HIDDEN" ); |
1537 | ++i; |
1538 | toplevel->have_hidden = TRUE; |
1539 | } |
1540 | |
1541 | if (i > 0) |
1542 | { |
1543 | XChangeProperty (xdisplay, |
1544 | xwindow, |
1545 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_STATE" ), |
1546 | XA_ATOM, 32, PropModeReplace, |
1547 | (guchar*) atoms, i); |
1548 | } |
1549 | else |
1550 | { |
1551 | XDeleteProperty (xdisplay, |
1552 | xwindow, |
1553 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_STATE" )); |
1554 | } |
1555 | |
1556 | if (surface->state & GDK_TOPLEVEL_STATE_STICKY) |
1557 | { |
1558 | atoms[0] = 0xFFFFFFFF; |
1559 | XChangeProperty (xdisplay, |
1560 | xwindow, |
1561 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_DESKTOP" ), |
1562 | XA_CARDINAL, 32, PropModeReplace, |
1563 | (guchar*) atoms, 1); |
1564 | toplevel->on_all_desktops = TRUE; |
1565 | } |
1566 | else |
1567 | { |
1568 | XDeleteProperty (xdisplay, |
1569 | xwindow, |
1570 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_DESKTOP" )); |
1571 | } |
1572 | |
1573 | toplevel->map_serial = NextRequest (xdisplay); |
1574 | } |
1575 | |
1576 | void |
1577 | gdk_x11_surface_show (GdkSurface *surface, gboolean already_mapped) |
1578 | { |
1579 | GdkDisplay *display; |
1580 | GdkX11Display *display_x11; |
1581 | GdkToplevelX11 *toplevel; |
1582 | Display *xdisplay = GDK_SURFACE_XDISPLAY (surface); |
1583 | Window xwindow = GDK_SURFACE_XID (surface); |
1584 | |
1585 | if (!already_mapped) |
1586 | set_initial_hints (surface); |
1587 | |
1588 | display = gdk_surface_get_display (surface); |
1589 | display_x11 = GDK_X11_DISPLAY (display); |
1590 | toplevel = _gdk_x11_surface_get_toplevel (surface); |
1591 | |
1592 | if (toplevel->user_time != 0 && |
1593 | display_x11->user_time != 0 && |
1594 | XSERVER_TIME_IS_LATER (display_x11->user_time, toplevel->user_time)) |
1595 | gdk_x11_surface_set_user_time (surface, timestamp: display_x11->user_time); |
1596 | |
1597 | if (GDK_PROFILER_IS_RUNNING) |
1598 | { |
1599 | GdkX11Surface *impl = GDK_X11_SURFACE (surface); |
1600 | if (impl->map_time == 0) |
1601 | impl->map_time = g_get_monotonic_time (); |
1602 | } |
1603 | |
1604 | XMapWindow (xdisplay, xwindow); |
1605 | |
1606 | /* Fullscreen on current monitor is the default, no need to apply this mode |
1607 | * when mapping a window. This also ensures that the default behavior remains |
1608 | * consistent with pre-fullscreen mode implementation. |
1609 | */ |
1610 | if (surface->fullscreen_mode != GDK_FULLSCREEN_ON_CURRENT_MONITOR) |
1611 | gdk_x11_surface_apply_fullscreen_mode (surface); |
1612 | } |
1613 | |
1614 | static void |
1615 | gdk_x11_surface_withdraw (GdkSurface *surface) |
1616 | { |
1617 | if (!surface->destroyed) |
1618 | { |
1619 | if (GDK_SURFACE_IS_MAPPED (surface)) |
1620 | gdk_surface_set_is_mapped (surface, FALSE); |
1621 | |
1622 | g_assert (!GDK_SURFACE_IS_MAPPED (surface)); |
1623 | XWithdrawWindow (GDK_SURFACE_XDISPLAY (surface), |
1624 | GDK_SURFACE_XID (surface), 0); |
1625 | } |
1626 | } |
1627 | |
1628 | static void |
1629 | gdk_x11_surface_hide (GdkSurface *surface) |
1630 | { |
1631 | GdkX11Surface *impl = GDK_X11_SURFACE (surface); |
1632 | |
1633 | /* We'll get the unmap notify eventually, and handle it then, |
1634 | * but checking here makes things more consistent if we are |
1635 | * just doing stuff ourself. |
1636 | */ |
1637 | _gdk_x11_surface_grab_check_unmap (window: surface, |
1638 | NextRequest (GDK_SURFACE_XDISPLAY (surface))); |
1639 | |
1640 | g_clear_handle_id (&impl->compute_size_source_id, g_source_remove); |
1641 | g_clear_pointer (&impl->toplevel_layout, gdk_toplevel_layout_unref); |
1642 | |
1643 | gdk_x11_surface_withdraw (surface); |
1644 | |
1645 | impl->glx_frame_counter = 0; |
1646 | } |
1647 | |
1648 | static inline void |
1649 | x11_surface_move (GdkSurface *surface, |
1650 | int x, |
1651 | int y) |
1652 | { |
1653 | GdkX11Surface *impl = GDK_X11_SURFACE (surface); |
1654 | |
1655 | XMoveWindow (GDK_SURFACE_XDISPLAY (surface), |
1656 | GDK_SURFACE_XID (surface), |
1657 | x * impl->surface_scale, y * impl->surface_scale); |
1658 | |
1659 | if (impl->override_redirect) |
1660 | { |
1661 | impl->abs_x = x; |
1662 | impl->abs_y = y; |
1663 | |
1664 | if (surface->parent) |
1665 | { |
1666 | surface->x = impl->abs_x - GDK_X11_SURFACE (surface->parent)->abs_x; |
1667 | surface->y = impl->abs_y - GDK_X11_SURFACE (surface->parent)->abs_y; |
1668 | } |
1669 | else |
1670 | { |
1671 | surface->x = x; |
1672 | surface->y = y; |
1673 | } |
1674 | |
1675 | impl->next_layout.surface_geometry_dirty = TRUE; |
1676 | gdk_surface_request_layout (surface); |
1677 | } |
1678 | } |
1679 | |
1680 | static inline void |
1681 | x11_surface_resize (GdkSurface *surface, |
1682 | int width, |
1683 | int height) |
1684 | { |
1685 | GdkX11Surface *impl = GDK_X11_SURFACE (surface); |
1686 | |
1687 | if (width < 1) |
1688 | width = 1; |
1689 | |
1690 | if (height < 1) |
1691 | height = 1; |
1692 | |
1693 | gdk_x11_surface_pre_damage (surface); |
1694 | |
1695 | XResizeWindow (GDK_SURFACE_XDISPLAY (surface), |
1696 | GDK_SURFACE_XID (surface), |
1697 | width * impl->surface_scale, height * impl->surface_scale); |
1698 | |
1699 | if (impl->override_redirect) |
1700 | { |
1701 | impl->unscaled_width = width * impl->surface_scale; |
1702 | impl->unscaled_height = height * impl->surface_scale; |
1703 | impl->next_layout.configured_width = width; |
1704 | impl->next_layout.configured_height = height; |
1705 | impl->next_layout.surface_geometry_dirty = TRUE; |
1706 | gdk_surface_request_layout (surface); |
1707 | } |
1708 | else |
1709 | { |
1710 | if (width * impl->surface_scale != impl->unscaled_width || |
1711 | height * impl->surface_scale != impl->unscaled_height) |
1712 | { |
1713 | surface->resize_count++; |
1714 | if (surface->resize_count == 1) |
1715 | gdk_surface_freeze_updates (surface); |
1716 | } |
1717 | } |
1718 | } |
1719 | |
1720 | static inline void |
1721 | x11_surface_move_resize (GdkSurface *surface, |
1722 | int x, |
1723 | int y, |
1724 | int width, |
1725 | int height) |
1726 | { |
1727 | GdkX11Surface *impl = GDK_X11_SURFACE (surface); |
1728 | |
1729 | if (width < 1) |
1730 | width = 1; |
1731 | |
1732 | if (height < 1) |
1733 | height = 1; |
1734 | |
1735 | gdk_x11_surface_pre_damage (surface); |
1736 | |
1737 | XMoveResizeWindow (GDK_SURFACE_XDISPLAY (surface), |
1738 | GDK_SURFACE_XID (surface), |
1739 | x * impl->surface_scale, y * impl->surface_scale, |
1740 | width * impl->surface_scale, height * impl->surface_scale); |
1741 | |
1742 | if (impl->override_redirect) |
1743 | { |
1744 | impl->abs_x = x; |
1745 | impl->abs_y = y; |
1746 | |
1747 | impl->unscaled_width = width * impl->surface_scale; |
1748 | impl->unscaled_height = height * impl->surface_scale; |
1749 | impl->next_layout.configured_width = width; |
1750 | impl->next_layout.configured_height = height; |
1751 | impl->next_layout.surface_geometry_dirty = TRUE; |
1752 | gdk_surface_request_layout (surface); |
1753 | |
1754 | if (surface->parent) |
1755 | { |
1756 | surface->x = impl->abs_x - GDK_X11_SURFACE (surface->parent)->abs_x; |
1757 | surface->y = impl->abs_y - GDK_X11_SURFACE (surface->parent)->abs_y; |
1758 | } |
1759 | else |
1760 | { |
1761 | surface->x = x; |
1762 | surface->y = y; |
1763 | } |
1764 | } |
1765 | else |
1766 | { |
1767 | if (width * impl->surface_scale != impl->unscaled_width || |
1768 | height * impl->surface_scale != impl->unscaled_height) |
1769 | { |
1770 | surface->resize_count++; |
1771 | if (surface->resize_count == 1) |
1772 | gdk_surface_freeze_updates (surface); |
1773 | } |
1774 | } |
1775 | } |
1776 | |
1777 | static void |
1778 | gdk_x11_surface_move_resize (GdkSurface *surface, |
1779 | gboolean with_move, |
1780 | int x, |
1781 | int y, |
1782 | int width, |
1783 | int height) |
1784 | { |
1785 | if (with_move && (width < 0 && height < 0)) |
1786 | x11_surface_move (surface, x, y); |
1787 | else |
1788 | { |
1789 | if (with_move) |
1790 | x11_surface_move_resize (surface, x, y, width, height); |
1791 | else |
1792 | x11_surface_resize (surface, width, height); |
1793 | } |
1794 | } |
1795 | |
1796 | static void |
1797 | gdk_x11_surface_toplevel_resize (GdkSurface *surface, |
1798 | int width, |
1799 | int height) |
1800 | { |
1801 | x11_surface_resize (surface, width, height); |
1802 | } |
1803 | |
1804 | void |
1805 | gdk_x11_surface_move (GdkSurface *surface, |
1806 | int x, |
1807 | int y) |
1808 | { |
1809 | gdk_x11_surface_move_resize (surface, TRUE, x, y, width: -1, height: -1); |
1810 | } |
1811 | |
1812 | static void |
1813 | (GdkSurface *surface, |
1814 | int width, |
1815 | int height, |
1816 | GdkPopupLayout *layout) |
1817 | { |
1818 | GdkX11Surface *impl = GDK_X11_SURFACE (surface); |
1819 | GdkMonitor *monitor; |
1820 | GdkRectangle bounds; |
1821 | GdkRectangle final_rect; |
1822 | int x, y; |
1823 | |
1824 | monitor = gdk_surface_get_layout_monitor (surface, layout, |
1825 | get_bounds: gdk_x11_monitor_get_workarea); |
1826 | if (monitor) |
1827 | gdk_x11_monitor_get_workarea (monitor, workarea: &bounds); |
1828 | else |
1829 | { |
1830 | monitor = gdk_surface_get_layout_monitor (surface, layout, |
1831 | get_bounds: gdk_monitor_get_geometry); |
1832 | gdk_monitor_get_geometry (monitor, geometry: &bounds); |
1833 | } |
1834 | |
1835 | gdk_popup_layout_get_shadow_width (layout, |
1836 | left: &impl->shadow_left, |
1837 | right: &impl->shadow_right, |
1838 | top: &impl->shadow_top, |
1839 | bottom: &impl->shadow_bottom); |
1840 | |
1841 | gdk_surface_layout_popup_helper (surface, |
1842 | width, |
1843 | height, |
1844 | shadow_left: impl->shadow_left, |
1845 | shadow_right: impl->shadow_right, |
1846 | shadow_top: impl->shadow_top, |
1847 | shadow_bottom: impl->shadow_bottom, |
1848 | monitor, |
1849 | bounds: &bounds, |
1850 | layout, |
1851 | out_final_rect: &final_rect); |
1852 | |
1853 | gdk_surface_get_origin (surface: surface->parent, x: &x, y: &y); |
1854 | x += final_rect.x; |
1855 | y += final_rect.y; |
1856 | |
1857 | if (final_rect.width != surface->width || |
1858 | final_rect.height != surface->height) |
1859 | { |
1860 | gdk_x11_surface_move_resize (surface, |
1861 | TRUE, |
1862 | x, |
1863 | y, |
1864 | width: final_rect.width, |
1865 | height: final_rect.height); |
1866 | } |
1867 | else |
1868 | { |
1869 | gdk_x11_surface_move (surface, x, y); |
1870 | } |
1871 | } |
1872 | |
1873 | static void |
1874 | (GdkSurface *surface) |
1875 | { |
1876 | gdk_x11_surface_raise (surface); |
1877 | gdk_surface_set_is_mapped (surface, TRUE); |
1878 | gdk_x11_surface_show (surface, FALSE); |
1879 | gdk_surface_invalidate_rect (surface, NULL); |
1880 | } |
1881 | |
1882 | static void |
1883 | (GdkSeat *seat, |
1884 | GdkSurface *surface, |
1885 | gpointer user_data) |
1886 | { |
1887 | show_popup (surface); |
1888 | } |
1889 | |
1890 | static gboolean |
1891 | (GdkSurface *surface, |
1892 | int width, |
1893 | int height, |
1894 | GdkPopupLayout *layout) |
1895 | { |
1896 | gdk_x11_surface_layout_popup (surface, width, height, layout); |
1897 | |
1898 | if (GDK_SURFACE_IS_MAPPED (surface)) |
1899 | return TRUE; |
1900 | |
1901 | if (surface->autohide) |
1902 | { |
1903 | gdk_seat_grab (seat: gdk_display_get_default_seat (display: surface->display), |
1904 | surface, |
1905 | capabilities: GDK_SEAT_CAPABILITY_ALL, |
1906 | TRUE, |
1907 | NULL, NULL, |
1908 | prepare_func: show_grabbing_popup, NULL); |
1909 | } |
1910 | else |
1911 | { |
1912 | show_popup (surface); |
1913 | } |
1914 | |
1915 | return GDK_SURFACE_IS_MAPPED (surface); |
1916 | } |
1917 | |
1918 | static void gdk_x11_surface_restack_toplevel (GdkSurface *surface, |
1919 | GdkSurface *sibling, |
1920 | gboolean above); |
1921 | |
1922 | void |
1923 | (GdkSurface *parent) |
1924 | { |
1925 | GList *l; |
1926 | |
1927 | for (l = parent->children; l; l = l->next) |
1928 | { |
1929 | GdkX11Surface * = l->data; |
1930 | GdkSurface * = GDK_SURFACE (popup_impl); |
1931 | int new_x, new_y; |
1932 | |
1933 | if (GDK_SURFACE_DESTROYED (popup)) |
1934 | continue; |
1935 | |
1936 | new_x = GDK_X11_SURFACE (parent)->abs_x + popup->x; |
1937 | new_y = GDK_X11_SURFACE (parent)->abs_y + popup->y; |
1938 | |
1939 | if (new_x != popup_impl->abs_x || new_y != popup_impl->abs_y) |
1940 | x11_surface_move (surface: popup, x: new_x, y: new_y); |
1941 | gdk_x11_surface_restack_toplevel (surface: popup, sibling: parent, TRUE); |
1942 | } |
1943 | } |
1944 | |
1945 | static void |
1946 | gdk_x11_surface_set_is_on_monitor (GdkSurface *surface, |
1947 | GdkMonitor *monitor, |
1948 | gboolean is_on_monitor) |
1949 | { |
1950 | GdkX11Surface *impl = GDK_X11_SURFACE (surface); |
1951 | GList *was_on_monitor; |
1952 | |
1953 | was_on_monitor = g_list_find (list: impl->surface_is_on_monitor, data: monitor); |
1954 | |
1955 | if (!was_on_monitor && is_on_monitor) |
1956 | { |
1957 | impl->surface_is_on_monitor = g_list_append (list: impl->surface_is_on_monitor, |
1958 | data: monitor); |
1959 | gdk_surface_enter_monitor (surface, monitor); |
1960 | } |
1961 | else if (was_on_monitor && !is_on_monitor) |
1962 | { |
1963 | impl->surface_is_on_monitor = g_list_remove (list: impl->surface_is_on_monitor, |
1964 | data: monitor); |
1965 | gdk_surface_leave_monitor (surface, monitor); |
1966 | } |
1967 | } |
1968 | |
1969 | void |
1970 | gdk_x11_surface_check_monitor (GdkSurface *surface, |
1971 | GdkMonitor *monitor) |
1972 | { |
1973 | GdkRectangle monitor_geometry; |
1974 | GdkRectangle surface_geometry; |
1975 | gboolean is_on_monitor; |
1976 | |
1977 | gdk_monitor_get_geometry (monitor, geometry: &monitor_geometry); |
1978 | gdk_surface_get_geometry (surface, |
1979 | x: &surface_geometry.x, |
1980 | y: &surface_geometry.y, |
1981 | width: &surface_geometry.width, |
1982 | height: &surface_geometry.height); |
1983 | |
1984 | is_on_monitor = gdk_rectangle_intersect (src1: &surface_geometry, |
1985 | src2: &monitor_geometry, |
1986 | NULL); |
1987 | |
1988 | gdk_x11_surface_set_is_on_monitor (surface, monitor, is_on_monitor); |
1989 | } |
1990 | |
1991 | void |
1992 | gdk_x11_surface_enter_leave_monitors (GdkSurface *surface) |
1993 | { |
1994 | GdkDisplay *display = gdk_surface_get_display (surface); |
1995 | GListModel *monitors; |
1996 | guint i; |
1997 | |
1998 | monitors = gdk_display_get_monitors (self: display); |
1999 | for (i = 0; i < g_list_model_get_n_items (list: monitors); i++) |
2000 | { |
2001 | GdkMonitor *monitor = g_list_model_get_item (list: monitors, position: i); |
2002 | gdk_x11_surface_check_monitor (surface, monitor); |
2003 | g_object_unref (object: monitor); |
2004 | } |
2005 | } |
2006 | |
2007 | void |
2008 | _gdk_x11_surface_set_surface_scale (GdkSurface *surface, |
2009 | int scale) |
2010 | { |
2011 | GdkX11Surface *impl; |
2012 | GdkToplevelX11 *toplevel; |
2013 | GdkSurfaceHints geom_mask; |
2014 | |
2015 | impl = GDK_X11_SURFACE (surface); |
2016 | |
2017 | if (!gdk_x11_surface_update_size (self: impl, width: surface->width, height: surface->height, scale)) |
2018 | return; |
2019 | |
2020 | toplevel = _gdk_x11_surface_get_toplevel (surface); |
2021 | if (toplevel) |
2022 | { |
2023 | /* These are affected by surface scale: */ |
2024 | geom_mask = toplevel->last_geometry_hints_mask & (GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE); |
2025 | if (geom_mask) |
2026 | gdk_x11_surface_set_geometry_hints (surface, |
2027 | geometry: &toplevel->last_geometry_hints, |
2028 | geom_mask); |
2029 | } |
2030 | |
2031 | if (impl->override_redirect) |
2032 | { |
2033 | impl->unscaled_width = surface->width * impl->surface_scale; |
2034 | impl->unscaled_height = surface->height * impl->surface_scale; |
2035 | } |
2036 | |
2037 | XResizeWindow (GDK_SURFACE_XDISPLAY (surface), |
2038 | GDK_SURFACE_XID (surface), |
2039 | surface->width * impl->surface_scale, |
2040 | surface->height * impl->surface_scale); |
2041 | |
2042 | gdk_surface_invalidate_rect (surface, NULL); |
2043 | |
2044 | g_object_notify (G_OBJECT (surface), property_name: "scale-factor" ); |
2045 | } |
2046 | |
2047 | void |
2048 | gdk_x11_surface_raise (GdkSurface *surface) |
2049 | { |
2050 | XRaiseWindow (GDK_SURFACE_XDISPLAY (surface), GDK_SURFACE_XID (surface)); |
2051 | } |
2052 | |
2053 | static void |
2054 | gdk_x11_surface_restack_toplevel (GdkSurface *surface, |
2055 | GdkSurface *sibling, |
2056 | gboolean above) |
2057 | { |
2058 | XWindowChanges changes; |
2059 | |
2060 | changes.sibling = GDK_SURFACE_XID (sibling); |
2061 | changes.stack_mode = above ? Above : Below; |
2062 | XReconfigureWMWindow (GDK_SURFACE_XDISPLAY (surface), |
2063 | GDK_SURFACE_XID (surface), |
2064 | gdk_x11_screen_get_screen_number (GDK_SURFACE_SCREEN (surface)), |
2065 | CWStackMode | CWSibling, &changes); |
2066 | } |
2067 | |
2068 | static void |
2069 | gdk_x11_surface_lower (GdkSurface *surface) |
2070 | { |
2071 | XLowerWindow (GDK_SURFACE_XDISPLAY (surface), GDK_SURFACE_XID (surface)); |
2072 | } |
2073 | |
2074 | /** |
2075 | * gdk_x11_surface_move_to_current_desktop: |
2076 | * @surface: (type GdkX11Surface): a `GdkSurface` |
2077 | * |
2078 | * Moves the surface to the correct workspace when running under a |
2079 | * window manager that supports multiple workspaces, as described |
2080 | * in the [Extended Window Manager Hints](http://www.freedesktop.org/Standards/wm-spec) specification. |
2081 | * Will not do anything if the surface is already on all workspaces. |
2082 | */ |
2083 | void |
2084 | gdk_x11_surface_move_to_current_desktop (GdkSurface *surface) |
2085 | { |
2086 | GdkToplevelX11 *toplevel; |
2087 | |
2088 | g_return_if_fail (GDK_IS_SURFACE (surface)); |
2089 | |
2090 | toplevel = _gdk_x11_surface_get_toplevel (surface); |
2091 | |
2092 | if (toplevel->on_all_desktops) |
2093 | return; |
2094 | |
2095 | move_to_current_desktop (surface); |
2096 | } |
2097 | |
2098 | static void |
2099 | move_to_current_desktop (GdkSurface *surface) |
2100 | { |
2101 | guint32 desktop; |
2102 | |
2103 | desktop = gdk_x11_screen_get_current_desktop (GDK_SURFACE_SCREEN (surface)); |
2104 | gdk_x11_surface_move_to_desktop (surface, desktop); |
2105 | } |
2106 | |
2107 | static guint32 |
2108 | get_netwm_cardinal_property (GdkSurface *surface, |
2109 | const char *name) |
2110 | { |
2111 | GdkX11Screen *x11_screen = GDK_SURFACE_SCREEN (surface); |
2112 | guint32 prop = 0; |
2113 | Atom type; |
2114 | int format; |
2115 | gulong nitems; |
2116 | gulong bytes_after; |
2117 | guchar *data; |
2118 | |
2119 | if (!gdk_x11_screen_supports_net_wm_hint (screen: x11_screen, property_name: name)) |
2120 | return 0; |
2121 | |
2122 | XGetWindowProperty (x11_screen->xdisplay, |
2123 | GDK_SURFACE_XID (surface), |
2124 | gdk_x11_get_xatom_by_name_for_display (GDK_SURFACE_DISPLAY (surface), atom_name: name), |
2125 | 0, G_MAXLONG, |
2126 | False, XA_CARDINAL, &type, &format, &nitems, |
2127 | &bytes_after, &data); |
2128 | if (type == XA_CARDINAL) |
2129 | { |
2130 | prop = *(gulong *)data; |
2131 | XFree (data); |
2132 | } |
2133 | |
2134 | return prop; |
2135 | } |
2136 | |
2137 | /** |
2138 | * gdk_x11_surface_get_desktop: |
2139 | * @surface: (type GdkX11Surface): a `GdkSurface` |
2140 | * |
2141 | * Gets the number of the workspace @surface is on. |
2142 | * |
2143 | * Returns: the current workspace of @surface |
2144 | */ |
2145 | guint32 |
2146 | gdk_x11_surface_get_desktop (GdkSurface *surface) |
2147 | { |
2148 | g_return_val_if_fail (GDK_IS_SURFACE (surface), 0); |
2149 | |
2150 | return get_netwm_cardinal_property (surface, name: "_NET_WM_DESKTOP" ); |
2151 | } |
2152 | |
2153 | /** |
2154 | * gdk_x11_surface_move_to_desktop: |
2155 | * @surface: (type GdkX11Surface): a `GdkSurface` |
2156 | * @desktop: the number of the workspace to move the surface to |
2157 | * |
2158 | * Moves the surface to the given workspace when running unde a |
2159 | * window manager that supports multiple workspaces, as described |
2160 | * in the [Extended Window Manager Hints](http://www.freedesktop.org/Standards/wm-spec) specification. |
2161 | */ |
2162 | void |
2163 | gdk_x11_surface_move_to_desktop (GdkSurface *surface, |
2164 | guint32 desktop) |
2165 | { |
2166 | const char *atom_name = "_NET_WM_DESKTOP" ; |
2167 | XClientMessageEvent xclient; |
2168 | |
2169 | g_return_if_fail (GDK_IS_SURFACE (surface)); |
2170 | |
2171 | if (!gdk_x11_screen_supports_net_wm_hint (GDK_SURFACE_SCREEN (surface), property_name: atom_name)) |
2172 | return; |
2173 | |
2174 | memset (s: &xclient, c: 0, n: sizeof (xclient)); |
2175 | xclient.type = ClientMessage; |
2176 | xclient.serial = 0; |
2177 | xclient.send_event = True; |
2178 | xclient.window = GDK_SURFACE_XID (surface); |
2179 | xclient.message_type = gdk_x11_get_xatom_by_name_for_display (GDK_SURFACE_DISPLAY (surface), atom_name); |
2180 | xclient.format = 32; |
2181 | |
2182 | xclient.data.l[0] = desktop; |
2183 | xclient.data.l[1] = 1; /* source indication */ |
2184 | xclient.data.l[2] = 0; |
2185 | xclient.data.l[3] = 0; |
2186 | xclient.data.l[4] = 0; |
2187 | |
2188 | XSendEvent (GDK_SURFACE_XDISPLAY (surface), |
2189 | GDK_SURFACE_XROOTWIN (surface), |
2190 | False, |
2191 | SubstructureRedirectMask | SubstructureNotifyMask, |
2192 | (XEvent *)&xclient); |
2193 | } |
2194 | |
2195 | static void |
2196 | gdk_x11_surface_focus (GdkSurface *surface, |
2197 | guint32 timestamp) |
2198 | { |
2199 | GdkDisplay *display; |
2200 | |
2201 | g_return_if_fail (GDK_IS_SURFACE (surface)); |
2202 | |
2203 | if (GDK_SURFACE_DESTROYED (surface)) |
2204 | return; |
2205 | |
2206 | display = GDK_SURFACE_DISPLAY (surface); |
2207 | |
2208 | if (gdk_x11_screen_supports_net_wm_hint (GDK_SURFACE_SCREEN (surface), |
2209 | property_name: g_intern_static_string (string: "_NET_ACTIVE_WINDOW" ))) |
2210 | { |
2211 | XClientMessageEvent xclient; |
2212 | |
2213 | memset (s: &xclient, c: 0, n: sizeof (xclient)); |
2214 | xclient.type = ClientMessage; |
2215 | xclient.window = GDK_SURFACE_XID (surface); |
2216 | xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, |
2217 | atom_name: "_NET_ACTIVE_WINDOW" ); |
2218 | xclient.format = 32; |
2219 | xclient.data.l[0] = 1; /* requestor type; we're an app */ |
2220 | xclient.data.l[1] = timestamp; |
2221 | xclient.data.l[2] = None; /* currently active window */ |
2222 | xclient.data.l[3] = 0; |
2223 | xclient.data.l[4] = 0; |
2224 | |
2225 | XSendEvent (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XROOTWIN (surface), False, |
2226 | SubstructureRedirectMask | SubstructureNotifyMask, |
2227 | (XEvent *)&xclient); |
2228 | } |
2229 | else |
2230 | { |
2231 | XRaiseWindow (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XID (surface)); |
2232 | |
2233 | /* There is no way of knowing reliably whether we are viewable; |
2234 | * so trap errors asynchronously around the XSetInputFocus call |
2235 | */ |
2236 | gdk_x11_display_error_trap_push (display); |
2237 | XSetInputFocus (GDK_DISPLAY_XDISPLAY (display), |
2238 | GDK_SURFACE_XID (surface), |
2239 | RevertToParent, |
2240 | timestamp); |
2241 | gdk_x11_display_error_trap_pop_ignored (display); |
2242 | } |
2243 | } |
2244 | |
2245 | static void |
2246 | gdk_x11_surface_set_type_hint (GdkSurface *surface, |
2247 | GdkSurfaceTypeHint hint) |
2248 | { |
2249 | GdkDisplay *display; |
2250 | Atom atom; |
2251 | |
2252 | if (GDK_SURFACE_DESTROYED (surface)) |
2253 | return; |
2254 | |
2255 | display = gdk_surface_get_display (surface); |
2256 | |
2257 | switch (hint) |
2258 | { |
2259 | case GDK_SURFACE_TYPE_HINT_DIALOG: |
2260 | atom = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_WINDOW_TYPE_DIALOG" ); |
2261 | break; |
2262 | case GDK_SURFACE_TYPE_HINT_MENU: |
2263 | atom = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_WINDOW_TYPE_MENU" ); |
2264 | break; |
2265 | case GDK_SURFACE_TYPE_HINT_TOOLBAR: |
2266 | atom = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_WINDOW_TYPE_TOOLBAR" ); |
2267 | break; |
2268 | case GDK_SURFACE_TYPE_HINT_UTILITY: |
2269 | atom = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_WINDOW_TYPE_UTILITY" ); |
2270 | break; |
2271 | case GDK_SURFACE_TYPE_HINT_SPLASHSCREEN: |
2272 | atom = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_WINDOW_TYPE_SPLASH" ); |
2273 | break; |
2274 | case GDK_SURFACE_TYPE_HINT_DOCK: |
2275 | atom = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_WINDOW_TYPE_DOCK" ); |
2276 | break; |
2277 | case GDK_SURFACE_TYPE_HINT_DESKTOP: |
2278 | atom = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_WINDOW_TYPE_DESKTOP" ); |
2279 | break; |
2280 | case GDK_SURFACE_TYPE_HINT_DROPDOWN_MENU: |
2281 | atom = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU" ); |
2282 | break; |
2283 | case GDK_SURFACE_TYPE_HINT_POPUP_MENU: |
2284 | atom = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_WINDOW_TYPE_POPUP_MENU" ); |
2285 | break; |
2286 | case GDK_SURFACE_TYPE_HINT_TOOLTIP: |
2287 | atom = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_WINDOW_TYPE_TOOLTIP" ); |
2288 | break; |
2289 | case GDK_SURFACE_TYPE_HINT_NOTIFICATION: |
2290 | atom = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_WINDOW_TYPE_NOTIFICATION" ); |
2291 | break; |
2292 | case GDK_SURFACE_TYPE_HINT_COMBO: |
2293 | atom = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_WINDOW_TYPE_COMBO" ); |
2294 | break; |
2295 | case GDK_SURFACE_TYPE_HINT_DND: |
2296 | atom = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_WINDOW_TYPE_DND" ); |
2297 | break; |
2298 | default: |
2299 | g_warning ("Unknown hint %d passed to gdk_surface_set_type_hint" , hint); |
2300 | G_GNUC_FALLTHROUGH; |
2301 | case GDK_SURFACE_TYPE_HINT_NORMAL: |
2302 | atom = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_WINDOW_TYPE_NORMAL" ); |
2303 | break; |
2304 | } |
2305 | |
2306 | XChangeProperty (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XID (surface), |
2307 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_WINDOW_TYPE" ), |
2308 | XA_ATOM, 32, PropModeReplace, |
2309 | (guchar *)&atom, 1); |
2310 | } |
2311 | |
2312 | static void |
2313 | gdk_wmspec_change_state (gboolean add, |
2314 | GdkSurface *surface, |
2315 | const char *state1, |
2316 | const char *state2) |
2317 | { |
2318 | GdkDisplay *display = GDK_SURFACE_DISPLAY (surface); |
2319 | XClientMessageEvent xclient; |
2320 | |
2321 | #define _NET_WM_STATE_REMOVE 0 /* remove/unset property */ |
2322 | #define _NET_WM_STATE_ADD 1 /* add/set property */ |
2323 | #define _NET_WM_STATE_TOGGLE 2 /* toggle property */ |
2324 | |
2325 | memset (s: &xclient, c: 0, n: sizeof (xclient)); |
2326 | xclient.type = ClientMessage; |
2327 | xclient.window = GDK_SURFACE_XID (surface); |
2328 | xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_STATE" ); |
2329 | xclient.format = 32; |
2330 | xclient.data.l[0] = add ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; |
2331 | xclient.data.l[1] = gdk_x11_get_xatom_by_name_for_display (display, atom_name: state1); |
2332 | xclient.data.l[2] = gdk_x11_get_xatom_by_name_for_display (display, atom_name: state2); |
2333 | xclient.data.l[3] = 1; /* source indication */ |
2334 | xclient.data.l[4] = 0; |
2335 | |
2336 | XSendEvent (GDK_SURFACE_XDISPLAY (surface), GDK_SURFACE_XROOTWIN (surface), False, |
2337 | SubstructureRedirectMask | SubstructureNotifyMask, |
2338 | (XEvent *)&xclient); |
2339 | } |
2340 | |
2341 | static void |
2342 | gdk_x11_surface_set_modal_hint (GdkSurface *surface, |
2343 | gboolean modal) |
2344 | { |
2345 | if (GDK_SURFACE_DESTROYED (surface)) |
2346 | return; |
2347 | |
2348 | surface->modal_hint = modal; |
2349 | |
2350 | if (GDK_SURFACE_IS_MAPPED (surface)) |
2351 | gdk_wmspec_change_state (add: modal, surface, |
2352 | state1: "_NET_WM_STATE_MODAL" , |
2353 | NULL); |
2354 | } |
2355 | |
2356 | /** |
2357 | * gdk_x11_surface_set_skip_taskbar_hint: |
2358 | * @surface: (type GdkX11Surface): a native `GdkSurface` |
2359 | * @skips_taskbar: %TRUE to skip taskbars |
2360 | * |
2361 | * Sets a hint on @surface that taskbars should not |
2362 | * display it. See the EWMH for details. |
2363 | */ |
2364 | void |
2365 | gdk_x11_surface_set_skip_taskbar_hint (GdkSurface *surface, |
2366 | gboolean skips_taskbar) |
2367 | { |
2368 | GdkToplevelX11 *toplevel; |
2369 | |
2370 | if (GDK_SURFACE_DESTROYED (surface)) |
2371 | return; |
2372 | |
2373 | toplevel = _gdk_x11_surface_get_toplevel (surface); |
2374 | toplevel->skip_taskbar_hint = skips_taskbar; |
2375 | |
2376 | if (GDK_SURFACE_IS_MAPPED (surface)) |
2377 | gdk_wmspec_change_state (add: skips_taskbar, surface, |
2378 | state1: "_NET_WM_STATE_SKIP_TASKBAR" , |
2379 | NULL); |
2380 | } |
2381 | |
2382 | /** |
2383 | * gdk_x11_surface_set_skip_pager_hint: |
2384 | * @surface: (type GdkX11Surface): a `GdkSurface` |
2385 | * @skips_pager: %TRUE to skip pagers |
2386 | * |
2387 | * Sets a hint on @surface that pagers should not |
2388 | * display it. See the EWMH for details. |
2389 | */ |
2390 | void |
2391 | (GdkSurface *surface, |
2392 | gboolean ) |
2393 | { |
2394 | GdkToplevelX11 *toplevel; |
2395 | |
2396 | if (GDK_SURFACE_DESTROYED (surface)) |
2397 | return; |
2398 | |
2399 | toplevel = _gdk_x11_surface_get_toplevel (surface); |
2400 | toplevel->skip_pager_hint = skips_pager; |
2401 | |
2402 | if (GDK_SURFACE_IS_MAPPED (surface)) |
2403 | gdk_wmspec_change_state (add: skips_pager, surface, |
2404 | state1: "_NET_WM_STATE_SKIP_PAGER" , |
2405 | NULL); |
2406 | } |
2407 | |
2408 | /** |
2409 | * gdk_x11_surface_set_urgency_hint: |
2410 | * @surface: (type GdkX11Surface): a native `GdkSurface` |
2411 | * @urgent: %TRUE to indicate urgenct attention needed |
2412 | * |
2413 | * Sets a hint on @surface that it needs user attention. |
2414 | * See the ICCCM for details. |
2415 | */ |
2416 | void |
2417 | gdk_x11_surface_set_urgency_hint (GdkSurface *surface, |
2418 | gboolean urgent) |
2419 | { |
2420 | GdkToplevelX11 *toplevel; |
2421 | |
2422 | if (GDK_SURFACE_DESTROYED (surface)) |
2423 | return; |
2424 | |
2425 | toplevel = _gdk_x11_surface_get_toplevel (surface); |
2426 | toplevel->urgency_hint = urgent; |
2427 | |
2428 | update_wm_hints (surface, FALSE); |
2429 | } |
2430 | |
2431 | static void |
2432 | gdk_x11_surface_set_geometry_hints (GdkSurface *surface, |
2433 | const GdkGeometry *geometry, |
2434 | GdkSurfaceHints geom_mask) |
2435 | { |
2436 | GdkX11Surface *impl = GDK_X11_SURFACE (surface); |
2437 | XSizeHints size_hints; |
2438 | GdkToplevelX11 *toplevel; |
2439 | |
2440 | if (GDK_SURFACE_DESTROYED (surface)) |
2441 | return; |
2442 | |
2443 | toplevel = _gdk_x11_surface_get_toplevel (surface); |
2444 | if (toplevel) |
2445 | { |
2446 | if (geometry) |
2447 | toplevel->last_geometry_hints = *geometry; |
2448 | toplevel->last_geometry_hints_mask = geom_mask; |
2449 | } |
2450 | |
2451 | size_hints.flags = 0; |
2452 | |
2453 | if (geom_mask & GDK_HINT_MIN_SIZE) |
2454 | { |
2455 | size_hints.flags |= PMinSize; |
2456 | size_hints.min_width = geometry->min_width * impl->surface_scale; |
2457 | size_hints.min_height = geometry->min_height * impl->surface_scale; |
2458 | } |
2459 | |
2460 | if (geom_mask & GDK_HINT_MAX_SIZE) |
2461 | { |
2462 | size_hints.flags |= PMaxSize; |
2463 | size_hints.max_width = MAX (geometry->max_width, 1) * impl->surface_scale; |
2464 | size_hints.max_height = MAX (geometry->max_height, 1) * impl->surface_scale; |
2465 | } |
2466 | |
2467 | else if (impl->surface_scale > 1) |
2468 | { |
2469 | size_hints.flags |= PResizeInc; |
2470 | size_hints.width_inc = impl->surface_scale; |
2471 | size_hints.height_inc = impl->surface_scale; |
2472 | } |
2473 | |
2474 | /* FIXME: Would it be better to delete this property if |
2475 | * geom_mask == 0? It would save space on the server |
2476 | */ |
2477 | XSetWMNormalHints (GDK_SURFACE_XDISPLAY (surface), |
2478 | GDK_SURFACE_XID (surface), |
2479 | &size_hints); |
2480 | } |
2481 | |
2482 | static void |
2483 | gdk_surface_get_geometry_hints (GdkSurface *surface, |
2484 | GdkGeometry *geometry, |
2485 | GdkSurfaceHints *geom_mask) |
2486 | { |
2487 | GdkX11Surface *impl; |
2488 | XSizeHints *size_hints; |
2489 | glong junk_supplied_mask = 0; |
2490 | |
2491 | g_return_if_fail (GDK_IS_SURFACE (surface)); |
2492 | g_return_if_fail (geometry != NULL); |
2493 | g_return_if_fail (geom_mask != NULL); |
2494 | |
2495 | *geom_mask = 0; |
2496 | |
2497 | if (GDK_SURFACE_DESTROYED (surface)) |
2498 | return; |
2499 | |
2500 | impl = GDK_X11_SURFACE (surface); |
2501 | |
2502 | size_hints = XAllocSizeHints (); |
2503 | if (!size_hints) |
2504 | return; |
2505 | |
2506 | if (!XGetWMNormalHints (GDK_SURFACE_XDISPLAY (surface), |
2507 | GDK_SURFACE_XID (surface), |
2508 | size_hints, |
2509 | &junk_supplied_mask)) |
2510 | size_hints->flags = 0; |
2511 | |
2512 | if (size_hints->flags & PMinSize) |
2513 | { |
2514 | *geom_mask |= GDK_HINT_MIN_SIZE; |
2515 | geometry->min_width = size_hints->min_width / impl->surface_scale; |
2516 | geometry->min_height = size_hints->min_height / impl->surface_scale; |
2517 | } |
2518 | |
2519 | if (size_hints->flags & PMaxSize) |
2520 | { |
2521 | *geom_mask |= GDK_HINT_MAX_SIZE; |
2522 | geometry->max_width = MAX (size_hints->max_width, 1) / impl->surface_scale; |
2523 | geometry->max_height = MAX (size_hints->max_height, 1) / impl->surface_scale; |
2524 | } |
2525 | |
2526 | XFree (size_hints); |
2527 | } |
2528 | |
2529 | static gboolean |
2530 | utf8_is_latin1 (const char *str) |
2531 | { |
2532 | const char *p = str; |
2533 | |
2534 | while (*p) |
2535 | { |
2536 | gunichar ch = g_utf8_get_char (p); |
2537 | |
2538 | if (ch > 0xff) |
2539 | return FALSE; |
2540 | |
2541 | p = g_utf8_next_char (p); |
2542 | } |
2543 | |
2544 | return TRUE; |
2545 | } |
2546 | |
2547 | /* Set the property to @utf8_str as STRING if the @utf8_str is fully |
2548 | * convertible to STRING, otherwise, set it as compound text |
2549 | */ |
2550 | static void |
2551 | set_text_property (GdkDisplay *display, |
2552 | Window xwindow, |
2553 | Atom property, |
2554 | const char *utf8_str) |
2555 | { |
2556 | char *prop_text = NULL; |
2557 | Atom prop_type; |
2558 | int prop_length; |
2559 | int prop_format; |
2560 | gboolean is_compound_text; |
2561 | |
2562 | if (utf8_is_latin1 (str: utf8_str)) |
2563 | { |
2564 | prop_type = XA_STRING; |
2565 | prop_text = gdk_x11_utf8_to_string_target (utf8_str, TRUE); |
2566 | prop_length = prop_text ? strlen (s: prop_text) : 0; |
2567 | prop_format = 8; |
2568 | is_compound_text = FALSE; |
2569 | } |
2570 | else |
2571 | { |
2572 | const char *gdk_type; |
2573 | |
2574 | gdk_x11_display_utf8_to_compound_text (display, |
2575 | str: utf8_str, encoding: &gdk_type, format: &prop_format, |
2576 | ctext: (guchar **)&prop_text, length: &prop_length); |
2577 | prop_type = gdk_x11_get_xatom_by_name_for_display (display, atom_name: gdk_type); |
2578 | is_compound_text = TRUE; |
2579 | } |
2580 | |
2581 | if (prop_text) |
2582 | { |
2583 | XChangeProperty (GDK_DISPLAY_XDISPLAY (display), |
2584 | xwindow, |
2585 | property, |
2586 | prop_type, prop_format, |
2587 | PropModeReplace, (guchar *)prop_text, |
2588 | prop_length); |
2589 | |
2590 | if (is_compound_text) |
2591 | gdk_x11_free_compound_text (ctext: (guchar *)prop_text); |
2592 | else |
2593 | g_free (mem: prop_text); |
2594 | } |
2595 | } |
2596 | |
2597 | /* Set WM_NAME and _NET_WM_NAME |
2598 | */ |
2599 | static void |
2600 | set_wm_name (GdkDisplay *display, |
2601 | Window xwindow, |
2602 | const char *name) |
2603 | { |
2604 | XChangeProperty (GDK_DISPLAY_XDISPLAY (display), xwindow, |
2605 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_NAME" ), |
2606 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: "UTF8_STRING" ), 8, |
2607 | PropModeReplace, (guchar *)name, strlen (s: name)); |
2608 | |
2609 | set_text_property (display, xwindow, |
2610 | property: gdk_x11_get_xatom_by_name_for_display (display, atom_name: "WM_NAME" ), |
2611 | utf8_str: name); |
2612 | } |
2613 | |
2614 | static void |
2615 | gdk_x11_surface_set_title (GdkSurface *surface, |
2616 | const char *title) |
2617 | { |
2618 | GdkDisplay *display; |
2619 | Display *xdisplay; |
2620 | Window xwindow; |
2621 | |
2622 | g_return_if_fail (title != NULL); |
2623 | |
2624 | if (GDK_SURFACE_DESTROYED (surface)) |
2625 | return; |
2626 | |
2627 | display = gdk_surface_get_display (surface); |
2628 | xdisplay = GDK_DISPLAY_XDISPLAY (display); |
2629 | xwindow = GDK_SURFACE_XID (surface); |
2630 | |
2631 | set_wm_name (display, xwindow, name: title); |
2632 | |
2633 | if (!gdk_surface_icon_name_set (surface)) |
2634 | { |
2635 | XChangeProperty (xdisplay, xwindow, |
2636 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_ICON_NAME" ), |
2637 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: "UTF8_STRING" ), 8, |
2638 | PropModeReplace, (guchar *)title, strlen (s: title)); |
2639 | |
2640 | set_text_property (display, xwindow, |
2641 | property: gdk_x11_get_xatom_by_name_for_display (display, atom_name: "WM_ICON_NAME" ), |
2642 | utf8_str: title); |
2643 | } |
2644 | } |
2645 | |
2646 | static void |
2647 | gdk_x11_surface_set_startup_id (GdkSurface *surface, |
2648 | const char *startup_id) |
2649 | { |
2650 | GdkDisplay *display; |
2651 | |
2652 | g_return_if_fail (GDK_IS_SURFACE (surface)); |
2653 | |
2654 | display = gdk_surface_get_display (surface); |
2655 | |
2656 | if (GDK_SURFACE_DESTROYED (surface)) |
2657 | return; |
2658 | |
2659 | if (startup_id) |
2660 | XChangeProperty (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XID (surface), |
2661 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_STARTUP_ID" ), |
2662 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: "UTF8_STRING" ), 8, |
2663 | PropModeReplace, (unsigned char *)startup_id, strlen (s: startup_id)); |
2664 | else |
2665 | XDeleteProperty (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XID (surface), |
2666 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_STARTUP_ID" )); |
2667 | } |
2668 | |
2669 | static void |
2670 | gdk_x11_surface_set_transient_for (GdkSurface *surface, |
2671 | GdkSurface *parent) |
2672 | { |
2673 | if (GDK_SURFACE_DESTROYED (surface)) |
2674 | return; |
2675 | |
2676 | /* XSetTransientForHint() doesn't allow unsetting, so do it manually */ |
2677 | if (parent && !GDK_SURFACE_DESTROYED (parent)) |
2678 | { |
2679 | XSetTransientForHint (GDK_SURFACE_XDISPLAY (surface), |
2680 | GDK_SURFACE_XID (surface), |
2681 | GDK_SURFACE_XID (parent)); |
2682 | gdk_x11_surface_set_type_hint (surface, hint: GDK_SURFACE_TYPE_HINT_DIALOG); |
2683 | } |
2684 | else |
2685 | { |
2686 | XDeleteProperty (GDK_SURFACE_XDISPLAY (surface), |
2687 | GDK_SURFACE_XID (surface), |
2688 | gdk_x11_get_xatom_by_name_for_display (GDK_SURFACE_DISPLAY (surface), atom_name: "WM_TRANSIENT_FOR" )); |
2689 | gdk_x11_surface_set_type_hint (surface, hint: GDK_SURFACE_TYPE_HINT_NORMAL); |
2690 | } |
2691 | } |
2692 | |
2693 | GdkCursor * |
2694 | _gdk_x11_surface_get_cursor (GdkSurface *surface) |
2695 | { |
2696 | GdkX11Surface *impl; |
2697 | |
2698 | g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL); |
2699 | |
2700 | impl = GDK_X11_SURFACE (surface); |
2701 | |
2702 | return impl->cursor; |
2703 | } |
2704 | |
2705 | static void |
2706 | gdk_x11_surface_get_geometry (GdkSurface *surface, |
2707 | int *x, |
2708 | int *y, |
2709 | int *width, |
2710 | int *height) |
2711 | { |
2712 | GdkX11Surface *impl; |
2713 | Window root; |
2714 | int tx; |
2715 | int ty; |
2716 | guint twidth; |
2717 | guint theight; |
2718 | guint tborder_width; |
2719 | guint tdepth; |
2720 | |
2721 | if (!GDK_SURFACE_DESTROYED (surface)) |
2722 | { |
2723 | impl = GDK_X11_SURFACE (surface); |
2724 | |
2725 | XGetGeometry (GDK_SURFACE_XDISPLAY (surface), |
2726 | GDK_SURFACE_XID (surface), |
2727 | &root, &tx, &ty, &twidth, &theight, &tborder_width, &tdepth); |
2728 | |
2729 | if (x) |
2730 | *x = tx / impl->surface_scale; |
2731 | if (y) |
2732 | *y = ty / impl->surface_scale; |
2733 | if (width) |
2734 | *width = twidth / impl->surface_scale; |
2735 | if (height) |
2736 | *height = theight / impl->surface_scale; |
2737 | } |
2738 | } |
2739 | |
2740 | void |
2741 | gdk_x11_surface_get_root_coords (GdkSurface *surface, |
2742 | int x, |
2743 | int y, |
2744 | int *root_x, |
2745 | int *root_y) |
2746 | { |
2747 | GdkX11Surface *impl = GDK_X11_SURFACE (surface); |
2748 | Window child; |
2749 | int tx; |
2750 | int ty; |
2751 | |
2752 | XTranslateCoordinates (GDK_SURFACE_XDISPLAY (surface), |
2753 | GDK_SURFACE_XID (surface), |
2754 | GDK_SURFACE_XROOTWIN (surface), |
2755 | x * impl->surface_scale, y * impl->surface_scale, &tx, &ty, |
2756 | &child); |
2757 | |
2758 | if (root_x) |
2759 | *root_x = tx / impl->surface_scale; |
2760 | if (root_y) |
2761 | *root_y = ty / impl->surface_scale; |
2762 | } |
2763 | |
2764 | static void |
2765 | gdk_x11_surface_get_frame_extents (GdkSurface *surface, |
2766 | GdkRectangle *rect) |
2767 | { |
2768 | GdkDisplay *display; |
2769 | GdkX11Surface *impl; |
2770 | Window xwindow; |
2771 | Window xparent; |
2772 | Window root; |
2773 | Window child; |
2774 | Window *children; |
2775 | guchar *data; |
2776 | Window *vroots; |
2777 | Atom type_return; |
2778 | guint nchildren; |
2779 | guint nvroots; |
2780 | gulong nitems_return; |
2781 | gulong bytes_after_return; |
2782 | int format_return; |
2783 | int i; |
2784 | guint ww, wh, wb, wd; |
2785 | int wx, wy; |
2786 | gboolean got_frame_extents = FALSE; |
2787 | |
2788 | g_return_if_fail (rect != NULL); |
2789 | |
2790 | rect->x = 0; |
2791 | rect->y = 0; |
2792 | rect->width = 1; |
2793 | rect->height = 1; |
2794 | |
2795 | impl = GDK_X11_SURFACE (surface); |
2796 | |
2797 | /* Refine our fallback answer a bit using local information */ |
2798 | rect->x = impl->abs_x * impl->surface_scale; |
2799 | rect->y = impl->abs_y * impl->surface_scale; |
2800 | rect->width = surface->width * impl->surface_scale; |
2801 | rect->height = surface->height * impl->surface_scale; |
2802 | |
2803 | if (GDK_SURFACE_DESTROYED (surface) || impl->override_redirect) |
2804 | return; |
2805 | |
2806 | nvroots = 0; |
2807 | vroots = NULL; |
2808 | |
2809 | display = gdk_surface_get_display (surface); |
2810 | |
2811 | gdk_x11_display_error_trap_push (display); |
2812 | |
2813 | xwindow = GDK_SURFACE_XID (surface); |
2814 | |
2815 | /* first try: use _NET_FRAME_EXTENTS */ |
2816 | if (gdk_x11_screen_supports_net_wm_hint (GDK_SURFACE_SCREEN (surface), |
2817 | property_name: g_intern_static_string (string: "_NET_FRAME_EXTENTS" )) && |
2818 | XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), xwindow, |
2819 | gdk_x11_get_xatom_by_name_for_display (display, |
2820 | atom_name: "_NET_FRAME_EXTENTS" ), |
2821 | 0, G_MAXLONG, False, XA_CARDINAL, &type_return, |
2822 | &format_return, &nitems_return, &bytes_after_return, |
2823 | &data) |
2824 | == Success) |
2825 | { |
2826 | if ((type_return == XA_CARDINAL) && (format_return == 32) && |
2827 | (nitems_return == 4) && (data)) |
2828 | { |
2829 | gulong *ldata = (gulong *) data; |
2830 | got_frame_extents = TRUE; |
2831 | |
2832 | /* try to get the real client window geometry */ |
2833 | if (XGetGeometry (GDK_DISPLAY_XDISPLAY (display), xwindow, |
2834 | &root, &wx, &wy, &ww, &wh, &wb, &wd) && |
2835 | XTranslateCoordinates (GDK_DISPLAY_XDISPLAY (display), |
2836 | xwindow, root, 0, 0, &wx, &wy, &child)) |
2837 | { |
2838 | rect->x = wx; |
2839 | rect->y = wy; |
2840 | rect->width = ww; |
2841 | rect->height = wh; |
2842 | } |
2843 | |
2844 | /* _NET_FRAME_EXTENTS format is left, right, top, bottom */ |
2845 | rect->x -= ldata[0]; |
2846 | rect->y -= ldata[2]; |
2847 | rect->width += ldata[0] + ldata[1]; |
2848 | rect->height += ldata[2] + ldata[3]; |
2849 | } |
2850 | |
2851 | if (data) |
2852 | XFree (data); |
2853 | } |
2854 | |
2855 | if (got_frame_extents) |
2856 | goto out; |
2857 | |
2858 | /* no frame extents property available, which means we either have a WM that |
2859 | is not EWMH compliant or is broken - try fallback and walk up the window |
2860 | tree to get our window's parent which hopefully is the window frame */ |
2861 | |
2862 | /* use NETWM_VIRTUAL_ROOTS if available */ |
2863 | root = GDK_SURFACE_XROOTWIN (surface); |
2864 | |
2865 | if (gdk_x11_screen_supports_net_wm_hint (GDK_SURFACE_SCREEN (surface), |
2866 | property_name: g_intern_static_string (string: "_NET_VIRTUAL_ROOTS" )) && |
2867 | XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), root, |
2868 | gdk_x11_get_xatom_by_name_for_display (display, |
2869 | atom_name: "_NET_VIRTUAL_ROOTS" ), |
2870 | 0, G_MAXLONG, False, XA_WINDOW, &type_return, |
2871 | &format_return, &nitems_return, &bytes_after_return, |
2872 | &data) |
2873 | == Success) |
2874 | { |
2875 | if ((type_return == XA_WINDOW) && (format_return == 32) && (data)) |
2876 | { |
2877 | nvroots = nitems_return; |
2878 | vroots = (Window *)data; |
2879 | } |
2880 | } |
2881 | |
2882 | xparent = GDK_SURFACE_XID (surface); |
2883 | |
2884 | do |
2885 | { |
2886 | xwindow = xparent; |
2887 | |
2888 | if (!XQueryTree (GDK_DISPLAY_XDISPLAY (display), xwindow, |
2889 | &root, &xparent, |
2890 | &children, &nchildren)) |
2891 | goto out; |
2892 | |
2893 | if (children) |
2894 | XFree (children); |
2895 | |
2896 | /* check virtual roots */ |
2897 | for (i = 0; i < nvroots; i++) |
2898 | { |
2899 | if (xparent == vroots[i]) |
2900 | { |
2901 | root = xparent; |
2902 | break; |
2903 | } |
2904 | } |
2905 | } |
2906 | while (xparent != root); |
2907 | |
2908 | if (XGetGeometry (GDK_DISPLAY_XDISPLAY (display), xwindow, |
2909 | &root, &wx, &wy, &ww, &wh, &wb, &wd)) |
2910 | { |
2911 | rect->x = wx; |
2912 | rect->y = wy; |
2913 | rect->width = ww; |
2914 | rect->height = wh; |
2915 | } |
2916 | |
2917 | out: |
2918 | if (vroots) |
2919 | XFree (vroots); |
2920 | |
2921 | /* Here we extend the size to include the extra pixels if we round x/y down |
2922 | as well as round the size up when we divide by scale so that the returned |
2923 | size is guaranteed to cover the real pixels, but it may overshoot a bit |
2924 | in case the window is not positioned/sized according to the scale */ |
2925 | rect->width = (rect->width + rect->x % impl->surface_scale + impl->surface_scale - 1) / impl->surface_scale; |
2926 | rect->height = (rect->height + rect->y % impl->surface_scale + impl->surface_scale - 1) / impl->surface_scale; |
2927 | rect->x = rect->x / impl->surface_scale; |
2928 | rect->y = rect->y / impl->surface_scale; |
2929 | gdk_x11_display_error_trap_pop_ignored (display); |
2930 | } |
2931 | |
2932 | static gboolean |
2933 | gdk_x11_surface_get_device_state (GdkSurface *surface, |
2934 | GdkDevice *device, |
2935 | double *x, |
2936 | double *y, |
2937 | GdkModifierType *mask) |
2938 | { |
2939 | if (GDK_SURFACE_DESTROYED (surface)) |
2940 | return FALSE; |
2941 | |
2942 | gdk_x11_device_xi2_query_state (device, surface, win_x: x, win_y: y, mask); |
2943 | |
2944 | return *x >= 0 && *y >= 0 && *x < surface->width && *y < surface->height; |
2945 | } |
2946 | |
2947 | static void |
2948 | gdk_x11_surface_set_input_region (GdkSurface *surface, |
2949 | cairo_region_t *input_region) |
2950 | { |
2951 | #ifdef ShapeInput |
2952 | GdkX11Surface *impl = GDK_X11_SURFACE (surface); |
2953 | |
2954 | if (GDK_SURFACE_DESTROYED (surface)) |
2955 | return; |
2956 | |
2957 | if (!gdk_display_supports_input_shapes (GDK_SURFACE_DISPLAY (surface))) |
2958 | return; |
2959 | |
2960 | if (input_region == NULL) |
2961 | { |
2962 | XShapeCombineMask (GDK_SURFACE_XDISPLAY (surface), |
2963 | GDK_SURFACE_XID (surface), |
2964 | ShapeInput, |
2965 | 0, 0, |
2966 | None, |
2967 | ShapeSet); |
2968 | return; |
2969 | } |
2970 | else |
2971 | { |
2972 | int n_rects = 0; |
2973 | XRectangle *xrects = NULL; |
2974 | |
2975 | _gdk_x11_region_get_xrectangles (region: input_region, |
2976 | x_offset: 0, y_offset: 0, scale: impl->surface_scale, |
2977 | rects: &xrects, n_rects: &n_rects); |
2978 | |
2979 | XShapeCombineRectangles (GDK_SURFACE_XDISPLAY (surface), |
2980 | GDK_SURFACE_XID (surface), |
2981 | ShapeInput, |
2982 | 0, 0, |
2983 | xrects, n_rects, |
2984 | ShapeSet, |
2985 | YXBanded); |
2986 | |
2987 | g_free (mem: xrects); |
2988 | } |
2989 | #endif |
2990 | } |
2991 | |
2992 | /** |
2993 | * gdk_x11_surface_set_user_time: |
2994 | * @surface: (type GdkX11Surface): A toplevel `GdkSurface` |
2995 | * @timestamp: An XServer timestamp to which the property should be set |
2996 | * |
2997 | * The application can use this call to update the _NET_WM_USER_TIME |
2998 | * property on a toplevel surface. This property stores an Xserver |
2999 | * time which represents the time of the last user input event |
3000 | * received for this surface. This property may be used by the window |
3001 | * manager to alter the focus, stacking, and/or placement behavior of |
3002 | * surfaces when they are mapped depending on whether the new surface |
3003 | * was created by a user action or is a "pop-up" surface activated by a |
3004 | * timer or some other event. |
3005 | * |
3006 | * Note that this property is automatically updated by GDK, so this |
3007 | * function should only be used by applications which handle input |
3008 | * events bypassing GDK. |
3009 | **/ |
3010 | void |
3011 | gdk_x11_surface_set_user_time (GdkSurface *surface, |
3012 | guint32 timestamp) |
3013 | { |
3014 | GdkDisplay *display; |
3015 | GdkX11Display *display_x11; |
3016 | GdkToplevelX11 *toplevel; |
3017 | glong timestamp_long = (glong)timestamp; |
3018 | Window xid; |
3019 | |
3020 | if (GDK_SURFACE_DESTROYED (surface)) |
3021 | return; |
3022 | |
3023 | display = gdk_surface_get_display (surface); |
3024 | display_x11 = GDK_X11_DISPLAY (display); |
3025 | toplevel = _gdk_x11_surface_get_toplevel (surface); |
3026 | |
3027 | if (!toplevel) |
3028 | { |
3029 | g_warning ("gdk_surface_set_user_time called on non-toplevel\n" ); |
3030 | return; |
3031 | } |
3032 | |
3033 | if (toplevel->focus_window != None && |
3034 | gdk_x11_screen_supports_net_wm_hint (GDK_SURFACE_SCREEN (surface), |
3035 | property_name: g_intern_static_string (string: "_NET_WM_USER_TIME_WINDOW" ))) |
3036 | xid = toplevel->focus_window; |
3037 | else |
3038 | xid = GDK_SURFACE_XID (surface); |
3039 | |
3040 | XChangeProperty (GDK_DISPLAY_XDISPLAY (display), xid, |
3041 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_USER_TIME" ), |
3042 | XA_CARDINAL, 32, PropModeReplace, |
3043 | (guchar *)×tamp_long, 1); |
3044 | |
3045 | if (timestamp_long != GDK_CURRENT_TIME && |
3046 | (display_x11->user_time == GDK_CURRENT_TIME || |
3047 | XSERVER_TIME_IS_LATER (timestamp_long, display_x11->user_time))) |
3048 | display_x11->user_time = timestamp_long; |
3049 | |
3050 | if (toplevel) |
3051 | toplevel->user_time = timestamp_long; |
3052 | } |
3053 | |
3054 | /** |
3055 | * gdk_x11_surface_set_utf8_property: |
3056 | * @surface: (type GdkX11Surface): a `GdkSurface` |
3057 | * @name: Property name, will be interned as an X atom |
3058 | * @value: (nullable): Property value, or %NULL to delete |
3059 | * |
3060 | * This function modifies or removes an arbitrary X11 window |
3061 | * property of type UTF8_STRING. If the given @surface is |
3062 | * not a toplevel surface, it is ignored. |
3063 | */ |
3064 | void |
3065 | gdk_x11_surface_set_utf8_property (GdkSurface *surface, |
3066 | const char *name, |
3067 | const char *value) |
3068 | { |
3069 | GdkDisplay *display; |
3070 | |
3071 | display = gdk_surface_get_display (surface); |
3072 | |
3073 | if (value != NULL) |
3074 | { |
3075 | XChangeProperty (GDK_DISPLAY_XDISPLAY (display), |
3076 | GDK_SURFACE_XID (surface), |
3077 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: name), |
3078 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: "UTF8_STRING" ), 8, |
3079 | PropModeReplace, (guchar *)value, strlen (s: value)); |
3080 | } |
3081 | else |
3082 | { |
3083 | XDeleteProperty (GDK_DISPLAY_XDISPLAY (display), |
3084 | GDK_SURFACE_XID (surface), |
3085 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: name)); |
3086 | } |
3087 | } |
3088 | |
3089 | /** |
3090 | * gdk_x11_surface_set_theme_variant: |
3091 | * @surface: (type GdkX11Surface): a `GdkSurface` |
3092 | * @variant: the theme variant to export |
3093 | * |
3094 | * GTK applications can request a dark theme variant. In order to |
3095 | * make other applications - namely window managers using GTK for |
3096 | * themeing - aware of this choice, GTK uses this function to |
3097 | * export the requested theme variant as _GTK_THEME_VARIANT property |
3098 | * on toplevel surfaces. |
3099 | * |
3100 | * Note that this property is automatically updated by GTK, so this |
3101 | * function should only be used by applications which do not use GTK |
3102 | * to create toplevel surfaces. |
3103 | */ |
3104 | void |
3105 | gdk_x11_surface_set_theme_variant (GdkSurface *surface, |
3106 | const char *variant) |
3107 | { |
3108 | gdk_x11_surface_set_utf8_property (surface, name: "_GTK_THEME_VARIANT" , |
3109 | value: variant ? variant : "" ); |
3110 | } |
3111 | |
3112 | #define GDK_SELECTION_MAX_SIZE(display) \ |
3113 | MIN(262144, \ |
3114 | XExtendedMaxRequestSize (GDK_DISPLAY_XDISPLAY (display)) == 0 \ |
3115 | ? XMaxRequestSize (GDK_DISPLAY_XDISPLAY (display)) - 100 \ |
3116 | : XExtendedMaxRequestSize (GDK_DISPLAY_XDISPLAY (display)) - 100) |
3117 | |
3118 | static void |
3119 | gdk_surface_update_icon (GdkSurface *surface, |
3120 | GList *icon_list) |
3121 | { |
3122 | GdkToplevelX11 *toplevel; |
3123 | GdkTexture *best_icon; |
3124 | GList *tmp_list; |
3125 | int best_size; |
3126 | |
3127 | toplevel = _gdk_x11_surface_get_toplevel (surface); |
3128 | |
3129 | if (toplevel->icon_pixmap != NULL) |
3130 | { |
3131 | cairo_surface_destroy (surface: toplevel->icon_pixmap); |
3132 | toplevel->icon_pixmap = NULL; |
3133 | } |
3134 | |
3135 | if (toplevel->icon_mask != NULL) |
3136 | { |
3137 | cairo_surface_destroy (surface: toplevel->icon_mask); |
3138 | toplevel->icon_mask = NULL; |
3139 | } |
3140 | |
3141 | #define IDEAL_SIZE 48 |
3142 | |
3143 | best_size = G_MAXINT; |
3144 | best_icon = NULL; |
3145 | for (tmp_list = icon_list; tmp_list; tmp_list = tmp_list->next) |
3146 | { |
3147 | GdkTexture *texture = tmp_list->data; |
3148 | int this; |
3149 | |
3150 | /* average width and height - if someone passes in a rectangular |
3151 | * icon they deserve what they get. |
3152 | */ |
3153 | this = gdk_texture_get_width (texture) + gdk_texture_get_height (texture); |
3154 | this /= 2; |
3155 | |
3156 | if (best_icon == NULL) |
3157 | { |
3158 | best_icon = texture; |
3159 | best_size = this; |
3160 | } |
3161 | else |
3162 | { |
3163 | /* icon is better if it's 32 pixels or larger, and closer to |
3164 | * the ideal size than the current best. |
3165 | */ |
3166 | if (this >= 32 && |
3167 | (ABS (best_size - IDEAL_SIZE) < |
3168 | ABS (this - IDEAL_SIZE))) |
3169 | { |
3170 | best_icon = texture; |
3171 | best_size = this; |
3172 | } |
3173 | } |
3174 | } |
3175 | |
3176 | if (best_icon) |
3177 | { |
3178 | int width = gdk_texture_get_width (texture: best_icon); |
3179 | int height = gdk_texture_get_height (texture: best_icon); |
3180 | cairo_surface_t *cairo_surface; |
3181 | cairo_t *cr; |
3182 | |
3183 | toplevel->icon_pixmap = gdk_x11_surface_create_pixmap_surface (surface, width, height); |
3184 | |
3185 | cairo_surface = gdk_texture_download_surface (texture: best_icon); |
3186 | |
3187 | cr = cairo_create (target: toplevel->icon_pixmap); |
3188 | cairo_set_operator (cr, op: CAIRO_OPERATOR_SOURCE); |
3189 | cairo_set_source_surface (cr, surface: cairo_surface, x: 0, y: 0); |
3190 | if (cairo_surface_get_content (surface: cairo_surface) == CAIRO_CONTENT_COLOR_ALPHA) |
3191 | { |
3192 | /* Saturate the image, so it has bilevel alpha */ |
3193 | cairo_push_group_with_content (cr, content: CAIRO_CONTENT_COLOR_ALPHA); |
3194 | cairo_paint (cr); |
3195 | cairo_set_operator (cr, op: CAIRO_OPERATOR_SATURATE); |
3196 | cairo_paint (cr); |
3197 | cairo_pop_group_to_source (cr); |
3198 | } |
3199 | cairo_paint (cr); |
3200 | cairo_destroy (cr); |
3201 | |
3202 | if (cairo_surface_get_content (surface: cairo_surface) == CAIRO_CONTENT_COLOR_ALPHA) |
3203 | { |
3204 | GdkDisplay *display = gdk_surface_get_display (surface); |
3205 | |
3206 | toplevel->icon_mask = _gdk_x11_display_create_bitmap_surface (display, width, height); |
3207 | |
3208 | cr = cairo_create (target: toplevel->icon_mask); |
3209 | cairo_set_source_surface (cr, surface: cairo_surface, x: 0, y: 0); |
3210 | cairo_set_operator (cr, op: CAIRO_OPERATOR_SOURCE); |
3211 | cairo_paint (cr); |
3212 | cairo_destroy (cr); |
3213 | } |
3214 | |
3215 | cairo_surface_destroy (surface: cairo_surface); |
3216 | } |
3217 | |
3218 | update_wm_hints (surface, FALSE); |
3219 | } |
3220 | |
3221 | static void |
3222 | gdk_x11_surface_set_icon_list (GdkSurface *surface, |
3223 | GList *textures) |
3224 | { |
3225 | gulong *data; |
3226 | gulong *p; |
3227 | int size; |
3228 | GList *l; |
3229 | int width, height; |
3230 | GdkTexture *texture; |
3231 | GdkDisplay *display; |
3232 | int i, n; |
3233 | |
3234 | if (GDK_SURFACE_DESTROYED (surface)) |
3235 | return; |
3236 | |
3237 | display = gdk_surface_get_display (surface); |
3238 | |
3239 | size = 0; |
3240 | n = 0; |
3241 | for (l = textures; l != NULL; l = l->next) |
3242 | { |
3243 | texture = l->data; |
3244 | |
3245 | width = gdk_texture_get_width (texture); |
3246 | height = gdk_texture_get_height (texture); |
3247 | |
3248 | /* silently ignore overlarge icons */ |
3249 | if (size + 2 + width * height > GDK_SELECTION_MAX_SIZE(display)) |
3250 | break; |
3251 | |
3252 | n++; |
3253 | size += 2 + width * height; |
3254 | } |
3255 | |
3256 | data = g_malloc (n_bytes: size * sizeof (gulong)); |
3257 | |
3258 | p = data; |
3259 | for (l = textures; l != NULL && n > 0; l = l->next) |
3260 | { |
3261 | texture = l->data; |
3262 | |
3263 | width = gdk_texture_get_width (texture); |
3264 | height = gdk_texture_get_height (texture); |
3265 | |
3266 | *p++ = width; |
3267 | *p++ = height; |
3268 | |
3269 | gdk_texture_download (texture, data: (guchar *) p, stride: width * 4); |
3270 | if (sizeof (gulong) > 4) |
3271 | { |
3272 | i = width * height; |
3273 | while (i-- > 0) |
3274 | p[i] = ((guint32 *) p)[i]; |
3275 | } |
3276 | |
3277 | p += width * height; |
3278 | n--; |
3279 | } |
3280 | |
3281 | if (size > 0) |
3282 | { |
3283 | XChangeProperty (GDK_DISPLAY_XDISPLAY (display), |
3284 | GDK_SURFACE_XID (surface), |
3285 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_ICON" ), |
3286 | XA_CARDINAL, 32, |
3287 | PropModeReplace, |
3288 | (guchar*) data, size); |
3289 | } |
3290 | else |
3291 | { |
3292 | XDeleteProperty (GDK_DISPLAY_XDISPLAY (display), |
3293 | GDK_SURFACE_XID (surface), |
3294 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_ICON" )); |
3295 | } |
3296 | |
3297 | g_free (mem: data); |
3298 | |
3299 | gdk_surface_update_icon (surface, icon_list: textures); |
3300 | } |
3301 | |
3302 | static gboolean |
3303 | gdk_surface_icon_name_set (GdkSurface *surface) |
3304 | { |
3305 | return GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (surface), |
3306 | g_quark_from_static_string ("gdk-icon-name-set" ))); |
3307 | } |
3308 | |
3309 | static void |
3310 | gdk_x11_surface_minimize (GdkSurface *surface) |
3311 | { |
3312 | if (GDK_SURFACE_DESTROYED (surface)) |
3313 | return; |
3314 | |
3315 | if (GDK_SURFACE_IS_MAPPED (surface)) |
3316 | { |
3317 | XIconifyWindow (GDK_SURFACE_XDISPLAY (surface), |
3318 | GDK_SURFACE_XID (surface), |
3319 | gdk_x11_screen_get_screen_number (GDK_SURFACE_SCREEN (surface))); |
3320 | } |
3321 | else |
3322 | { |
3323 | /* Flip our client side flag, the real work happens on map. */ |
3324 | gdk_synthesize_surface_state (surface, unset_flags: 0, set_flags: GDK_TOPLEVEL_STATE_MINIMIZED); |
3325 | gdk_wmspec_change_state (TRUE, surface, |
3326 | state1: "_NET_WM_STATE_HIDDEN" , |
3327 | NULL); |
3328 | } |
3329 | } |
3330 | |
3331 | static void |
3332 | gdk_x11_surface_unminimize (GdkSurface *surface) |
3333 | { |
3334 | if (GDK_SURFACE_DESTROYED (surface)) |
3335 | return; |
3336 | |
3337 | if (GDK_SURFACE_IS_MAPPED (surface)) |
3338 | { |
3339 | gdk_x11_surface_show (surface, TRUE); |
3340 | gdk_wmspec_change_state (FALSE, surface, |
3341 | state1: "_NET_WM_STATE_HIDDEN" , |
3342 | NULL); |
3343 | } |
3344 | else |
3345 | { |
3346 | /* Flip our client side flag, the real work happens on map. */ |
3347 | gdk_synthesize_surface_state (surface, unset_flags: GDK_TOPLEVEL_STATE_MINIMIZED, set_flags: 0); |
3348 | gdk_wmspec_change_state (FALSE, surface, |
3349 | state1: "_NET_WM_STATE_HIDDEN" , |
3350 | NULL); |
3351 | } |
3352 | } |
3353 | |
3354 | static void |
3355 | gdk_x11_surface_maximize (GdkSurface *surface) |
3356 | { |
3357 | if (GDK_SURFACE_DESTROYED (surface)) |
3358 | return; |
3359 | |
3360 | if (GDK_SURFACE_IS_MAPPED (surface)) |
3361 | gdk_wmspec_change_state (TRUE, surface, |
3362 | state1: "_NET_WM_STATE_MAXIMIZED_VERT" , |
3363 | state2: "_NET_WM_STATE_MAXIMIZED_HORZ" ); |
3364 | else |
3365 | gdk_synthesize_surface_state (surface, |
3366 | unset_flags: 0, |
3367 | set_flags: GDK_TOPLEVEL_STATE_MAXIMIZED); |
3368 | } |
3369 | |
3370 | static void |
3371 | gdk_x11_surface_unmaximize (GdkSurface *surface) |
3372 | { |
3373 | if (GDK_SURFACE_DESTROYED (surface)) |
3374 | return; |
3375 | |
3376 | if (GDK_SURFACE_IS_MAPPED (surface)) |
3377 | gdk_wmspec_change_state (FALSE, surface, |
3378 | state1: "_NET_WM_STATE_MAXIMIZED_VERT" , |
3379 | state2: "_NET_WM_STATE_MAXIMIZED_HORZ" ); |
3380 | else |
3381 | gdk_synthesize_surface_state (surface, |
3382 | unset_flags: GDK_TOPLEVEL_STATE_MAXIMIZED, |
3383 | set_flags: 0); |
3384 | } |
3385 | |
3386 | static void |
3387 | gdk_x11_surface_apply_fullscreen_mode (GdkSurface *surface) |
3388 | { |
3389 | if (GDK_SURFACE_DESTROYED (surface)) |
3390 | return; |
3391 | |
3392 | /* _NET_WM_FULLSCREEN_MONITORS gives an indication to the window manager as |
3393 | * to which monitors so span across when the surface is fullscreen, but it's |
3394 | * not a state in itself so this would have no effect if the surface is not |
3395 | * mapped. |
3396 | */ |
3397 | |
3398 | if (GDK_SURFACE_IS_MAPPED (surface)) |
3399 | { |
3400 | XClientMessageEvent xclient; |
3401 | int monitors[4]; |
3402 | int i; |
3403 | |
3404 | memset (s: &xclient, c: 0, n: sizeof (xclient)); |
3405 | xclient.type = ClientMessage; |
3406 | xclient.window = GDK_SURFACE_XID (surface); |
3407 | xclient.display = GDK_SURFACE_XDISPLAY (surface); |
3408 | xclient.format = 32; |
3409 | |
3410 | switch (surface->fullscreen_mode) |
3411 | { |
3412 | case GDK_FULLSCREEN_ON_CURRENT_MONITOR: |
3413 | |
3414 | /* FIXME: This is not part of the EWMH spec! |
3415 | * |
3416 | * There is no documented mechanism to remove the property |
3417 | * _NET_WM_FULLSCREEN_MONITORS once set, so we use a set of |
3418 | * invalid, largest possible value. |
3419 | * |
3420 | * When given values larger than actual possible monitor values, most |
3421 | * window managers who support the _NET_WM_FULLSCREEN_MONITORS spec |
3422 | * will simply unset _NET_WM_FULLSCREEN_MONITORS and revert to their |
3423 | * default behavior. |
3424 | * |
3425 | * Successfully tested on mutter/metacity, kwin, compiz and xfwm4. |
3426 | * |
3427 | * Note, this (non documented) mechanism is unlikely to be an issue |
3428 | * as it's used only for transitionning back from "all monitors" to |
3429 | * "current monitor" mode. |
3430 | * |
3431 | * Applications who don't change the default mode won't trigger this |
3432 | * mechanism. |
3433 | */ |
3434 | for (i = 0; i < 4; ++i) |
3435 | xclient.data.l[i] = G_MAXLONG; |
3436 | |
3437 | break; |
3438 | |
3439 | case GDK_FULLSCREEN_ON_ALL_MONITORS: |
3440 | |
3441 | _gdk_x11_screen_get_edge_monitors (GDK_SURFACE_SCREEN (surface), |
3442 | top: &monitors[0], |
3443 | bottom: &monitors[1], |
3444 | left: &monitors[2], |
3445 | right: &monitors[3]); |
3446 | /* Translate all 4 monitors from the GDK set into XINERAMA indices */ |
3447 | for (i = 0; i < 4; ++i) |
3448 | { |
3449 | xclient.data.l[i] = monitors[i]; |
3450 | /* Sanity check, if XINERAMA is not available, we could have invalid |
3451 | * negative values for the XINERAMA indices. |
3452 | */ |
3453 | if (xclient.data.l[i] < 0) |
3454 | { |
3455 | g_warning ("gdk_x11_surface_apply_fullscreen_mode: Invalid XINERAMA monitor index" ); |
3456 | return; |
3457 | } |
3458 | } |
3459 | break; |
3460 | |
3461 | default: |
3462 | g_warning ("gdk_x11_surface_apply_fullscreen_mode: Unhandled fullscreen mode %d" , |
3463 | surface->fullscreen_mode); |
3464 | return; |
3465 | } |
3466 | |
3467 | /* Send fullscreen monitors client message */ |
3468 | xclient.data.l[4] = 1; /* source indication */ |
3469 | xclient.message_type = gdk_x11_get_xatom_by_name_for_display (GDK_SURFACE_DISPLAY (surface), |
3470 | atom_name: "_NET_WM_FULLSCREEN_MONITORS" ); |
3471 | XSendEvent (GDK_SURFACE_XDISPLAY (surface), GDK_SURFACE_XROOTWIN (surface), False, |
3472 | SubstructureRedirectMask | SubstructureNotifyMask, |
3473 | (XEvent *)&xclient); |
3474 | } |
3475 | } |
3476 | |
3477 | static void |
3478 | gdk_x11_surface_fullscreen (GdkSurface *surface) |
3479 | { |
3480 | if (GDK_SURFACE_DESTROYED (surface)) |
3481 | return; |
3482 | |
3483 | if (GDK_SURFACE_IS_MAPPED (surface)) |
3484 | { |
3485 | gdk_wmspec_change_state (TRUE, surface, |
3486 | state1: "_NET_WM_STATE_FULLSCREEN" , |
3487 | NULL); |
3488 | /* Actual XRandR layout may have change since we computed the fullscreen |
3489 | * monitors in GDK_FULLSCREEN_ON_ALL_MONITORS mode. |
3490 | */ |
3491 | if (surface->fullscreen_mode == GDK_FULLSCREEN_ON_ALL_MONITORS) |
3492 | gdk_x11_surface_apply_fullscreen_mode (surface); |
3493 | } |
3494 | else |
3495 | gdk_synthesize_surface_state (surface, |
3496 | unset_flags: 0, |
3497 | set_flags: GDK_TOPLEVEL_STATE_FULLSCREEN); |
3498 | } |
3499 | |
3500 | static void |
3501 | gdk_x11_surface_fullscreen_on_monitor (GdkSurface *surface, |
3502 | GdkMonitor *monitor) |
3503 | { |
3504 | GdkRectangle geom; |
3505 | |
3506 | if (GDK_SURFACE_DESTROYED (surface)) |
3507 | return; |
3508 | |
3509 | gdk_monitor_get_geometry (monitor, geometry: &geom); |
3510 | gdk_x11_surface_move (surface, x: geom.x, y: geom.y); |
3511 | |
3512 | surface->fullscreen_mode = GDK_FULLSCREEN_ON_CURRENT_MONITOR; |
3513 | g_object_notify (G_OBJECT (surface), property_name: "fullscreen-mode" ); |
3514 | gdk_x11_surface_fullscreen (surface); |
3515 | } |
3516 | |
3517 | static void |
3518 | gdk_x11_surface_unfullscreen (GdkSurface *surface) |
3519 | { |
3520 | if (GDK_SURFACE_DESTROYED (surface)) |
3521 | return; |
3522 | |
3523 | if (GDK_SURFACE_IS_MAPPED (surface)) |
3524 | gdk_wmspec_change_state (FALSE, surface, |
3525 | state1: "_NET_WM_STATE_FULLSCREEN" , |
3526 | NULL); |
3527 | |
3528 | else |
3529 | gdk_synthesize_surface_state (surface, |
3530 | unset_flags: GDK_TOPLEVEL_STATE_FULLSCREEN, |
3531 | set_flags: 0); |
3532 | } |
3533 | |
3534 | /** |
3535 | * gdk_x11_surface_get_group: |
3536 | * @surface: (type GdkX11Surface): The `GdkSurface` |
3537 | * |
3538 | * Returns the group this surface belongs to. |
3539 | * |
3540 | * Returns: (transfer none) (nullable): The group of this surface; |
3541 | */ |
3542 | GdkSurface * |
3543 | gdk_x11_surface_get_group (GdkSurface *surface) |
3544 | { |
3545 | GdkToplevelX11 *toplevel; |
3546 | |
3547 | if (GDK_SURFACE_DESTROYED (surface)) |
3548 | return NULL; |
3549 | |
3550 | toplevel = _gdk_x11_surface_get_toplevel (surface); |
3551 | |
3552 | return toplevel->group_leader; |
3553 | } |
3554 | |
3555 | /** |
3556 | * gdk_x11_surface_set_group: |
3557 | * @surface: (type GdkX11Surface): a native `GdkSurface` |
3558 | * @leader: a `GdkSurface` |
3559 | * |
3560 | * Sets the group leader of @surface to be @leader. |
3561 | * See the ICCCM for details. |
3562 | */ |
3563 | void |
3564 | gdk_x11_surface_set_group (GdkSurface *surface, |
3565 | GdkSurface *leader) |
3566 | { |
3567 | GdkToplevelX11 *toplevel; |
3568 | |
3569 | g_return_if_fail (GDK_IS_SURFACE (surface)); |
3570 | g_return_if_fail (leader == NULL || GDK_IS_SURFACE (leader)); |
3571 | |
3572 | if (GDK_SURFACE_DESTROYED (surface) || |
3573 | (leader != NULL && GDK_SURFACE_DESTROYED (leader))) |
3574 | return; |
3575 | |
3576 | toplevel = _gdk_x11_surface_get_toplevel (surface); |
3577 | |
3578 | if (leader == NULL) |
3579 | leader = gdk_x11_display_get_default_group (display: gdk_surface_get_display (surface)); |
3580 | |
3581 | if (toplevel->group_leader != leader) |
3582 | { |
3583 | if (toplevel->group_leader) |
3584 | g_object_unref (object: toplevel->group_leader); |
3585 | toplevel->group_leader = g_object_ref (leader); |
3586 | (_gdk_x11_surface_get_toplevel (surface: leader))->is_leader = TRUE; |
3587 | } |
3588 | |
3589 | update_wm_hints (surface, FALSE); |
3590 | } |
3591 | |
3592 | static MotifWmHints * |
3593 | gdk_surface_get_mwm_hints (GdkSurface *surface) |
3594 | { |
3595 | GdkDisplay *display; |
3596 | Atom hints_atom = None; |
3597 | guchar *data; |
3598 | Atom type; |
3599 | int format; |
3600 | gulong nitems; |
3601 | gulong bytes_after; |
3602 | |
3603 | if (GDK_SURFACE_DESTROYED (surface)) |
3604 | return NULL; |
3605 | |
3606 | display = gdk_surface_get_display (surface); |
3607 | |
3608 | hints_atom = gdk_x11_get_xatom_by_name_for_display (display, _XA_MOTIF_WM_HINTS); |
3609 | |
3610 | XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XID (surface), |
3611 | hints_atom, 0, sizeof (MotifWmHints)/sizeof (long), |
3612 | False, AnyPropertyType, &type, &format, &nitems, |
3613 | &bytes_after, &data); |
3614 | |
3615 | if (type == None) |
3616 | return NULL; |
3617 | |
3618 | return (MotifWmHints *)data; |
3619 | } |
3620 | |
3621 | static void |
3622 | gdk_surface_set_mwm_hints (GdkSurface *surface, |
3623 | MotifWmHints *new_hints) |
3624 | { |
3625 | GdkDisplay *display; |
3626 | Atom hints_atom = None; |
3627 | guchar *data; |
3628 | MotifWmHints *hints; |
3629 | Atom type; |
3630 | int format; |
3631 | gulong nitems; |
3632 | gulong bytes_after; |
3633 | |
3634 | if (GDK_SURFACE_DESTROYED (surface)) |
3635 | return; |
3636 | |
3637 | display = gdk_surface_get_display (surface); |
3638 | |
3639 | hints_atom = gdk_x11_get_xatom_by_name_for_display (display, _XA_MOTIF_WM_HINTS); |
3640 | |
3641 | XGetWindowProperty (GDK_SURFACE_XDISPLAY (surface), GDK_SURFACE_XID (surface), |
3642 | hints_atom, 0, sizeof (MotifWmHints)/sizeof (long), |
3643 | False, AnyPropertyType, &type, &format, &nitems, |
3644 | &bytes_after, &data); |
3645 | |
3646 | if (type == None) |
3647 | hints = new_hints; |
3648 | else |
3649 | { |
3650 | hints = (MotifWmHints *)data; |
3651 | |
3652 | if (new_hints->flags & MWM_HINTS_FUNCTIONS) |
3653 | { |
3654 | hints->flags |= MWM_HINTS_FUNCTIONS; |
3655 | hints->functions = new_hints->functions; |
3656 | } |
3657 | if (new_hints->flags & MWM_HINTS_DECORATIONS) |
3658 | { |
3659 | hints->flags |= MWM_HINTS_DECORATIONS; |
3660 | hints->decorations = new_hints->decorations; |
3661 | } |
3662 | } |
3663 | |
3664 | XChangeProperty (GDK_SURFACE_XDISPLAY (surface), GDK_SURFACE_XID (surface), |
3665 | hints_atom, hints_atom, 32, PropModeReplace, |
3666 | (guchar *)hints, sizeof (MotifWmHints)/sizeof (long)); |
3667 | |
3668 | if (hints != new_hints) |
3669 | XFree (hints); |
3670 | } |
3671 | |
3672 | typedef enum |
3673 | { |
3674 | GDK_DECOR_ALL = 1 << 0, |
3675 | GDK_DECOR_BORDER = 1 << 1, |
3676 | GDK_DECOR_RESIZEH = 1 << 2, |
3677 | GDK_DECOR_TITLE = 1 << 3, |
3678 | = 1 << 4, |
3679 | GDK_DECOR_MINIMIZE = 1 << 5, |
3680 | GDK_DECOR_MAXIMIZE = 1 << 6 |
3681 | } GdkWMDecoration; |
3682 | |
3683 | static void |
3684 | gdk_x11_surface_set_decorations (GdkSurface *surface, |
3685 | GdkWMDecoration decorations) |
3686 | { |
3687 | MotifWmHints hints; |
3688 | |
3689 | if (GDK_SURFACE_DESTROYED (surface)) |
3690 | return; |
3691 | |
3692 | /* initialize to zero to avoid writing uninitialized data to socket */ |
3693 | memset(s: &hints, c: 0, n: sizeof(hints)); |
3694 | hints.flags = MWM_HINTS_DECORATIONS; |
3695 | hints.decorations = decorations; |
3696 | |
3697 | gdk_surface_set_mwm_hints (surface, new_hints: &hints); |
3698 | } |
3699 | |
3700 | static gboolean |
3701 | gdk_x11_surface_get_decorations(GdkSurface *surface, |
3702 | GdkWMDecoration *decorations) |
3703 | { |
3704 | MotifWmHints *hints; |
3705 | gboolean result = FALSE; |
3706 | |
3707 | if (GDK_SURFACE_DESTROYED (surface)) |
3708 | return FALSE; |
3709 | |
3710 | hints = gdk_surface_get_mwm_hints (surface); |
3711 | |
3712 | if (hints) |
3713 | { |
3714 | if (hints->flags & MWM_HINTS_DECORATIONS) |
3715 | { |
3716 | if (decorations) |
3717 | *decorations = hints->decorations; |
3718 | result = TRUE; |
3719 | } |
3720 | |
3721 | XFree (hints); |
3722 | } |
3723 | |
3724 | return result; |
3725 | } |
3726 | |
3727 | typedef enum |
3728 | { |
3729 | GDK_FUNC_ALL = 1 << 0, |
3730 | GDK_FUNC_RESIZE = 1 << 1, |
3731 | GDK_FUNC_MOVE = 1 << 2, |
3732 | GDK_FUNC_MINIMIZE = 1 << 3, |
3733 | GDK_FUNC_MAXIMIZE = 1 << 4, |
3734 | GDK_FUNC_CLOSE = 1 << 5 |
3735 | } GdkWMFunction; |
3736 | |
3737 | static void |
3738 | gdk_x11_surface_set_functions (GdkSurface *surface, |
3739 | GdkWMFunction functions) |
3740 | { |
3741 | MotifWmHints hints; |
3742 | |
3743 | g_return_if_fail (GDK_IS_SURFACE (surface)); |
3744 | |
3745 | if (GDK_SURFACE_DESTROYED (surface)) |
3746 | return; |
3747 | |
3748 | /* initialize to zero to avoid writing uninitialized data to socket */ |
3749 | memset(s: &hints, c: 0, n: sizeof(hints)); |
3750 | hints.flags = MWM_HINTS_FUNCTIONS; |
3751 | hints.functions = functions; |
3752 | |
3753 | gdk_surface_set_mwm_hints (surface, new_hints: &hints); |
3754 | } |
3755 | |
3756 | static gboolean |
3757 | gdk_x11_surface_get_functions (GdkSurface *surface, |
3758 | GdkWMFunction *functions) |
3759 | { |
3760 | MotifWmHints *hints; |
3761 | gboolean result = FALSE; |
3762 | |
3763 | if (GDK_SURFACE_DESTROYED (surface)) |
3764 | return FALSE; |
3765 | |
3766 | hints = gdk_surface_get_mwm_hints (surface); |
3767 | |
3768 | if (hints) |
3769 | { |
3770 | if (hints->flags & MWM_HINTS_DECORATIONS) |
3771 | { |
3772 | if (functions) |
3773 | *functions = hints->functions; |
3774 | result = TRUE; |
3775 | } |
3776 | |
3777 | XFree (hints); |
3778 | } |
3779 | |
3780 | return result; |
3781 | } |
3782 | |
3783 | cairo_region_t * |
3784 | _gdk_x11_xwindow_get_shape (Display *xdisplay, |
3785 | Window window, |
3786 | int scale, |
3787 | int shape_type) |
3788 | { |
3789 | cairo_region_t *shape; |
3790 | GdkRectangle *rl; |
3791 | XRectangle *xrl; |
3792 | int rn, ord, i; |
3793 | |
3794 | shape = NULL; |
3795 | rn = 0; |
3796 | |
3797 | /* Note that XShapeGetRectangles returns NULL in two situations: |
3798 | * - the server doesn't support the SHAPE extension |
3799 | * - the shape is empty |
3800 | * |
3801 | * Since we can't discriminate these here, we always return |
3802 | * an empty shape. It is the callers responsibility to check |
3803 | * whether the server supports the SHAPE extensions beforehand. |
3804 | */ |
3805 | xrl = XShapeGetRectangles (xdisplay, window, shape_type, &rn, &ord); |
3806 | |
3807 | if (rn == 0) |
3808 | return cairo_region_create (); /* Empty */ |
3809 | |
3810 | if (ord != YXBanded) |
3811 | { |
3812 | /* This really shouldn't happen with any xserver, as they |
3813 | * generally convert regions to YXBanded internally |
3814 | */ |
3815 | g_warning ("non YXBanded shape masks not supported" ); |
3816 | XFree (xrl); |
3817 | return NULL; |
3818 | } |
3819 | |
3820 | /* NOTE: The scale divisions here may lose some precision if someone |
3821 | else set the shape to be non-scale precision */ |
3822 | rl = g_new (GdkRectangle, rn); |
3823 | for (i = 0; i < rn; i++) |
3824 | { |
3825 | rl[i].x = xrl[i].x / scale; |
3826 | rl[i].y = xrl[i].y / scale; |
3827 | rl[i].width = xrl[i].width / scale; |
3828 | rl[i].height = xrl[i].height / scale; |
3829 | } |
3830 | XFree (xrl); |
3831 | |
3832 | shape = cairo_region_create_rectangles (rects: rl, count: rn); |
3833 | g_free (mem: rl); |
3834 | |
3835 | return shape; |
3836 | } |
3837 | |
3838 | /* From the WM spec */ |
3839 | #define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0 |
3840 | #define _NET_WM_MOVERESIZE_SIZE_TOP 1 |
3841 | #define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2 |
3842 | #define _NET_WM_MOVERESIZE_SIZE_RIGHT 3 |
3843 | #define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4 |
3844 | #define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5 |
3845 | #define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6 |
3846 | #define _NET_WM_MOVERESIZE_SIZE_LEFT 7 |
3847 | #define _NET_WM_MOVERESIZE_MOVE 8 /* movement only */ |
3848 | #define _NET_WM_MOVERESIZE_SIZE_KEYBOARD 9 /* size via keyboard */ |
3849 | #define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10 /* move via keyboard */ |
3850 | #define _NET_WM_MOVERESIZE_CANCEL 11 /* cancel operation */ |
3851 | |
3852 | static void |
3853 | wmspec_send_message (GdkDisplay *display, |
3854 | GdkSurface *surface, |
3855 | int root_x, |
3856 | int root_y, |
3857 | int action, |
3858 | int button) |
3859 | { |
3860 | GdkX11Surface *impl = GDK_X11_SURFACE (surface); |
3861 | XClientMessageEvent xclient; |
3862 | |
3863 | memset (s: &xclient, c: 0, n: sizeof (xclient)); |
3864 | xclient.type = ClientMessage; |
3865 | xclient.window = GDK_SURFACE_XID (surface); |
3866 | xclient.message_type = |
3867 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_MOVERESIZE" ); |
3868 | xclient.format = 32; |
3869 | xclient.data.l[0] = root_x * impl->surface_scale; |
3870 | xclient.data.l[1] = root_y * impl->surface_scale; |
3871 | xclient.data.l[2] = action; |
3872 | xclient.data.l[3] = button; |
3873 | xclient.data.l[4] = 1; /* source indication */ |
3874 | |
3875 | XSendEvent (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XROOTWIN (surface), False, |
3876 | SubstructureRedirectMask | SubstructureNotifyMask, |
3877 | (XEvent *)&xclient); |
3878 | } |
3879 | |
3880 | static void |
3881 | handle_wmspec_button_release (GdkDisplay *display, |
3882 | const XEvent *xevent) |
3883 | { |
3884 | GdkX11Display *display_x11 = GDK_X11_DISPLAY (display); |
3885 | GdkSurface *surface; |
3886 | |
3887 | XIEvent *xiev = (XIEvent *) xevent->xcookie.data; |
3888 | XIDeviceEvent *xidev = (XIDeviceEvent *) xiev; |
3889 | |
3890 | if (xevent->xany.type == GenericEvent) |
3891 | surface = gdk_x11_surface_lookup_for_display (display, window: xidev->event); |
3892 | else |
3893 | surface = gdk_x11_surface_lookup_for_display (display, window: xevent->xany.window); |
3894 | |
3895 | if (display_x11->wm_moveresize_button != 0 && surface != NULL) |
3896 | { |
3897 | if ((xevent->xany.type == ButtonRelease && |
3898 | xevent->xbutton.button == display_x11->wm_moveresize_button) || |
3899 | (xevent->xany.type == GenericEvent && |
3900 | xiev->evtype == XI_ButtonRelease && |
3901 | xidev->detail == display_x11->wm_moveresize_button)) |
3902 | { |
3903 | display_x11->wm_moveresize_button = 0; |
3904 | wmspec_send_message (display, surface, root_x: 0, root_y: 0, _NET_WM_MOVERESIZE_CANCEL, button: 0); |
3905 | } |
3906 | } |
3907 | } |
3908 | |
3909 | static void |
3910 | wmspec_moveresize (GdkSurface *surface, |
3911 | int direction, |
3912 | GdkDevice *device, |
3913 | int button, |
3914 | int root_x, |
3915 | int root_y, |
3916 | guint32 timestamp) |
3917 | { |
3918 | GdkDisplay *display = GDK_SURFACE_DISPLAY (surface); |
3919 | |
3920 | if (button != 0) |
3921 | gdk_seat_ungrab (seat: gdk_device_get_seat (device)); /* Release passive grab */ |
3922 | GDK_X11_DISPLAY (display)->wm_moveresize_button = button; |
3923 | |
3924 | wmspec_send_message (display, surface, root_x, root_y, action: direction, button); |
3925 | } |
3926 | |
3927 | static void |
3928 | wmspec_resize_drag (GdkSurface *surface, |
3929 | GdkSurfaceEdge edge, |
3930 | GdkDevice *device, |
3931 | int button, |
3932 | int root_x, |
3933 | int root_y, |
3934 | guint32 timestamp) |
3935 | { |
3936 | int direction; |
3937 | |
3938 | if (button == 0) |
3939 | direction = _NET_WM_MOVERESIZE_SIZE_KEYBOARD; |
3940 | else |
3941 | switch (edge) |
3942 | { |
3943 | /* Let the compiler turn a switch into a table, instead |
3944 | * of doing the table manually, this way is easier to verify. |
3945 | */ |
3946 | case GDK_SURFACE_EDGE_NORTH_WEST: |
3947 | direction = _NET_WM_MOVERESIZE_SIZE_TOPLEFT; |
3948 | break; |
3949 | |
3950 | case GDK_SURFACE_EDGE_NORTH: |
3951 | direction = _NET_WM_MOVERESIZE_SIZE_TOP; |
3952 | break; |
3953 | |
3954 | case GDK_SURFACE_EDGE_NORTH_EAST: |
3955 | direction = _NET_WM_MOVERESIZE_SIZE_TOPRIGHT; |
3956 | break; |
3957 | |
3958 | case GDK_SURFACE_EDGE_WEST: |
3959 | direction = _NET_WM_MOVERESIZE_SIZE_LEFT; |
3960 | break; |
3961 | |
3962 | case GDK_SURFACE_EDGE_EAST: |
3963 | direction = _NET_WM_MOVERESIZE_SIZE_RIGHT; |
3964 | break; |
3965 | |
3966 | case GDK_SURFACE_EDGE_SOUTH_WEST: |
3967 | direction = _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT; |
3968 | break; |
3969 | |
3970 | case GDK_SURFACE_EDGE_SOUTH: |
3971 | direction = _NET_WM_MOVERESIZE_SIZE_BOTTOM; |
3972 | break; |
3973 | |
3974 | case GDK_SURFACE_EDGE_SOUTH_EAST: |
3975 | direction = _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT; |
3976 | break; |
3977 | |
3978 | default: |
3979 | g_warning ("gdk_toplevel_begin_resize: bad resize edge %d!" , |
3980 | edge); |
3981 | return; |
3982 | } |
3983 | |
3984 | wmspec_moveresize (surface, direction, device, button, root_x, root_y, timestamp); |
3985 | } |
3986 | |
3987 | typedef struct _MoveResizeData MoveResizeData; |
3988 | |
3989 | struct _MoveResizeData |
3990 | { |
3991 | GdkDisplay *display; |
3992 | |
3993 | GdkSurface *moveresize_surface; |
3994 | GdkSurface *moveresize_emulation_surface; |
3995 | gboolean is_resize; |
3996 | GdkSurfaceEdge resize_edge; |
3997 | GdkDevice *device; |
3998 | int moveresize_button; |
3999 | int moveresize_x; |
4000 | int moveresize_y; |
4001 | int moveresize_orig_x; |
4002 | int moveresize_orig_y; |
4003 | int moveresize_orig_width; |
4004 | int moveresize_orig_height; |
4005 | GdkSurfaceHints moveresize_geom_mask; |
4006 | GdkGeometry moveresize_geometry; |
4007 | Time moveresize_process_time; |
4008 | XEvent *moveresize_pending_event; |
4009 | }; |
4010 | |
4011 | static MoveResizeData * |
4012 | get_move_resize_data (GdkDisplay *display, |
4013 | gboolean create) |
4014 | { |
4015 | MoveResizeData *mv_resize; |
4016 | static GQuark move_resize_quark = 0; |
4017 | |
4018 | if (!move_resize_quark) |
4019 | move_resize_quark = g_quark_from_static_string (string: "gdk-surface-moveresize" ); |
4020 | |
4021 | mv_resize = g_object_get_qdata (G_OBJECT (display), quark: move_resize_quark); |
4022 | |
4023 | if (!mv_resize && create) |
4024 | { |
4025 | mv_resize = g_new0 (MoveResizeData, 1); |
4026 | mv_resize->display = display; |
4027 | |
4028 | g_object_set_qdata (G_OBJECT (display), quark: move_resize_quark, data: mv_resize); |
4029 | } |
4030 | |
4031 | return mv_resize; |
4032 | } |
4033 | |
4034 | static void |
4035 | check_maximize (MoveResizeData *mv_resize, |
4036 | double x_root, |
4037 | double y_root) |
4038 | { |
4039 | GdkToplevelState state; |
4040 | int y; |
4041 | |
4042 | if (mv_resize->is_resize) |
4043 | return; |
4044 | |
4045 | state = gdk_toplevel_get_state (toplevel: GDK_TOPLEVEL (ptr: mv_resize->moveresize_surface)); |
4046 | |
4047 | if (state & GDK_TOPLEVEL_STATE_MAXIMIZED) |
4048 | return; |
4049 | |
4050 | y = mv_resize->moveresize_orig_y + (y_root - mv_resize->moveresize_y); |
4051 | |
4052 | if (y < 10) |
4053 | gdk_x11_surface_maximize (surface: mv_resize->moveresize_surface); |
4054 | } |
4055 | |
4056 | static void |
4057 | check_unmaximize (MoveResizeData *mv_resize, |
4058 | double x_root, |
4059 | double y_root) |
4060 | { |
4061 | GdkToplevelState state; |
4062 | int dx, dy; |
4063 | |
4064 | if (mv_resize->is_resize) |
4065 | return; |
4066 | |
4067 | state = gdk_toplevel_get_state (toplevel: GDK_TOPLEVEL (ptr: mv_resize->moveresize_surface)); |
4068 | |
4069 | if ((state & (GDK_TOPLEVEL_STATE_MAXIMIZED | GDK_TOPLEVEL_STATE_TILED)) == 0) |
4070 | return; |
4071 | |
4072 | dx = x_root - mv_resize->moveresize_x; |
4073 | dy = y_root - mv_resize->moveresize_y; |
4074 | |
4075 | if (ABS (dx) > 20 || ABS (dy) > 20) |
4076 | gdk_x11_surface_unmaximize (surface: mv_resize->moveresize_surface); |
4077 | } |
4078 | |
4079 | static void |
4080 | update_pos (MoveResizeData *mv_resize, |
4081 | int new_root_x, |
4082 | int new_root_y) |
4083 | { |
4084 | int dx, dy; |
4085 | |
4086 | check_unmaximize (mv_resize, x_root: new_root_x, y_root: new_root_y); |
4087 | dx = new_root_x - mv_resize->moveresize_x; |
4088 | dy = new_root_y - mv_resize->moveresize_y; |
4089 | |
4090 | if (mv_resize->is_resize) |
4091 | { |
4092 | int x, y, w, h; |
4093 | |
4094 | x = mv_resize->moveresize_orig_x; |
4095 | y = mv_resize->moveresize_orig_y; |
4096 | |
4097 | w = mv_resize->moveresize_orig_width; |
4098 | h = mv_resize->moveresize_orig_height; |
4099 | |
4100 | switch (mv_resize->resize_edge) |
4101 | { |
4102 | case GDK_SURFACE_EDGE_NORTH_WEST: |
4103 | x += dx; |
4104 | y += dy; |
4105 | w -= dx; |
4106 | h -= dy; |
4107 | break; |
4108 | case GDK_SURFACE_EDGE_NORTH: |
4109 | y += dy; |
4110 | h -= dy; |
4111 | break; |
4112 | case GDK_SURFACE_EDGE_NORTH_EAST: |
4113 | y += dy; |
4114 | h -= dy; |
4115 | w += dx; |
4116 | break; |
4117 | case GDK_SURFACE_EDGE_SOUTH_WEST: |
4118 | h += dy; |
4119 | x += dx; |
4120 | w -= dx; |
4121 | break; |
4122 | case GDK_SURFACE_EDGE_SOUTH_EAST: |
4123 | w += dx; |
4124 | h += dy; |
4125 | break; |
4126 | case GDK_SURFACE_EDGE_SOUTH: |
4127 | h += dy; |
4128 | break; |
4129 | case GDK_SURFACE_EDGE_EAST: |
4130 | w += dx; |
4131 | break; |
4132 | case GDK_SURFACE_EDGE_WEST: |
4133 | x += dx; |
4134 | w -= dx; |
4135 | break; |
4136 | default: |
4137 | break; |
4138 | } |
4139 | |
4140 | x = MAX (x, 0); |
4141 | y = MAX (y, 0); |
4142 | w = MAX (w, 1); |
4143 | h = MAX (h, 1); |
4144 | |
4145 | if (mv_resize->moveresize_geom_mask) |
4146 | { |
4147 | gdk_surface_constrain_size (geometry: &mv_resize->moveresize_geometry, |
4148 | flags: mv_resize->moveresize_geom_mask, |
4149 | width: w, height: h, new_width: &w, new_height: &h); |
4150 | } |
4151 | |
4152 | gdk_x11_surface_move_resize (surface: mv_resize->moveresize_surface, TRUE, |
4153 | x, y, width: w, height: h); |
4154 | } |
4155 | else |
4156 | { |
4157 | int x, y; |
4158 | |
4159 | x = mv_resize->moveresize_orig_x + dx; |
4160 | y = mv_resize->moveresize_orig_y + dy; |
4161 | |
4162 | gdk_x11_surface_move (surface: mv_resize->moveresize_surface, x, y); |
4163 | } |
4164 | } |
4165 | |
4166 | static void |
4167 | finish_drag (MoveResizeData *mv_resize) |
4168 | { |
4169 | gdk_surface_destroy (surface: mv_resize->moveresize_emulation_surface); |
4170 | mv_resize->moveresize_emulation_surface = NULL; |
4171 | g_clear_object (&mv_resize->moveresize_surface); |
4172 | g_clear_pointer (&mv_resize->moveresize_pending_event, g_free); |
4173 | } |
4174 | |
4175 | static int |
4176 | lookahead_motion_predicate (Display *xdisplay, |
4177 | XEvent *event, |
4178 | XPointer arg) |
4179 | { |
4180 | gboolean *seen_release = (gboolean *)arg; |
4181 | GdkDisplay *display = gdk_x11_lookup_xdisplay (xdisplay); |
4182 | MoveResizeData *mv_resize = get_move_resize_data (display, FALSE); |
4183 | |
4184 | if (*seen_release) |
4185 | return False; |
4186 | |
4187 | switch (event->xany.type) |
4188 | { |
4189 | case ButtonRelease: |
4190 | *seen_release = TRUE; |
4191 | break; |
4192 | case MotionNotify: |
4193 | mv_resize->moveresize_process_time = event->xmotion.time; |
4194 | break; |
4195 | default: |
4196 | break; |
4197 | } |
4198 | |
4199 | return False; |
4200 | } |
4201 | |
4202 | static gboolean |
4203 | moveresize_lookahead (MoveResizeData *mv_resize, |
4204 | const XEvent *event) |
4205 | { |
4206 | XEvent tmp_event; |
4207 | gboolean seen_release = FALSE; |
4208 | |
4209 | if (mv_resize->moveresize_process_time) |
4210 | { |
4211 | if (event->xmotion.time == mv_resize->moveresize_process_time) |
4212 | { |
4213 | mv_resize->moveresize_process_time = 0; |
4214 | return TRUE; |
4215 | } |
4216 | else |
4217 | return FALSE; |
4218 | } |
4219 | |
4220 | XCheckIfEvent (event->xany.display, &tmp_event, |
4221 | lookahead_motion_predicate, (XPointer) & seen_release); |
4222 | |
4223 | return mv_resize->moveresize_process_time == 0; |
4224 | } |
4225 | |
4226 | gboolean |
4227 | _gdk_x11_moveresize_handle_event (const XEvent *event) |
4228 | { |
4229 | guint button_mask = 0; |
4230 | GdkDisplay *display = gdk_x11_lookup_xdisplay (xdisplay: event->xany.display); |
4231 | MoveResizeData *mv_resize = get_move_resize_data (display, FALSE); |
4232 | GdkX11Surface *impl; |
4233 | |
4234 | if (!mv_resize || !mv_resize->moveresize_surface) |
4235 | { |
4236 | handle_wmspec_button_release (display, xevent: event); |
4237 | return FALSE; |
4238 | } |
4239 | |
4240 | impl = GDK_X11_SURFACE (mv_resize->moveresize_surface); |
4241 | |
4242 | if (mv_resize->moveresize_button != 0) |
4243 | button_mask = GDK_BUTTON1_MASK << (mv_resize->moveresize_button - 1); |
4244 | |
4245 | switch (event->xany.type) |
4246 | { |
4247 | case MotionNotify: |
4248 | if (mv_resize->moveresize_surface->resize_count > 0) |
4249 | { |
4250 | if (mv_resize->moveresize_pending_event) |
4251 | *mv_resize->moveresize_pending_event = *event; |
4252 | else |
4253 | mv_resize->moveresize_pending_event = |
4254 | g_memdup2 (mem: event, byte_size: sizeof (XEvent)); |
4255 | |
4256 | break; |
4257 | } |
4258 | if (!moveresize_lookahead (mv_resize, event)) |
4259 | break; |
4260 | |
4261 | update_pos (mv_resize, |
4262 | new_root_x: event->xmotion.x_root / impl->surface_scale, |
4263 | new_root_y: event->xmotion.y_root / impl->surface_scale); |
4264 | |
4265 | /* This should never be triggered in normal cases, but in the |
4266 | * case where the drag started without an implicit grab being |
4267 | * in effect, we could miss the release if it occurs before |
4268 | * we grab the pointer; this ensures that we will never |
4269 | * get a permanently stuck grab. |
4270 | */ |
4271 | if ((event->xmotion.state & button_mask) == 0) |
4272 | { |
4273 | check_maximize (mv_resize, |
4274 | x_root: event->xmotion.x_root / impl->surface_scale, |
4275 | y_root: event->xmotion.y_root / impl->surface_scale); |
4276 | finish_drag (mv_resize); |
4277 | } |
4278 | break; |
4279 | |
4280 | case ButtonRelease: |
4281 | update_pos (mv_resize, |
4282 | new_root_x: event->xbutton.x_root / impl->surface_scale, |
4283 | new_root_y: event->xbutton.y_root / impl->surface_scale); |
4284 | |
4285 | if (event->xbutton.button == mv_resize->moveresize_button) |
4286 | { |
4287 | check_maximize (mv_resize, |
4288 | x_root: event->xmotion.x_root / impl->surface_scale, |
4289 | y_root: event->xmotion.y_root / impl->surface_scale); |
4290 | finish_drag (mv_resize); |
4291 | } |
4292 | break; |
4293 | |
4294 | case GenericEvent: |
4295 | { |
4296 | /* we just assume this is an XI2 event */ |
4297 | XIEvent *ev = (XIEvent *) event->xcookie.data; |
4298 | XIDeviceEvent *xev = (XIDeviceEvent *)ev; |
4299 | int state; |
4300 | switch (ev->evtype) |
4301 | { |
4302 | case XI_Motion: |
4303 | update_pos (mv_resize, new_root_x: xev->root_x / impl->surface_scale, new_root_y: xev->root_y / impl->surface_scale); |
4304 | state = _gdk_x11_device_xi2_translate_state (mods_state: &xev->mods, buttons_state: &xev->buttons, group_state: &xev->group); |
4305 | if ((state & button_mask) == 0) |
4306 | { |
4307 | check_maximize (mv_resize, |
4308 | x_root: xev->root_x / impl->surface_scale, |
4309 | y_root: xev->root_y / impl->surface_scale); |
4310 | finish_drag (mv_resize); |
4311 | } |
4312 | break; |
4313 | |
4314 | case XI_ButtonRelease: |
4315 | update_pos (mv_resize, new_root_x: xev->root_x / impl->surface_scale, new_root_y: xev->root_y / impl->surface_scale); |
4316 | if (xev->detail == mv_resize->moveresize_button) |
4317 | { |
4318 | check_maximize (mv_resize, |
4319 | x_root: xev->root_x / impl->surface_scale, |
4320 | y_root: xev->root_y / impl->surface_scale); |
4321 | finish_drag (mv_resize); |
4322 | } |
4323 | break; |
4324 | default: |
4325 | break; |
4326 | } |
4327 | } |
4328 | break; |
4329 | |
4330 | default: |
4331 | break; |
4332 | |
4333 | } |
4334 | return TRUE; |
4335 | } |
4336 | |
4337 | gboolean |
4338 | _gdk_x11_moveresize_configure_done (GdkDisplay *display, |
4339 | GdkSurface *surface) |
4340 | { |
4341 | XEvent *tmp_event; |
4342 | MoveResizeData *mv_resize = get_move_resize_data (display, FALSE); |
4343 | |
4344 | gdk_surface_thaw_updates (surface); |
4345 | gdk_surface_request_layout (surface); |
4346 | |
4347 | if (!mv_resize || surface != mv_resize->moveresize_surface) |
4348 | return FALSE; |
4349 | |
4350 | if (mv_resize->moveresize_pending_event) |
4351 | { |
4352 | tmp_event = mv_resize->moveresize_pending_event; |
4353 | mv_resize->moveresize_pending_event = NULL; |
4354 | _gdk_x11_moveresize_handle_event (event: tmp_event); |
4355 | g_free (mem: tmp_event); |
4356 | } |
4357 | |
4358 | return TRUE; |
4359 | } |
4360 | |
4361 | static void |
4362 | create_moveresize_surface (MoveResizeData *mv_resize, |
4363 | guint32 timestamp) |
4364 | { |
4365 | GdkGrabStatus status; |
4366 | |
4367 | g_assert (mv_resize->moveresize_emulation_surface == NULL); |
4368 | |
4369 | mv_resize->moveresize_emulation_surface = |
4370 | _gdk_x11_display_create_surface (display: mv_resize->display, |
4371 | surface_type: GDK_SURFACE_TEMP, |
4372 | NULL, |
4373 | x: -100, y: -100, width: 1, height: 1); |
4374 | |
4375 | gdk_surface_set_is_mapped (surface: mv_resize->moveresize_emulation_surface, TRUE); |
4376 | gdk_x11_surface_show (surface: mv_resize->moveresize_emulation_surface, FALSE); |
4377 | |
4378 | status = gdk_seat_grab (seat: gdk_device_get_seat (device: mv_resize->device), |
4379 | surface: mv_resize->moveresize_emulation_surface, |
4380 | capabilities: GDK_SEAT_CAPABILITY_POINTER, FALSE, |
4381 | NULL, NULL, NULL, NULL); |
4382 | |
4383 | if (status != GDK_GRAB_SUCCESS) |
4384 | { |
4385 | /* If this fails, some other client has grabbed the surface |
4386 | * already. |
4387 | */ |
4388 | finish_drag (mv_resize); |
4389 | } |
4390 | |
4391 | mv_resize->moveresize_process_time = 0; |
4392 | } |
4393 | |
4394 | /* |
4395 | Calculate mv_resize->moveresize_orig_x and mv_resize->moveresize_orig_y |
4396 | so that calling XMoveWindow with these coordinates will not move the |
4397 | surface. |
4398 | Note that this depends on the WM to implement ICCCM-compliant reference |
4399 | point handling. |
4400 | */ |
4401 | static void |
4402 | calculate_unmoving_origin (MoveResizeData *mv_resize) |
4403 | { |
4404 | GdkRectangle rect; |
4405 | |
4406 | gdk_x11_surface_get_frame_extents (surface: mv_resize->moveresize_surface, rect: &rect); |
4407 | mv_resize->moveresize_orig_x = rect.x; |
4408 | mv_resize->moveresize_orig_y = rect.y; |
4409 | } |
4410 | |
4411 | static void |
4412 | emulate_resize_drag (GdkSurface *surface, |
4413 | GdkSurfaceEdge edge, |
4414 | GdkDevice *device, |
4415 | int button, |
4416 | int root_x, |
4417 | int root_y, |
4418 | guint32 timestamp) |
4419 | { |
4420 | MoveResizeData *mv_resize = get_move_resize_data (GDK_SURFACE_DISPLAY (surface), TRUE); |
4421 | |
4422 | if (mv_resize->moveresize_surface != NULL) |
4423 | return; /* already a drag operation in progress */ |
4424 | |
4425 | mv_resize->is_resize = TRUE; |
4426 | mv_resize->moveresize_button = button; |
4427 | mv_resize->resize_edge = edge; |
4428 | mv_resize->device = device; |
4429 | mv_resize->moveresize_x = root_x; |
4430 | mv_resize->moveresize_y = root_y; |
4431 | mv_resize->moveresize_surface = g_object_ref (surface); |
4432 | |
4433 | mv_resize->moveresize_orig_width = gdk_surface_get_width (surface); |
4434 | mv_resize->moveresize_orig_height = gdk_surface_get_height (surface); |
4435 | |
4436 | mv_resize->moveresize_geom_mask = 0; |
4437 | gdk_surface_get_geometry_hints (surface, |
4438 | geometry: &mv_resize->moveresize_geometry, |
4439 | geom_mask: &mv_resize->moveresize_geom_mask); |
4440 | |
4441 | calculate_unmoving_origin (mv_resize); |
4442 | |
4443 | create_moveresize_surface (mv_resize, timestamp); |
4444 | } |
4445 | |
4446 | static void |
4447 | emulate_move_drag (GdkSurface *surface, |
4448 | GdkDevice *device, |
4449 | int button, |
4450 | int root_x, |
4451 | int root_y, |
4452 | guint32 timestamp) |
4453 | { |
4454 | MoveResizeData *mv_resize = get_move_resize_data (GDK_SURFACE_DISPLAY (surface), TRUE); |
4455 | |
4456 | if (mv_resize->moveresize_surface != NULL) |
4457 | return; /* already a drag operation in progress */ |
4458 | |
4459 | mv_resize->is_resize = FALSE; |
4460 | mv_resize->device = device; |
4461 | mv_resize->moveresize_button = button; |
4462 | mv_resize->moveresize_x = root_x; |
4463 | mv_resize->moveresize_y = root_y; |
4464 | |
4465 | mv_resize->moveresize_surface = g_object_ref (surface); |
4466 | |
4467 | calculate_unmoving_origin (mv_resize); |
4468 | |
4469 | create_moveresize_surface (mv_resize, timestamp); |
4470 | } |
4471 | |
4472 | static gboolean |
4473 | _should_perform_ewmh_drag (GdkSurface *surface, |
4474 | GdkDevice *device) |
4475 | { |
4476 | GdkPointerSurfaceInfo *info; |
4477 | GdkDisplay *display; |
4478 | |
4479 | display = gdk_surface_get_display (surface); |
4480 | info = _gdk_display_get_pointer_info (display, device); |
4481 | |
4482 | if ((info->last_physical_device == NULL || |
4483 | gdk_device_get_source (device: info->last_physical_device) != GDK_SOURCE_TOUCHSCREEN) && |
4484 | gdk_x11_screen_supports_net_wm_hint (GDK_SURFACE_SCREEN (surface), |
4485 | property_name: g_intern_static_string (string: "_NET_WM_MOVERESIZE" ))) |
4486 | return TRUE; |
4487 | |
4488 | return FALSE; |
4489 | } |
4490 | |
4491 | static void |
4492 | gdk_x11_toplevel_begin_resize (GdkToplevel *toplevel, |
4493 | GdkSurfaceEdge edge, |
4494 | GdkDevice *device, |
4495 | int button, |
4496 | double x, |
4497 | double y, |
4498 | guint32 timestamp) |
4499 | { |
4500 | GdkSurface *surface = GDK_SURFACE (toplevel); |
4501 | int root_x, root_y; |
4502 | |
4503 | if (GDK_SURFACE_DESTROYED (surface)) |
4504 | return; |
4505 | |
4506 | gdk_x11_surface_get_root_coords (surface, x, y, root_x: &root_x, root_y: &root_y); |
4507 | |
4508 | /* Avoid EWMH for touch devices */ |
4509 | if (_should_perform_ewmh_drag (surface, device)) |
4510 | wmspec_resize_drag (surface, edge, device, button, root_x, root_y, timestamp); |
4511 | else |
4512 | emulate_resize_drag (surface, edge, device, button, root_x, root_y, timestamp); |
4513 | } |
4514 | |
4515 | static void |
4516 | gdk_x11_toplevel_begin_move (GdkToplevel *toplevel, |
4517 | GdkDevice *device, |
4518 | int button, |
4519 | double x, |
4520 | double y, |
4521 | guint32 timestamp) |
4522 | { |
4523 | GdkSurface *surface = GDK_SURFACE (toplevel); |
4524 | int root_x, root_y; |
4525 | int direction; |
4526 | |
4527 | if (GDK_SURFACE_DESTROYED (surface)) |
4528 | return; |
4529 | |
4530 | if (button == 0) |
4531 | direction = _NET_WM_MOVERESIZE_MOVE_KEYBOARD; |
4532 | else |
4533 | direction = _NET_WM_MOVERESIZE_MOVE; |
4534 | |
4535 | gdk_x11_surface_get_root_coords (surface, x, y, root_x: &root_x, root_y: &root_y); |
4536 | |
4537 | /* Avoid EWMH for touch devices */ |
4538 | if (_should_perform_ewmh_drag (surface, device)) |
4539 | wmspec_moveresize (surface, direction, device, button, root_x, root_y, timestamp); |
4540 | else |
4541 | emulate_move_drag (surface, device, button, root_x, root_y, timestamp); |
4542 | } |
4543 | |
4544 | static gboolean |
4545 | gdk_x11_surface_beep (GdkSurface *surface) |
4546 | { |
4547 | GdkDisplay *display; |
4548 | |
4549 | display = GDK_SURFACE_DISPLAY (surface); |
4550 | |
4551 | if (!GDK_X11_DISPLAY (display)->trusted_client) |
4552 | return FALSE; |
4553 | |
4554 | #ifdef HAVE_XKB |
4555 | if (GDK_X11_DISPLAY (display)->use_xkb) |
4556 | { |
4557 | XkbBell (GDK_DISPLAY_XDISPLAY (display), |
4558 | GDK_SURFACE_XID (surface), |
4559 | 0, |
4560 | None); |
4561 | return TRUE; |
4562 | } |
4563 | #endif |
4564 | |
4565 | return FALSE; |
4566 | } |
4567 | |
4568 | void |
4569 | gdk_x11_surface_set_opacity (GdkSurface *surface, |
4570 | double opacity) |
4571 | { |
4572 | GdkDisplay *display; |
4573 | gulong cardinal; |
4574 | |
4575 | g_return_if_fail (GDK_IS_SURFACE (surface)); |
4576 | |
4577 | if (GDK_SURFACE_DESTROYED (surface)) |
4578 | return; |
4579 | |
4580 | display = gdk_surface_get_display (surface); |
4581 | |
4582 | if (opacity < 0) |
4583 | opacity = 0; |
4584 | else if (opacity > 1) |
4585 | opacity = 1; |
4586 | |
4587 | cardinal = opacity * 0xffffffff; |
4588 | |
4589 | if (cardinal == 0xffffffff) |
4590 | XDeleteProperty (GDK_DISPLAY_XDISPLAY (display), |
4591 | GDK_SURFACE_XID (surface), |
4592 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_WINDOW_OPACITY" )); |
4593 | else |
4594 | XChangeProperty (GDK_DISPLAY_XDISPLAY (display), |
4595 | GDK_SURFACE_XID (surface), |
4596 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_WINDOW_OPACITY" ), |
4597 | XA_CARDINAL, 32, |
4598 | PropModeReplace, |
4599 | (guchar *) &cardinal, 1); |
4600 | } |
4601 | |
4602 | static Bool |
4603 | timestamp_predicate (Display *display, |
4604 | XEvent *xevent, |
4605 | XPointer arg) |
4606 | { |
4607 | Window xwindow = GPOINTER_TO_UINT (arg); |
4608 | GdkDisplay *gdk_display = gdk_x11_lookup_xdisplay (xdisplay: display); |
4609 | |
4610 | if (xevent->type == PropertyNotify && |
4611 | xevent->xproperty.window == xwindow && |
4612 | xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display: gdk_display, |
4613 | atom_name: "GDK_TIMESTAMP_PROP" )) |
4614 | return True; |
4615 | |
4616 | return False; |
4617 | } |
4618 | |
4619 | /** |
4620 | * gdk_x11_get_server_time: |
4621 | * @surface: (type GdkX11Surface): a `GdkSurface`, used for communication |
4622 | * with the server. The surface must have `GDK_PROPERTY_CHANGE_MASK` in |
4623 | * its events mask or a hang will result. |
4624 | * |
4625 | * Routine to get the current X server time stamp. |
4626 | * |
4627 | * Returns: the time stamp |
4628 | */ |
4629 | guint32 |
4630 | gdk_x11_get_server_time (GdkSurface *surface) |
4631 | { |
4632 | Display *xdisplay; |
4633 | Window xwindow; |
4634 | guchar c = 'a'; |
4635 | XEvent xevent; |
4636 | Atom timestamp_prop_atom; |
4637 | |
4638 | g_return_val_if_fail (GDK_IS_SURFACE (surface), 0); |
4639 | g_return_val_if_fail (!GDK_SURFACE_DESTROYED (surface), 0); |
4640 | |
4641 | xdisplay = GDK_SURFACE_XDISPLAY (surface); |
4642 | xwindow = GDK_SURFACE_XID (surface); |
4643 | timestamp_prop_atom = |
4644 | gdk_x11_get_xatom_by_name_for_display (GDK_SURFACE_DISPLAY (surface), |
4645 | atom_name: "GDK_TIMESTAMP_PROP" ); |
4646 | |
4647 | XChangeProperty (xdisplay, xwindow, timestamp_prop_atom, |
4648 | timestamp_prop_atom, |
4649 | 8, PropModeReplace, &c, 1); |
4650 | |
4651 | XIfEvent (xdisplay, &xevent, |
4652 | timestamp_predicate, GUINT_TO_POINTER(xwindow)); |
4653 | |
4654 | return xevent.xproperty.time; |
4655 | } |
4656 | |
4657 | /** |
4658 | * gdk_x11_surface_get_xid: |
4659 | * @surface: (type GdkX11Surface): a native `GdkSurface`. |
4660 | * |
4661 | * Returns the X resource (surface) belonging to a `GdkSurface`. |
4662 | * |
4663 | * Returns: the ID of @drawable’s X resource. |
4664 | **/ |
4665 | XID |
4666 | gdk_x11_surface_get_xid (GdkSurface *surface) |
4667 | { |
4668 | return GDK_X11_SURFACE (surface)->xid; |
4669 | } |
4670 | |
4671 | static int |
4672 | gdk_x11_surface_get_scale_factor (GdkSurface *surface) |
4673 | { |
4674 | GdkX11Surface *impl = GDK_X11_SURFACE (surface); |
4675 | |
4676 | if (GDK_SURFACE_DESTROYED (surface)) |
4677 | return 1; |
4678 | |
4679 | return impl->surface_scale; |
4680 | } |
4681 | |
4682 | /** |
4683 | * gdk_x11_surface_set_frame_sync_enabled: |
4684 | * @surface: (type GdkX11Surface): a native `GdkSurface` |
4685 | * @frame_sync_enabled: whether frame-synchronization should be enabled |
4686 | * |
4687 | * This function can be used to disable frame synchronization for a surface. |
4688 | * Normally frame synchronziation will be enabled or disabled based on whether |
4689 | * the system has a compositor that supports frame synchronization, but if |
4690 | * the surface is not directly managed by the window manager, then frame |
4691 | * synchronziation may need to be disabled. This is the case for a surface |
4692 | * embedded via the XEMBED protocol. |
4693 | */ |
4694 | void |
4695 | gdk_x11_surface_set_frame_sync_enabled (GdkSurface *surface, |
4696 | gboolean frame_sync_enabled) |
4697 | { |
4698 | GDK_X11_SURFACE (surface)->frame_sync_enabled = FALSE; |
4699 | } |
4700 | |
4701 | static void |
4702 | gdk_x11_surface_set_opaque_region (GdkSurface *surface, |
4703 | cairo_region_t *region) |
4704 | { |
4705 | GdkX11Surface *impl = GDK_X11_SURFACE (surface); |
4706 | GdkDisplay *display; |
4707 | int nitems; |
4708 | gulong *data; |
4709 | |
4710 | if (GDK_SURFACE_DESTROYED (surface)) |
4711 | return; |
4712 | |
4713 | if (region != NULL) |
4714 | { |
4715 | int i, nrects; |
4716 | |
4717 | nrects = cairo_region_num_rectangles (region); |
4718 | nitems = nrects * 4; |
4719 | data = g_new (gulong, nitems); |
4720 | |
4721 | for (i = 0; i < nrects; i++) |
4722 | { |
4723 | cairo_rectangle_int_t rect; |
4724 | cairo_region_get_rectangle (region, nth: i, rectangle: &rect); |
4725 | data[i*4+0] = rect.x * impl->surface_scale; |
4726 | data[i*4+1] = rect.y * impl->surface_scale; |
4727 | data[i*4+2] = rect.width * impl->surface_scale; |
4728 | data[i*4+3] = rect.height * impl->surface_scale; |
4729 | } |
4730 | } |
4731 | else |
4732 | { |
4733 | nitems = 0; |
4734 | data = NULL; |
4735 | } |
4736 | |
4737 | display = gdk_surface_get_display (surface); |
4738 | |
4739 | XChangeProperty (GDK_DISPLAY_XDISPLAY (display), |
4740 | GDK_SURFACE_XID (surface), |
4741 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_OPAQUE_REGION" ), |
4742 | XA_CARDINAL, 32, PropModeReplace, |
4743 | (guchar *) data, nitems); |
4744 | |
4745 | g_free (mem: data); |
4746 | } |
4747 | |
4748 | static gboolean |
4749 | (GdkSurface *surface, |
4750 | GdkEvent *event) |
4751 | { |
4752 | GdkX11Surface *impl = GDK_X11_SURFACE (surface); |
4753 | GdkDisplay *display = GDK_SURFACE_DISPLAY (surface); |
4754 | GdkDevice *device; |
4755 | int device_id; |
4756 | double x, y; |
4757 | int x_root, y_root; |
4758 | XClientMessageEvent xclient = { 0 }; |
4759 | |
4760 | GdkEventType event_type = gdk_event_get_event_type (event); |
4761 | |
4762 | switch ((guint) event_type) |
4763 | { |
4764 | case GDK_BUTTON_PRESS: |
4765 | case GDK_BUTTON_RELEASE: |
4766 | break; |
4767 | default: |
4768 | return FALSE; |
4769 | } |
4770 | |
4771 | if (!gdk_x11_screen_supports_net_wm_hint (GDK_SURFACE_SCREEN (surface), |
4772 | property_name: g_intern_static_string (string: "_GTK_SHOW_WINDOW_MENU" ))) |
4773 | return FALSE; |
4774 | |
4775 | gdk_event_get_position (event, x: &x, y: &y); |
4776 | gdk_x11_surface_get_root_coords (surface, x, y, root_x: &x_root, root_y: &y_root); |
4777 | device = gdk_event_get_device (event); |
4778 | g_object_get (G_OBJECT (device), |
4779 | first_property_name: "device-id" , &device_id, |
4780 | NULL); |
4781 | |
4782 | /* Ungrab the implicit grab */ |
4783 | gdk_seat_ungrab (seat: gdk_device_get_seat (device)); |
4784 | |
4785 | xclient.type = ClientMessage; |
4786 | xclient.window = GDK_SURFACE_XID (surface); |
4787 | xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_GTK_SHOW_WINDOW_MENU" ); |
4788 | xclient.data.l[0] = device_id; |
4789 | xclient.data.l[1] = x_root * impl->surface_scale; |
4790 | xclient.data.l[2] = y_root * impl->surface_scale; |
4791 | xclient.format = 32; |
4792 | |
4793 | XSendEvent (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XROOTWIN (surface), False, |
4794 | SubstructureRedirectMask | SubstructureNotifyMask, |
4795 | (XEvent *)&xclient); |
4796 | |
4797 | return TRUE; |
4798 | } |
4799 | |
4800 | static void |
4801 | gdk_x11_surface_class_init (GdkX11SurfaceClass *klass) |
4802 | { |
4803 | GObjectClass *object_class = G_OBJECT_CLASS (klass); |
4804 | GdkSurfaceClass *impl_class = GDK_SURFACE_CLASS (klass); |
4805 | |
4806 | object_class->finalize = gdk_x11_surface_finalize; |
4807 | |
4808 | impl_class->hide = gdk_x11_surface_hide; |
4809 | impl_class->get_geometry = gdk_x11_surface_get_geometry; |
4810 | impl_class->get_root_coords = gdk_x11_surface_get_root_coords; |
4811 | impl_class->get_device_state = gdk_x11_surface_get_device_state; |
4812 | impl_class->set_input_region = gdk_x11_surface_set_input_region; |
4813 | impl_class->destroy = gdk_x11_surface_destroy; |
4814 | impl_class->beep = gdk_x11_surface_beep; |
4815 | |
4816 | impl_class->destroy_notify = gdk_x11_surface_destroy_notify; |
4817 | impl_class->drag_begin = _gdk_x11_surface_drag_begin; |
4818 | impl_class->get_scale_factor = gdk_x11_surface_get_scale_factor; |
4819 | impl_class->set_opaque_region = gdk_x11_surface_set_opaque_region; |
4820 | impl_class->request_layout = gdk_x11_surface_request_layout; |
4821 | impl_class->compute_size = gdk_x11_surface_compute_size; |
4822 | } |
4823 | |
4824 | #define LAST_PROP 1 |
4825 | |
4826 | typedef struct { |
4827 | GdkX11Surface ; |
4828 | } ; |
4829 | |
4830 | typedef struct { |
4831 | GdkX11SurfaceClass ; |
4832 | } ; |
4833 | |
4834 | static void gdk_x11_popup_iface_init (GdkPopupInterface *iface); |
4835 | |
4836 | G_DEFINE_TYPE_WITH_CODE (GdkX11Popup, gdk_x11_popup, GDK_TYPE_X11_SURFACE, |
4837 | G_IMPLEMENT_INTERFACE (GDK_TYPE_POPUP, |
4838 | gdk_x11_popup_iface_init)) |
4839 | |
4840 | static void |
4841 | (GdkX11Popup *) |
4842 | { |
4843 | } |
4844 | |
4845 | static void |
4846 | (GObject *object, |
4847 | guint prop_id, |
4848 | GValue *value, |
4849 | GParamSpec *pspec) |
4850 | { |
4851 | GdkSurface *surface = GDK_SURFACE (object); |
4852 | |
4853 | switch (prop_id) |
4854 | { |
4855 | case LAST_PROP + GDK_POPUP_PROP_PARENT: |
4856 | g_value_set_object (value, v_object: surface->parent); |
4857 | break; |
4858 | |
4859 | case LAST_PROP + GDK_POPUP_PROP_AUTOHIDE: |
4860 | g_value_set_boolean (value, v_boolean: surface->autohide); |
4861 | break; |
4862 | |
4863 | default: |
4864 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
4865 | break; |
4866 | } |
4867 | } |
4868 | |
4869 | static void |
4870 | (GObject *object, |
4871 | guint prop_id, |
4872 | const GValue *value, |
4873 | GParamSpec *pspec) |
4874 | { |
4875 | GdkSurface *surface = GDK_SURFACE (object); |
4876 | |
4877 | switch (prop_id) |
4878 | { |
4879 | case LAST_PROP + GDK_POPUP_PROP_PARENT: |
4880 | surface->parent = g_value_dup_object (value); |
4881 | if (surface->parent != NULL) |
4882 | surface->parent->children = g_list_prepend (list: surface->parent->children, data: surface); |
4883 | break; |
4884 | |
4885 | case LAST_PROP + GDK_POPUP_PROP_AUTOHIDE: |
4886 | surface->autohide = g_value_get_boolean (value); |
4887 | break; |
4888 | |
4889 | default: |
4890 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
4891 | break; |
4892 | } |
4893 | } |
4894 | |
4895 | |
4896 | static void |
4897 | (GdkX11PopupClass *class) |
4898 | { |
4899 | GObjectClass *object_class = G_OBJECT_CLASS (class); |
4900 | |
4901 | object_class->get_property = gdk_x11_popup_get_property; |
4902 | object_class->set_property = gdk_x11_popup_set_property; |
4903 | |
4904 | gdk_popup_install_properties (object_class, first_prop: 1); |
4905 | } |
4906 | |
4907 | static gboolean |
4908 | (GdkPopup *, |
4909 | int width, |
4910 | int height, |
4911 | GdkPopupLayout *layout) |
4912 | { |
4913 | return gdk_x11_surface_present_popup (GDK_SURFACE (popup), width, height, layout); |
4914 | } |
4915 | |
4916 | static GdkGravity |
4917 | (GdkPopup *) |
4918 | { |
4919 | return GDK_SURFACE (popup)->popup.surface_anchor; |
4920 | } |
4921 | |
4922 | static GdkGravity |
4923 | (GdkPopup *) |
4924 | { |
4925 | return GDK_SURFACE (popup)->popup.rect_anchor; |
4926 | } |
4927 | |
4928 | static int |
4929 | (GdkPopup *) |
4930 | { |
4931 | return GDK_SURFACE (popup)->x; |
4932 | } |
4933 | |
4934 | static int |
4935 | (GdkPopup *) |
4936 | { |
4937 | return GDK_SURFACE (popup)->y; |
4938 | } |
4939 | |
4940 | static void |
4941 | (GdkPopupInterface *iface) |
4942 | { |
4943 | iface->present = gdk_x11_popup_present; |
4944 | iface->get_surface_anchor = gdk_x11_popup_get_surface_anchor; |
4945 | iface->get_rect_anchor = gdk_x11_popup_get_rect_anchor; |
4946 | iface->get_position_x = gdk_x11_popup_get_position_x; |
4947 | iface->get_position_y = gdk_x11_popup_get_position_y; |
4948 | } |
4949 | |
4950 | static void gdk_x11_toplevel_iface_init (GdkToplevelInterface *iface); |
4951 | |
4952 | G_DEFINE_TYPE_WITH_CODE (GdkX11Toplevel, gdk_x11_toplevel, GDK_TYPE_X11_SURFACE, |
4953 | G_IMPLEMENT_INTERFACE (GDK_TYPE_TOPLEVEL, |
4954 | gdk_x11_toplevel_iface_init)) |
4955 | |
4956 | static void |
4957 | gdk_x11_toplevel_init (GdkX11Toplevel *toplevel) |
4958 | { |
4959 | } |
4960 | static void |
4961 | gdk_x11_toplevel_set_property (GObject *object, |
4962 | guint prop_id, |
4963 | const GValue *value, |
4964 | GParamSpec *pspec) |
4965 | { |
4966 | GdkSurface *surface = GDK_SURFACE (object); |
4967 | |
4968 | switch (prop_id) |
4969 | { |
4970 | case LAST_PROP + GDK_TOPLEVEL_PROP_TITLE: |
4971 | gdk_x11_surface_set_title (surface, title: g_value_get_string (value)); |
4972 | g_object_notify_by_pspec (G_OBJECT (surface), pspec); |
4973 | break; |
4974 | |
4975 | case LAST_PROP + GDK_TOPLEVEL_PROP_STARTUP_ID: |
4976 | gdk_x11_surface_set_startup_id (surface, startup_id: g_value_get_string (value)); |
4977 | g_object_notify_by_pspec (G_OBJECT (surface), pspec); |
4978 | break; |
4979 | |
4980 | case LAST_PROP + GDK_TOPLEVEL_PROP_TRANSIENT_FOR: |
4981 | gdk_x11_surface_set_transient_for (surface, parent: g_value_get_object (value)); |
4982 | g_object_notify_by_pspec (G_OBJECT (surface), pspec); |
4983 | break; |
4984 | |
4985 | case LAST_PROP + GDK_TOPLEVEL_PROP_MODAL: |
4986 | gdk_x11_surface_set_modal_hint (surface, modal: g_value_get_boolean (value)); |
4987 | g_object_notify_by_pspec (G_OBJECT (surface), pspec); |
4988 | break; |
4989 | |
4990 | case LAST_PROP + GDK_TOPLEVEL_PROP_ICON_LIST: |
4991 | gdk_x11_surface_set_icon_list (surface, textures: g_value_get_pointer (value)); |
4992 | g_object_notify_by_pspec (G_OBJECT (surface), pspec); |
4993 | break; |
4994 | |
4995 | case LAST_PROP + GDK_TOPLEVEL_PROP_DECORATED: |
4996 | gdk_x11_surface_set_decorations (surface, decorations: g_value_get_boolean (value) ? GDK_DECOR_ALL : 0); |
4997 | g_object_notify_by_pspec (G_OBJECT (surface), pspec); |
4998 | break; |
4999 | |
5000 | case LAST_PROP + GDK_TOPLEVEL_PROP_DELETABLE: |
5001 | gdk_x11_surface_set_functions (surface, functions: g_value_get_boolean (value) ? GDK_FUNC_ALL : GDK_FUNC_ALL | GDK_FUNC_CLOSE); |
5002 | g_object_notify_by_pspec (G_OBJECT (surface), pspec); |
5003 | break; |
5004 | |
5005 | case LAST_PROP + GDK_TOPLEVEL_PROP_FULLSCREEN_MODE: |
5006 | surface->fullscreen_mode = g_value_get_enum (value); |
5007 | gdk_x11_surface_apply_fullscreen_mode (surface); |
5008 | g_object_notify_by_pspec (G_OBJECT (surface), pspec); |
5009 | break; |
5010 | |
5011 | case LAST_PROP + GDK_TOPLEVEL_PROP_SHORTCUTS_INHIBITED: |
5012 | break; |
5013 | |
5014 | default: |
5015 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
5016 | break; |
5017 | } |
5018 | } |
5019 | |
5020 | static void |
5021 | gdk_x11_toplevel_get_property (GObject *object, |
5022 | guint prop_id, |
5023 | GValue *value, |
5024 | GParamSpec *pspec) |
5025 | { |
5026 | GdkSurface *surface = GDK_SURFACE (object); |
5027 | |
5028 | switch (prop_id) |
5029 | { |
5030 | case LAST_PROP + GDK_TOPLEVEL_PROP_STATE: |
5031 | g_value_set_flags (value, v_flags: surface->state); |
5032 | break; |
5033 | |
5034 | case LAST_PROP + GDK_TOPLEVEL_PROP_TITLE: |
5035 | g_value_set_string (value, v_string: "" ); |
5036 | break; |
5037 | |
5038 | case LAST_PROP + GDK_TOPLEVEL_PROP_STARTUP_ID: |
5039 | g_value_set_string (value, v_string: "" ); |
5040 | break; |
5041 | |
5042 | case LAST_PROP + GDK_TOPLEVEL_PROP_TRANSIENT_FOR: |
5043 | g_value_set_object (value, v_object: surface->transient_for); |
5044 | break; |
5045 | |
5046 | case LAST_PROP + GDK_TOPLEVEL_PROP_MODAL: |
5047 | g_value_set_boolean (value, v_boolean: surface->modal_hint); |
5048 | break; |
5049 | |
5050 | case LAST_PROP + GDK_TOPLEVEL_PROP_ICON_LIST: |
5051 | g_value_set_pointer (value, NULL); |
5052 | break; |
5053 | |
5054 | case LAST_PROP + GDK_TOPLEVEL_PROP_DECORATED: |
5055 | { |
5056 | GdkWMDecoration decorations = GDK_DECOR_ALL; |
5057 | gdk_x11_surface_get_decorations (surface, decorations: &decorations); |
5058 | g_value_set_boolean (value, v_boolean: decorations != 0); |
5059 | } |
5060 | break; |
5061 | |
5062 | case LAST_PROP + GDK_TOPLEVEL_PROP_DELETABLE: |
5063 | { |
5064 | GdkWMFunction functions = GDK_FUNC_ALL; |
5065 | gdk_x11_surface_get_functions (surface, functions: &functions); |
5066 | g_value_set_boolean (value, v_boolean: functions == GDK_FUNC_ALL); |
5067 | } |
5068 | break; |
5069 | |
5070 | case LAST_PROP + GDK_TOPLEVEL_PROP_FULLSCREEN_MODE: |
5071 | g_value_set_enum (value, v_enum: surface->fullscreen_mode); |
5072 | break; |
5073 | |
5074 | case LAST_PROP + GDK_TOPLEVEL_PROP_SHORTCUTS_INHIBITED: |
5075 | g_value_set_boolean (value, v_boolean: surface->shortcuts_inhibited); |
5076 | break; |
5077 | |
5078 | default: |
5079 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
5080 | break; |
5081 | } |
5082 | } |
5083 | |
5084 | static void |
5085 | gdk_x11_toplevel_class_init (GdkX11ToplevelClass *class) |
5086 | { |
5087 | GObjectClass *object_class = G_OBJECT_CLASS (class); |
5088 | |
5089 | object_class->get_property = gdk_x11_toplevel_get_property; |
5090 | object_class->set_property = gdk_x11_toplevel_set_property; |
5091 | |
5092 | gdk_toplevel_install_properties (object_class, LAST_PROP); |
5093 | } |
5094 | |
5095 | static void |
5096 | gdk_x11_toplevel_present (GdkToplevel *toplevel, |
5097 | GdkToplevelLayout *layout) |
5098 | { |
5099 | GdkSurface *surface = GDK_SURFACE (toplevel); |
5100 | GdkX11Surface *impl = GDK_X11_SURFACE (surface); |
5101 | int width, height; |
5102 | gboolean was_mapped; |
5103 | gboolean maximize; |
5104 | gboolean fullscreen; |
5105 | |
5106 | if (surface->destroyed) |
5107 | return; |
5108 | |
5109 | was_mapped = GDK_SURFACE_IS_MAPPED (surface); |
5110 | |
5111 | gdk_x11_surface_unminimize (surface); |
5112 | |
5113 | g_clear_pointer (&impl->toplevel_layout, gdk_toplevel_layout_unref); |
5114 | impl->toplevel_layout = gdk_toplevel_layout_copy (layout); |
5115 | |
5116 | if (compute_toplevel_size (surface, DONT_UPDATE_GEOMETRY, width: &width, height: &height)) |
5117 | gdk_x11_surface_toplevel_resize (surface, width, height); |
5118 | |
5119 | if (gdk_toplevel_layout_get_maximized (layout, maximized: &maximize)) |
5120 | { |
5121 | if (maximize) |
5122 | gdk_x11_surface_maximize (surface); |
5123 | else |
5124 | gdk_x11_surface_unmaximize (surface); |
5125 | } |
5126 | |
5127 | if (gdk_toplevel_layout_get_fullscreen (layout, fullscreen: &fullscreen)) |
5128 | { |
5129 | if (fullscreen) |
5130 | { |
5131 | GdkMonitor *fullscreen_monitor = |
5132 | gdk_toplevel_layout_get_fullscreen_monitor (layout); |
5133 | |
5134 | if (fullscreen_monitor) |
5135 | gdk_x11_surface_fullscreen_on_monitor (surface, monitor: fullscreen_monitor); |
5136 | else |
5137 | gdk_x11_surface_fullscreen (surface); |
5138 | } |
5139 | else |
5140 | { |
5141 | gdk_x11_surface_unfullscreen (surface); |
5142 | } |
5143 | } |
5144 | |
5145 | impl->next_layout.surface_geometry_dirty = TRUE; |
5146 | gdk_surface_request_layout (surface); |
5147 | |
5148 | if (!was_mapped) |
5149 | gdk_surface_set_is_mapped (surface, TRUE); |
5150 | |
5151 | gdk_x11_surface_show (surface, already_mapped: was_mapped); |
5152 | |
5153 | if (!was_mapped) |
5154 | gdk_surface_invalidate_rect (surface, NULL); |
5155 | } |
5156 | |
5157 | static gboolean |
5158 | gdk_x11_toplevel_minimize (GdkToplevel *toplevel) |
5159 | { |
5160 | gdk_x11_surface_minimize (GDK_SURFACE (toplevel)); |
5161 | |
5162 | return TRUE; |
5163 | } |
5164 | |
5165 | static gboolean |
5166 | gdk_x11_toplevel_lower (GdkToplevel *toplevel) |
5167 | { |
5168 | gdk_x11_surface_lower (GDK_SURFACE (toplevel)); |
5169 | |
5170 | return TRUE; |
5171 | } |
5172 | |
5173 | static void |
5174 | gdk_x11_toplevel_focus (GdkToplevel *toplevel, |
5175 | guint32 timestamp) |
5176 | { |
5177 | gdk_x11_surface_focus (GDK_SURFACE (toplevel), timestamp); |
5178 | } |
5179 | |
5180 | static gboolean |
5181 | (GdkToplevel *toplevel, |
5182 | GdkEvent *event) |
5183 | { |
5184 | return gdk_x11_surface_show_window_menu (GDK_SURFACE (toplevel), event); |
5185 | } |
5186 | |
5187 | static gboolean |
5188 | gdk_x11_toplevel_supports_edge_constraints (GdkToplevel *toplevel) |
5189 | { |
5190 | return gdk_x11_surface_supports_edge_constraints (GDK_SURFACE (toplevel)); |
5191 | } |
5192 | |
5193 | static void |
5194 | gdk_x11_toplevel_inhibit_system_shortcuts (GdkToplevel *toplevel, |
5195 | GdkEvent *gdk_event) |
5196 | { |
5197 | GdkSurface *surface = GDK_SURFACE (toplevel); |
5198 | GdkSeat *gdk_seat; |
5199 | GdkGrabStatus status; |
5200 | |
5201 | if (surface->shortcuts_inhibited) |
5202 | return; /* Already inhibited */ |
5203 | |
5204 | if (!(surface->state & GDK_TOPLEVEL_STATE_FOCUSED)) |
5205 | return; |
5206 | |
5207 | gdk_seat = gdk_surface_get_seat_from_event (surface, event: gdk_event); |
5208 | |
5209 | if (!(gdk_seat_get_capabilities (seat: gdk_seat) & GDK_SEAT_CAPABILITY_KEYBOARD)) |
5210 | return; |
5211 | |
5212 | status = gdk_seat_grab (seat: gdk_seat, surface, capabilities: GDK_SEAT_CAPABILITY_KEYBOARD, |
5213 | TRUE, NULL, event: gdk_event, NULL, NULL); |
5214 | |
5215 | if (status != GDK_GRAB_SUCCESS) |
5216 | return; |
5217 | |
5218 | surface->shortcuts_inhibited = TRUE; |
5219 | surface->current_shortcuts_inhibited_seat = gdk_seat; |
5220 | g_object_notify (G_OBJECT (toplevel), property_name: "shortcuts-inhibited" ); |
5221 | } |
5222 | |
5223 | static void |
5224 | gdk_x11_toplevel_restore_system_shortcuts (GdkToplevel *toplevel) |
5225 | { |
5226 | GdkSurface *surface = GDK_SURFACE (toplevel); |
5227 | GdkSeat *gdk_seat; |
5228 | |
5229 | if (!surface->shortcuts_inhibited) |
5230 | return; /* Not inhibited */ |
5231 | |
5232 | gdk_seat = surface->current_shortcuts_inhibited_seat; |
5233 | gdk_seat_ungrab (seat: gdk_seat); |
5234 | surface->current_shortcuts_inhibited_seat = NULL; |
5235 | |
5236 | surface->shortcuts_inhibited = FALSE; |
5237 | g_object_notify (G_OBJECT (toplevel), property_name: "shortcuts-inhibited" ); |
5238 | } |
5239 | |
5240 | static void |
5241 | gdk_x11_toplevel_state_callback (GdkSurface *surface) |
5242 | { |
5243 | if (surface->state & GDK_TOPLEVEL_STATE_FOCUSED) |
5244 | return; |
5245 | |
5246 | if (surface->shortcuts_inhibited) |
5247 | gdk_x11_toplevel_restore_system_shortcuts (toplevel: GDK_TOPLEVEL (ptr: surface)); |
5248 | } |
5249 | |
5250 | static gboolean |
5251 | gdk_x11_toplevel_event_callback (GdkSurface *surface, |
5252 | GdkEvent *gdk_event) |
5253 | { |
5254 | GdkSeat *gdk_seat; |
5255 | |
5256 | if (!surface->shortcuts_inhibited) |
5257 | return FALSE; |
5258 | |
5259 | if (gdk_event_get_event_type (event: gdk_event) != GDK_GRAB_BROKEN) |
5260 | return FALSE; |
5261 | |
5262 | gdk_seat = gdk_surface_get_seat_from_event (surface, event: gdk_event); |
5263 | if (gdk_seat != surface->current_shortcuts_inhibited_seat) |
5264 | return FALSE; |
5265 | |
5266 | surface->current_shortcuts_inhibited_seat = NULL; |
5267 | surface->shortcuts_inhibited = FALSE; |
5268 | g_object_notify (G_OBJECT (surface), property_name: "shortcuts-inhibited" ); |
5269 | |
5270 | return FALSE; |
5271 | } |
5272 | |
5273 | static void |
5274 | gdk_x11_toplevel_iface_init (GdkToplevelInterface *iface) |
5275 | { |
5276 | iface->present = gdk_x11_toplevel_present; |
5277 | iface->minimize = gdk_x11_toplevel_minimize; |
5278 | iface->lower = gdk_x11_toplevel_lower; |
5279 | iface->focus = gdk_x11_toplevel_focus; |
5280 | iface->show_window_menu = gdk_x11_toplevel_show_window_menu; |
5281 | iface->supports_edge_constraints = gdk_x11_toplevel_supports_edge_constraints; |
5282 | iface->inhibit_system_shortcuts = gdk_x11_toplevel_inhibit_system_shortcuts; |
5283 | iface->restore_system_shortcuts = gdk_x11_toplevel_restore_system_shortcuts; |
5284 | iface->begin_resize = gdk_x11_toplevel_begin_resize; |
5285 | iface->begin_move = gdk_x11_toplevel_begin_move; |
5286 | } |
5287 | |
5288 | typedef struct { |
5289 | GdkX11Surface parent_instance; |
5290 | } GdkX11DragSurface; |
5291 | |
5292 | typedef struct { |
5293 | GdkX11SurfaceClass parent_class; |
5294 | } GdkX11DragSurfaceClass; |
5295 | |
5296 | static void gdk_x11_drag_surface_iface_init (GdkDragSurfaceInterface *iface); |
5297 | |
5298 | G_DEFINE_TYPE_WITH_CODE (GdkX11DragSurface, gdk_x11_drag_surface, GDK_TYPE_X11_SURFACE, |
5299 | G_IMPLEMENT_INTERFACE (GDK_TYPE_DRAG_SURFACE, |
5300 | gdk_x11_drag_surface_iface_init)) |
5301 | |
5302 | static void |
5303 | gdk_x11_drag_surface_init (GdkX11DragSurface *surface) |
5304 | { |
5305 | } |
5306 | |
5307 | static void |
5308 | gdk_x11_drag_surface_class_init (GdkX11DragSurfaceClass *class) |
5309 | { |
5310 | } |
5311 | |
5312 | static gboolean |
5313 | gdk_x11_drag_surface_present (GdkDragSurface *drag_surface, |
5314 | int width, |
5315 | int height) |
5316 | { |
5317 | GdkSurface *surface = GDK_SURFACE (drag_surface); |
5318 | |
5319 | gdk_x11_surface_toplevel_resize (surface, width, height); |
5320 | gdk_surface_set_is_mapped (surface, TRUE); |
5321 | gdk_x11_surface_show (surface, FALSE); |
5322 | gdk_surface_invalidate_rect (surface, NULL); |
5323 | |
5324 | return TRUE; |
5325 | } |
5326 | |
5327 | static void |
5328 | gdk_x11_drag_surface_iface_init (GdkDragSurfaceInterface *iface) |
5329 | { |
5330 | iface->present = gdk_x11_drag_surface_present; |
5331 | } |
5332 | |