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 <cairo-gobject.h> |
29 | |
30 | #include "gdksurface.h" |
31 | |
32 | #include "gdk-private.h" |
33 | #include "gdkcontentprovider.h" |
34 | #include "gdkdeviceprivate.h" |
35 | #include "gdkdisplayprivate.h" |
36 | #include "gdkdragsurfaceprivate.h" |
37 | #include "gdkeventsprivate.h" |
38 | #include "gdkframeclockidleprivate.h" |
39 | #include "gdkglcontextprivate.h" |
40 | #include "gdkintl.h" |
41 | #include "gdkmarshalers.h" |
42 | #include "gdkpopupprivate.h" |
43 | #include "gdkrectangle.h" |
44 | #include "gdktoplevelprivate.h" |
45 | #include "gdkvulkancontext.h" |
46 | |
47 | #include <math.h> |
48 | |
49 | #ifdef HAVE_EGL |
50 | #include <epoxy/egl.h> |
51 | #endif |
52 | |
53 | /** |
54 | * GdkSurface: |
55 | * |
56 | * A `GdkSurface` is a rectangular region on the screen. |
57 | * |
58 | * It’s a low-level object, used to implement high-level objects |
59 | * such as [class@Gtk.Window] or [class@Gtk.Dialog] in GTK. |
60 | * |
61 | * The surfaces you see in practice are either [iface@Gdk.Toplevel] or |
62 | * [iface@Gdk.Popup], and those interfaces provide much of the required |
63 | * API to interact with these surfaces. Other, more specialized surface |
64 | * types exist, but you will rarely interact with them directly. |
65 | */ |
66 | |
67 | typedef struct _GdkSurfacePrivate GdkSurfacePrivate; |
68 | |
69 | struct _GdkSurfacePrivate |
70 | { |
71 | gpointer egl_native_window; |
72 | #ifdef HAVE_EGL |
73 | EGLSurface egl_surface; |
74 | gboolean egl_surface_high_depth; |
75 | #endif |
76 | |
77 | gpointer widget; |
78 | }; |
79 | |
80 | enum { |
81 | LAYOUT, |
82 | RENDER, |
83 | EVENT, |
84 | ENTER_MONITOR, |
85 | LEAVE_MONITOR, |
86 | LAST_SIGNAL |
87 | }; |
88 | |
89 | enum { |
90 | PROP_0, |
91 | PROP_CURSOR, |
92 | PROP_DISPLAY, |
93 | PROP_FRAME_CLOCK, |
94 | PROP_MAPPED, |
95 | PROP_WIDTH, |
96 | PROP_HEIGHT, |
97 | PROP_SCALE_FACTOR, |
98 | LAST_PROP |
99 | }; |
100 | |
101 | /* Global info */ |
102 | |
103 | static void gdk_surface_finalize (GObject *object); |
104 | |
105 | static void gdk_surface_set_property (GObject *object, |
106 | guint prop_id, |
107 | const GValue *value, |
108 | GParamSpec *pspec); |
109 | static void gdk_surface_get_property (GObject *object, |
110 | guint prop_id, |
111 | GValue *value, |
112 | GParamSpec *pspec); |
113 | |
114 | static void update_cursor (GdkDisplay *display, |
115 | GdkDevice *device); |
116 | |
117 | static void gdk_surface_set_frame_clock (GdkSurface *surface, |
118 | GdkFrameClock *clock); |
119 | |
120 | static void gdk_surface_queue_set_is_mapped (GdkSurface *surface, |
121 | gboolean is_mapped); |
122 | |
123 | |
124 | static guint signals[LAST_SIGNAL] = { 0 }; |
125 | static GParamSpec *properties[LAST_PROP] = { NULL, }; |
126 | |
127 | G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GdkSurface, gdk_surface, G_TYPE_OBJECT) |
128 | |
129 | static gboolean |
130 | gdk_surface_real_beep (GdkSurface *surface) |
131 | { |
132 | return FALSE; |
133 | } |
134 | |
135 | static GdkDisplay * |
136 | get_display_for_surface (GdkSurface *primary, |
137 | GdkSurface *secondary) |
138 | { |
139 | GdkDisplay *display = primary->display; |
140 | |
141 | if (display) |
142 | return display; |
143 | |
144 | display = secondary->display; |
145 | |
146 | if (display) |
147 | return display; |
148 | |
149 | g_warning ("no display for surface, using default" ); |
150 | return gdk_display_get_default (); |
151 | } |
152 | |
153 | static GdkMonitor * |
154 | get_monitor_for_rect (GdkDisplay *display, |
155 | const GdkRectangle *rect, |
156 | void (*get_bounds) (GdkMonitor *monitor, |
157 | GdkRectangle *bounds)) |
158 | { |
159 | int biggest_area = G_MININT; |
160 | GdkMonitor *best_monitor = NULL; |
161 | GdkMonitor *monitor; |
162 | GdkRectangle workarea; |
163 | GdkRectangle intersection; |
164 | GListModel *monitors; |
165 | guint i; |
166 | |
167 | monitors = gdk_display_get_monitors (self: display); |
168 | for (i = 0; i < g_list_model_get_n_items (list: monitors); i++) |
169 | { |
170 | monitor = g_list_model_get_item (list: monitors, position: i); |
171 | get_bounds (monitor, &workarea); |
172 | |
173 | if (gdk_rectangle_intersect (src1: &workarea, src2: rect, dest: &intersection)) |
174 | { |
175 | if (intersection.width * intersection.height > biggest_area) |
176 | { |
177 | biggest_area = intersection.width * intersection.height; |
178 | best_monitor = monitor; |
179 | } |
180 | } |
181 | g_object_unref (object: monitor); |
182 | } |
183 | |
184 | return best_monitor; |
185 | } |
186 | |
187 | static int |
188 | get_anchor_x_sign (GdkGravity anchor) |
189 | { |
190 | switch (anchor) |
191 | { |
192 | case GDK_GRAVITY_STATIC: |
193 | case GDK_GRAVITY_NORTH_WEST: |
194 | case GDK_GRAVITY_WEST: |
195 | case GDK_GRAVITY_SOUTH_WEST: |
196 | return -1; |
197 | |
198 | default: |
199 | case GDK_GRAVITY_NORTH: |
200 | case GDK_GRAVITY_CENTER: |
201 | case GDK_GRAVITY_SOUTH: |
202 | return 0; |
203 | |
204 | case GDK_GRAVITY_NORTH_EAST: |
205 | case GDK_GRAVITY_EAST: |
206 | case GDK_GRAVITY_SOUTH_EAST: |
207 | return 1; |
208 | } |
209 | } |
210 | |
211 | static int |
212 | get_anchor_y_sign (GdkGravity anchor) |
213 | { |
214 | switch (anchor) |
215 | { |
216 | case GDK_GRAVITY_STATIC: |
217 | case GDK_GRAVITY_NORTH_WEST: |
218 | case GDK_GRAVITY_NORTH: |
219 | case GDK_GRAVITY_NORTH_EAST: |
220 | return -1; |
221 | |
222 | default: |
223 | case GDK_GRAVITY_WEST: |
224 | case GDK_GRAVITY_CENTER: |
225 | case GDK_GRAVITY_EAST: |
226 | return 0; |
227 | |
228 | case GDK_GRAVITY_SOUTH_WEST: |
229 | case GDK_GRAVITY_SOUTH: |
230 | case GDK_GRAVITY_SOUTH_EAST: |
231 | return 1; |
232 | } |
233 | } |
234 | |
235 | static int |
236 | maybe_flip_position (int bounds_pos, |
237 | int bounds_size, |
238 | int rect_pos, |
239 | int rect_size, |
240 | int surface_size, |
241 | int rect_sign, |
242 | int surface_sign, |
243 | int offset, |
244 | gboolean flip, |
245 | gboolean *flipped) |
246 | { |
247 | int primary; |
248 | int secondary; |
249 | |
250 | *flipped = FALSE; |
251 | primary = rect_pos + (1 + rect_sign) * rect_size / 2 + offset - (1 + surface_sign) * surface_size / 2; |
252 | |
253 | if (!flip || (primary >= bounds_pos && primary + surface_size <= bounds_pos + bounds_size)) |
254 | return primary; |
255 | |
256 | *flipped = TRUE; |
257 | secondary = rect_pos + (1 - rect_sign) * rect_size / 2 - offset - (1 - surface_sign) * surface_size / 2; |
258 | |
259 | if (secondary >= bounds_pos && secondary + surface_size <= bounds_pos + bounds_size) |
260 | return secondary; |
261 | |
262 | *flipped = FALSE; |
263 | return primary; |
264 | } |
265 | |
266 | GdkMonitor * |
267 | gdk_surface_get_layout_monitor (GdkSurface *surface, |
268 | GdkPopupLayout *layout, |
269 | void (*get_bounds) (GdkMonitor *monitor, |
270 | GdkRectangle *bounds)) |
271 | { |
272 | GdkDisplay *display; |
273 | GdkRectangle root_rect; |
274 | |
275 | root_rect = *gdk_popup_layout_get_anchor_rect (layout); |
276 | gdk_surface_get_root_coords (surface: surface->parent, |
277 | x: root_rect.x, |
278 | y: root_rect.y, |
279 | root_x: &root_rect.x, |
280 | root_y: &root_rect.y); |
281 | |
282 | root_rect.width = MAX (1, root_rect.width); |
283 | root_rect.height = MAX (1, root_rect.height); |
284 | |
285 | display = get_display_for_surface (primary: surface, secondary: surface->transient_for); |
286 | return get_monitor_for_rect (display, rect: &root_rect, get_bounds); |
287 | } |
288 | |
289 | void |
290 | (GdkSurface *surface, |
291 | int width, |
292 | int height, |
293 | int shadow_left, |
294 | int shadow_right, |
295 | int shadow_top, |
296 | int shadow_bottom, |
297 | GdkMonitor *monitor, |
298 | GdkRectangle *bounds, |
299 | GdkPopupLayout *layout, |
300 | GdkRectangle *out_final_rect) |
301 | { |
302 | GdkRectangle root_rect; |
303 | GdkGravity rect_anchor; |
304 | GdkGravity surface_anchor; |
305 | int rect_anchor_dx; |
306 | int rect_anchor_dy; |
307 | GdkAnchorHints anchor_hints; |
308 | GdkRectangle final_rect; |
309 | gboolean flipped_x; |
310 | gboolean flipped_y; |
311 | int x, y; |
312 | |
313 | g_return_if_fail (GDK_IS_POPUP (surface)); |
314 | |
315 | root_rect = *gdk_popup_layout_get_anchor_rect (layout); |
316 | gdk_surface_get_root_coords (surface: surface->parent, |
317 | x: root_rect.x, |
318 | y: root_rect.y, |
319 | root_x: &root_rect.x, |
320 | root_y: &root_rect.y); |
321 | |
322 | rect_anchor = gdk_popup_layout_get_rect_anchor (layout); |
323 | surface_anchor = gdk_popup_layout_get_surface_anchor (layout); |
324 | gdk_popup_layout_get_offset (layout, dx: &rect_anchor_dx, dy: &rect_anchor_dy); |
325 | anchor_hints = gdk_popup_layout_get_anchor_hints (layout); |
326 | |
327 | final_rect.width = width - shadow_left - shadow_right; |
328 | final_rect.height = height - shadow_top - shadow_bottom; |
329 | final_rect.x = maybe_flip_position (bounds_pos: bounds->x, |
330 | bounds_size: bounds->width, |
331 | rect_pos: root_rect.x, |
332 | rect_size: root_rect.width, |
333 | surface_size: final_rect.width, |
334 | rect_sign: get_anchor_x_sign (anchor: rect_anchor), |
335 | surface_sign: get_anchor_x_sign (anchor: surface_anchor), |
336 | offset: rect_anchor_dx, |
337 | flip: anchor_hints & GDK_ANCHOR_FLIP_X, |
338 | flipped: &flipped_x); |
339 | final_rect.y = maybe_flip_position (bounds_pos: bounds->y, |
340 | bounds_size: bounds->height, |
341 | rect_pos: root_rect.y, |
342 | rect_size: root_rect.height, |
343 | surface_size: final_rect.height, |
344 | rect_sign: get_anchor_y_sign (anchor: rect_anchor), |
345 | surface_sign: get_anchor_y_sign (anchor: surface_anchor), |
346 | offset: rect_anchor_dy, |
347 | flip: anchor_hints & GDK_ANCHOR_FLIP_Y, |
348 | flipped: &flipped_y); |
349 | |
350 | if (anchor_hints & GDK_ANCHOR_SLIDE_X) |
351 | { |
352 | if (final_rect.x + final_rect.width > bounds->x + bounds->width) |
353 | final_rect.x = bounds->x + bounds->width - final_rect.width; |
354 | |
355 | if (final_rect.x < bounds->x) |
356 | final_rect.x = bounds->x; |
357 | } |
358 | |
359 | if (anchor_hints & GDK_ANCHOR_SLIDE_Y) |
360 | { |
361 | if (final_rect.y + final_rect.height > bounds->y + bounds->height) |
362 | final_rect.y = bounds->y + bounds->height - final_rect.height; |
363 | |
364 | if (final_rect.y < bounds->y) |
365 | final_rect.y = bounds->y; |
366 | } |
367 | |
368 | if (anchor_hints & GDK_ANCHOR_RESIZE_X) |
369 | { |
370 | if (final_rect.x < bounds->x) |
371 | { |
372 | final_rect.width -= bounds->x - final_rect.x; |
373 | final_rect.x = bounds->x; |
374 | } |
375 | |
376 | if (final_rect.x + final_rect.width > bounds->x + bounds->width) |
377 | final_rect.width = bounds->x + bounds->width - final_rect.x; |
378 | } |
379 | |
380 | if (anchor_hints & GDK_ANCHOR_RESIZE_Y) |
381 | { |
382 | if (final_rect.y < bounds->y) |
383 | { |
384 | final_rect.height -= bounds->y - final_rect.y; |
385 | final_rect.y = bounds->y; |
386 | } |
387 | |
388 | if (final_rect.y + final_rect.height > bounds->y + bounds->height) |
389 | final_rect.height = bounds->y + bounds->height - final_rect.y; |
390 | } |
391 | |
392 | final_rect.x -= shadow_left; |
393 | final_rect.y -= shadow_top; |
394 | final_rect.width += shadow_left + shadow_right; |
395 | final_rect.height += shadow_top + shadow_bottom; |
396 | |
397 | gdk_surface_get_origin (surface: surface->parent, x: &x, y: &y); |
398 | final_rect.x -= x; |
399 | final_rect.y -= y; |
400 | |
401 | if (flipped_x) |
402 | { |
403 | rect_anchor = gdk_gravity_flip_horizontally (anchor: rect_anchor); |
404 | surface_anchor = gdk_gravity_flip_horizontally (anchor: surface_anchor); |
405 | } |
406 | if (flipped_y) |
407 | { |
408 | rect_anchor = gdk_gravity_flip_vertically (anchor: rect_anchor); |
409 | surface_anchor = gdk_gravity_flip_vertically (anchor: surface_anchor); |
410 | } |
411 | |
412 | surface->popup.rect_anchor = rect_anchor; |
413 | surface->popup.surface_anchor = surface_anchor; |
414 | |
415 | *out_final_rect = final_rect; |
416 | } |
417 | |
418 | /* Since GdkEvent is a GTypeInstance, GValue can only store it as a pointer, |
419 | * and GClosure does not know how to handle its memory management. To avoid |
420 | * the event going away in the middle of the signal emission, we provide a |
421 | * marshaller that keeps the event alive for the duration of the closure. |
422 | */ |
423 | static void |
424 | gdk_surface_event_marshaller (GClosure *closure, |
425 | GValue *return_value, |
426 | guint n_param_values, |
427 | const GValue *param_values, |
428 | gpointer invocation_hint, |
429 | gpointer marshal_data) |
430 | { |
431 | GdkEvent *event = g_value_get_pointer (value: ¶m_values[1]); |
432 | |
433 | gdk_event_ref (event); |
434 | |
435 | _gdk_marshal_BOOLEAN__POINTER (closure, |
436 | return_value, |
437 | n_param_values, |
438 | param_values, |
439 | invocation_hint, |
440 | marshal_data); |
441 | |
442 | |
443 | gdk_event_unref (event); |
444 | } |
445 | |
446 | static void |
447 | gdk_surface_event_marshallerv (GClosure *closure, |
448 | GValue *return_value, |
449 | gpointer instance, |
450 | va_list args, |
451 | gpointer marshal_data, |
452 | int n_params, |
453 | GType *param_types) |
454 | { |
455 | va_list args_copy; |
456 | GdkEvent *event; |
457 | |
458 | G_VA_COPY (args_copy, args); |
459 | event = va_arg (args_copy, gpointer); |
460 | |
461 | gdk_event_ref (event); |
462 | |
463 | _gdk_marshal_BOOLEAN__POINTERv (closure, |
464 | return_value, |
465 | instance, |
466 | args, |
467 | marshal_data, |
468 | n_params, |
469 | param_types); |
470 | |
471 | gdk_event_unref (event); |
472 | |
473 | va_end (args_copy); |
474 | } |
475 | |
476 | static void |
477 | gdk_surface_init (GdkSurface *surface) |
478 | { |
479 | /* 0-initialization is good for all other fields. */ |
480 | |
481 | surface->state = 0; |
482 | surface->fullscreen_mode = GDK_FULLSCREEN_ON_CURRENT_MONITOR; |
483 | surface->width = 1; |
484 | surface->height = 1; |
485 | |
486 | surface->alpha = 255; |
487 | |
488 | surface->device_cursor = g_hash_table_new_full (NULL, NULL, |
489 | NULL, value_destroy_func: g_object_unref); |
490 | } |
491 | |
492 | static void |
493 | gdk_surface_class_init (GdkSurfaceClass *klass) |
494 | { |
495 | GObjectClass *object_class = G_OBJECT_CLASS (klass); |
496 | |
497 | object_class->finalize = gdk_surface_finalize; |
498 | object_class->set_property = gdk_surface_set_property; |
499 | object_class->get_property = gdk_surface_get_property; |
500 | |
501 | klass->beep = gdk_surface_real_beep; |
502 | |
503 | /** |
504 | * GdkSurface:cursor: (attributes org.gtk.Property.get=gdk_surface_get_cursor org.gtk.Property.set=gdk_surface_set_cursor) |
505 | * |
506 | * The mouse pointer for the `GdkSurface`. |
507 | */ |
508 | properties[PROP_CURSOR] = |
509 | g_param_spec_object (name: "cursor" , |
510 | P_("Cursor" ), |
511 | P_("Cursor" ), |
512 | GDK_TYPE_CURSOR, |
513 | flags: G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); |
514 | |
515 | /** |
516 | * GdkSurface:display: (attributes org.gtk.Property.get=gdk_surface_get_display) |
517 | * |
518 | * The `GdkDisplay` connection of the surface. |
519 | */ |
520 | properties[PROP_DISPLAY] = |
521 | g_param_spec_object (name: "display" , |
522 | P_("Display" ), |
523 | P_("Display" ), |
524 | GDK_TYPE_DISPLAY, |
525 | flags: G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); |
526 | |
527 | /** |
528 | * GdkSurface:frame-clock: (attributes org.gtk.Property.get=gdk_surface_get_frame_clock) |
529 | * |
530 | * The `GdkFrameClock` of the surface. |
531 | */ |
532 | properties[PROP_FRAME_CLOCK] = |
533 | g_param_spec_object (name: "frame-clock" , |
534 | P_("Frame Clock" ), |
535 | P_("Frame Clock" ), |
536 | GDK_TYPE_FRAME_CLOCK, |
537 | flags: G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); |
538 | |
539 | /** |
540 | * GdkSurface:mapped: (attributes org.gtk.Property.get=gdk_surface_get_mapped) |
541 | * |
542 | * Whether the surface is mapped. |
543 | */ |
544 | properties[PROP_MAPPED] = |
545 | g_param_spec_boolean (name: "mapped" , |
546 | P_("Mapped" ), |
547 | P_("Mapped" ), |
548 | FALSE, |
549 | flags: G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); |
550 | |
551 | /** |
552 | * GdkSurface:width: (attributes org.gtk.Property.get=gdk_surface_get_width) |
553 | * |
554 | * The width of the surface in pixels. |
555 | */ |
556 | properties[PROP_WIDTH] = |
557 | g_param_spec_int (name: "width" , |
558 | P_("Width" ), |
559 | P_("Width" ), |
560 | minimum: 0, G_MAXINT, default_value: 0, |
561 | flags: G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); |
562 | |
563 | /** |
564 | * GdkSurface:height: (attributes org.gtk.Property.get=gdk_surface_get_height) |
565 | * |
566 | * The height of the surface, in pixels. |
567 | */ |
568 | properties[PROP_HEIGHT] = |
569 | g_param_spec_int (name: "height" , |
570 | P_("Height" ), |
571 | P_("Height" ), |
572 | minimum: 0, G_MAXINT, default_value: 0, |
573 | flags: G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); |
574 | |
575 | /** |
576 | * GdkSurface:scale-factor: (attributes org.gtk.Property.get=gdk_surface_get_scale_factor) |
577 | * |
578 | * The scale factor of the surface. |
579 | */ |
580 | properties[PROP_SCALE_FACTOR] = |
581 | g_param_spec_int (name: "scale-factor" , |
582 | P_("Scale factor" ), |
583 | P_("Scale factor" ), |
584 | minimum: 1, G_MAXINT, default_value: 1, |
585 | flags: G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); |
586 | |
587 | g_object_class_install_properties (oclass: object_class, n_pspecs: LAST_PROP, pspecs: properties); |
588 | |
589 | /** |
590 | * GdkSurface::layout: |
591 | * @surface: the `GdkSurface` |
592 | * @width: the current width |
593 | * @height: the current height |
594 | * |
595 | * Emitted when the size of @surface is changed, or when relayout should |
596 | * be performed. |
597 | * |
598 | * Surface size is reported in ”application pixels”, not |
599 | * ”device pixels” (see gdk_surface_get_scale_factor()). |
600 | */ |
601 | signals[LAYOUT] = |
602 | g_signal_new (signal_name: g_intern_static_string (string: "layout" ), |
603 | G_OBJECT_CLASS_TYPE (object_class), |
604 | signal_flags: G_SIGNAL_RUN_FIRST, |
605 | class_offset: 0, |
606 | NULL, |
607 | NULL, |
608 | c_marshaller: _gdk_marshal_VOID__INT_INT, |
609 | G_TYPE_NONE, |
610 | n_params: 2, |
611 | G_TYPE_INT, |
612 | G_TYPE_INT); |
613 | g_signal_set_va_marshaller (signal_id: signals[LAYOUT], |
614 | G_OBJECT_CLASS_TYPE (object_class), |
615 | va_marshaller: _gdk_marshal_VOID__INT_INTv); |
616 | |
617 | /** |
618 | * GdkSurface::render: |
619 | * @surface: the `GdkSurface` |
620 | * @region: the region that needs to be redrawn |
621 | * |
622 | * Emitted when part of the surface needs to be redrawn. |
623 | * |
624 | * Returns: %TRUE to indicate that the signal has been handled |
625 | */ |
626 | signals[RENDER] = |
627 | g_signal_new (signal_name: g_intern_static_string (string: "render" ), |
628 | G_OBJECT_CLASS_TYPE (object_class), |
629 | signal_flags: G_SIGNAL_RUN_LAST, |
630 | class_offset: 0, |
631 | accumulator: g_signal_accumulator_true_handled, |
632 | NULL, |
633 | c_marshaller: _gdk_marshal_BOOLEAN__BOXED, |
634 | G_TYPE_BOOLEAN, |
635 | n_params: 1, |
636 | CAIRO_GOBJECT_TYPE_REGION); |
637 | g_signal_set_va_marshaller (signal_id: signals[RENDER], |
638 | G_OBJECT_CLASS_TYPE (object_class), |
639 | va_marshaller: _gdk_marshal_BOOLEAN__BOXEDv); |
640 | |
641 | /** |
642 | * GdkSurface::event: |
643 | * @surface: the `GdkSurface` |
644 | * @event: (type Gdk.Event): an input event |
645 | * |
646 | * Emitted when GDK receives an input event for @surface. |
647 | * |
648 | * Returns: %TRUE to indicate that the event has been handled |
649 | */ |
650 | signals[EVENT] = |
651 | g_signal_new (signal_name: g_intern_static_string (string: "event" ), |
652 | G_OBJECT_CLASS_TYPE (object_class), |
653 | signal_flags: G_SIGNAL_RUN_LAST, |
654 | class_offset: 0, |
655 | accumulator: g_signal_accumulator_true_handled, |
656 | NULL, |
657 | c_marshaller: gdk_surface_event_marshaller, |
658 | G_TYPE_BOOLEAN, |
659 | n_params: 1, |
660 | G_TYPE_POINTER); |
661 | g_signal_set_va_marshaller (signal_id: signals[EVENT], |
662 | G_OBJECT_CLASS_TYPE (object_class), |
663 | va_marshaller: gdk_surface_event_marshallerv); |
664 | |
665 | /** |
666 | * GdkSurface::enter-monitor: |
667 | * @surface: the `GdkSurface` |
668 | * @monitor: the monitor |
669 | * |
670 | * Emitted when @surface starts being present on the monitor. |
671 | */ |
672 | signals[ENTER_MONITOR] = |
673 | g_signal_new (signal_name: g_intern_static_string (string: "enter-monitor" ), |
674 | G_OBJECT_CLASS_TYPE (object_class), |
675 | signal_flags: G_SIGNAL_RUN_FIRST, |
676 | class_offset: 0, |
677 | NULL, |
678 | NULL, |
679 | NULL, |
680 | G_TYPE_NONE, |
681 | n_params: 1, |
682 | GDK_TYPE_MONITOR); |
683 | |
684 | /** |
685 | * GdkSurface::leave-monitor: |
686 | * @surface: the `GdkSurface` |
687 | * @monitor: the monitor |
688 | * |
689 | * Emitted when @surface stops being present on the monitor. |
690 | */ |
691 | signals[LEAVE_MONITOR] = |
692 | g_signal_new (signal_name: g_intern_static_string (string: "leave-monitor" ), |
693 | G_OBJECT_CLASS_TYPE (object_class), |
694 | signal_flags: G_SIGNAL_RUN_FIRST, |
695 | class_offset: 0, |
696 | NULL, |
697 | NULL, |
698 | NULL, |
699 | G_TYPE_NONE, |
700 | n_params: 1, |
701 | GDK_TYPE_MONITOR); |
702 | } |
703 | |
704 | static void |
705 | seat_removed_cb (GdkDisplay *display, |
706 | GdkSeat *seat, |
707 | GdkSurface *surface) |
708 | { |
709 | GdkDevice *device = gdk_seat_get_pointer (seat); |
710 | |
711 | surface->devices_inside = g_list_remove (list: surface->devices_inside, data: device); |
712 | g_hash_table_remove (hash_table: surface->device_cursor, key: device); |
713 | } |
714 | |
715 | static void |
716 | gdk_surface_finalize (GObject *object) |
717 | { |
718 | GdkSurface *surface = GDK_SURFACE (object); |
719 | |
720 | g_clear_handle_id (&surface->request_motion_id, g_source_remove); |
721 | |
722 | g_signal_handlers_disconnect_by_func (surface->display, |
723 | seat_removed_cb, surface); |
724 | |
725 | if (!GDK_SURFACE_DESTROYED (surface)) |
726 | { |
727 | g_warning ("losing last reference to undestroyed surface" ); |
728 | _gdk_surface_destroy (surface, FALSE); |
729 | } |
730 | |
731 | g_clear_pointer (&surface->input_region, cairo_region_destroy); |
732 | g_clear_object (&surface->cursor); |
733 | g_clear_pointer (&surface->device_cursor, g_hash_table_destroy); |
734 | g_clear_pointer (&surface->devices_inside, g_list_free); |
735 | |
736 | g_clear_object (&surface->display); |
737 | |
738 | g_clear_pointer (&surface->opaque_region, cairo_region_destroy); |
739 | |
740 | if (surface->parent) |
741 | surface->parent->children = g_list_remove (list: surface->parent->children, data: surface); |
742 | |
743 | G_OBJECT_CLASS (gdk_surface_parent_class)->finalize (object); |
744 | } |
745 | |
746 | static void |
747 | gdk_surface_set_property (GObject *object, |
748 | guint prop_id, |
749 | const GValue *value, |
750 | GParamSpec *pspec) |
751 | { |
752 | GdkSurface *surface = GDK_SURFACE (object); |
753 | |
754 | switch (prop_id) |
755 | { |
756 | case PROP_CURSOR: |
757 | gdk_surface_set_cursor (surface, cursor: g_value_get_object (value)); |
758 | break; |
759 | |
760 | case PROP_DISPLAY: |
761 | surface->display = g_value_dup_object (value); |
762 | g_assert (surface->display != NULL); |
763 | g_signal_connect (surface->display, "seat-removed" , |
764 | G_CALLBACK (seat_removed_cb), surface); |
765 | break; |
766 | |
767 | case PROP_FRAME_CLOCK: |
768 | gdk_surface_set_frame_clock (surface, GDK_FRAME_CLOCK (g_value_get_object (value))); |
769 | break; |
770 | |
771 | default: |
772 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
773 | break; |
774 | } |
775 | } |
776 | |
777 | #define GDK_SURFACE_IS_STICKY(surface) (((surface)->state & GDK_TOPLEVEL_STATE_STICKY)) |
778 | |
779 | static void |
780 | gdk_surface_get_property (GObject *object, |
781 | guint prop_id, |
782 | GValue *value, |
783 | GParamSpec *pspec) |
784 | { |
785 | GdkSurface *surface = GDK_SURFACE (object); |
786 | |
787 | switch (prop_id) |
788 | { |
789 | case PROP_CURSOR: |
790 | g_value_set_object (value, v_object: gdk_surface_get_cursor (surface)); |
791 | break; |
792 | |
793 | case PROP_DISPLAY: |
794 | g_value_set_object (value, v_object: surface->display); |
795 | break; |
796 | |
797 | case PROP_FRAME_CLOCK: |
798 | g_value_set_object (value, v_object: surface->frame_clock); |
799 | break; |
800 | |
801 | case PROP_MAPPED: |
802 | g_value_set_boolean (value, GDK_SURFACE_IS_MAPPED (surface)); |
803 | break; |
804 | |
805 | case PROP_WIDTH: |
806 | g_value_set_int (value, v_int: surface->width); |
807 | break; |
808 | |
809 | case PROP_HEIGHT: |
810 | g_value_set_int (value, v_int: surface->height); |
811 | break; |
812 | |
813 | case PROP_SCALE_FACTOR: |
814 | g_value_set_int (value, v_int: gdk_surface_get_scale_factor (surface)); |
815 | break; |
816 | |
817 | default: |
818 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
819 | break; |
820 | } |
821 | } |
822 | |
823 | void |
824 | _gdk_surface_update_size (GdkSurface *surface) |
825 | { |
826 | GSList *l; |
827 | |
828 | for (l = surface->draw_contexts; l; l = l->next) |
829 | gdk_draw_context_surface_resized (context: l->data); |
830 | |
831 | g_object_notify (G_OBJECT (surface), property_name: "width" ); |
832 | g_object_notify (G_OBJECT (surface), property_name: "height" ); |
833 | } |
834 | |
835 | static GdkSurface * |
836 | gdk_surface_new (GdkDisplay *display, |
837 | GdkSurfaceType surface_type, |
838 | GdkSurface *parent, |
839 | int x, |
840 | int y, |
841 | int width, |
842 | int height) |
843 | { |
844 | return gdk_display_create_surface (display, |
845 | surface_type, |
846 | parent, |
847 | x, y, width, height); |
848 | } |
849 | |
850 | /** |
851 | * gdk_surface_new_toplevel: (constructor) |
852 | * @display: the display to create the surface on |
853 | * |
854 | * Creates a new toplevel surface. |
855 | * |
856 | * Returns: (transfer full): the new `GdkSurface` |
857 | */ |
858 | GdkSurface * |
859 | gdk_surface_new_toplevel (GdkDisplay *display) |
860 | { |
861 | g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL); |
862 | |
863 | return gdk_surface_new (display, surface_type: GDK_SURFACE_TOPLEVEL, |
864 | NULL, x: 0, y: 0, width: 1, height: 1); |
865 | } |
866 | |
867 | /** |
868 | * gdk_surface_new_popup: (constructor) |
869 | * @parent: the parent surface to attach the surface to |
870 | * @autohide: whether to hide the surface on outside clicks |
871 | * |
872 | * Create a new popup surface. |
873 | * |
874 | * The surface will be attached to @parent and can be positioned |
875 | * relative to it using [method@Gdk.Popup.present]. |
876 | * |
877 | * Returns: (transfer full): a new `GdkSurface` |
878 | */ |
879 | GdkSurface * |
880 | (GdkSurface *parent, |
881 | gboolean autohide) |
882 | { |
883 | GdkSurface *surface; |
884 | |
885 | g_return_val_if_fail (GDK_IS_SURFACE (parent), NULL); |
886 | |
887 | surface = gdk_surface_new (display: parent->display, surface_type: GDK_SURFACE_POPUP, |
888 | parent, x: 0, y: 0, width: 100, height: 100); |
889 | |
890 | surface->autohide = autohide; |
891 | |
892 | return surface; |
893 | } |
894 | |
895 | static void |
896 | update_pointer_info_foreach (GdkDisplay *display, |
897 | GdkDevice *device, |
898 | GdkPointerSurfaceInfo *pointer_info, |
899 | gpointer user_data) |
900 | { |
901 | GdkSurface *surface = user_data; |
902 | |
903 | if (pointer_info->surface_under_pointer == surface) |
904 | { |
905 | g_object_unref (object: pointer_info->surface_under_pointer); |
906 | pointer_info->surface_under_pointer = NULL; |
907 | } |
908 | } |
909 | |
910 | static void |
911 | surface_remove_from_pointer_info (GdkSurface *surface, |
912 | GdkDisplay *display) |
913 | { |
914 | _gdk_display_pointer_info_foreach (display, |
915 | func: update_pointer_info_foreach, |
916 | user_data: surface); |
917 | } |
918 | |
919 | /** |
920 | * _gdk_surface_destroy_hierarchy: |
921 | * @surface: a `GdkSurface` |
922 | * @recursing_native: If %TRUE, then this is being called because a native |
923 | * parent was destroyed. This generally means that the call to the windowing |
924 | * system to destroy the surface can be omitted, since it will be destroyed |
925 | * as a result of the parent being destroyed. Unless @foreign_destroy. |
926 | * @foreign_destroy: If %TRUE, the surface or a parent was destroyed by some |
927 | * external agency. The surface has already been destroyed and no windowing |
928 | * system calls should be made. (This may never happen for some windowing |
929 | * systems.) |
930 | * |
931 | * Internal function to destroy a surface. Like gdk_surface_destroy(), |
932 | * but does not drop the reference count created by gdk_surface_new(). |
933 | */ |
934 | static void |
935 | _gdk_surface_destroy_hierarchy (GdkSurface *surface, |
936 | gboolean foreign_destroy) |
937 | { |
938 | G_GNUC_UNUSED GdkSurfacePrivate *priv = gdk_surface_get_instance_private (self: surface); |
939 | |
940 | g_return_if_fail (GDK_IS_SURFACE (surface)); |
941 | |
942 | if (GDK_SURFACE_DESTROYED (surface)) |
943 | return; |
944 | |
945 | GDK_SURFACE_GET_CLASS (surface)->destroy (surface, foreign_destroy); |
946 | |
947 | /* backend must have unset this */ |
948 | g_assert (priv->egl_native_window == NULL); |
949 | |
950 | if (surface->gl_paint_context) |
951 | { |
952 | /* Make sure to destroy if current */ |
953 | g_object_run_dispose (G_OBJECT (surface->gl_paint_context)); |
954 | g_object_unref (object: surface->gl_paint_context); |
955 | surface->gl_paint_context = NULL; |
956 | } |
957 | |
958 | if (surface->frame_clock) |
959 | { |
960 | if (surface->parent == NULL) |
961 | g_object_run_dispose (G_OBJECT (surface->frame_clock)); |
962 | gdk_surface_set_frame_clock (surface, NULL); |
963 | } |
964 | |
965 | _gdk_surface_clear_update_area (surface); |
966 | |
967 | g_clear_handle_id (&surface->set_is_mapped_source_id, g_source_remove); |
968 | surface->is_mapped = FALSE; |
969 | surface->pending_is_mapped = FALSE; |
970 | |
971 | surface->destroyed = TRUE; |
972 | |
973 | surface_remove_from_pointer_info (surface, display: surface->display); |
974 | |
975 | if (GDK_IS_TOPLEVEL (ptr: surface)) |
976 | g_object_notify (G_OBJECT (surface), property_name: "state" ); |
977 | g_object_notify_by_pspec (G_OBJECT (surface), pspec: properties[PROP_MAPPED]); |
978 | } |
979 | |
980 | /** |
981 | * _gdk_surface_destroy: |
982 | * @surface: a `GdkSurface` |
983 | * @foreign_destroy: If %TRUE, the surface or a parent was destroyed by some |
984 | * external agency. The surface has already been destroyed and no windowing |
985 | * system calls should be made. (This may never happen for some windowing |
986 | * systems.) |
987 | * |
988 | * Internal function to destroy a surface. Like gdk_surface_destroy(), |
989 | * but does not drop the reference count created by gdk_surface_new(). |
990 | */ |
991 | void |
992 | _gdk_surface_destroy (GdkSurface *surface, |
993 | gboolean foreign_destroy) |
994 | { |
995 | _gdk_surface_destroy_hierarchy (surface, foreign_destroy); |
996 | } |
997 | |
998 | /** |
999 | * gdk_surface_destroy: |
1000 | * @surface: a `GdkSurface` |
1001 | * |
1002 | * Destroys the window system resources associated with @surface and |
1003 | * decrements @surface's reference count. |
1004 | * |
1005 | * The window system resources for all children of @surface are also |
1006 | * destroyed, but the children’s reference counts are not decremented. |
1007 | * |
1008 | * Note that a surface will not be destroyed automatically when its |
1009 | * reference count reaches zero. You must call this function yourself |
1010 | * before that happens. |
1011 | */ |
1012 | void |
1013 | gdk_surface_destroy (GdkSurface *surface) |
1014 | { |
1015 | _gdk_surface_destroy_hierarchy (surface, FALSE); |
1016 | g_object_unref (object: surface); |
1017 | } |
1018 | |
1019 | void |
1020 | gdk_surface_set_widget (GdkSurface *self, |
1021 | gpointer widget) |
1022 | { |
1023 | GdkSurfacePrivate *priv = gdk_surface_get_instance_private (self); |
1024 | |
1025 | priv->widget = widget; |
1026 | } |
1027 | |
1028 | gpointer |
1029 | gdk_surface_get_widget (GdkSurface *self) |
1030 | { |
1031 | GdkSurfacePrivate *priv = gdk_surface_get_instance_private (self); |
1032 | |
1033 | return priv->widget; |
1034 | } |
1035 | |
1036 | /** |
1037 | * gdk_surface_get_display: (attributes org.gtk.Method.get_property=display) |
1038 | * @surface: a `GdkSurface` |
1039 | * |
1040 | * Gets the `GdkDisplay` associated with a `GdkSurface`. |
1041 | * |
1042 | * Returns: (transfer none): the `GdkDisplay` associated with @surface |
1043 | */ |
1044 | GdkDisplay * |
1045 | gdk_surface_get_display (GdkSurface *surface) |
1046 | { |
1047 | g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL); |
1048 | |
1049 | return surface->display; |
1050 | } |
1051 | /** |
1052 | * gdk_surface_is_destroyed: |
1053 | * @surface: a `GdkSurface` |
1054 | * |
1055 | * Check to see if a surface is destroyed. |
1056 | * |
1057 | * Returns: %TRUE if the surface is destroyed |
1058 | */ |
1059 | gboolean |
1060 | gdk_surface_is_destroyed (GdkSurface *surface) |
1061 | { |
1062 | return GDK_SURFACE_DESTROYED (surface); |
1063 | } |
1064 | |
1065 | /** |
1066 | * gdk_surface_get_mapped: (attributes org.gtk.Method.get_property=mapped) |
1067 | * @surface: a `GdkSurface` |
1068 | * |
1069 | * Checks whether the surface has been mapped. |
1070 | * |
1071 | * A surface is mapped with [method@Gdk.Toplevel.present] |
1072 | * or [method@Gdk.Popup.present]. |
1073 | * |
1074 | * Returns: %TRUE if the surface is mapped |
1075 | */ |
1076 | gboolean |
1077 | gdk_surface_get_mapped (GdkSurface *surface) |
1078 | { |
1079 | g_return_val_if_fail (GDK_IS_SURFACE (surface), FALSE); |
1080 | |
1081 | return GDK_SURFACE_IS_MAPPED (surface); |
1082 | } |
1083 | |
1084 | void |
1085 | gdk_surface_set_egl_native_window (GdkSurface *self, |
1086 | gpointer native_window) |
1087 | { |
1088 | #ifdef HAVE_EGL |
1089 | GdkSurfacePrivate *priv = gdk_surface_get_instance_private (self); |
1090 | |
1091 | /* This checks that all EGL platforms we support conform to the same struct sizes. |
1092 | * When this ever fails, there will be some fun times happening for whoever tries |
1093 | * this weird EGL backend... */ |
1094 | G_STATIC_ASSERT (sizeof (gpointer) == sizeof (EGLNativeWindowType)); |
1095 | |
1096 | if (priv->egl_surface != NULL) |
1097 | { |
1098 | gdk_gl_context_clear_current_if_surface (surface: self); |
1099 | eglDestroySurface (gdk_surface_get_display (surface: self), priv->egl_surface); |
1100 | priv->egl_surface = NULL; |
1101 | } |
1102 | |
1103 | priv->egl_native_window = native_window; |
1104 | } |
1105 | |
1106 | gpointer /* EGLSurface */ |
1107 | gdk_surface_get_egl_surface (GdkSurface *self) |
1108 | { |
1109 | GdkSurfacePrivate *priv = gdk_surface_get_instance_private (self); |
1110 | |
1111 | return priv->egl_surface; |
1112 | } |
1113 | |
1114 | void |
1115 | gdk_surface_ensure_egl_surface (GdkSurface *self, |
1116 | gboolean high_depth) |
1117 | { |
1118 | GdkSurfacePrivate *priv = gdk_surface_get_instance_private (self); |
1119 | GdkDisplay *display = gdk_surface_get_display (surface: self); |
1120 | |
1121 | g_return_if_fail (priv->egl_native_window != NULL); |
1122 | |
1123 | if (priv->egl_surface_high_depth != high_depth && |
1124 | priv->egl_surface != NULL && |
1125 | gdk_display_get_egl_config_high_depth (display) != gdk_display_get_egl_config (display)) |
1126 | { |
1127 | gdk_gl_context_clear_current_if_surface (surface: self); |
1128 | eglDestroySurface (gdk_display_get_egl_display (display), priv->egl_surface); |
1129 | priv->egl_surface = NULL; |
1130 | } |
1131 | |
1132 | if (priv->egl_surface == NULL) |
1133 | { |
1134 | priv->egl_surface = eglCreateWindowSurface (gdk_display_get_egl_display (display), |
1135 | high_depth ? gdk_display_get_egl_config_high_depth (display) |
1136 | : gdk_display_get_egl_config (display), |
1137 | (EGLNativeWindowType) priv->egl_native_window, |
1138 | NULL); |
1139 | priv->egl_surface_high_depth = high_depth; |
1140 | } |
1141 | #endif |
1142 | } |
1143 | |
1144 | GdkGLContext * |
1145 | gdk_surface_get_paint_gl_context (GdkSurface *surface, |
1146 | GError **error) |
1147 | { |
1148 | if (!gdk_display_prepare_gl (self: surface->display, error)) |
1149 | return NULL; |
1150 | |
1151 | if (surface->gl_paint_context == NULL) |
1152 | { |
1153 | surface->gl_paint_context = gdk_surface_create_gl_context (surface, error); |
1154 | if (surface->gl_paint_context == NULL) |
1155 | return NULL; |
1156 | } |
1157 | |
1158 | if (!gdk_gl_context_realize (context: surface->gl_paint_context, error)) |
1159 | { |
1160 | g_clear_object (&surface->gl_paint_context); |
1161 | return NULL; |
1162 | } |
1163 | |
1164 | return surface->gl_paint_context; |
1165 | } |
1166 | |
1167 | /** |
1168 | * gdk_surface_create_gl_context: |
1169 | * @surface: a `GdkSurface` |
1170 | * @error: return location for an error |
1171 | * |
1172 | * Creates a new `GdkGLContext` for the `GdkSurface`. |
1173 | * |
1174 | * The context is disconnected from any particular surface or surface. |
1175 | * If the creation of the `GdkGLContext` failed, @error will be set. |
1176 | * Before using the returned `GdkGLContext`, you will need to |
1177 | * call [method@Gdk.GLContext.make_current] or [method@Gdk.GLContext.realize]. |
1178 | * |
1179 | * Returns: (transfer full): the newly created `GdkGLContext` |
1180 | */ |
1181 | GdkGLContext * |
1182 | gdk_surface_create_gl_context (GdkSurface *surface, |
1183 | GError **error) |
1184 | { |
1185 | g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL); |
1186 | g_return_val_if_fail (error == NULL || *error == NULL, NULL); |
1187 | |
1188 | if (!gdk_display_prepare_gl (self: surface->display, error)) |
1189 | return NULL; |
1190 | |
1191 | return gdk_gl_context_new (display: surface->display, surface); |
1192 | } |
1193 | |
1194 | /** |
1195 | * gdk_surface_create_cairo_context: |
1196 | * @surface: a `GdkSurface` |
1197 | * |
1198 | * Creates a new `GdkCairoContext` for rendering on @surface. |
1199 | * |
1200 | * Returns: (transfer full): the newly created `GdkCairoContext` |
1201 | */ |
1202 | GdkCairoContext * |
1203 | gdk_surface_create_cairo_context (GdkSurface *surface) |
1204 | { |
1205 | GdkDisplay *display; |
1206 | |
1207 | g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL); |
1208 | |
1209 | display = surface->display; |
1210 | |
1211 | return g_object_new (GDK_DISPLAY_GET_CLASS (display)->cairo_context_type, |
1212 | first_property_name: "surface" , surface, |
1213 | NULL); |
1214 | } |
1215 | |
1216 | /** |
1217 | * gdk_surface_create_vulkan_context: |
1218 | * @surface: a `GdkSurface` |
1219 | * @error: return location for an error |
1220 | * |
1221 | * Creates a new `GdkVulkanContext` for rendering on @surface. |
1222 | * |
1223 | * If the creation of the `GdkVulkanContext` failed, @error will be set. |
1224 | * |
1225 | * Returns: (transfer full): the newly created `GdkVulkanContext`, or |
1226 | * %NULL on error |
1227 | */ |
1228 | GdkVulkanContext * |
1229 | gdk_surface_create_vulkan_context (GdkSurface *surface, |
1230 | GError **error) |
1231 | { |
1232 | GdkDisplay *display; |
1233 | |
1234 | g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL); |
1235 | g_return_val_if_fail (error == NULL || *error == NULL, NULL); |
1236 | |
1237 | if (GDK_DISPLAY_DEBUG_CHECK (surface->display, VULKAN_DISABLE)) |
1238 | { |
1239 | g_set_error_literal (err: error, GDK_VULKAN_ERROR, code: GDK_VULKAN_ERROR_NOT_AVAILABLE, |
1240 | _("Vulkan support disabled via GDK_DEBUG" )); |
1241 | return NULL; |
1242 | } |
1243 | |
1244 | display = surface->display; |
1245 | |
1246 | if (GDK_DISPLAY_GET_CLASS (display)->vk_extension_name == NULL) |
1247 | { |
1248 | g_set_error (err: error, GDK_VULKAN_ERROR, code: GDK_VULKAN_ERROR_UNSUPPORTED, |
1249 | format: "The %s backend has no Vulkan support." , G_OBJECT_TYPE_NAME (display)); |
1250 | return FALSE; |
1251 | } |
1252 | |
1253 | return g_initable_new (GDK_DISPLAY_GET_CLASS (display)->vk_context_type, |
1254 | NULL, |
1255 | error, |
1256 | first_property_name: "surface" , surface, |
1257 | NULL); |
1258 | } |
1259 | |
1260 | /* Code for dirty-region queueing |
1261 | */ |
1262 | static GSList *update_surfaces = NULL; |
1263 | |
1264 | static void |
1265 | gdk_surface_add_update_surface (GdkSurface *surface) |
1266 | { |
1267 | GSList *tmp; |
1268 | |
1269 | /* Check whether "surface" is already in "update_surfaces" list. |
1270 | * It could be added during execution of gtk_widget_destroy() when |
1271 | * setting focus widget to NULL and redrawing old focus widget. |
1272 | * See bug 711552. |
1273 | */ |
1274 | tmp = g_slist_find (list: update_surfaces, data: surface); |
1275 | if (tmp != NULL) |
1276 | return; |
1277 | |
1278 | update_surfaces = g_slist_prepend (list: update_surfaces, g_object_ref (surface)); |
1279 | } |
1280 | |
1281 | static void |
1282 | gdk_surface_remove_update_surface (GdkSurface *surface) |
1283 | { |
1284 | GSList *link; |
1285 | |
1286 | link = g_slist_find (list: update_surfaces, data: surface); |
1287 | if (link != NULL) |
1288 | { |
1289 | update_surfaces = g_slist_delete_link (list: update_surfaces, link_: link); |
1290 | g_object_unref (object: surface); |
1291 | } |
1292 | } |
1293 | |
1294 | static gboolean |
1295 | gdk_surface_is_toplevel_frozen (GdkSurface *surface) |
1296 | { |
1297 | return surface->update_and_descendants_freeze_count > 0; |
1298 | } |
1299 | |
1300 | static void |
1301 | gdk_surface_schedule_update (GdkSurface *surface) |
1302 | { |
1303 | GdkFrameClock *frame_clock; |
1304 | |
1305 | g_return_if_fail (surface); |
1306 | |
1307 | surface->pending_phases |= GDK_FRAME_CLOCK_PHASE_PAINT; |
1308 | |
1309 | if (surface->update_freeze_count || |
1310 | gdk_surface_is_toplevel_frozen (surface)) |
1311 | return; |
1312 | |
1313 | /* If there's no frame clock (a foreign surface), then the invalid |
1314 | * region will just stick around unless gdk_surface_process_updates() |
1315 | * is called. */ |
1316 | frame_clock = gdk_surface_get_frame_clock (surface); |
1317 | if (frame_clock) |
1318 | gdk_frame_clock_request_phase (frame_clock: gdk_surface_get_frame_clock (surface), |
1319 | phase: GDK_FRAME_CLOCK_PHASE_PAINT); |
1320 | } |
1321 | |
1322 | static void |
1323 | gdk_surface_process_updates_internal (GdkSurface *surface) |
1324 | { |
1325 | /* Ensure the surface lives while updating it */ |
1326 | g_object_ref (surface); |
1327 | |
1328 | surface->in_update = TRUE; |
1329 | |
1330 | /* If an update got queued during update processing, we can get a |
1331 | * surface in the update queue that has an empty update_area. |
1332 | * just ignore it. |
1333 | */ |
1334 | if (surface->update_area) |
1335 | { |
1336 | g_assert (surface->active_update_area == NULL); /* No reentrancy */ |
1337 | |
1338 | surface->active_update_area = surface->update_area; |
1339 | surface->update_area = NULL; |
1340 | |
1341 | if (GDK_SURFACE_IS_MAPPED (surface)) |
1342 | { |
1343 | cairo_region_t *expose_region; |
1344 | gboolean handled; |
1345 | |
1346 | expose_region = cairo_region_copy (original: surface->active_update_area); |
1347 | |
1348 | g_signal_emit (instance: surface, signal_id: signals[RENDER], detail: 0, expose_region, &handled); |
1349 | |
1350 | cairo_region_destroy (region: expose_region); |
1351 | } |
1352 | |
1353 | cairo_region_destroy (region: surface->active_update_area); |
1354 | surface->active_update_area = NULL; |
1355 | } |
1356 | |
1357 | surface->in_update = FALSE; |
1358 | |
1359 | g_object_unref (object: surface); |
1360 | } |
1361 | |
1362 | static void |
1363 | gdk_surface_layout_on_clock (GdkFrameClock *clock, |
1364 | void *data) |
1365 | { |
1366 | GdkSurface *surface = GDK_SURFACE (data); |
1367 | GdkSurfaceClass *class; |
1368 | |
1369 | g_return_if_fail (GDK_IS_SURFACE (surface)); |
1370 | |
1371 | if (GDK_SURFACE_DESTROYED (surface)) |
1372 | return; |
1373 | |
1374 | if (!GDK_SURFACE_IS_MAPPED (surface)) |
1375 | return; |
1376 | |
1377 | surface->pending_phases &= ~GDK_FRAME_CLOCK_PHASE_LAYOUT; |
1378 | |
1379 | class = GDK_SURFACE_GET_CLASS (surface); |
1380 | if (class->compute_size) |
1381 | { |
1382 | if (class->compute_size (surface)) |
1383 | return; |
1384 | } |
1385 | |
1386 | g_signal_emit (instance: surface, signal_id: signals[LAYOUT], detail: 0, surface->width, surface->height); |
1387 | } |
1388 | |
1389 | /** |
1390 | * gdk_surface_request_layout: |
1391 | * @surface: a `GdkSurface` |
1392 | * |
1393 | * Request a layout phase from the surface's frame clock. |
1394 | * |
1395 | * See [method@Gdk.FrameClock.request_phase]. |
1396 | */ |
1397 | void |
1398 | gdk_surface_request_layout (GdkSurface *surface) |
1399 | { |
1400 | GdkSurfaceClass *class; |
1401 | GdkFrameClock *frame_clock; |
1402 | |
1403 | class = GDK_SURFACE_GET_CLASS (surface); |
1404 | if (class->request_layout) |
1405 | class->request_layout (surface); |
1406 | |
1407 | frame_clock = gdk_surface_get_frame_clock (surface); |
1408 | g_return_if_fail (frame_clock); |
1409 | |
1410 | gdk_frame_clock_request_phase (frame_clock, |
1411 | phase: GDK_FRAME_CLOCK_PHASE_LAYOUT); |
1412 | } |
1413 | |
1414 | static void |
1415 | gdk_surface_paint_on_clock (GdkFrameClock *clock, |
1416 | void *data) |
1417 | { |
1418 | GdkSurface *surface = GDK_SURFACE (data); |
1419 | |
1420 | g_return_if_fail (GDK_IS_SURFACE (surface)); |
1421 | |
1422 | if (GDK_SURFACE_DESTROYED (surface)) |
1423 | return; |
1424 | |
1425 | g_object_ref (surface); |
1426 | |
1427 | if (surface->update_area && |
1428 | !surface->update_freeze_count && |
1429 | !gdk_surface_is_toplevel_frozen (surface) && |
1430 | |
1431 | /* Don't recurse into process_updates_internal, we'll |
1432 | * do the update later when idle instead. */ |
1433 | !surface->in_update) |
1434 | { |
1435 | surface->pending_phases &= ~GDK_FRAME_CLOCK_PHASE_PAINT; |
1436 | gdk_surface_process_updates_internal (surface); |
1437 | gdk_surface_remove_update_surface (surface); |
1438 | } |
1439 | |
1440 | g_object_unref (object: surface); |
1441 | } |
1442 | |
1443 | /* |
1444 | * gdk_surface_invalidate_rect: |
1445 | * @surface: a `GdkSurface` |
1446 | * @rect: (nullable): rectangle to invalidate or %NULL to |
1447 | * invalidate the whole surface |
1448 | * |
1449 | * Invalidate a rectangular region of @surface. |
1450 | * |
1451 | * This is a convenience wrapper around |
1452 | * [method@Gdk.Surface.invalidate_region]. |
1453 | */ |
1454 | void |
1455 | gdk_surface_invalidate_rect (GdkSurface *surface, |
1456 | const GdkRectangle *rect) |
1457 | { |
1458 | GdkRectangle surface_rect; |
1459 | cairo_region_t *region; |
1460 | |
1461 | g_return_if_fail (GDK_IS_SURFACE (surface)); |
1462 | |
1463 | if (!GDK_SURFACE_IS_MAPPED (surface)) |
1464 | return; |
1465 | |
1466 | if (!rect) |
1467 | { |
1468 | surface_rect.x = 0; |
1469 | surface_rect.y = 0; |
1470 | surface_rect.width = surface->width; |
1471 | surface_rect.height = surface->height; |
1472 | rect = &surface_rect; |
1473 | } |
1474 | |
1475 | region = cairo_region_create_rectangle (rectangle: rect); |
1476 | gdk_surface_invalidate_region (surface, region); |
1477 | cairo_region_destroy (region); |
1478 | } |
1479 | |
1480 | static void |
1481 | impl_surface_add_update_area (GdkSurface *impl_surface, |
1482 | cairo_region_t *region) |
1483 | { |
1484 | if (impl_surface->update_area) |
1485 | cairo_region_union (dst: impl_surface->update_area, other: region); |
1486 | else |
1487 | { |
1488 | gdk_surface_add_update_surface (surface: impl_surface); |
1489 | impl_surface->update_area = cairo_region_copy (original: region); |
1490 | gdk_surface_schedule_update (surface: impl_surface); |
1491 | } |
1492 | } |
1493 | |
1494 | /** |
1495 | * gdk_surface_queue_render: |
1496 | * @surface: a `GdkSurface` |
1497 | * |
1498 | * Forces a [signal@Gdk.Surface::render] signal emission for @surface |
1499 | * to be scheduled. |
1500 | * |
1501 | * This function is useful for implementations that track invalid |
1502 | * regions on their own. |
1503 | */ |
1504 | void |
1505 | gdk_surface_queue_render (GdkSurface *surface) |
1506 | { |
1507 | cairo_region_t *region; |
1508 | |
1509 | g_return_if_fail (GDK_IS_SURFACE (surface)); |
1510 | |
1511 | region = cairo_region_create (); |
1512 | impl_surface_add_update_area (impl_surface: surface, region); |
1513 | cairo_region_destroy (region); |
1514 | } |
1515 | |
1516 | /* |
1517 | * gdk_surface_invalidate_region: |
1518 | * @surface: a `GdkSurface` |
1519 | * @region: a `cairo_region_t` |
1520 | * |
1521 | * Adds @region to the update area for @surface. |
1522 | * |
1523 | * The update area is the region that needs to be redrawn, |
1524 | * or “dirty region.” |
1525 | * |
1526 | * GDK will process all updates whenever the frame clock schedules |
1527 | * a redraw, so there’s no need to do forces redraws manually, you |
1528 | * just need to invalidate regions that you know should be redrawn. |
1529 | */ |
1530 | void |
1531 | gdk_surface_invalidate_region (GdkSurface *surface, |
1532 | const cairo_region_t *region) |
1533 | { |
1534 | cairo_region_t *visible_region; |
1535 | cairo_rectangle_int_t r; |
1536 | |
1537 | g_return_if_fail (GDK_IS_SURFACE (surface)); |
1538 | |
1539 | if (!GDK_SURFACE_IS_MAPPED (surface)) |
1540 | return; |
1541 | |
1542 | if (cairo_region_is_empty (region)) |
1543 | return; |
1544 | |
1545 | r.x = 0; |
1546 | r.y = 0; |
1547 | r.width = surface->width; |
1548 | r.height = surface->height; |
1549 | |
1550 | visible_region = cairo_region_copy (original: region); |
1551 | |
1552 | cairo_region_intersect_rectangle (dst: visible_region, rectangle: &r); |
1553 | impl_surface_add_update_area (impl_surface: surface, region: visible_region); |
1554 | |
1555 | cairo_region_destroy (region: visible_region); |
1556 | } |
1557 | |
1558 | /* |
1559 | * _gdk_surface_clear_update_area: |
1560 | * @surface: a `GdkSurface` |
1561 | * |
1562 | * Internal function to clear the update area for a surface. |
1563 | * This is called when the surface is hidden or destroyed. |
1564 | */ |
1565 | void |
1566 | _gdk_surface_clear_update_area (GdkSurface *surface) |
1567 | { |
1568 | g_return_if_fail (GDK_IS_SURFACE (surface)); |
1569 | |
1570 | if (surface->update_area) |
1571 | { |
1572 | gdk_surface_remove_update_surface (surface); |
1573 | |
1574 | cairo_region_destroy (region: surface->update_area); |
1575 | surface->update_area = NULL; |
1576 | } |
1577 | } |
1578 | |
1579 | /* |
1580 | * gdk_surface_freeze_updates: |
1581 | * @surface: a `GdkSurface` |
1582 | * |
1583 | * Temporarily freezes a surface such that it won’t receive expose |
1584 | * events. The surface will begin receiving expose events again when |
1585 | * gdk_surface_thaw_updates() is called. If gdk_surface_freeze_updates() |
1586 | * has been called more than once, gdk_surface_thaw_updates() must be |
1587 | * called an equal number of times to begin processing exposes. |
1588 | */ |
1589 | void |
1590 | gdk_surface_freeze_updates (GdkSurface *surface) |
1591 | { |
1592 | g_return_if_fail (GDK_IS_SURFACE (surface)); |
1593 | |
1594 | surface->update_freeze_count++; |
1595 | if (surface->update_freeze_count == 1) |
1596 | _gdk_frame_clock_uninhibit_freeze (clock: surface->frame_clock); |
1597 | } |
1598 | |
1599 | static gboolean |
1600 | request_motion_cb (void *data) |
1601 | { |
1602 | GdkSurface *surface = GDK_SURFACE (data); |
1603 | GdkFrameClock *clock = gdk_surface_get_frame_clock (surface); |
1604 | |
1605 | if (clock) |
1606 | gdk_frame_clock_request_phase (frame_clock: clock, phase: GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS); |
1607 | surface->request_motion_id = 0; |
1608 | |
1609 | return G_SOURCE_REMOVE; |
1610 | } |
1611 | |
1612 | |
1613 | /* |
1614 | * gdk_surface_thaw_updates: |
1615 | * @surface: a `GdkSurface` |
1616 | * |
1617 | * Thaws a surface frozen with gdk_surface_freeze_updates(). Note that this |
1618 | * will not necessarily schedule updates if the surface freeze count reaches |
1619 | * zero. |
1620 | */ |
1621 | void |
1622 | gdk_surface_thaw_updates (GdkSurface *surface) |
1623 | { |
1624 | g_return_if_fail (GDK_IS_SURFACE (surface)); |
1625 | |
1626 | g_return_if_fail (surface->update_freeze_count > 0); |
1627 | |
1628 | if (--surface->update_freeze_count == 0) |
1629 | { |
1630 | GdkFrameClock *frame_clock = surface->frame_clock; |
1631 | |
1632 | _gdk_frame_clock_inhibit_freeze (clock: frame_clock); |
1633 | |
1634 | if (surface->pending_phases) |
1635 | gdk_frame_clock_request_phase (frame_clock, phase: surface->pending_phases); |
1636 | |
1637 | if (surface->request_motion && surface->request_motion_id == 0) |
1638 | { |
1639 | surface->request_motion_id = |
1640 | g_idle_add_full (GDK_PRIORITY_REDRAW + 20, |
1641 | function: request_motion_cb, data: surface, NULL); |
1642 | } |
1643 | } |
1644 | } |
1645 | |
1646 | /* |
1647 | * gdk_surface_constrain_size: |
1648 | * @geometry: a `GdkGeometry` structure |
1649 | * @flags: a mask indicating what portions of @geometry are set |
1650 | * @width: desired width of surface |
1651 | * @height: desired height of the surface |
1652 | * @new_width: (out): location to store resulting width |
1653 | * @new_height: (out): location to store resulting height |
1654 | * |
1655 | * Constrains a desired width and height according to a |
1656 | * set of geometry hints (such as minimum and maximum size). |
1657 | */ |
1658 | void |
1659 | gdk_surface_constrain_size (GdkGeometry *geometry, |
1660 | GdkSurfaceHints flags, |
1661 | int width, |
1662 | int height, |
1663 | int *new_width, |
1664 | int *new_height) |
1665 | { |
1666 | /* This routine is partially borrowed from fvwm. |
1667 | * |
1668 | * Copyright 1993, Robert Nation |
1669 | * You may use this code for any purpose, as long as the original |
1670 | * copyright remains in the source code and all documentation |
1671 | * |
1672 | * which in turn borrows parts of the algorithm from uwm |
1673 | */ |
1674 | int min_width = 0; |
1675 | int min_height = 0; |
1676 | int max_width = G_MAXINT; |
1677 | int max_height = G_MAXINT; |
1678 | |
1679 | if (flags & GDK_HINT_MIN_SIZE) |
1680 | { |
1681 | min_width = geometry->min_width; |
1682 | min_height = geometry->min_height; |
1683 | } |
1684 | |
1685 | if (flags & GDK_HINT_MAX_SIZE) |
1686 | { |
1687 | max_width = geometry->max_width ; |
1688 | max_height = geometry->max_height; |
1689 | } |
1690 | |
1691 | /* clamp width and height to min and max values |
1692 | */ |
1693 | width = CLAMP (width, min_width, max_width); |
1694 | height = CLAMP (height, min_height, max_height); |
1695 | |
1696 | *new_width = width; |
1697 | *new_height = height; |
1698 | } |
1699 | |
1700 | /** |
1701 | * gdk_surface_get_device_position: |
1702 | * @surface: a `GdkSurface` |
1703 | * @device: pointer `GdkDevice` to query to |
1704 | * @x: (out) (optional): return location for the X coordinate of @device |
1705 | * @y: (out) (optional): return location for the Y coordinate of @device |
1706 | * @mask: (out) (optional): return location for the modifier mask |
1707 | * |
1708 | * Obtains the current device position and modifier state. |
1709 | * |
1710 | * The position is given in coordinates relative to the upper |
1711 | * left corner of @surface. |
1712 | * |
1713 | * Return: %TRUE if the device is over the surface |
1714 | */ |
1715 | gboolean |
1716 | gdk_surface_get_device_position (GdkSurface *surface, |
1717 | GdkDevice *device, |
1718 | double *x, |
1719 | double *y, |
1720 | GdkModifierType *mask) |
1721 | { |
1722 | double tmp_x, tmp_y; |
1723 | GdkModifierType tmp_mask; |
1724 | gboolean ret; |
1725 | |
1726 | g_return_val_if_fail (GDK_IS_SURFACE (surface), FALSE); |
1727 | g_return_val_if_fail (GDK_IS_DEVICE (device), FALSE); |
1728 | g_return_val_if_fail (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD, FALSE); |
1729 | |
1730 | tmp_x = 0; |
1731 | tmp_y = 0; |
1732 | tmp_mask = 0; |
1733 | |
1734 | ret = GDK_SURFACE_GET_CLASS (surface)->get_device_state (surface, |
1735 | device, |
1736 | &tmp_x, &tmp_y, |
1737 | &tmp_mask); |
1738 | |
1739 | if (x) |
1740 | *x = tmp_x; |
1741 | if (y) |
1742 | *y = tmp_y; |
1743 | if (mask) |
1744 | *mask = tmp_mask; |
1745 | |
1746 | return ret; |
1747 | } |
1748 | |
1749 | /** |
1750 | * gdk_surface_hide: |
1751 | * @surface: a `GdkSurface` |
1752 | * |
1753 | * Hide the surface. |
1754 | * |
1755 | * For toplevel surfaces, withdraws them, so they will no longer be |
1756 | * known to the window manager; for all surfaces, unmaps them, so |
1757 | * they won’t be displayed. Normally done automatically as |
1758 | * part of [method@Gtk.Widget.hide]. |
1759 | */ |
1760 | void |
1761 | gdk_surface_hide (GdkSurface *surface) |
1762 | { |
1763 | gboolean was_mapped; |
1764 | |
1765 | g_return_if_fail (GDK_IS_SURFACE (surface)); |
1766 | |
1767 | if (surface->destroyed) |
1768 | return; |
1769 | |
1770 | was_mapped = GDK_SURFACE_IS_MAPPED (surface); |
1771 | |
1772 | gdk_surface_queue_set_is_mapped (surface, FALSE); |
1773 | |
1774 | if (was_mapped) |
1775 | { |
1776 | GdkDisplay *display; |
1777 | GdkSeat *seat; |
1778 | GList *devices, *d; |
1779 | |
1780 | /* May need to break grabs on children */ |
1781 | display = surface->display; |
1782 | seat = gdk_display_get_default_seat (display); |
1783 | if (seat) |
1784 | { |
1785 | devices = gdk_seat_get_devices (seat, capabilities: GDK_SEAT_CAPABILITY_ALL); |
1786 | devices = g_list_prepend (list: devices, data: gdk_seat_get_keyboard (seat)); |
1787 | devices = g_list_prepend (list: devices, data: gdk_seat_get_pointer (seat)); |
1788 | } |
1789 | else |
1790 | devices = NULL; |
1791 | |
1792 | for (d = devices; d; d = d->next) |
1793 | { |
1794 | GdkDevice *device = d->data; |
1795 | |
1796 | if (_gdk_display_end_device_grab (display, |
1797 | device, |
1798 | serial: _gdk_display_get_next_serial (display), |
1799 | if_child: surface, |
1800 | TRUE)) |
1801 | { |
1802 | G_GNUC_BEGIN_IGNORE_DEPRECATIONS |
1803 | gdk_device_ungrab (device, GDK_CURRENT_TIME); |
1804 | G_GNUC_END_IGNORE_DEPRECATIONS |
1805 | } |
1806 | } |
1807 | |
1808 | g_list_free (list: devices); |
1809 | } |
1810 | |
1811 | GDK_SURFACE_GET_CLASS (surface)->hide (surface); |
1812 | |
1813 | surface->popup.rect_anchor = 0; |
1814 | surface->popup.surface_anchor = 0; |
1815 | surface->x = 0; |
1816 | surface->y = 0; |
1817 | } |
1818 | |
1819 | static void |
1820 | gdk_surface_set_cursor_internal (GdkSurface *surface, |
1821 | GdkDevice *device, |
1822 | GdkCursor *cursor) |
1823 | { |
1824 | GdkPointerSurfaceInfo *pointer_info; |
1825 | |
1826 | if (GDK_SURFACE_DESTROYED (surface)) |
1827 | return; |
1828 | |
1829 | g_assert (surface->display == gdk_device_get_display (device)); |
1830 | |
1831 | pointer_info = _gdk_display_get_pointer_info (display: surface->display, device); |
1832 | |
1833 | if (surface == pointer_info->surface_under_pointer) |
1834 | update_cursor (display: surface->display, device); |
1835 | } |
1836 | |
1837 | /** |
1838 | * gdk_surface_get_cursor: (attributes org.gtk.Method.get_property=cursor) |
1839 | * @surface: a `GdkSurface` |
1840 | * |
1841 | * Retrieves a `GdkCursor` pointer for the cursor currently set on the |
1842 | * `GdkSurface`. |
1843 | * |
1844 | * If the return value is %NULL then there is no custom cursor set on |
1845 | * the surface, and it is using the cursor for its parent surface. |
1846 | * |
1847 | * Use [method@Gdk.Surface.set_cursor] to unset the cursor of the surface. |
1848 | * |
1849 | * Returns: (nullable) (transfer none): a `GdkCursor` |
1850 | */ |
1851 | GdkCursor * |
1852 | gdk_surface_get_cursor (GdkSurface *surface) |
1853 | { |
1854 | g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL); |
1855 | |
1856 | return surface->cursor; |
1857 | } |
1858 | |
1859 | /** |
1860 | * gdk_surface_set_cursor: (attributes org.gtk.Method.set_property=cursor) |
1861 | * @surface: a `GdkSurface` |
1862 | * @cursor: (nullable): a `GdkCursor` |
1863 | * |
1864 | * Sets the default mouse pointer for a `GdkSurface`. |
1865 | * |
1866 | * Passing %NULL for the @cursor argument means that @surface will use |
1867 | * the cursor of its parent surface. Most surfaces should use this default. |
1868 | * Note that @cursor must be for the same display as @surface. |
1869 | * |
1870 | * Use [ctor@Gdk.Cursor.new_from_name] or [ctor@Gdk.Cursor.new_from_texture] |
1871 | * to create the cursor. To make the cursor invisible, use %GDK_BLANK_CURSOR. |
1872 | */ |
1873 | void |
1874 | gdk_surface_set_cursor (GdkSurface *surface, |
1875 | GdkCursor *cursor) |
1876 | { |
1877 | g_return_if_fail (GDK_IS_SURFACE (surface)); |
1878 | |
1879 | if (surface->cursor) |
1880 | { |
1881 | g_object_unref (object: surface->cursor); |
1882 | surface->cursor = NULL; |
1883 | } |
1884 | |
1885 | if (!GDK_SURFACE_DESTROYED (surface)) |
1886 | { |
1887 | GdkDevice *device; |
1888 | GList *seats, *s; |
1889 | |
1890 | if (cursor) |
1891 | surface->cursor = g_object_ref (cursor); |
1892 | |
1893 | seats = gdk_display_list_seats (display: surface->display); |
1894 | |
1895 | for (s = seats; s; s = s->next) |
1896 | { |
1897 | GList *devices, *d; |
1898 | |
1899 | device = gdk_seat_get_pointer (seat: s->data); |
1900 | gdk_surface_set_cursor_internal (surface, device, cursor: surface->cursor); |
1901 | |
1902 | devices = gdk_seat_get_devices (seat: s->data, capabilities: GDK_SEAT_CAPABILITY_TABLET_STYLUS); |
1903 | for (d = devices; d; d = d->next) |
1904 | { |
1905 | device = d->data; |
1906 | gdk_surface_set_cursor_internal (surface, device, cursor: surface->cursor); |
1907 | } |
1908 | g_list_free (list: devices); |
1909 | } |
1910 | |
1911 | g_list_free (list: seats); |
1912 | g_object_notify_by_pspec (G_OBJECT (surface), pspec: properties[PROP_CURSOR]); |
1913 | } |
1914 | } |
1915 | |
1916 | /** |
1917 | * gdk_surface_get_device_cursor: |
1918 | * @surface: a `GdkSurface` |
1919 | * @device: a pointer `GdkDevice` |
1920 | * |
1921 | * Retrieves a `GdkCursor` pointer for the @device currently set on the |
1922 | * specified `GdkSurface`. |
1923 | * |
1924 | * If the return value is %NULL then there is no custom cursor set on the |
1925 | * specified surface, and it is using the cursor for its parent surface. |
1926 | * |
1927 | * Use [method@Gdk.Surface.set_cursor] to unset the cursor of the surface. |
1928 | * |
1929 | * Returns: (nullable) (transfer none): a `GdkCursor` |
1930 | */ |
1931 | GdkCursor * |
1932 | gdk_surface_get_device_cursor (GdkSurface *surface, |
1933 | GdkDevice *device) |
1934 | { |
1935 | g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL); |
1936 | g_return_val_if_fail (GDK_IS_DEVICE (device), NULL); |
1937 | g_return_val_if_fail (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD, NULL); |
1938 | |
1939 | return g_hash_table_lookup (hash_table: surface->device_cursor, key: device); |
1940 | } |
1941 | |
1942 | /** |
1943 | * gdk_surface_set_device_cursor: |
1944 | * @surface: a `GdkSurface` |
1945 | * @device: a pointer `GdkDevice` |
1946 | * @cursor: a `GdkCursor` |
1947 | * |
1948 | * Sets a specific `GdkCursor` for a given device when it gets inside @surface. |
1949 | * |
1950 | * Passing %NULL for the @cursor argument means that @surface will use the |
1951 | * cursor of its parent surface. Most surfaces should use this default. |
1952 | * |
1953 | * Use [ctor@Gdk.Cursor.new_from_name] or [ctor@Gdk.Cursor.new_from_texture] |
1954 | * to create the cursor. To make the cursor invisible, use %GDK_BLANK_CURSOR. |
1955 | */ |
1956 | void |
1957 | gdk_surface_set_device_cursor (GdkSurface *surface, |
1958 | GdkDevice *device, |
1959 | GdkCursor *cursor) |
1960 | { |
1961 | g_return_if_fail (GDK_IS_SURFACE (surface)); |
1962 | g_return_if_fail (GDK_IS_DEVICE (device)); |
1963 | g_return_if_fail (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD); |
1964 | |
1965 | if (!cursor) |
1966 | g_hash_table_remove (hash_table: surface->device_cursor, key: device); |
1967 | else |
1968 | g_hash_table_replace (hash_table: surface->device_cursor, key: device, g_object_ref (cursor)); |
1969 | |
1970 | gdk_surface_set_cursor_internal (surface, device, cursor); |
1971 | } |
1972 | |
1973 | /* |
1974 | * gdk_surface_get_geometry: |
1975 | * @surface: a `GdkSurface` |
1976 | * @x: (out) (optional): return location for X coordinate of surface (relative to its parent) |
1977 | * @y: (out) (optional): return location for Y coordinate of surface (relative to its parent) |
1978 | * @width: (out) (optional): return location for width of surface |
1979 | * @height: (out) (optional): return location for height of surface |
1980 | * |
1981 | * Get the geometry of the surface. |
1982 | * |
1983 | * The X and Y coordinates returned are relative to the parent surface |
1984 | * of @surface, which for toplevels usually means relative to the |
1985 | * surface decorations (titlebar, etc.) rather than relative to the |
1986 | * root window (screen-size background window). |
1987 | * |
1988 | * On the X11 platform, the geometry is obtained from the X server, so |
1989 | * reflects the latest position of @surface; this may be out-of-sync with |
1990 | * the position of @surface delivered in the most-recently-processed |
1991 | * `GdkEventConfigure`. [method@Gdk.Surface.get_position] in contrast gets |
1992 | * the position from the most recent configure event. |
1993 | * |
1994 | * Any of the return location arguments to this function may be %NULL, |
1995 | * if you aren’t interested in getting the value of that field. |
1996 | * |
1997 | * Note: If @surface is not a toplevel, it is much better to call |
1998 | * [method@Gdk.Surface.get_position], [method@Gdk.Surface.get_width] and |
1999 | * [method@Gdk.Surface.get_height] instead, because it avoids the roundtrip |
2000 | * to the X server and because these functions support the full 32-bit |
2001 | * coordinate space, whereas gdk_surface_get_geometry() is restricted to |
2002 | * the 16-bit coordinates of X11. |
2003 | */ |
2004 | void |
2005 | gdk_surface_get_geometry (GdkSurface *surface, |
2006 | int *x, |
2007 | int *y, |
2008 | int *width, |
2009 | int *height) |
2010 | { |
2011 | g_return_if_fail (GDK_IS_SURFACE (surface)); |
2012 | |
2013 | if (GDK_SURFACE_DESTROYED (surface)) |
2014 | return; |
2015 | |
2016 | GDK_SURFACE_GET_CLASS (surface)->get_geometry (surface, x, y, width, height); |
2017 | } |
2018 | |
2019 | /** |
2020 | * gdk_surface_get_width: (attributes org.gtk.Method.get_property=width) |
2021 | * @surface: a `GdkSurface` |
2022 | * |
2023 | * Returns the width of the given @surface. |
2024 | * |
2025 | * Surface size is reported in ”application pixels”, not |
2026 | * ”device pixels” (see [method@Gdk.Surface.get_scale_factor]). |
2027 | * |
2028 | * Returns: The width of @surface |
2029 | */ |
2030 | int |
2031 | gdk_surface_get_width (GdkSurface *surface) |
2032 | { |
2033 | g_return_val_if_fail (GDK_IS_SURFACE (surface), 0); |
2034 | |
2035 | return surface->width; |
2036 | } |
2037 | |
2038 | /** |
2039 | * gdk_surface_get_height: (attributes org.gtk.Method.get_property=height) |
2040 | * @surface: a `GdkSurface` |
2041 | * |
2042 | * Returns the height of the given @surface. |
2043 | * |
2044 | * Surface size is reported in ”application pixels”, not |
2045 | * ”device pixels” (see [method@Gdk.Surface.get_scale_factor]). |
2046 | * |
2047 | * Returns: The height of @surface |
2048 | */ |
2049 | int |
2050 | gdk_surface_get_height (GdkSurface *surface) |
2051 | { |
2052 | g_return_val_if_fail (GDK_IS_SURFACE (surface), 0); |
2053 | |
2054 | return surface->height; |
2055 | } |
2056 | |
2057 | /* |
2058 | * gdk_surface_get_origin: |
2059 | * @surface: a `GdkSurface` |
2060 | * @x: (out): return location for X coordinate |
2061 | * @y: (out): return location for Y coordinate |
2062 | * |
2063 | * Obtains the position of a surface in root window coordinates. |
2064 | * |
2065 | * (Compare with gdk_surface_get_position() and |
2066 | * gdk_surface_get_geometry() which return the position |
2067 | * of a surface relative to its parent surface.) |
2068 | */ |
2069 | void |
2070 | gdk_surface_get_origin (GdkSurface *surface, |
2071 | int *x, |
2072 | int *y) |
2073 | { |
2074 | g_return_if_fail (GDK_IS_SURFACE (surface)); |
2075 | |
2076 | gdk_surface_get_root_coords (surface, x: 0, y: 0, root_x: x, root_y: y); |
2077 | } |
2078 | |
2079 | /* |
2080 | * gdk_surface_get_root_coords: |
2081 | * @surface: a `GdkSurface` |
2082 | * @x: X coordinate in surface |
2083 | * @y: Y coordinate in surface |
2084 | * @root_x: (out): return location for X coordinate |
2085 | * @root_y: (out): return location for Y coordinate |
2086 | * |
2087 | * Obtains the position of a surface position in root |
2088 | * window coordinates. This is similar to |
2089 | * gdk_surface_get_origin() but allows you to pass |
2090 | * in any position in the surface, not just the origin. |
2091 | */ |
2092 | void |
2093 | gdk_surface_get_root_coords (GdkSurface *surface, |
2094 | int x, |
2095 | int y, |
2096 | int *root_x, |
2097 | int *root_y) |
2098 | { |
2099 | g_return_if_fail (GDK_IS_SURFACE (surface)); |
2100 | |
2101 | if (GDK_SURFACE_DESTROYED (surface)) |
2102 | { |
2103 | *root_x = 0; |
2104 | *root_y = 0; |
2105 | return; |
2106 | } |
2107 | |
2108 | GDK_SURFACE_GET_CLASS (surface)->get_root_coords (surface, x, y, root_x, root_y); |
2109 | } |
2110 | |
2111 | /** |
2112 | * gdk_surface_set_input_region: |
2113 | * @surface: a `GdkSurface` |
2114 | * @region: region of surface to be reactive |
2115 | * |
2116 | * Apply the region to the surface for the purpose of event |
2117 | * handling. |
2118 | * |
2119 | * Mouse events which happen while the pointer position corresponds |
2120 | * to an unset bit in the mask will be passed on the surface below |
2121 | * @surface. |
2122 | * |
2123 | * An input region is typically used with RGBA surfaces. The alpha |
2124 | * channel of the surface defines which pixels are invisible and |
2125 | * allows for nicely antialiased borders, and the input region |
2126 | * controls where the surface is “clickable”. |
2127 | * |
2128 | * Use [method@Gdk.Display.supports_input_shapes] to find out if |
2129 | * a particular backend supports input regions. |
2130 | */ |
2131 | void |
2132 | gdk_surface_set_input_region (GdkSurface *surface, |
2133 | cairo_region_t *region) |
2134 | { |
2135 | g_return_if_fail (GDK_IS_SURFACE (surface)); |
2136 | |
2137 | if (GDK_SURFACE_DESTROYED (surface)) |
2138 | return; |
2139 | |
2140 | if (cairo_region_equal (a: surface->input_region, b: region)) |
2141 | return; |
2142 | |
2143 | if (surface->input_region) |
2144 | cairo_region_destroy (region: surface->input_region); |
2145 | |
2146 | if (region) |
2147 | surface->input_region = cairo_region_copy (original: region); |
2148 | else |
2149 | surface->input_region = NULL; |
2150 | |
2151 | GDK_SURFACE_GET_CLASS (surface)->set_input_region (surface, surface->input_region); |
2152 | } |
2153 | |
2154 | static void |
2155 | update_cursor (GdkDisplay *display, |
2156 | GdkDevice *device) |
2157 | { |
2158 | GdkSurface *cursor_surface; |
2159 | GdkSurface *pointer_surface; |
2160 | GdkPointerSurfaceInfo *pointer_info; |
2161 | GdkDeviceGrabInfo *grab; |
2162 | GdkCursor *cursor; |
2163 | |
2164 | g_assert (display); |
2165 | g_assert (device); |
2166 | |
2167 | pointer_info = _gdk_display_get_pointer_info (display, device); |
2168 | pointer_surface = pointer_info->surface_under_pointer; |
2169 | |
2170 | /* We ignore the serials here and just pick the last grab |
2171 | we've sent, as that would shortly be used anyway. */ |
2172 | grab = _gdk_display_get_last_device_grab (display, device); |
2173 | if (grab != NULL) |
2174 | { |
2175 | /* use the cursor from the grab surface */ |
2176 | cursor_surface = grab->surface; |
2177 | } |
2178 | else |
2179 | { |
2180 | /* otherwise use the cursor from the pointer surface */ |
2181 | cursor_surface = pointer_surface; |
2182 | } |
2183 | |
2184 | cursor = g_hash_table_lookup (hash_table: cursor_surface->device_cursor, key: device); |
2185 | |
2186 | if (!cursor) |
2187 | cursor = cursor_surface->cursor; |
2188 | |
2189 | GDK_DEVICE_GET_CLASS (device)->set_surface_cursor (device, pointer_surface, cursor); |
2190 | } |
2191 | |
2192 | /** |
2193 | * gdk_surface_beep: |
2194 | * @surface: a toplevel `GdkSurface` |
2195 | * |
2196 | * Emits a short beep associated to @surface. |
2197 | * |
2198 | * If the display of @surface does not support per-surface beeps, |
2199 | * emits a short beep on the display just as [method@Gdk.Display.beep]. |
2200 | */ |
2201 | void |
2202 | gdk_surface_beep (GdkSurface *surface) |
2203 | { |
2204 | g_return_if_fail (GDK_IS_SURFACE (surface)); |
2205 | |
2206 | if (GDK_SURFACE_DESTROYED (surface)) |
2207 | return; |
2208 | |
2209 | if (GDK_SURFACE_GET_CLASS (surface)->beep (surface)) |
2210 | return; |
2211 | |
2212 | gdk_display_beep (display: surface->display); |
2213 | } |
2214 | |
2215 | void |
2216 | _gdk_display_set_surface_under_pointer (GdkDisplay *display, |
2217 | GdkDevice *device, |
2218 | GdkSurface *surface) |
2219 | { |
2220 | GdkPointerSurfaceInfo *device_info; |
2221 | |
2222 | device_info = _gdk_display_get_pointer_info (display, device); |
2223 | |
2224 | if (device_info->surface_under_pointer) |
2225 | g_object_unref (object: device_info->surface_under_pointer); |
2226 | device_info->surface_under_pointer = surface; |
2227 | |
2228 | if (surface) |
2229 | { |
2230 | g_object_ref (surface); |
2231 | update_cursor (display, device); |
2232 | } |
2233 | } |
2234 | |
2235 | #define GDK_ANY_BUTTON_MASK (GDK_BUTTON1_MASK | \ |
2236 | GDK_BUTTON2_MASK | \ |
2237 | GDK_BUTTON3_MASK | \ |
2238 | GDK_BUTTON4_MASK | \ |
2239 | GDK_BUTTON5_MASK) |
2240 | |
2241 | void |
2242 | _gdk_windowing_got_event (GdkDisplay *display, |
2243 | GList *event_link, |
2244 | GdkEvent *event, |
2245 | gulong serial) |
2246 | { |
2247 | GdkSurface *event_surface = NULL; |
2248 | gboolean unlink_event = FALSE; |
2249 | GdkDeviceGrabInfo *button_release_grab; |
2250 | GdkPointerSurfaceInfo *pointer_info = NULL; |
2251 | GdkDevice *device; |
2252 | GdkEventType type; |
2253 | guint32 timestamp; |
2254 | |
2255 | _gdk_display_update_last_event (display, event); |
2256 | |
2257 | device = gdk_event_get_device (event); |
2258 | timestamp = gdk_event_get_time (event); |
2259 | |
2260 | if (device) |
2261 | { |
2262 | if (timestamp != GDK_CURRENT_TIME) |
2263 | gdk_device_set_timestamp (device, timestamp); |
2264 | |
2265 | if (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD && |
2266 | gdk_device_get_source (device) != GDK_SOURCE_TABLET_PAD) |
2267 | { |
2268 | pointer_info = _gdk_display_get_pointer_info (display, device); |
2269 | pointer_info->last_physical_device = device; |
2270 | } |
2271 | |
2272 | _gdk_display_device_grab_update (display, device, current_serial: serial); |
2273 | } |
2274 | |
2275 | event_surface = gdk_event_get_surface (event); |
2276 | if (!event_surface) |
2277 | goto out; |
2278 | |
2279 | type = gdk_event_get_event_type (event); |
2280 | if (type == GDK_ENTER_NOTIFY) |
2281 | _gdk_display_set_surface_under_pointer (display, device, surface: event_surface); |
2282 | else if (type == GDK_LEAVE_NOTIFY) |
2283 | _gdk_display_set_surface_under_pointer (display, device, NULL); |
2284 | |
2285 | if (type == GDK_BUTTON_PRESS) |
2286 | { |
2287 | GdkSurface *grab_surface; |
2288 | gboolean owner_events; |
2289 | |
2290 | if (!gdk_device_grab_info (display, device, grab_surface: &grab_surface, owner_events: &owner_events)) |
2291 | { |
2292 | _gdk_display_add_device_grab (display, |
2293 | device, |
2294 | surface: event_surface, |
2295 | FALSE, |
2296 | event_mask: GDK_ALL_EVENTS_MASK, |
2297 | serial_start: serial, |
2298 | time: gdk_event_get_time (event), |
2299 | TRUE); |
2300 | _gdk_display_device_grab_update (display, device, current_serial: serial); |
2301 | } |
2302 | } |
2303 | else if (type == GDK_BUTTON_RELEASE || |
2304 | type == GDK_TOUCH_CANCEL || |
2305 | type == GDK_TOUCH_END) |
2306 | { |
2307 | if (type == GDK_BUTTON_RELEASE || |
2308 | gdk_event_get_pointer_emulated (event)) |
2309 | { |
2310 | button_release_grab = |
2311 | _gdk_display_has_device_grab (display, device, serial); |
2312 | |
2313 | if (button_release_grab && |
2314 | button_release_grab->implicit && |
2315 | (gdk_event_get_modifier_state (event) & GDK_ANY_BUTTON_MASK & ~(GDK_BUTTON1_MASK << (gdk_button_event_get_button (event) - 1))) == 0) |
2316 | { |
2317 | button_release_grab->serial_end = serial; |
2318 | button_release_grab->implicit_ungrab = FALSE; |
2319 | _gdk_display_device_grab_update (display, device, current_serial: serial); |
2320 | } |
2321 | } |
2322 | } |
2323 | |
2324 | out: |
2325 | if (unlink_event) |
2326 | { |
2327 | _gdk_event_queue_remove_link (display, node: event_link); |
2328 | g_list_free_1 (list: event_link); |
2329 | gdk_event_unref (event); |
2330 | } |
2331 | |
2332 | /* This does two things - first it sees if there are motions at the |
2333 | * end of the queue that can be compressed. Second, if there is just |
2334 | * a single motion that won't be dispatched because it is a compression |
2335 | * candidate it queues up flushing the event queue. |
2336 | */ |
2337 | _gdk_event_queue_handle_motion_compression (display); |
2338 | gdk_event_queue_handle_scroll_compression (display); |
2339 | |
2340 | if (event_surface) |
2341 | { |
2342 | GdkFrameClock *clock = gdk_surface_get_frame_clock (surface: event_surface); |
2343 | |
2344 | if (clock) /* might be NULL if surface was destroyed */ |
2345 | gdk_frame_clock_request_phase (frame_clock: clock, phase: GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS); |
2346 | } |
2347 | } |
2348 | |
2349 | /** |
2350 | * gdk_surface_create_similar_surface: |
2351 | * @surface: surface to make new surface similar to |
2352 | * @content: the content for the new surface |
2353 | * @width: width of the new surface |
2354 | * @height: height of the new surface |
2355 | * |
2356 | * Create a new Cairo surface that is as compatible as possible with the |
2357 | * given @surface. |
2358 | * |
2359 | * For example the new surface will have the same fallback resolution |
2360 | * and font options as @surface. Generally, the new surface will also |
2361 | * use the same backend as @surface, unless that is not possible for |
2362 | * some reason. The type of the returned surface may be examined with |
2363 | * cairo_surface_get_type(). |
2364 | * |
2365 | * Initially the surface contents are all 0 (transparent if contents |
2366 | * have transparency, black otherwise.) |
2367 | * |
2368 | * This function always returns a valid pointer, but it will return a |
2369 | * pointer to a “nil” surface if @other is already in an error state |
2370 | * or any other error occurs. |
2371 | * |
2372 | * Returns: a pointer to the newly allocated surface. The caller |
2373 | * owns the surface and should call cairo_surface_destroy() when done |
2374 | * with it. |
2375 | */ |
2376 | cairo_surface_t * |
2377 | gdk_surface_create_similar_surface (GdkSurface * surface, |
2378 | cairo_content_t content, |
2379 | int width, |
2380 | int height) |
2381 | { |
2382 | cairo_surface_t *similar_surface; |
2383 | int scale; |
2384 | |
2385 | g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL); |
2386 | |
2387 | scale = gdk_surface_get_scale_factor (surface); |
2388 | |
2389 | similar_surface = cairo_image_surface_create (format: content == CAIRO_CONTENT_COLOR ? CAIRO_FORMAT_RGB24 : |
2390 | content == CAIRO_CONTENT_ALPHA ? CAIRO_FORMAT_A8 : CAIRO_FORMAT_ARGB32, |
2391 | width: width * scale, height: height * scale); |
2392 | cairo_surface_set_device_scale (surface: similar_surface, x_scale: scale, y_scale: scale); |
2393 | |
2394 | return similar_surface; |
2395 | } |
2396 | |
2397 | /* This function is called when the XWindow is really gone. |
2398 | */ |
2399 | void |
2400 | gdk_surface_destroy_notify (GdkSurface *surface) |
2401 | { |
2402 | GDK_SURFACE_GET_CLASS (surface)->destroy_notify (surface); |
2403 | } |
2404 | |
2405 | /** |
2406 | * gdk_drag_begin: |
2407 | * @surface: the source surface for this drag |
2408 | * @device: the device that controls this drag |
2409 | * @content: (transfer none): the offered content |
2410 | * @actions: the actions supported by this drag |
2411 | * @dx: the x offset to @device's position where the drag nominally started |
2412 | * @dy: the y offset to @device's position where the drag nominally started |
2413 | * |
2414 | * Starts a drag and creates a new drag context for it. |
2415 | * |
2416 | * This function is called by the drag source. After this call, you |
2417 | * probably want to set up the drag icon using the surface returned |
2418 | * by [method@Gdk.Drag.get_drag_surface]. |
2419 | * |
2420 | * This function returns a reference to the [class@Gdk.Drag] object, |
2421 | * but GTK keeps its own reference as well, as long as the DND operation |
2422 | * is going on. |
2423 | * |
2424 | * Note: if @actions include %GDK_ACTION_MOVE, you need to listen for |
2425 | * the [signal@Gdk.Drag::dnd-finished] signal and delete the data at |
2426 | * the source if [method@Gdk.Drag.get_selected_action] returns |
2427 | * %GDK_ACTION_MOVE. |
2428 | * |
2429 | * Returns: (transfer full) (nullable): a newly created `GdkDrag` |
2430 | */ |
2431 | GdkDrag * |
2432 | gdk_drag_begin (GdkSurface *surface, |
2433 | GdkDevice *device, |
2434 | GdkContentProvider *content, |
2435 | GdkDragAction actions, |
2436 | double dx, |
2437 | double dy) |
2438 | { |
2439 | g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL); |
2440 | g_return_val_if_fail (GDK_IS_DEVICE (device), NULL); |
2441 | g_return_val_if_fail (surface->display == gdk_device_get_display (device), NULL); |
2442 | g_return_val_if_fail (GDK_IS_CONTENT_PROVIDER (content), NULL); |
2443 | |
2444 | return GDK_SURFACE_GET_CLASS (surface)->drag_begin (surface, device, content, actions, dx, dy); |
2445 | } |
2446 | |
2447 | static void |
2448 | gdk_surface_ensure_motion (GdkSurface *surface) |
2449 | { |
2450 | GdkDisplay *display; |
2451 | GdkSeat *seat; |
2452 | GdkDevice *device; |
2453 | GdkEvent *event; |
2454 | double x, y; |
2455 | GdkModifierType state; |
2456 | GdkSurface *grab_surface; |
2457 | |
2458 | if (!surface->request_motion) |
2459 | return; |
2460 | |
2461 | surface->request_motion = FALSE; |
2462 | |
2463 | display = gdk_surface_get_display (surface); |
2464 | seat = gdk_display_get_default_seat (display); |
2465 | if (!seat) |
2466 | return; |
2467 | |
2468 | device = gdk_seat_get_pointer (seat); |
2469 | |
2470 | if (!gdk_surface_get_device_position (surface, device, x: &x, y: &y, mask: &state)) |
2471 | return; |
2472 | |
2473 | if (gdk_device_grab_info (display, device, grab_surface: &grab_surface, NULL)) |
2474 | { |
2475 | if (grab_surface != surface) |
2476 | return; |
2477 | } |
2478 | |
2479 | event = gdk_motion_event_new (surface, |
2480 | device, |
2481 | NULL, |
2482 | GDK_CURRENT_TIME, |
2483 | state, |
2484 | x, y, |
2485 | NULL); |
2486 | |
2487 | gdk_surface_handle_event (event); |
2488 | gdk_event_unref (event); |
2489 | } |
2490 | |
2491 | static void |
2492 | gdk_surface_flush_events (GdkFrameClock *clock, |
2493 | void *data) |
2494 | { |
2495 | GdkSurface *surface = GDK_SURFACE (data); |
2496 | |
2497 | _gdk_event_queue_flush (display: surface->display); |
2498 | gdk_surface_ensure_motion (surface); |
2499 | _gdk_display_pause_events (display: surface->display); |
2500 | |
2501 | gdk_frame_clock_request_phase (frame_clock: clock, phase: GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS); |
2502 | surface->frame_clock_events_paused = TRUE; |
2503 | } |
2504 | |
2505 | static void |
2506 | gdk_surface_resume_events (GdkFrameClock *clock, |
2507 | void *data) |
2508 | { |
2509 | GdkSurface *surface = GDK_SURFACE (data); |
2510 | |
2511 | if (surface->frame_clock_events_paused) |
2512 | { |
2513 | _gdk_display_unpause_events (display: surface->display); |
2514 | surface->frame_clock_events_paused = FALSE; |
2515 | } |
2516 | } |
2517 | |
2518 | static void |
2519 | gdk_surface_set_frame_clock (GdkSurface *surface, |
2520 | GdkFrameClock *clock) |
2521 | { |
2522 | g_return_if_fail (GDK_IS_SURFACE (surface)); |
2523 | g_return_if_fail (clock == NULL || GDK_IS_FRAME_CLOCK (clock)); |
2524 | |
2525 | if (clock == surface->frame_clock) |
2526 | return; |
2527 | |
2528 | if (clock) |
2529 | { |
2530 | g_object_ref (clock); |
2531 | |
2532 | g_signal_connect (G_OBJECT (clock), |
2533 | "flush-events" , |
2534 | G_CALLBACK (gdk_surface_flush_events), |
2535 | surface); |
2536 | g_signal_connect (G_OBJECT (clock), |
2537 | "resume-events" , |
2538 | G_CALLBACK (gdk_surface_resume_events), |
2539 | surface); |
2540 | g_signal_connect (G_OBJECT (clock), |
2541 | "layout" , |
2542 | G_CALLBACK (gdk_surface_layout_on_clock), |
2543 | surface); |
2544 | g_signal_connect (G_OBJECT (clock), |
2545 | "paint" , |
2546 | G_CALLBACK (gdk_surface_paint_on_clock), |
2547 | surface); |
2548 | |
2549 | if (surface->update_freeze_count == 0) |
2550 | _gdk_frame_clock_inhibit_freeze (clock); |
2551 | } |
2552 | |
2553 | if (surface->frame_clock) |
2554 | { |
2555 | if (surface->frame_clock_events_paused) |
2556 | gdk_surface_resume_events (clock: surface->frame_clock, G_OBJECT (surface)); |
2557 | |
2558 | g_signal_handlers_disconnect_by_func (G_OBJECT (surface->frame_clock), |
2559 | G_CALLBACK (gdk_surface_flush_events), |
2560 | surface); |
2561 | g_signal_handlers_disconnect_by_func (G_OBJECT (surface->frame_clock), |
2562 | G_CALLBACK (gdk_surface_resume_events), |
2563 | surface); |
2564 | g_signal_handlers_disconnect_by_func (G_OBJECT (surface->frame_clock), |
2565 | G_CALLBACK (gdk_surface_layout_on_clock), |
2566 | surface); |
2567 | g_signal_handlers_disconnect_by_func (G_OBJECT (surface->frame_clock), |
2568 | G_CALLBACK (gdk_surface_paint_on_clock), |
2569 | surface); |
2570 | |
2571 | if (surface->update_freeze_count == 0) |
2572 | _gdk_frame_clock_uninhibit_freeze (clock: surface->frame_clock); |
2573 | |
2574 | g_object_unref (object: surface->frame_clock); |
2575 | } |
2576 | |
2577 | surface->frame_clock = clock; |
2578 | } |
2579 | |
2580 | /** |
2581 | * gdk_surface_get_frame_clock: (attributes org.gtk.Method.get_property=frame-clock) |
2582 | * @surface: surface to get frame clock for |
2583 | * |
2584 | * Gets the frame clock for the surface. |
2585 | * |
2586 | * The frame clock for a surface never changes unless the surface is |
2587 | * reparented to a new toplevel surface. |
2588 | * |
2589 | * Returns: (transfer none): the frame clock |
2590 | */ |
2591 | GdkFrameClock * |
2592 | gdk_surface_get_frame_clock (GdkSurface *surface) |
2593 | { |
2594 | g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL); |
2595 | |
2596 | return surface->frame_clock; |
2597 | } |
2598 | |
2599 | /** |
2600 | * gdk_surface_get_scale_factor: (attributes org.gtk.Method.get_property=scale-factor) |
2601 | * @surface: surface to get scale factor for |
2602 | * |
2603 | * Returns the internal scale factor that maps from surface coordinates |
2604 | * to the actual device pixels. |
2605 | * |
2606 | * On traditional systems this is 1, but on very high density outputs |
2607 | * this can be a higher value (often 2). A higher value means that drawing |
2608 | * is automatically scaled up to a higher resolution, so any code doing |
2609 | * drawing will automatically look nicer. However, if you are supplying |
2610 | * pixel-based data the scale value can be used to determine whether to |
2611 | * use a pixel resource with higher resolution data. |
2612 | * |
2613 | * The scale of a surface may change during runtime. |
2614 | * |
2615 | * Returns: the scale factor |
2616 | */ |
2617 | int |
2618 | gdk_surface_get_scale_factor (GdkSurface *surface) |
2619 | { |
2620 | GdkSurfaceClass *class; |
2621 | |
2622 | g_return_val_if_fail (GDK_IS_SURFACE (surface), 1); |
2623 | |
2624 | if (GDK_SURFACE_DESTROYED (surface)) |
2625 | return 1; |
2626 | |
2627 | class = GDK_SURFACE_GET_CLASS (surface); |
2628 | if (class->get_scale_factor) |
2629 | return class->get_scale_factor (surface); |
2630 | |
2631 | return 1; |
2632 | } |
2633 | |
2634 | /** |
2635 | * gdk_surface_set_opaque_region: |
2636 | * @surface: a top-level `GdkSurface` |
2637 | * @region: (nullable): a region, or %NULL to make the entire |
2638 | * surface opaque |
2639 | * |
2640 | * Marks a region of the `GdkSurface` as opaque. |
2641 | * |
2642 | * For optimisation purposes, compositing window managers may |
2643 | * like to not draw obscured regions of surfaces, or turn off blending |
2644 | * during for these regions. With RGB windows with no transparency, |
2645 | * this is just the shape of the window, but with ARGB32 windows, the |
2646 | * compositor does not know what regions of the window are transparent |
2647 | * or not. |
2648 | * |
2649 | * This function only works for toplevel surfaces. |
2650 | * |
2651 | * GTK will update this property automatically if the @surface background |
2652 | * is opaque, as we know where the opaque regions are. If your surface |
2653 | * background is not opaque, please update this property in your |
2654 | * [vfunc@Gtk.Widget.css_changed] handler. |
2655 | */ |
2656 | void |
2657 | gdk_surface_set_opaque_region (GdkSurface *surface, |
2658 | cairo_region_t *region) |
2659 | { |
2660 | GdkSurfaceClass *class; |
2661 | |
2662 | g_return_if_fail (GDK_IS_SURFACE (surface)); |
2663 | g_return_if_fail (!GDK_SURFACE_DESTROYED (surface)); |
2664 | |
2665 | if (cairo_region_equal (a: surface->opaque_region, b: region)) |
2666 | return; |
2667 | |
2668 | g_clear_pointer (&surface->opaque_region, cairo_region_destroy); |
2669 | |
2670 | if (region != NULL) |
2671 | surface->opaque_region = cairo_region_reference (region); |
2672 | |
2673 | class = GDK_SURFACE_GET_CLASS (surface); |
2674 | if (class->set_opaque_region) |
2675 | class->set_opaque_region (surface, region); |
2676 | } |
2677 | |
2678 | void |
2679 | gdk_surface_set_state (GdkSurface *surface, |
2680 | GdkToplevelState new_state) |
2681 | { |
2682 | gboolean was_sticky, sticky; |
2683 | g_return_if_fail (GDK_IS_SURFACE (surface)); |
2684 | |
2685 | if (new_state == surface->state) |
2686 | return; /* No actual work to do, nothing changed. */ |
2687 | |
2688 | /* Actually update the field in GdkSurface, this is sort of an odd |
2689 | * place to do it, but seems like the safest since it ensures we expose no |
2690 | * inconsistent state to the user. |
2691 | */ |
2692 | |
2693 | was_sticky = GDK_SURFACE_IS_STICKY (surface); |
2694 | |
2695 | surface->state = new_state; |
2696 | |
2697 | sticky = GDK_SURFACE_IS_STICKY (surface); |
2698 | |
2699 | if (GDK_IS_TOPLEVEL (ptr: surface)) |
2700 | g_object_notify (G_OBJECT (surface), property_name: "state" ); |
2701 | |
2702 | if (was_sticky != sticky) |
2703 | g_object_notify (G_OBJECT (surface), property_name: "sticky" ); |
2704 | } |
2705 | |
2706 | void |
2707 | gdk_synthesize_surface_state (GdkSurface *surface, |
2708 | GdkToplevelState unset_flags, |
2709 | GdkToplevelState set_flags) |
2710 | { |
2711 | gdk_surface_set_state (surface, new_state: (surface->state | set_flags) & ~unset_flags); |
2712 | } |
2713 | |
2714 | void |
2715 | gdk_surface_queue_state_change (GdkSurface *surface, |
2716 | GdkToplevelState unset_flags, |
2717 | GdkToplevelState set_flags) |
2718 | { |
2719 | surface->pending_unset_flags |= unset_flags; |
2720 | surface->pending_set_flags &= ~unset_flags; |
2721 | |
2722 | surface->pending_set_flags |= set_flags; |
2723 | surface->pending_unset_flags &= ~set_flags; |
2724 | } |
2725 | |
2726 | void |
2727 | gdk_surface_apply_state_change (GdkSurface *surface) |
2728 | { |
2729 | if (!surface->pending_unset_flags && !surface->pending_set_flags) |
2730 | return; |
2731 | |
2732 | gdk_synthesize_surface_state (surface, |
2733 | unset_flags: surface->pending_unset_flags, |
2734 | set_flags: surface->pending_set_flags); |
2735 | surface->pending_unset_flags = 0; |
2736 | surface->pending_set_flags = 0; |
2737 | } |
2738 | |
2739 | static gboolean |
2740 | set_is_mapped_idle (gpointer user_data) |
2741 | { |
2742 | GdkSurface *surface = GDK_SURFACE (user_data); |
2743 | |
2744 | surface->set_is_mapped_source_id = 0; |
2745 | |
2746 | g_return_val_if_fail (surface->pending_is_mapped != surface->is_mapped, |
2747 | G_SOURCE_REMOVE); |
2748 | |
2749 | surface->is_mapped = surface->pending_is_mapped; |
2750 | if (surface->is_mapped) |
2751 | gdk_surface_invalidate_rect (surface, NULL); |
2752 | |
2753 | g_object_notify (G_OBJECT (surface), property_name: "mapped" ); |
2754 | |
2755 | return G_SOURCE_REMOVE; |
2756 | } |
2757 | |
2758 | void |
2759 | gdk_surface_set_is_mapped (GdkSurface *surface, |
2760 | gboolean is_mapped) |
2761 | { |
2762 | gboolean was_mapped; |
2763 | |
2764 | if (surface->pending_is_mapped != surface->is_mapped) |
2765 | g_clear_handle_id (&surface->set_is_mapped_source_id, g_source_remove); |
2766 | |
2767 | surface->pending_is_mapped = is_mapped; |
2768 | |
2769 | was_mapped = surface->is_mapped; |
2770 | surface->is_mapped = is_mapped; |
2771 | if (surface->is_mapped) |
2772 | gdk_surface_invalidate_rect (surface, NULL); |
2773 | |
2774 | if (was_mapped != is_mapped) |
2775 | g_object_notify (G_OBJECT (surface), property_name: "mapped" ); |
2776 | } |
2777 | |
2778 | static void |
2779 | gdk_surface_queue_set_is_mapped (GdkSurface *surface, |
2780 | gboolean is_mapped) |
2781 | { |
2782 | if (surface->pending_is_mapped == is_mapped) |
2783 | return; |
2784 | |
2785 | surface->pending_is_mapped = is_mapped; |
2786 | |
2787 | if (surface->is_mapped == surface->pending_is_mapped) |
2788 | { |
2789 | g_clear_handle_id (&surface->set_is_mapped_source_id, g_source_remove); |
2790 | } |
2791 | else |
2792 | { |
2793 | g_return_if_fail (!surface->set_is_mapped_source_id); |
2794 | |
2795 | surface->set_is_mapped_source_id = |
2796 | g_idle_add_full (G_PRIORITY_HIGH - 10, |
2797 | function: set_is_mapped_idle, |
2798 | data: surface, NULL); |
2799 | } |
2800 | } |
2801 | |
2802 | static gboolean |
2803 | check_autohide (GdkEvent *event) |
2804 | { |
2805 | GdkDisplay *display; |
2806 | GdkDevice *device; |
2807 | GdkSurface *grab_surface; |
2808 | |
2809 | switch ((guint) gdk_event_get_event_type (event)) |
2810 | { |
2811 | case GDK_BUTTON_PRESS: |
2812 | #if 0 |
2813 | // FIXME: we need to ignore the release that is paired |
2814 | // with the press starting the grab - due to implicit |
2815 | // grabs, it will be delivered to the same place as the |
2816 | // press, and will cause the auto dismissal to be triggered. |
2817 | case GDK_BUTTON_RELEASE: |
2818 | #endif |
2819 | case GDK_TOUCH_BEGIN: |
2820 | case GDK_TOUCH_END: |
2821 | case GDK_TOUCH_CANCEL: |
2822 | case GDK_TOUCHPAD_SWIPE: |
2823 | case GDK_TOUCHPAD_PINCH: |
2824 | display = gdk_event_get_display (event); |
2825 | device = gdk_event_get_device (event); |
2826 | if (gdk_device_grab_info (display, device, grab_surface: &grab_surface, NULL)) |
2827 | { |
2828 | GdkSurface *event_surface; |
2829 | |
2830 | event_surface = gdk_event_get_surface (event); |
2831 | |
2832 | if (grab_surface != event_surface && |
2833 | grab_surface != event_surface->parent && |
2834 | grab_surface->autohide) |
2835 | { |
2836 | GdkSurface *surface = grab_surface; |
2837 | |
2838 | do |
2839 | { |
2840 | gdk_surface_hide (surface); |
2841 | surface = surface->parent; |
2842 | } |
2843 | while (surface->autohide && surface != event_surface); |
2844 | |
2845 | return TRUE; |
2846 | } |
2847 | } |
2848 | break; |
2849 | default:; |
2850 | } |
2851 | |
2852 | return FALSE; |
2853 | } |
2854 | |
2855 | static inline void |
2856 | add_event_mark (GdkEvent *event, |
2857 | gint64 time, |
2858 | gint64 end_time) |
2859 | { |
2860 | #ifdef HAVE_SYSPROF |
2861 | char *message = NULL; |
2862 | const char *kind; |
2863 | GEnumClass *class; |
2864 | GEnumValue *value; |
2865 | GdkEventType event_type; |
2866 | |
2867 | event_type = gdk_event_get_event_type (event); |
2868 | class = g_type_class_ref (GDK_TYPE_EVENT_TYPE); |
2869 | value = g_enum_get_value (class, event_type); |
2870 | g_type_class_unref (class); |
2871 | kind = value ? value->value_nick : "event" ; |
2872 | |
2873 | switch ((int) event_type) |
2874 | { |
2875 | case GDK_MOTION_NOTIFY: |
2876 | { |
2877 | double x, y; |
2878 | gdk_event_get_position (event, &x, &y); |
2879 | message = g_strdup_printf ("%s {x=%lf, y=%lf, state=0x%x}" , |
2880 | kind, |
2881 | x, y, |
2882 | gdk_event_get_modifier_state (event)); |
2883 | break; |
2884 | } |
2885 | |
2886 | case GDK_BUTTON_PRESS: |
2887 | case GDK_BUTTON_RELEASE: |
2888 | { |
2889 | double x, y; |
2890 | gdk_event_get_position (event, &x, &y); |
2891 | message = g_strdup_printf ("%s {button=%u, x=%lf, y=%lf, state=0x%x}" , |
2892 | kind, |
2893 | gdk_button_event_get_button (event), |
2894 | x, y, |
2895 | gdk_event_get_modifier_state (event)); |
2896 | break; |
2897 | } |
2898 | |
2899 | case GDK_KEY_PRESS: |
2900 | case GDK_KEY_RELEASE: |
2901 | { |
2902 | message = g_strdup_printf ("%s {keyval=%u, state=0x%x, keycode=%u layout=%u level=%u is_modifier=%u}" , |
2903 | kind, |
2904 | gdk_key_event_get_keyval (event), |
2905 | gdk_event_get_modifier_state (event), |
2906 | gdk_key_event_get_keycode (event), |
2907 | gdk_key_event_get_layout (event), |
2908 | gdk_key_event_get_level (event), |
2909 | gdk_key_event_is_modifier (event)); |
2910 | break; |
2911 | } |
2912 | |
2913 | case GDK_ENTER_NOTIFY: |
2914 | case GDK_LEAVE_NOTIFY: |
2915 | case GDK_TOUCHPAD_SWIPE: |
2916 | case GDK_TOUCHPAD_PINCH: |
2917 | case GDK_SCROLL: |
2918 | case GDK_DRAG_ENTER: |
2919 | case GDK_DRAG_LEAVE: |
2920 | case GDK_DRAG_MOTION: |
2921 | case GDK_DROP_START: |
2922 | case GDK_TOUCH_BEGIN: |
2923 | case GDK_TOUCH_UPDATE: |
2924 | case GDK_TOUCH_END: |
2925 | case GDK_TOUCH_CANCEL: |
2926 | case GDK_PAD_BUTTON_PRESS: |
2927 | case GDK_PAD_BUTTON_RELEASE: |
2928 | case GDK_PAD_RING: |
2929 | case GDK_PAD_STRIP: |
2930 | case GDK_PAD_GROUP_MODE: |
2931 | case GDK_GRAB_BROKEN: |
2932 | case GDK_DELETE: |
2933 | case GDK_FOCUS_CHANGE: |
2934 | case GDK_PROXIMITY_IN: |
2935 | case GDK_PROXIMITY_OUT: |
2936 | case GDK_EVENT_LAST: |
2937 | default: |
2938 | break; |
2939 | } |
2940 | |
2941 | gdk_profiler_add_mark (time, end_time - time, "event" , message ? message : kind); |
2942 | |
2943 | g_free (message); |
2944 | #endif |
2945 | } |
2946 | |
2947 | gboolean |
2948 | gdk_surface_handle_event (GdkEvent *event) |
2949 | { |
2950 | GdkSurface *surface = gdk_event_get_surface (event); |
2951 | gint64 begin_time = GDK_PROFILER_CURRENT_TIME; |
2952 | gboolean handled = FALSE; |
2953 | |
2954 | if (check_autohide (event)) |
2955 | return TRUE; |
2956 | |
2957 | |
2958 | if (gdk_event_get_event_type (event) == GDK_MOTION_NOTIFY) |
2959 | surface->request_motion = FALSE; |
2960 | |
2961 | g_signal_emit (instance: surface, signal_id: signals[EVENT], detail: 0, event, &handled); |
2962 | |
2963 | if (GDK_PROFILER_IS_RUNNING) |
2964 | add_event_mark (event, time: begin_time, GDK_PROFILER_CURRENT_TIME); |
2965 | |
2966 | return handled; |
2967 | } |
2968 | |
2969 | /* |
2970 | * gdk_surface_request_motion: |
2971 | * @surface: a `GdkSurface` |
2972 | * |
2973 | * Request that the next frame cycle should deliver a motion |
2974 | * event for @surface. |
2975 | * |
2976 | * The motion event will be delivered if the pointer is over the |
2977 | * surface, regardless whether the pointer has moved or not. This |
2978 | * is used by GTK after moving widgets around. |
2979 | */ |
2980 | void |
2981 | gdk_surface_request_motion (GdkSurface *surface) |
2982 | { |
2983 | surface->request_motion = TRUE; |
2984 | } |
2985 | |
2986 | /** |
2987 | * gdk_surface_translate_coordinates: |
2988 | * @from: the origin surface |
2989 | * @to: the target surface |
2990 | * @x: (inout): coordinates to translate |
2991 | * @y: (inout): coordinates to translate |
2992 | * |
2993 | * Translates coordinates between two surfaces. |
2994 | * |
2995 | * Note that this only works if @to and @from are popups or |
2996 | * transient-for to the same toplevel (directly or indirectly). |
2997 | * |
2998 | * Returns: %TRUE if the coordinates were successfully translated |
2999 | */ |
3000 | gboolean |
3001 | gdk_surface_translate_coordinates (GdkSurface *from, |
3002 | GdkSurface *to, |
3003 | double *x, |
3004 | double *y) |
3005 | { |
3006 | double in_x, in_y, out_x, out_y; |
3007 | int x1, y1, x2, y2; |
3008 | GdkSurface *f, *t; |
3009 | |
3010 | g_return_val_if_fail (GDK_IS_SURFACE (from), FALSE); |
3011 | g_return_val_if_fail (GDK_IS_SURFACE (to), FALSE); |
3012 | g_return_val_if_fail (x != NULL, FALSE); |
3013 | g_return_val_if_fail (y != NULL, FALSE); |
3014 | |
3015 | in_x = *x; |
3016 | in_y = *y; |
3017 | |
3018 | x1 = 0; |
3019 | y1 = 0; |
3020 | f = from; |
3021 | while (f->parent) |
3022 | { |
3023 | x1 += f->x; |
3024 | y1 += f->y; |
3025 | f = f->parent; |
3026 | } |
3027 | |
3028 | x2 = 0; |
3029 | y2 = 0; |
3030 | t = to; |
3031 | while (t->parent) |
3032 | { |
3033 | x2 += t->x; |
3034 | y2 += t->y; |
3035 | t = t->parent; |
3036 | } |
3037 | |
3038 | if (f != t) |
3039 | return FALSE; |
3040 | |
3041 | out_x = in_x + (x1 - x2); |
3042 | out_y = in_y + (y1 - y2); |
3043 | |
3044 | *x = out_x; |
3045 | *y = out_y; |
3046 | |
3047 | return TRUE; |
3048 | } |
3049 | |
3050 | GdkSeat * |
3051 | gdk_surface_get_seat_from_event (GdkSurface *surface, |
3052 | GdkEvent *event) |
3053 | { |
3054 | if (event) |
3055 | { |
3056 | GdkSeat *seat = NULL; |
3057 | |
3058 | seat = gdk_event_get_seat (event); |
3059 | |
3060 | if (seat) |
3061 | return seat; |
3062 | } |
3063 | return gdk_display_get_default_seat (display: surface->display); |
3064 | } |
3065 | |
3066 | void |
3067 | gdk_surface_enter_monitor (GdkSurface *surface, |
3068 | GdkMonitor *monitor) |
3069 | { |
3070 | g_signal_emit (instance: surface, signal_id: signals[ENTER_MONITOR], detail: 0, monitor); |
3071 | } |
3072 | |
3073 | void |
3074 | gdk_surface_leave_monitor (GdkSurface *surface, |
3075 | GdkMonitor *monitor) |
3076 | { |
3077 | g_signal_emit (instance: surface, signal_id: signals[LEAVE_MONITOR], detail: 0, monitor); |
3078 | } |
3079 | |