| /* GDK - The GIMP Drawing Kit |
| * Copyright (C) 1995-2007 Peter Mattis, Spencer Kimball, |
| * Josh MacDonald, Ryan Lortie |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| /* |
| * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS |
| * file for a list of people on the GTK+ Team. See the ChangeLog |
| * files for a list of changes. These files are distributed with |
| * GTK+ at ftp://ftp.gtk.org/pub/gtk/. |
| */ |
| |
| #include "config.h" |
| |
| #include <cairo-gobject.h> |
| |
| #include "gdksurface.h" |
| |
| #include "gdkprivate.h" |
| #include "gdkcontentprovider.h" |
| #include "gdkdeviceprivate.h" |
| #include "gdkdisplayprivate.h" |
| #include "gdkdragsurfaceprivate.h" |
| #include "gdkeventsprivate.h" |
| #include "gdkframeclockidleprivate.h" |
| #include "gdkglcontextprivate.h" |
| #include <glib/gi18n-lib.h> |
| #include "gdkmarshalers.h" |
| #include "gdkpopupprivate.h" |
| #include "gdkrectangle.h" |
| #include "gdktoplevelprivate.h" |
| #include "gdkvulkancontext.h" |
| |
| #include <math.h> |
| |
| #ifdef HAVE_EGL |
| #include <epoxy/egl.h> |
| #endif |
| |
| /** |
| * GdkSurface: |
| * |
| * A `GdkSurface` is a rectangular region on the screen. |
| * |
| * It’s a low-level object, used to implement high-level objects |
| * such as [class@Gtk.Window] or [class@Gtk.Dialog] in GTK. |
| * |
| * The surfaces you see in practice are either [iface@Gdk.Toplevel] or |
| * [iface@Gdk.Popup], and those interfaces provide much of the required |
| * API to interact with these surfaces. Other, more specialized surface |
| * types exist, but you will rarely interact with them directly. |
| */ |
| |
| typedef struct _GdkSurfacePrivate GdkSurfacePrivate; |
| |
| struct _GdkSurfacePrivate |
| { |
| gpointer egl_native_window; |
| #ifdef HAVE_EGL |
| EGLSurface egl_surface; |
| gboolean egl_surface_high_depth; |
| #endif |
| |
| gpointer widget; |
| }; |
| |
| enum { |
| LAYOUT, |
| RENDER, |
| EVENT, |
| ENTER_MONITOR, |
| LEAVE_MONITOR, |
| LAST_SIGNAL |
| }; |
| |
| enum { |
| PROP_0, |
| PROP_CURSOR, |
| PROP_DISPLAY, |
| PROP_FRAME_CLOCK, |
| PROP_MAPPED, |
| PROP_WIDTH, |
| PROP_HEIGHT, |
| PROP_SCALE_FACTOR, |
| LAST_PROP |
| }; |
| |
| /* Global info */ |
| |
| static void gdk_surface_finalize (GObject *object); |
| |
| static void gdk_surface_set_property (GObject *object, |
| guint prop_id, |
| const GValue *value, |
| GParamSpec *pspec); |
| static void gdk_surface_get_property (GObject *object, |
| guint prop_id, |
| GValue *value, |
| GParamSpec *pspec); |
| |
| static void update_cursor (GdkDisplay *display, |
| GdkDevice *device); |
| |
| static void gdk_surface_set_frame_clock (GdkSurface *surface, |
| GdkFrameClock *clock); |
| |
| static void gdk_surface_queue_set_is_mapped (GdkSurface *surface, |
| gboolean is_mapped); |
| |
| |
| static guint signals[LAST_SIGNAL] = { 0 }; |
| static GParamSpec *properties[LAST_PROP] = { NULL, }; |
| |
| G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GdkSurface, gdk_surface, G_TYPE_OBJECT) |
| |
| static gboolean |
| gdk_surface_real_beep (GdkSurface *surface) |
| { |
| return FALSE; |
| } |
| |
| static GdkDisplay * |
| get_display_for_surface (GdkSurface *primary, |
| GdkSurface *secondary) |
| { |
| GdkDisplay *display = primary->display; |
| |
| if (display) |
| return display; |
| |
| display = secondary->display; |
| |
| if (display) |
| return display; |
| |
| g_warning ("no display for surface, using default"); |
| return gdk_display_get_default (); |
| } |
| |
| static GdkMonitor * |
| get_monitor_for_rect (GdkDisplay *display, |
| const GdkRectangle *rect, |
| void (*get_bounds) (GdkMonitor *monitor, |
| GdkRectangle *bounds)) |
| { |
| int biggest_area = G_MININT; |
| GdkMonitor *best_monitor = NULL; |
| GdkMonitor *monitor; |
| GdkRectangle workarea; |
| GdkRectangle intersection; |
| GListModel *monitors; |
| guint i; |
| |
| monitors = gdk_display_get_monitors (display); |
| for (i = 0; i < g_list_model_get_n_items (monitors); i++) |
| { |
| monitor = g_list_model_get_item (monitors, i); |
| get_bounds (monitor, &workarea); |
| |
| if (gdk_rectangle_intersect (&workarea, rect, &intersection)) |
| { |
| if (intersection.width * intersection.height > biggest_area) |
| { |
| biggest_area = intersection.width * intersection.height; |
| best_monitor = monitor; |
| } |
| } |
| g_object_unref (monitor); |
| } |
| |
| return best_monitor; |
| } |
| |
| static int |
| get_anchor_x_sign (GdkGravity anchor) |
| { |
| switch (anchor) |
| { |
| case GDK_GRAVITY_STATIC: |
| case GDK_GRAVITY_NORTH_WEST: |
| case GDK_GRAVITY_WEST: |
| case GDK_GRAVITY_SOUTH_WEST: |
| return -1; |
| |
| default: |
| case GDK_GRAVITY_NORTH: |
| case GDK_GRAVITY_CENTER: |
| case GDK_GRAVITY_SOUTH: |
| return 0; |
| |
| case GDK_GRAVITY_NORTH_EAST: |
| case GDK_GRAVITY_EAST: |
| case GDK_GRAVITY_SOUTH_EAST: |
| return 1; |
| } |
| } |
| |
| static int |
| get_anchor_y_sign (GdkGravity anchor) |
| { |
| switch (anchor) |
| { |
| case GDK_GRAVITY_STATIC: |
| case GDK_GRAVITY_NORTH_WEST: |
| case GDK_GRAVITY_NORTH: |
| case GDK_GRAVITY_NORTH_EAST: |
| return -1; |
| |
| default: |
| case GDK_GRAVITY_WEST: |
| case GDK_GRAVITY_CENTER: |
| case GDK_GRAVITY_EAST: |
| return 0; |
| |
| case GDK_GRAVITY_SOUTH_WEST: |
| case GDK_GRAVITY_SOUTH: |
| case GDK_GRAVITY_SOUTH_EAST: |
| return 1; |
| } |
| } |
| |
| static int |
| maybe_flip_position (int bounds_pos, |
| int bounds_size, |
| int rect_pos, |
| int rect_size, |
| int surface_size, |
| int rect_sign, |
| int surface_sign, |
| int offset, |
| gboolean flip, |
| gboolean *flipped) |
| { |
| int primary; |
| int secondary; |
| |
| *flipped = FALSE; |
| primary = rect_pos + (1 + rect_sign) * rect_size / 2 + offset - (1 + surface_sign) * surface_size / 2; |
| |
| if (!flip || (primary >= bounds_pos && primary + surface_size <= bounds_pos + bounds_size)) |
| return primary; |
| |
| *flipped = TRUE; |
| secondary = rect_pos + (1 - rect_sign) * rect_size / 2 - offset - (1 - surface_sign) * surface_size / 2; |
| |
| if (secondary >= bounds_pos && secondary + surface_size <= bounds_pos + bounds_size) |
| return secondary; |
| |
| *flipped = FALSE; |
| return primary; |
| } |
| |
| GdkMonitor * |
| gdk_surface_get_layout_monitor (GdkSurface *surface, |
| GdkPopupLayout *layout, |
| void (*get_bounds) (GdkMonitor *monitor, |
| GdkRectangle *bounds)) |
| { |
| GdkDisplay *display; |
| GdkRectangle root_rect; |
| |
| root_rect = *gdk_popup_layout_get_anchor_rect (layout); |
| gdk_surface_get_root_coords (surface->parent, |
| root_rect.x, |
| root_rect.y, |
| &root_rect.x, |
| &root_rect.y); |
| |
| root_rect.width = MAX (1, root_rect.width); |
| root_rect.height = MAX (1, root_rect.height); |
| |
| display = get_display_for_surface (surface, surface->transient_for); |
| return get_monitor_for_rect (display, &root_rect, get_bounds); |
| } |
| |
| void |
| gdk_surface_layout_popup_helper (GdkSurface *surface, |
| int width, |
| int height, |
| int shadow_left, |
| int shadow_right, |
| int shadow_top, |
| int shadow_bottom, |
| GdkMonitor *monitor, |
| GdkRectangle *bounds, |
| GdkPopupLayout *layout, |
| GdkRectangle *out_final_rect) |
| { |
| GdkRectangle root_rect; |
| GdkGravity rect_anchor; |
| GdkGravity surface_anchor; |
| int rect_anchor_dx; |
| int rect_anchor_dy; |
| GdkAnchorHints anchor_hints; |
| GdkRectangle final_rect; |
| gboolean flipped_x; |
| gboolean flipped_y; |
| int x, y; |
| |
| g_return_if_fail (GDK_IS_POPUP (surface)); |
| |
| root_rect = *gdk_popup_layout_get_anchor_rect (layout); |
| gdk_surface_get_root_coords (surface->parent, |
| root_rect.x, |
| root_rect.y, |
| &root_rect.x, |
| &root_rect.y); |
| |
| rect_anchor = gdk_popup_layout_get_rect_anchor (layout); |
| surface_anchor = gdk_popup_layout_get_surface_anchor (layout); |
| gdk_popup_layout_get_offset (layout, &rect_anchor_dx, &rect_anchor_dy); |
| anchor_hints = gdk_popup_layout_get_anchor_hints (layout); |
| |
| final_rect.width = width - shadow_left - shadow_right; |
| final_rect.height = height - shadow_top - shadow_bottom; |
| final_rect.x = maybe_flip_position (bounds->x, |
| bounds->width, |
| root_rect.x, |
| root_rect.width, |
| final_rect.width, |
| get_anchor_x_sign (rect_anchor), |
| get_anchor_x_sign (surface_anchor), |
| rect_anchor_dx, |
| anchor_hints & GDK_ANCHOR_FLIP_X, |
| &flipped_x); |
| final_rect.y = maybe_flip_position (bounds->y, |
| bounds->height, |
| root_rect.y, |
| root_rect.height, |
| final_rect.height, |
| get_anchor_y_sign (rect_anchor), |
| get_anchor_y_sign (surface_anchor), |
| rect_anchor_dy, |
| anchor_hints & GDK_ANCHOR_FLIP_Y, |
| &flipped_y); |
| |
| if (anchor_hints & GDK_ANCHOR_SLIDE_X) |
| { |
| if (final_rect.x + final_rect.width > bounds->x + bounds->width) |
| final_rect.x = bounds->x + bounds->width - final_rect.width; |
| |
| if (final_rect.x < bounds->x) |
| final_rect.x = bounds->x; |
| } |
| |
| if (anchor_hints & GDK_ANCHOR_SLIDE_Y) |
| { |
| if (final_rect.y + final_rect.height > bounds->y + bounds->height) |
| final_rect.y = bounds->y + bounds->height - final_rect.height; |
| |
| if (final_rect.y < bounds->y) |
| final_rect.y = bounds->y; |
| } |
| |
| if (anchor_hints & GDK_ANCHOR_RESIZE_X) |
| { |
| if (final_rect.x < bounds->x) |
| { |
| final_rect.width -= bounds->x - final_rect.x; |
| final_rect.x = bounds->x; |
| } |
| |
| if (final_rect.x + final_rect.width > bounds->x + bounds->width) |
| final_rect.width = bounds->x + bounds->width - final_rect.x; |
| } |
| |
| if (anchor_hints & GDK_ANCHOR_RESIZE_Y) |
| { |
| if (final_rect.y < bounds->y) |
| { |
| final_rect.height -= bounds->y - final_rect.y; |
| final_rect.y = bounds->y; |
| } |
| |
| if (final_rect.y + final_rect.height > bounds->y + bounds->height) |
| final_rect.height = bounds->y + bounds->height - final_rect.y; |
| } |
| |
| final_rect.x -= shadow_left; |
| final_rect.y -= shadow_top; |
| final_rect.width += shadow_left + shadow_right; |
| final_rect.height += shadow_top + shadow_bottom; |
| |
| gdk_surface_get_origin (surface->parent, &x, &y); |
| final_rect.x -= x; |
| final_rect.y -= y; |
| |
| if (flipped_x) |
| { |
| rect_anchor = gdk_gravity_flip_horizontally (rect_anchor); |
| surface_anchor = gdk_gravity_flip_horizontally (surface_anchor); |
| } |
| if (flipped_y) |
| { |
| rect_anchor = gdk_gravity_flip_vertically (rect_anchor); |
| surface_anchor = gdk_gravity_flip_vertically (surface_anchor); |
| } |
| |
| surface->popup.rect_anchor = rect_anchor; |
| surface->popup.surface_anchor = surface_anchor; |
| |
| *out_final_rect = final_rect; |
| } |
| |
| /* Since GdkEvent is a GTypeInstance, GValue can only store it as a pointer, |
| * and GClosure does not know how to handle its memory management. To avoid |
| * the event going away in the middle of the signal emission, we provide a |
| * marshaller that keeps the event alive for the duration of the closure. |
| */ |
| static void |
| gdk_surface_event_marshaller (GClosure *closure, |
| GValue *return_value, |
| guint n_param_values, |
| const GValue *param_values, |
| gpointer invocation_hint, |
| gpointer marshal_data) |
| { |
| GdkEvent *event = g_value_get_pointer (¶m_values[1]); |
| |
| gdk_event_ref (event); |
| |
| _gdk_marshal_BOOLEAN__POINTER (closure, |
| return_value, |
| n_param_values, |
| param_values, |
| invocation_hint, |
| marshal_data); |
| |
| |
| gdk_event_unref (event); |
| } |
| |
| static void |
| gdk_surface_event_marshallerv (GClosure *closure, |
| GValue *return_value, |
| gpointer instance, |
| va_list args, |
| gpointer marshal_data, |
| int n_params, |
| GType *param_types) |
| { |
| va_list args_copy; |
| GdkEvent *event; |
| |
| G_VA_COPY (args_copy, args); |
| event = va_arg (args_copy, gpointer); |
| |
| gdk_event_ref (event); |
| |
| _gdk_marshal_BOOLEAN__POINTERv (closure, |
| return_value, |
| instance, |
| args, |
| marshal_data, |
| n_params, |
| param_types); |
| |
| gdk_event_unref (event); |
| |
| va_end (args_copy); |
| } |
| |
| static void |
| gdk_surface_init (GdkSurface *surface) |
| { |
| /* 0-initialization is good for all other fields. */ |
| |
| surface->state = 0; |
| surface->fullscreen_mode = GDK_FULLSCREEN_ON_CURRENT_MONITOR; |
| surface->width = 1; |
| surface->height = 1; |
| |
| surface->alpha = 255; |
| |
| surface->device_cursor = g_hash_table_new_full (NULL, NULL, |
| NULL, g_object_unref); |
| } |
| |
| static void |
| gdk_surface_class_init (GdkSurfaceClass *klass) |
| { |
| GObjectClass *object_class = G_OBJECT_CLASS (klass); |
| |
| object_class->finalize = gdk_surface_finalize; |
| object_class->set_property = gdk_surface_set_property; |
| object_class->get_property = gdk_surface_get_property; |
| |
| klass->beep = gdk_surface_real_beep; |
| |
| /** |
| * GdkSurface:cursor: (attributes org.gtk.Property.get=gdk_surface_get_cursor org.gtk.Property.set=gdk_surface_set_cursor) |
| * |
| * The mouse pointer for the `GdkSurface`. |
| */ |
| properties[PROP_CURSOR] = |
| g_param_spec_object ("cursor", NULL, NULL, |
| GDK_TYPE_CURSOR, |
| G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); |
| |
| /** |
| * GdkSurface:display: (attributes org.gtk.Property.get=gdk_surface_get_display) |
| * |
| * The `GdkDisplay` connection of the surface. |
| */ |
| properties[PROP_DISPLAY] = |
| g_param_spec_object ("display", NULL, NULL, |
| GDK_TYPE_DISPLAY, |
| G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); |
| |
| /** |
| * GdkSurface:frame-clock: (attributes org.gtk.Property.get=gdk_surface_get_frame_clock) |
| * |
| * The `GdkFrameClock` of the surface. |
| */ |
| properties[PROP_FRAME_CLOCK] = |
| g_param_spec_object ("frame-clock", NULL, NULL, |
| GDK_TYPE_FRAME_CLOCK, |
| G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); |
| |
| /** |
| * GdkSurface:mapped: (attributes org.gtk.Property.get=gdk_surface_get_mapped) |
| * |
| * Whether the surface is mapped. |
| */ |
| properties[PROP_MAPPED] = |
| g_param_spec_boolean ("mapped", NULL, NULL, |
| FALSE, |
| G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); |
| |
| /** |
| * GdkSurface:width: (attributes org.gtk.Property.get=gdk_surface_get_width) |
| * |
| * The width of the surface in pixels. |
| */ |
| properties[PROP_WIDTH] = |
| g_param_spec_int ("width", NULL, NULL, |
| 0, G_MAXINT, 0, |
| G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); |
| |
| /** |
| * GdkSurface:height: (attributes org.gtk.Property.get=gdk_surface_get_height) |
| * |
| * The height of the surface, in pixels. |
| */ |
| properties[PROP_HEIGHT] = |
| g_param_spec_int ("height", NULL, NULL, |
| 0, G_MAXINT, 0, |
| G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); |
| |
| /** |
| * GdkSurface:scale-factor: (attributes org.gtk.Property.get=gdk_surface_get_scale_factor) |
| * |
| * The scale factor of the surface. |
| */ |
| properties[PROP_SCALE_FACTOR] = |
| g_param_spec_int ("scale-factor", NULL, NULL, |
| 1, G_MAXINT, 1, |
| G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); |
| |
| g_object_class_install_properties (object_class, LAST_PROP, properties); |
| |
| /** |
| * GdkSurface::layout: |
| * @surface: the `GdkSurface` |
| * @width: the current width |
| * @height: the current height |
| * |
| * Emitted when the size of @surface is changed, or when relayout should |
| * be performed. |
| * |
| * Surface size is reported in ”application pixels”, not |
| * ”device pixels” (see gdk_surface_get_scale_factor()). |
| */ |
| signals[LAYOUT] = |
| g_signal_new (g_intern_static_string ("layout"), |
| G_OBJECT_CLASS_TYPE (object_class), |
| G_SIGNAL_RUN_FIRST, |
| 0, |
| NULL, |
| NULL, |
| _gdk_marshal_VOID__INT_INT, |
| G_TYPE_NONE, |
| 2, |
| G_TYPE_INT, |
| G_TYPE_INT); |
| g_signal_set_va_marshaller (signals[LAYOUT], |
| G_OBJECT_CLASS_TYPE (object_class), |
| _gdk_marshal_VOID__INT_INTv); |
| |
| /** |
| * GdkSurface::render: |
| * @surface: the `GdkSurface` |
| * @region: the region that needs to be redrawn |
| * |
| * Emitted when part of the surface needs to be redrawn. |
| * |
| * Returns: %TRUE to indicate that the signal has been handled |
| */ |
| signals[RENDER] = |
| g_signal_new (g_intern_static_string ("render"), |
| G_OBJECT_CLASS_TYPE (object_class), |
| G_SIGNAL_RUN_LAST, |
| 0, |
| g_signal_accumulator_true_handled, |
| NULL, |
| _gdk_marshal_BOOLEAN__BOXED, |
| G_TYPE_BOOLEAN, |
| 1, |
| CAIRO_GOBJECT_TYPE_REGION); |
| g_signal_set_va_marshaller (signals[RENDER], |
| G_OBJECT_CLASS_TYPE (object_class), |
| _gdk_marshal_BOOLEAN__BOXEDv); |
| |
| /** |
| * GdkSurface::event: |
| * @surface: the `GdkSurface` |
| * @event: (type Gdk.Event): an input event |
| * |
| * Emitted when GDK receives an input event for @surface. |
| * |
| * Returns: %TRUE to indicate that the event has been handled |
| */ |
| signals[EVENT] = |
| g_signal_new (g_intern_static_string ("event"), |
| G_OBJECT_CLASS_TYPE (object_class), |
| G_SIGNAL_RUN_LAST, |
| 0, |
| g_signal_accumulator_true_handled, |
| NULL, |
| gdk_surface_event_marshaller, |
| G_TYPE_BOOLEAN, |
| 1, |
| G_TYPE_POINTER); |
| g_signal_set_va_marshaller (signals[EVENT], |
| G_OBJECT_CLASS_TYPE (object_class), |
| gdk_surface_event_marshallerv); |
| |
| /** |
| * GdkSurface::enter-monitor: |
| * @surface: the `GdkSurface` |
| * @monitor: the monitor |
| * |
| * Emitted when @surface starts being present on the monitor. |
| */ |
| signals[ENTER_MONITOR] = |
| g_signal_new (g_intern_static_string ("enter-monitor"), |
| G_OBJECT_CLASS_TYPE (object_class), |
| G_SIGNAL_RUN_FIRST, |
| 0, |
| NULL, |
| NULL, |
| NULL, |
| G_TYPE_NONE, |
| 1, |
| GDK_TYPE_MONITOR); |
| |
| /** |
| * GdkSurface::leave-monitor: |
| * @surface: the `GdkSurface` |
| * @monitor: the monitor |
| * |
| * Emitted when @surface stops being present on the monitor. |
| */ |
| signals[LEAVE_MONITOR] = |
| g_signal_new (g_intern_static_string ("leave-monitor"), |
| G_OBJECT_CLASS_TYPE (object_class), |
| G_SIGNAL_RUN_FIRST, |
| 0, |
| NULL, |
| NULL, |
| NULL, |
| G_TYPE_NONE, |
| 1, |
| GDK_TYPE_MONITOR); |
| } |
| |
| static void |
| seat_removed_cb (GdkDisplay *display, |
| GdkSeat *seat, |
| GdkSurface *surface) |
| { |
| GdkDevice *device = gdk_seat_get_pointer (seat); |
| |
| surface->devices_inside = g_list_remove (surface->devices_inside, device); |
| g_hash_table_remove (surface->device_cursor, device); |
| } |
| |
| static void |
| gdk_surface_finalize (GObject *object) |
| { |
| GdkSurface *surface = GDK_SURFACE (object); |
| |
| g_clear_handle_id (&surface->request_motion_id, g_source_remove); |
| |
| g_signal_handlers_disconnect_by_func (surface->display, |
| seat_removed_cb, surface); |
| |
| if (!GDK_SURFACE_DESTROYED (surface)) |
| { |
| g_warning ("losing last reference to undestroyed surface"); |
| _gdk_surface_destroy (surface, FALSE); |
| } |
| |
| g_clear_pointer (&surface->input_region, cairo_region_destroy); |
| g_clear_object (&surface->cursor); |
| g_clear_pointer (&surface->device_cursor, g_hash_table_destroy); |
| g_clear_pointer (&surface->devices_inside, g_list_free); |
| |
| g_clear_object (&surface->display); |
| |
| g_clear_pointer (&surface->opaque_region, cairo_region_destroy); |
| |
| if (surface->parent) |
| surface->parent->children = g_list_remove (surface->parent->children, surface); |
| |
| G_OBJECT_CLASS (gdk_surface_parent_class)->finalize (object); |
| } |
| |
| static void |
| gdk_surface_set_property (GObject *object, |
| guint prop_id, |
| const GValue *value, |
| GParamSpec *pspec) |
| { |
| GdkSurface *surface = GDK_SURFACE (object); |
| |
| switch (prop_id) |
| { |
| case PROP_CURSOR: |
| gdk_surface_set_cursor (surface, g_value_get_object (value)); |
| break; |
| |
| case PROP_DISPLAY: |
| surface->display = g_value_dup_object (value); |
| g_assert (surface->display != NULL); |
| g_signal_connect (surface->display, "seat-removed", |
| G_CALLBACK (seat_removed_cb), surface); |
| break; |
| |
| case PROP_FRAME_CLOCK: |
| gdk_surface_set_frame_clock (surface, GDK_FRAME_CLOCK (g_value_get_object (value))); |
| break; |
| |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| } |
| |
| #define GDK_SURFACE_IS_STICKY(surface) (((surface)->state & GDK_TOPLEVEL_STATE_STICKY)) |
| |
| static void |
| gdk_surface_get_property (GObject *object, |
| guint prop_id, |
| GValue *value, |
| GParamSpec *pspec) |
| { |
| GdkSurface *surface = GDK_SURFACE (object); |
| |
| switch (prop_id) |
| { |
| case PROP_CURSOR: |
| g_value_set_object (value, gdk_surface_get_cursor (surface)); |
| break; |
| |
| case PROP_DISPLAY: |
| g_value_set_object (value, surface->display); |
| break; |
| |
| case PROP_FRAME_CLOCK: |
| g_value_set_object (value, surface->frame_clock); |
| break; |
| |
| case PROP_MAPPED: |
| g_value_set_boolean (value, GDK_SURFACE_IS_MAPPED (surface)); |
| break; |
| |
| case PROP_WIDTH: |
| g_value_set_int (value, surface->width); |
| break; |
| |
| case PROP_HEIGHT: |
| g_value_set_int (value, surface->height); |
| break; |
| |
| case PROP_SCALE_FACTOR: |
| g_value_set_int (value, gdk_surface_get_scale_factor (surface)); |
| break; |
| |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| } |
| |
| void |
| _gdk_surface_update_size (GdkSurface *surface) |
| { |
| GSList *l; |
| |
| for (l = surface->draw_contexts; l; l = l->next) |
| gdk_draw_context_surface_resized (l->data); |
| |
| g_object_notify (G_OBJECT (surface), "width"); |
| g_object_notify (G_OBJECT (surface), "height"); |
| } |
| |
| static GdkSurface * |
| gdk_surface_new (GdkDisplay *display, |
| GdkSurfaceType surface_type, |
| GdkSurface *parent, |
| int x, |
| int y, |
| int width, |
| int height) |
| { |
| return gdk_display_create_surface (display, |
| surface_type, |
| parent, |
| x, y, width, height); |
| } |
| |
| /** |
| * gdk_surface_new_toplevel: (constructor) |
| * @display: the display to create the surface on |
| * |
| * Creates a new toplevel surface. |
| * |
| * Returns: (transfer full): the new `GdkSurface` |
| */ |
| GdkSurface * |
| gdk_surface_new_toplevel (GdkDisplay *display) |
| { |
| g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL); |
| |
| return gdk_surface_new (display, GDK_SURFACE_TOPLEVEL, |
| NULL, 0, 0, 1, 1); |
| } |
| |
| /** |
| * gdk_surface_new_popup: (constructor) |
| * @parent: the parent surface to attach the surface to |
| * @autohide: whether to hide the surface on outside clicks |
| * |
| * Create a new popup surface. |
| * |
| * The surface will be attached to @parent and can be positioned |
| * relative to it using [method@Gdk.Popup.present]. |
| * |
| * Returns: (transfer full): a new `GdkSurface` |
| */ |
| GdkSurface * |
| gdk_surface_new_popup (GdkSurface *parent, |
| gboolean autohide) |
| { |
| GdkSurface *surface; |
| |
| g_return_val_if_fail (GDK_IS_SURFACE (parent), NULL); |
| |
| surface = gdk_surface_new (parent->display, GDK_SURFACE_POPUP, |
| parent, 0, 0, 100, 100); |
| |
| surface->autohide = autohide; |
| |
| return surface; |
| } |
| |
| static void |
| update_pointer_info_foreach (GdkDisplay *display, |
| GdkDevice *device, |
| GdkPointerSurfaceInfo *pointer_info, |
| gpointer user_data) |
| { |
| GdkSurface *surface = user_data; |
| |
| if (pointer_info->surface_under_pointer == surface) |
| { |
| g_object_unref (pointer_info->surface_under_pointer); |
| pointer_info->surface_under_pointer = NULL; |
| } |
| } |
| |
| static void |
| surface_remove_from_pointer_info (GdkSurface *surface, |
| GdkDisplay *display) |
| { |
| _gdk_display_pointer_info_foreach (display, |
| update_pointer_info_foreach, |
| surface); |
| } |
| |
| /** |
| * _gdk_surface_destroy_hierarchy: |
| * @surface: a `GdkSurface` |
| * @recursing_native: If %TRUE, then this is being called because a native |
| * parent was destroyed. This generally means that the call to the windowing |
| * system to destroy the surface can be omitted, since it will be destroyed |
| * as a result of the parent being destroyed. Unless @foreign_destroy. |
| * @foreign_destroy: If %TRUE, the surface or a parent was destroyed by some |
| * external agency. The surface has already been destroyed and no windowing |
| * system calls should be made. (This may never happen for some windowing |
| * systems.) |
| * |
| * Internal function to destroy a surface. Like gdk_surface_destroy(), |
| * but does not drop the reference count created by gdk_surface_new(). |
| */ |
| static void |
| _gdk_surface_destroy_hierarchy (GdkSurface *surface, |
| gboolean foreign_destroy) |
| { |
| G_GNUC_UNUSED GdkSurfacePrivate *priv = gdk_surface_get_instance_private (surface); |
| |
| g_return_if_fail (GDK_IS_SURFACE (surface)); |
| |
| if (GDK_SURFACE_DESTROYED (surface)) |
| return; |
| |
| GDK_SURFACE_GET_CLASS (surface)->destroy (surface, foreign_destroy); |
| |
| /* backend must have unset this */ |
| g_assert (priv->egl_native_window == NULL); |
| |
| if (surface->gl_paint_context) |
| { |
| /* Make sure to destroy if current */ |
| g_object_run_dispose (G_OBJECT (surface->gl_paint_context)); |
| g_object_unref (surface->gl_paint_context); |
| surface->gl_paint_context = NULL; |
| } |
| |
| if (surface->frame_clock) |
| { |
| if (surface->parent == NULL) |
| g_object_run_dispose (G_OBJECT (surface->frame_clock)); |
| gdk_surface_set_frame_clock (surface, NULL); |
| } |
| |
| _gdk_surface_clear_update_area (surface); |
| |
| g_clear_handle_id (&surface->set_is_mapped_source_id, g_source_remove); |
| surface->is_mapped = FALSE; |
| surface->pending_is_mapped = FALSE; |
| |
| surface->destroyed = TRUE; |
| |
| surface_remove_from_pointer_info (surface, surface->display); |
| |
| if (GDK_IS_TOPLEVEL (surface)) |
| g_object_notify (G_OBJECT (surface), "state"); |
| g_object_notify_by_pspec (G_OBJECT (surface), properties[PROP_MAPPED]); |
| } |
| |
| /** |
| * _gdk_surface_destroy: |
| * @surface: a `GdkSurface` |
| * @foreign_destroy: If %TRUE, the surface or a parent was destroyed by some |
| * external agency. The surface has already been destroyed and no windowing |
| * system calls should be made. (This may never happen for some windowing |
| * systems.) |
| * |
| * Internal function to destroy a surface. Like gdk_surface_destroy(), |
| * but does not drop the reference count created by gdk_surface_new(). |
| */ |
| void |
| _gdk_surface_destroy (GdkSurface *surface, |
| gboolean foreign_destroy) |
| { |
| _gdk_surface_destroy_hierarchy (surface, foreign_destroy); |
| } |
| |
| /** |
| * gdk_surface_destroy: |
| * @surface: a `GdkSurface` |
| * |
| * Destroys the window system resources associated with @surface and |
| * decrements @surface's reference count. |
| * |
| * The window system resources for all children of @surface are also |
| * destroyed, but the children’s reference counts are not decremented. |
| * |
| * Note that a surface will not be destroyed automatically when its |
| * reference count reaches zero. You must call this function yourself |
| * before that happens. |
| */ |
| void |
| gdk_surface_destroy (GdkSurface *surface) |
| { |
| _gdk_surface_destroy_hierarchy (surface, FALSE); |
| g_object_unref (surface); |
| } |
| |
| void |
| gdk_surface_set_widget (GdkSurface *self, |
| gpointer widget) |
| { |
| GdkSurfacePrivate *priv = gdk_surface_get_instance_private (self); |
| |
| priv->widget = widget; |
| } |
| |
| gpointer |
| gdk_surface_get_widget (GdkSurface *self) |
| { |
| GdkSurfacePrivate *priv = gdk_surface_get_instance_private (self); |
| |
| return priv->widget; |
| } |
| |
| /** |
| * gdk_surface_get_display: (attributes org.gtk.Method.get_property=display) |
| * @surface: a `GdkSurface` |
| * |
| * Gets the `GdkDisplay` associated with a `GdkSurface`. |
| * |
| * Returns: (transfer none): the `GdkDisplay` associated with @surface |
| */ |
| GdkDisplay * |
| gdk_surface_get_display (GdkSurface *surface) |
| { |
| g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL); |
| |
| return surface->display; |
| } |
| /** |
| * gdk_surface_is_destroyed: |
| * @surface: a `GdkSurface` |
| * |
| * Check to see if a surface is destroyed. |
| * |
| * Returns: %TRUE if the surface is destroyed |
| */ |
| gboolean |
| gdk_surface_is_destroyed (GdkSurface *surface) |
| { |
| return GDK_SURFACE_DESTROYED (surface); |
| } |
| |
| /** |
| * gdk_surface_get_mapped: (attributes org.gtk.Method.get_property=mapped) |
| * @surface: a `GdkSurface` |
| * |
| * Checks whether the surface has been mapped. |
| * |
| * A surface is mapped with [method@Gdk.Toplevel.present] |
| * or [method@Gdk.Popup.present]. |
| * |
| * Returns: %TRUE if the surface is mapped |
| */ |
| gboolean |
| gdk_surface_get_mapped (GdkSurface *surface) |
| { |
| g_return_val_if_fail (GDK_IS_SURFACE (surface), FALSE); |
| |
| return GDK_SURFACE_IS_MAPPED (surface); |
| } |
| |
| void |
| gdk_surface_set_egl_native_window (GdkSurface *self, |
| gpointer native_window) |
| { |
| #ifdef HAVE_EGL |
| GdkSurfacePrivate *priv = gdk_surface_get_instance_private (self); |
| |
| /* This checks that all EGL platforms we support conform to the same struct sizes. |
| * When this ever fails, there will be some fun times happening for whoever tries |
| * this weird EGL backend... */ |
| G_STATIC_ASSERT (sizeof (gpointer) == sizeof (EGLNativeWindowType)); |
| |
| if (priv->egl_surface != NULL) |
| { |
| gdk_gl_context_clear_current_if_surface (self); |
| eglDestroySurface (gdk_surface_get_display (self), priv->egl_surface); |
| priv->egl_surface = NULL; |
| } |
| |
| priv->egl_native_window = native_window; |
| } |
| |
| gpointer /* EGLSurface */ |
| gdk_surface_get_egl_surface (GdkSurface *self) |
| { |
| GdkSurfacePrivate *priv = gdk_surface_get_instance_private (self); |
| |
| return priv->egl_surface; |
| } |
| |
| void |
| gdk_surface_ensure_egl_surface (GdkSurface *self, |
| gboolean high_depth) |
| { |
| GdkSurfacePrivate *priv = gdk_surface_get_instance_private (self); |
| GdkDisplay *display = gdk_surface_get_display (self); |
| |
| g_return_if_fail (priv->egl_native_window != NULL); |
| |
| if (priv->egl_surface_high_depth != high_depth && |
| priv->egl_surface != NULL && |
| gdk_display_get_egl_config_high_depth (display) != gdk_display_get_egl_config (display)) |
| { |
| gdk_gl_context_clear_current_if_surface (self); |
| eglDestroySurface (gdk_display_get_egl_display (display), priv->egl_surface); |
| priv->egl_surface = NULL; |
| } |
| |
| if (priv->egl_surface == NULL) |
| { |
| priv->egl_surface = eglCreateWindowSurface (gdk_display_get_egl_display (display), |
| high_depth ? gdk_display_get_egl_config_high_depth (display) |
| : gdk_display_get_egl_config (display), |
| (EGLNativeWindowType) priv->egl_native_window, |
| NULL); |
| priv->egl_surface_high_depth = high_depth; |
| } |
| #endif |
| } |
| |
| GdkGLContext * |
| gdk_surface_get_paint_gl_context (GdkSurface *surface, |
| GError **error) |
| { |
| if (!gdk_display_prepare_gl (surface->display, error)) |
| return NULL; |
| |
| if (surface->gl_paint_context == NULL) |
| { |
| surface->gl_paint_context = gdk_surface_create_gl_context (surface, error); |
| if (surface->gl_paint_context == NULL) |
| return NULL; |
| } |
| |
| if (!gdk_gl_context_realize (surface->gl_paint_context, error)) |
| { |
| g_clear_object (&surface->gl_paint_context); |
| return NULL; |
| } |
| |
| return surface->gl_paint_context; |
| } |
| |
| /** |
| * gdk_surface_create_gl_context: |
| * @surface: a `GdkSurface` |
| * @error: return location for an error |
| * |
| * Creates a new `GdkGLContext` for the `GdkSurface`. |
| * |
| * The context is disconnected from any particular surface or surface. |
| * If the creation of the `GdkGLContext` failed, @error will be set. |
| * Before using the returned `GdkGLContext`, you will need to |
| * call [method@Gdk.GLContext.make_current] or [method@Gdk.GLContext.realize]. |
| * |
| * Returns: (transfer full): the newly created `GdkGLContext` |
| */ |
| GdkGLContext * |
| gdk_surface_create_gl_context (GdkSurface *surface, |
| GError **error) |
| { |
| g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL); |
| g_return_val_if_fail (error == NULL || *error == NULL, NULL); |
| |
| if (!gdk_display_prepare_gl (surface->display, error)) |
| return NULL; |
| |
| return gdk_gl_context_new (surface->display, surface); |
| } |
| |
| /** |
| * gdk_surface_create_cairo_context: |
| * @surface: a `GdkSurface` |
| * |
| * Creates a new `GdkCairoContext` for rendering on @surface. |
| * |
| * Returns: (transfer full): the newly created `GdkCairoContext` |
| */ |
| GdkCairoContext * |
| gdk_surface_create_cairo_context (GdkSurface *surface) |
| { |
| GdkDisplay *display; |
| |
| g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL); |
| |
| display = surface->display; |
| |
| return g_object_new (GDK_DISPLAY_GET_CLASS (display)->cairo_context_type, |
| "surface", surface, |
| NULL); |
| } |
| |
| /** |
| * gdk_surface_create_vulkan_context: |
| * @surface: a `GdkSurface` |
| * @error: return location for an error |
| * |
| * Creates a new `GdkVulkanContext` for rendering on @surface. |
| * |
| * If the creation of the `GdkVulkanContext` failed, @error will be set. |
| * |
| * Returns: (transfer full): the newly created `GdkVulkanContext`, or |
| * %NULL on error |
| */ |
| GdkVulkanContext * |
| gdk_surface_create_vulkan_context (GdkSurface *surface, |
| GError **error) |
| { |
| GdkDisplay *display; |
| |
| g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL); |
| g_return_val_if_fail (error == NULL || *error == NULL, NULL); |
| |
| if (gdk_display_get_debug_flags (surface->display) & GDK_DEBUG_VULKAN_DISABLE) |
| { |
| g_set_error_literal (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE, |
| _("Vulkan support disabled via GDK_DEBUG")); |
| return NULL; |
| } |
| |
| display = surface->display; |
| |
| if (GDK_DISPLAY_GET_CLASS (display)->vk_extension_name == NULL) |
| { |
| g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_UNSUPPORTED, |
| "The %s backend has no Vulkan support.", G_OBJECT_TYPE_NAME (display)); |
| return FALSE; |
| } |
| |
| return g_initable_new (GDK_DISPLAY_GET_CLASS (display)->vk_context_type, |
| NULL, |
| error, |
| "surface", surface, |
| NULL); |
| } |
| |
| /* Code for dirty-region queueing |
| */ |
| static GSList *update_surfaces = NULL; |
| |
| static void |
| gdk_surface_add_update_surface (GdkSurface *surface) |
| { |
| GSList *tmp; |
| |
| /* Check whether "surface" is already in "update_surfaces" list. |
| * It could be added during execution of gtk_widget_destroy() when |
| * setting focus widget to NULL and redrawing old focus widget. |
| * See bug 711552. |
| */ |
| tmp = g_slist_find (update_surfaces, surface); |
| if (tmp != NULL) |
| return; |
| |
| update_surfaces = g_slist_prepend (update_surfaces, g_object_ref (surface)); |
| } |
| |
| static void |
| gdk_surface_remove_update_surface (GdkSurface *surface) |
| { |
| GSList *link; |
| |
| link = g_slist_find (update_surfaces, surface); |
| if (link != NULL) |
| { |
| update_surfaces = g_slist_delete_link (update_surfaces, link); |
| g_object_unref (surface); |
| } |
| } |
| |
| static gboolean |
| gdk_surface_is_toplevel_frozen (GdkSurface *surface) |
| { |
| return surface->update_and_descendants_freeze_count > 0; |
| } |
| |
| static void |
| gdk_surface_schedule_update (GdkSurface *surface) |
| { |
| GdkFrameClock *frame_clock; |
| |
| g_return_if_fail (surface); |
| |
| surface->pending_phases |= GDK_FRAME_CLOCK_PHASE_PAINT; |
| |
| if (surface->update_freeze_count || |
| gdk_surface_is_toplevel_frozen (surface)) |
| return; |
| |
| /* If there's no frame clock (a foreign surface), then the invalid |
| * region will just stick around unless gdk_surface_process_updates() |
| * is called. */ |
| frame_clock = gdk_surface_get_frame_clock (surface); |
| if (frame_clock) |
| gdk_frame_clock_request_phase (gdk_surface_get_frame_clock (surface), |
| GDK_FRAME_CLOCK_PHASE_PAINT); |
| } |
| |
| static void |
| gdk_surface_process_updates_internal (GdkSurface *surface) |
| { |
| /* Ensure the surface lives while updating it */ |
| g_object_ref (surface); |
| |
| surface->in_update = TRUE; |
| |
| /* If an update got queued during update processing, we can get a |
| * surface in the update queue that has an empty update_area. |
| * just ignore it. |
| */ |
| if (surface->update_area) |
| { |
| g_assert (surface->active_update_area == NULL); /* No reentrancy */ |
| |
| surface->active_update_area = surface->update_area; |
| surface->update_area = NULL; |
| |
| if (GDK_SURFACE_IS_MAPPED (surface)) |
| { |
| cairo_region_t *expose_region; |
| gboolean handled; |
| |
| expose_region = cairo_region_copy (surface->active_update_area); |
| |
| g_signal_emit (surface, signals[RENDER], 0, expose_region, &handled); |
| |
| cairo_region_destroy (expose_region); |
| } |
| |
| cairo_region_destroy (surface->active_update_area); |
| surface->active_update_area = NULL; |
| } |
| |
| surface->in_update = FALSE; |
| |
| g_object_unref (surface); |
| } |
| |
| static void |
| gdk_surface_layout_on_clock (GdkFrameClock *clock, |
| void *data) |
| { |
| GdkSurface *surface = GDK_SURFACE (data); |
| GdkSurfaceClass *class; |
| |
| g_return_if_fail (GDK_IS_SURFACE (surface)); |
| |
| if (GDK_SURFACE_DESTROYED (surface)) |
| return; |
| |
| if (!GDK_SURFACE_IS_MAPPED (surface)) |
| return; |
| |
| surface->pending_phases &= ~GDK_FRAME_CLOCK_PHASE_LAYOUT; |
| |
| class = GDK_SURFACE_GET_CLASS (surface); |
| if (class->compute_size) |
| { |
| if (class->compute_size (surface)) |
| return; |
| } |
| |
| g_signal_emit (surface, signals[LAYOUT], 0, surface->width, surface->height); |
| } |
| |
| /** |
| * gdk_surface_request_layout: |
| * @surface: a `GdkSurface` |
| * |
| * Request a layout phase from the surface's frame clock. |
| * |
| * See [method@Gdk.FrameClock.request_phase]. |
| */ |
| void |
| gdk_surface_request_layout (GdkSurface *surface) |
| { |
| GdkSurfaceClass *class; |
| GdkFrameClock *frame_clock; |
| |
| class = GDK_SURFACE_GET_CLASS (surface); |
| if (class->request_layout) |
| class->request_layout (surface); |
| |
| frame_clock = gdk_surface_get_frame_clock (surface); |
| g_return_if_fail (frame_clock); |
| |
| gdk_frame_clock_request_phase (frame_clock, |
| GDK_FRAME_CLOCK_PHASE_LAYOUT); |
| } |
| |
| static void |
| gdk_surface_paint_on_clock (GdkFrameClock *clock, |
| void *data) |
| { |
| GdkSurface *surface = GDK_SURFACE (data); |
| |
| g_return_if_fail (GDK_IS_SURFACE (surface)); |
| |
| if (GDK_SURFACE_DESTROYED (surface)) |
| return; |
| |
| g_object_ref (surface); |
| |
| if (surface->update_area && |
| !surface->update_freeze_count && |
| !gdk_surface_is_toplevel_frozen (surface) && |
| |
| /* Don't recurse into process_updates_internal, we'll |
| * do the update later when idle instead. */ |
| !surface->in_update) |
| { |
| surface->pending_phases &= ~GDK_FRAME_CLOCK_PHASE_PAINT; |
| gdk_surface_process_updates_internal (surface); |
| gdk_surface_remove_update_surface (surface); |
| } |
| |
| g_object_unref (surface); |
| } |
| |
| /* |
| * gdk_surface_invalidate_rect: |
| * @surface: a `GdkSurface` |
| * @rect: (nullable): rectangle to invalidate or %NULL to |
| * invalidate the whole surface |
| * |
| * Invalidate a rectangular region of @surface. |
| * |
| * This is a convenience wrapper around |
| * [method@Gdk.Surface.invalidate_region]. |
| */ |
| void |
| gdk_surface_invalidate_rect (GdkSurface *surface, |
| const GdkRectangle *rect) |
| { |
| GdkRectangle surface_rect; |
| cairo_region_t *region; |
| |
| g_return_if_fail (GDK_IS_SURFACE (surface)); |
| |
| if (!GDK_SURFACE_IS_MAPPED (surface)) |
| return; |
| |
| if (!rect) |
| { |
| surface_rect.x = 0; |
| surface_rect.y = 0; |
| surface_rect.width = surface->width; |
| surface_rect.height = surface->height; |
| rect = &surface_rect; |
| } |
| |
| region = cairo_region_create_rectangle (rect); |
| gdk_surface_invalidate_region (surface, region); |
| cairo_region_destroy (region); |
| } |
| |
| static void |
| impl_surface_add_update_area (GdkSurface *impl_surface, |
| cairo_region_t *region) |
| { |
| if (impl_surface->update_area) |
| cairo_region_union (impl_surface->update_area, region); |
| else |
| { |
| gdk_surface_add_update_surface (impl_surface); |
| impl_surface->update_area = cairo_region_copy (region); |
| gdk_surface_schedule_update (impl_surface); |
| } |
| } |
| |
| /** |
| * gdk_surface_queue_render: |
| * @surface: a `GdkSurface` |
| * |
| * Forces a [signal@Gdk.Surface::render] signal emission for @surface |
| * to be scheduled. |
| * |
| * This function is useful for implementations that track invalid |
| * regions on their own. |
| */ |
| void |
| gdk_surface_queue_render (GdkSurface *surface) |
| { |
| cairo_region_t *region; |
| |
| g_return_if_fail (GDK_IS_SURFACE (surface)); |
| |
| region = cairo_region_create (); |
| impl_surface_add_update_area (surface, region); |
| cairo_region_destroy (region); |
| } |
| |
| /* |
| * gdk_surface_invalidate_region: |
| * @surface: a `GdkSurface` |
| * @region: a `cairo_region_t` |
| * |
| * Adds @region to the update area for @surface. |
| * |
| * The update area is the region that needs to be redrawn, |
| * or “dirty region.” |
| * |
| * GDK will process all updates whenever the frame clock schedules |
| * a redraw, so there’s no need to do forces redraws manually, you |
| * just need to invalidate regions that you know should be redrawn. |
| */ |
| void |
| gdk_surface_invalidate_region (GdkSurface *surface, |
| const cairo_region_t *region) |
| { |
| cairo_region_t *visible_region; |
| cairo_rectangle_int_t r; |
| |
| g_return_if_fail (GDK_IS_SURFACE (surface)); |
| |
| if (!GDK_SURFACE_IS_MAPPED (surface)) |
| return; |
| |
| if (cairo_region_is_empty (region)) |
| return; |
| |
| r.x = 0; |
| r.y = 0; |
| r.width = surface->width; |
| r.height = surface->height; |
| |
| visible_region = cairo_region_copy (region); |
| |
| cairo_region_intersect_rectangle (visible_region, &r); |
| impl_surface_add_update_area (surface, visible_region); |
| |
| cairo_region_destroy (visible_region); |
| } |
| |
| /* |
| * _gdk_surface_clear_update_area: |
| * @surface: a `GdkSurface` |
| * |
| * Internal function to clear the update area for a surface. |
| * This is called when the surface is hidden or destroyed. |
| */ |
| void |
| _gdk_surface_clear_update_area (GdkSurface *surface) |
| { |
| g_return_if_fail (GDK_IS_SURFACE (surface)); |
| |
| if (surface->update_area) |
| { |
| gdk_surface_remove_update_surface (surface); |
| |
| cairo_region_destroy (surface->update_area); |
| surface->update_area = NULL; |
| } |
| } |
| |
| /* |
| * gdk_surface_freeze_updates: |
| * @surface: a `GdkSurface` |
| * |
| * Temporarily freezes a surface such that it won’t receive expose |
| * events. The surface will begin receiving expose events again when |
| * gdk_surface_thaw_updates() is called. If gdk_surface_freeze_updates() |
| * has been called more than once, gdk_surface_thaw_updates() must be |
| * called an equal number of times to begin processing exposes. |
| */ |
| void |
| gdk_surface_freeze_updates (GdkSurface *surface) |
| { |
| g_return_if_fail (GDK_IS_SURFACE (surface)); |
| |
| surface->update_freeze_count++; |
| if (surface->update_freeze_count == 1) |
| _gdk_frame_clock_uninhibit_freeze (surface->frame_clock); |
| } |
| |
| static gboolean |
| request_motion_cb (void *data) |
| { |
| GdkSurface *surface = GDK_SURFACE (data); |
| GdkFrameClock *clock = gdk_surface_get_frame_clock (surface); |
| |
| if (clock) |
| gdk_frame_clock_request_phase (clock, GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS); |
| surface->request_motion_id = 0; |
| |
| return G_SOURCE_REMOVE; |
| } |
| |
| |
| /* |
| * gdk_surface_thaw_updates: |
| * @surface: a `GdkSurface` |
| * |
| * Thaws a surface frozen with gdk_surface_freeze_updates(). Note that this |
| * will not necessarily schedule updates if the surface freeze count reaches |
| * zero. |
| */ |
| void |
| gdk_surface_thaw_updates (GdkSurface *surface) |
| { |
| g_return_if_fail (GDK_IS_SURFACE (surface)); |
| |
| g_return_if_fail (surface->update_freeze_count > 0); |
| |
| if (--surface->update_freeze_count == 0) |
| { |
| GdkFrameClock *frame_clock = surface->frame_clock; |
| |
| _gdk_frame_clock_inhibit_freeze (frame_clock); |
| |
| if (surface->pending_phases) |
| gdk_frame_clock_request_phase (frame_clock, surface->pending_phases); |
| |
| if (surface->request_motion && surface->request_motion_id == 0) |
| { |
| surface->request_motion_id = |
| g_idle_add_full (GDK_PRIORITY_REDRAW + 20, request_motion_cb, surface, NULL); |
| gdk_source_set_static_name_by_id (surface->request_motion_id, "[gtk] request_motion_cb"); |
| } |
| } |
| } |
| |
| /* |
| * gdk_surface_constrain_size: |
| * @geometry: a `GdkGeometry` structure |
| * @flags: a mask indicating what portions of @geometry are set |
| * @width: desired width of surface |
| * @height: desired height of the surface |
| * @new_width: (out): location to store resulting width |
| * @new_height: (out): location to store resulting height |
| * |
| * Constrains a desired width and height according to a |
| * set of geometry hints (such as minimum and maximum size). |
| */ |
| void |
| gdk_surface_constrain_size (GdkGeometry *geometry, |
| GdkSurfaceHints flags, |
| int width, |
| int height, |
| int *new_width, |
| int *new_height) |
| { |
| /* This routine is partially borrowed from fvwm. |
| * |
| * Copyright 1993, Robert Nation |
| * You may use this code for any purpose, as long as the original |
| * copyright remains in the source code and all documentation |
| * |
| * which in turn borrows parts of the algorithm from uwm |
| */ |
| int min_width = 0; |
| int min_height = 0; |
| int max_width = G_MAXINT; |
| int max_height = G_MAXINT; |
| |
| if (flags & GDK_HINT_MIN_SIZE) |
| { |
| min_width = geometry->min_width; |
| min_height = geometry->min_height; |
| } |
| |
| if (flags & GDK_HINT_MAX_SIZE) |
| { |
| max_width = geometry->max_width ; |
| max_height = geometry->max_height; |
| } |
| |
| /* clamp width and height to min and max values |
| */ |
| width = CLAMP (width, min_width, max_width); |
| height = CLAMP (height, min_height, max_height); |
| |
| *new_width = width; |
| *new_height = height; |
| } |
| |
| /** |
| * gdk_surface_get_device_position: |
| * @surface: a `GdkSurface` |
| * @device: pointer `GdkDevice` to query to |
| * @x: (out) (optional): return location for the X coordinate of @device |
| * @y: (out) (optional): return location for the Y coordinate of @device |
| * @mask: (out) (optional): return location for the modifier mask |
| * |
| * Obtains the current device position and modifier state. |
| * |
| * The position is given in coordinates relative to the upper |
| * left corner of @surface. |
| * |
| * Return: %TRUE if the device is over the surface |
| */ |
| gboolean |
| gdk_surface_get_device_position (GdkSurface *surface, |
| GdkDevice *device, |
| double *x, |
| double *y, |
| GdkModifierType *mask) |
| { |
| double tmp_x, tmp_y; |
| GdkModifierType tmp_mask; |
| gboolean ret; |
| |
| g_return_val_if_fail (GDK_IS_SURFACE (surface), FALSE); |
| g_return_val_if_fail (GDK_IS_DEVICE (device), FALSE); |
| g_return_val_if_fail (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD, FALSE); |
| |
| tmp_x = 0; |
| tmp_y = 0; |
| tmp_mask = 0; |
| |
| ret = GDK_SURFACE_GET_CLASS (surface)->get_device_state (surface, |
| device, |
| &tmp_x, &tmp_y, |
| &tmp_mask); |
| |
| if (x) |
| *x = tmp_x; |
| if (y) |
| *y = tmp_y; |
| if (mask) |
| *mask = tmp_mask; |
| |
| return ret; |
| } |
| |
| /** |
| * gdk_surface_hide: |
| * @surface: a `GdkSurface` |
| * |
| * Hide the surface. |
| * |
| * For toplevel surfaces, withdraws them, so they will no longer be |
| * known to the window manager; for all surfaces, unmaps them, so |
| * they won’t be displayed. Normally done automatically as |
| * part of [method@Gtk.Widget.hide]. |
| */ |
| void |
| gdk_surface_hide (GdkSurface *surface) |
| { |
| gboolean was_mapped; |
| |
| g_return_if_fail (GDK_IS_SURFACE (surface)); |
| |
| if (surface->destroyed) |
| return; |
| |
| was_mapped = GDK_SURFACE_IS_MAPPED (surface); |
| |
| gdk_surface_queue_set_is_mapped (surface, FALSE); |
| |
| if (was_mapped) |
| { |
| GdkDisplay *display; |
| GdkSeat *seat; |
| GList *devices, *d; |
| |
| /* May need to break grabs on children */ |
| display = surface->display; |
| seat = gdk_display_get_default_seat (display); |
| if (seat) |
| { |
| devices = gdk_seat_get_devices (seat, GDK_SEAT_CAPABILITY_ALL); |
| devices = g_list_prepend (devices, gdk_seat_get_keyboard (seat)); |
| devices = g_list_prepend (devices, gdk_seat_get_pointer (seat)); |
| } |
| else |
| devices = NULL; |
| |
| for (d = devices; d; d = d->next) |
| { |
| GdkDevice *device = d->data; |
| |
| if (_gdk_display_end_device_grab (display, |
| device, |
| _gdk_display_get_next_serial (display), |
| surface, |
| TRUE)) |
| { |
| G_GNUC_BEGIN_IGNORE_DEPRECATIONS |
| gdk_device_ungrab (device, GDK_CURRENT_TIME); |
| G_GNUC_END_IGNORE_DEPRECATIONS |
| } |
| } |
| |
| g_list_free (devices); |
| } |
| |
| GDK_SURFACE_GET_CLASS (surface)->hide (surface); |
| |
| surface->popup.rect_anchor = 0; |
| surface->popup.surface_anchor = 0; |
| surface->x = 0; |
| surface->y = 0; |
| } |
| |
| static void |
| gdk_surface_set_cursor_internal (GdkSurface *surface, |
| GdkDevice *device, |
| GdkCursor *cursor) |
| { |
| GdkPointerSurfaceInfo *pointer_info; |
| |
| if (GDK_SURFACE_DESTROYED (surface)) |
| return; |
| |
| g_assert (surface->display == gdk_device_get_display (device)); |
| |
| pointer_info = _gdk_display_get_pointer_info (surface->display, device); |
| |
| if (surface == pointer_info->surface_under_pointer) |
| update_cursor (surface->display, device); |
| } |
| |
| /** |
| * gdk_surface_get_cursor: (attributes org.gtk.Method.get_property=cursor) |
| * @surface: a `GdkSurface` |
| * |
| * Retrieves a `GdkCursor` pointer for the cursor currently set on the |
| * `GdkSurface`. |
| * |
| * If the return value is %NULL then there is no custom cursor set on |
| * the surface, and it is using the cursor for its parent surface. |
| * |
| * Use [method@Gdk.Surface.set_cursor] to unset the cursor of the surface. |
| * |
| * Returns: (nullable) (transfer none): a `GdkCursor` |
| */ |
| GdkCursor * |
| gdk_surface_get_cursor (GdkSurface *surface) |
| { |
| g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL); |
| |
| return surface->cursor; |
| } |
| |
| /** |
| * gdk_surface_set_cursor: (attributes org.gtk.Method.set_property=cursor) |
| * @surface: a `GdkSurface` |
| * @cursor: (nullable): a `GdkCursor` |
| * |
| * Sets the default mouse pointer for a `GdkSurface`. |
| * |
| * Passing %NULL for the @cursor argument means that @surface will use |
| * the cursor of its parent surface. Most surfaces should use this default. |
| * Note that @cursor must be for the same display as @surface. |
| * |
| * Use [ctor@Gdk.Cursor.new_from_name] or [ctor@Gdk.Cursor.new_from_texture] |
| * to create the cursor. To make the cursor invisible, use %GDK_BLANK_CURSOR. |
| */ |
| void |
| gdk_surface_set_cursor (GdkSurface *surface, |
| GdkCursor *cursor) |
| { |
| g_return_if_fail (GDK_IS_SURFACE (surface)); |
| |
| if (surface->cursor) |
| { |
| g_object_unref (surface->cursor); |
| surface->cursor = NULL; |
| } |
| |
| if (!GDK_SURFACE_DESTROYED (surface)) |
| { |
| GdkDevice *device; |
| GList *seats, *s; |
| |
| if (cursor) |
| surface->cursor = g_object_ref (cursor); |
| |
| seats = gdk_display_list_seats (surface->display); |
| |
| for (s = seats; s; s = s->next) |
| { |
| GList *devices, *d; |
| |
| device = gdk_seat_get_pointer (s->data); |
| gdk_surface_set_cursor_internal (surface, device, surface->cursor); |
| |
| devices = gdk_seat_get_devices (s->data, GDK_SEAT_CAPABILITY_TABLET_STYLUS); |
| for (d = devices; d; d = d->next) |
| { |
| device = d->data; |
| gdk_surface_set_cursor_internal (surface, device, surface->cursor); |
| } |
| g_list_free (devices); |
| } |
| |
| g_list_free (seats); |
| g_object_notify_by_pspec (G_OBJECT (surface), properties[PROP_CURSOR]); |
| } |
| } |
| |
| /** |
| * gdk_surface_get_device_cursor: |
| * @surface: a `GdkSurface` |
| * @device: a pointer `GdkDevice` |
| * |
| * Retrieves a `GdkCursor` pointer for the @device currently set on the |
| * specified `GdkSurface`. |
| * |
| * If the return value is %NULL then there is no custom cursor set on the |
| * specified surface, and it is using the cursor for its parent surface. |
| * |
| * Use [method@Gdk.Surface.set_cursor] to unset the cursor of the surface. |
| * |
| * Returns: (nullable) (transfer none): a `GdkCursor` |
| */ |
| GdkCursor * |
| gdk_surface_get_device_cursor (GdkSurface *surface, |
| GdkDevice *device) |
| { |
| g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL); |
| g_return_val_if_fail (GDK_IS_DEVICE (device), NULL); |
| g_return_val_if_fail (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD, NULL); |
| |
| return g_hash_table_lookup (surface->device_cursor, device); |
| } |
| |
| /** |
| * gdk_surface_set_device_cursor: |
| * @surface: a `GdkSurface` |
| * @device: a pointer `GdkDevice` |
| * @cursor: a `GdkCursor` |
| * |
| * Sets a specific `GdkCursor` for a given device when it gets inside @surface. |
| * |
| * Passing %NULL for the @cursor argument means that @surface will use the |
| * cursor of its parent surface. Most surfaces should use this default. |
| * |
| * Use [ctor@Gdk.Cursor.new_from_name] or [ctor@Gdk.Cursor.new_from_texture] |
| * to create the cursor. To make the cursor invisible, use %GDK_BLANK_CURSOR. |
| */ |
| void |
| gdk_surface_set_device_cursor (GdkSurface *surface, |
| GdkDevice *device, |
| GdkCursor *cursor) |
| { |
| g_return_if_fail (GDK_IS_SURFACE (surface)); |
| g_return_if_fail (GDK_IS_DEVICE (device)); |
| g_return_if_fail (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD); |
| |
| if (!cursor) |
| g_hash_table_remove (surface->device_cursor, device); |
| else |
| g_hash_table_replace (surface->device_cursor, device, g_object_ref (cursor)); |
| |
| gdk_surface_set_cursor_internal (surface, device, cursor); |
| } |
| |
| /* |
| * gdk_surface_get_geometry: |
| * @surface: a `GdkSurface` |
| * @x: (out) (optional): return location for X coordinate of surface (relative to its parent) |
| * @y: (out) (optional): return location for Y coordinate of surface (relative to its parent) |
| * @width: (out) (optional): return location for width of surface |
| * @height: (out) (optional): return location for height of surface |
| * |
| * Get the geometry of the surface. |
| * |
| * The X and Y coordinates returned are relative to the parent surface |
| * of @surface, which for toplevels usually means relative to the |
| * surface decorations (titlebar, etc.) rather than relative to the |
| * root window (screen-size background window). |
| * |
| * On the X11 platform, the geometry is obtained from the X server, so |
| * reflects the latest position of @surface; this may be out-of-sync with |
| * the position of @surface delivered in the most-recently-processed |
| * `GdkEventConfigure`. [method@Gdk.Surface.get_position] in contrast gets |
| * the position from the most recent configure event. |
| * |
| * Any of the return location arguments to this function may be %NULL, |
| * if you aren’t interested in getting the value of that field. |
| * |
| * Note: If @surface is not a toplevel, it is much better to call |
| * [method@Gdk.Surface.get_position], [method@Gdk.Surface.get_width] and |
| * [method@Gdk.Surface.get_height] instead, because it avoids the roundtrip |
| * to the X server and because these functions support the full 32-bit |
| * coordinate space, whereas gdk_surface_get_geometry() is restricted to |
| * the 16-bit coordinates of X11. |
| */ |
| void |
| gdk_surface_get_geometry (GdkSurface *surface, |
| int *x, |
| int *y, |
| int *width, |
| int *height) |
| { |
| g_return_if_fail (GDK_IS_SURFACE (surface)); |
| |
| if (GDK_SURFACE_DESTROYED (surface)) |
| return; |
| |
| GDK_SURFACE_GET_CLASS (surface)->get_geometry (surface, x, y, width, height); |
| } |
| |
| /** |
| * gdk_surface_get_width: (attributes org.gtk.Method.get_property=width) |
| * @surface: a `GdkSurface` |
| * |
| * Returns the width of the given @surface. |
| * |
| * Surface size is reported in ”application pixels”, not |
| * ”device pixels” (see [method@Gdk.Surface.get_scale_factor]). |
| * |
| * Returns: The width of @surface |
| */ |
| int |
| gdk_surface_get_width (GdkSurface *surface) |
| { |
| g_return_val_if_fail (GDK_IS_SURFACE (surface), 0); |
| |
| return surface->width; |
| } |
| |
| /** |
| * gdk_surface_get_height: (attributes org.gtk.Method.get_property=height) |
| * @surface: a `GdkSurface` |
| * |
| * Returns the height of the given @surface. |
| * |
| * Surface size is reported in ”application pixels”, not |
| * ”device pixels” (see [method@Gdk.Surface.get_scale_factor]). |
| * |
| * Returns: The height of @surface |
| */ |
| int |
| gdk_surface_get_height (GdkSurface *surface) |
| { |
| g_return_val_if_fail (GDK_IS_SURFACE (surface), 0); |
| |
| return surface->height; |
| } |
| |
| /* |
| * gdk_surface_get_origin: |
| * @surface: a `GdkSurface` |
| * @x: (out): return location for X coordinate |
| * @y: (out): return location for Y coordinate |
| * |
| * Obtains the position of a surface in root window coordinates. |
| * |
| * (Compare with gdk_surface_get_position() and |
| * gdk_surface_get_geometry() which return the position |
| * of a surface relative to its parent surface.) |
| */ |
| void |
| gdk_surface_get_origin (GdkSurface *surface, |
| int *x, |
| int *y) |
| { |
| g_return_if_fail (GDK_IS_SURFACE (surface)); |
| |
| gdk_surface_get_root_coords (surface, 0, 0, x, y); |
| } |
| |
| /* |
| * gdk_surface_get_root_coords: |
| * @surface: a `GdkSurface` |
| * @x: X coordinate in surface |
| * @y: Y coordinate in surface |
| * @root_x: (out): return location for X coordinate |
| * @root_y: (out): return location for Y coordinate |
| * |
| * Obtains the position of a surface position in root |
| * window coordinates. This is similar to |
| * gdk_surface_get_origin() but allows you to pass |
| * in any position in the surface, not just the origin. |
| */ |
| void |
| gdk_surface_get_root_coords (GdkSurface *surface, |
| int x, |
| int y, |
| int *root_x, |
| int *root_y) |
| { |
| g_return_if_fail (GDK_IS_SURFACE (surface)); |
| |
| if (GDK_SURFACE_DESTROYED (surface)) |
| { |
| *root_x = 0; |
| *root_y = 0; |
| return; |
| } |
| |
| GDK_SURFACE_GET_CLASS (surface)->get_root_coords (surface, x, y, root_x, root_y); |
| } |
| |
| /** |
| * gdk_surface_set_input_region: |
| * @surface: a `GdkSurface` |
| * @region: region of surface to be reactive |
| * |
| * Apply the region to the surface for the purpose of event |
| * handling. |
| * |
| * Mouse events which happen while the pointer position corresponds |
| * to an unset bit in the mask will be passed on the surface below |
| * @surface. |
| * |
| * An input region is typically used with RGBA surfaces. The alpha |
| * channel of the surface defines which pixels are invisible and |
| * allows for nicely antialiased borders, and the input region |
| * controls where the surface is “clickable”. |
| * |
| * Use [method@Gdk.Display.supports_input_shapes] to find out if |
| * a particular backend supports input regions. |
| */ |
| void |
| gdk_surface_set_input_region (GdkSurface *surface, |
| cairo_region_t *region) |
| { |
| g_return_if_fail (GDK_IS_SURFACE (surface)); |
| |
| if (GDK_SURFACE_DESTROYED (surface)) |
| return; |
| |
| if (cairo_region_equal (surface->input_region, region)) |
| return; |
| |
| if (surface->input_region) |
| cairo_region_destroy (surface->input_region); |
| |
| if (region) |
| surface->input_region = cairo_region_copy (region); |
| else |
| surface->input_region = NULL; |
| |
| GDK_SURFACE_GET_CLASS (surface)->set_input_region (surface, surface->input_region); |
| } |
| |
| static void |
| update_cursor (GdkDisplay *display, |
| GdkDevice *device) |
| { |
| GdkSurface *cursor_surface; |
| GdkSurface *pointer_surface; |
| GdkPointerSurfaceInfo *pointer_info; |
| GdkDeviceGrabInfo *grab; |
| GdkCursor *cursor; |
| |
| g_assert (display); |
| g_assert (device); |
| |
| pointer_info = _gdk_display_get_pointer_info (display, device); |
| pointer_surface = pointer_info->surface_under_pointer; |
| |
| /* We ignore the serials here and just pick the last grab |
| we've sent, as that would shortly be used anyway. */ |
| grab = _gdk_display_get_last_device_grab (display, device); |
| if (grab != NULL) |
| { |
| /* use the cursor from the grab surface */ |
| cursor_surface = grab->surface; |
| } |
| else |
| { |
| /* otherwise use the cursor from the pointer surface */ |
| cursor_surface = pointer_surface; |
| } |
| |
| cursor = g_hash_table_lookup (cursor_surface->device_cursor, device); |
| |
| if (!cursor) |
| cursor = cursor_surface->cursor; |
| |
| GDK_DEVICE_GET_CLASS (device)->set_surface_cursor (device, pointer_surface, cursor); |
| } |
| |
| /** |
| * gdk_surface_beep: |
| * @surface: a toplevel `GdkSurface` |
| * |
| * Emits a short beep associated to @surface. |
| * |
| * If the display of @surface does not support per-surface beeps, |
| * emits a short beep on the display just as [method@Gdk.Display.beep]. |
| */ |
| void |
| gdk_surface_beep (GdkSurface *surface) |
| { |
| g_return_if_fail (GDK_IS_SURFACE (surface)); |
| |
| if (GDK_SURFACE_DESTROYED (surface)) |
| return; |
| |
| if (GDK_SURFACE_GET_CLASS (surface)->beep (surface)) |
| return; |
| |
| gdk_display_beep (surface->display); |
| } |
| |
| void |
| _gdk_display_set_surface_under_pointer (GdkDisplay *display, |
| GdkDevice *device, |
| GdkSurface *surface) |
| { |
| GdkPointerSurfaceInfo *device_info; |
| |
| device_info = _gdk_display_get_pointer_info (display, device); |
| |
| if (device_info->surface_under_pointer) |
| g_object_unref (device_info->surface_under_pointer); |
| device_info->surface_under_pointer = surface; |
| |
| if (surface) |
| { |
| g_object_ref (surface); |
| update_cursor (display, device); |
| } |
| } |
| |
| #define GDK_ANY_BUTTON_MASK (GDK_BUTTON1_MASK | \ |
| GDK_BUTTON2_MASK | \ |
| GDK_BUTTON3_MASK | \ |
| GDK_BUTTON4_MASK | \ |
| GDK_BUTTON5_MASK) |
| |
| void |
| _gdk_windowing_got_event (GdkDisplay *display, |
| GList *event_link, |
| GdkEvent *event, |
| gulong serial) |
| { |
| GdkSurface *event_surface = NULL; |
| gboolean unlink_event = FALSE; |
| GdkDeviceGrabInfo *button_release_grab; |
| GdkPointerSurfaceInfo *pointer_info = NULL; |
| GdkDevice *device; |
| GdkEventType type; |
| guint32 timestamp; |
| |
| _gdk_display_update_last_event (display, event); |
| |
| device = gdk_event_get_device (event); |
| timestamp = gdk_event_get_time (event); |
| |
| if (device) |
| { |
| if (timestamp != GDK_CURRENT_TIME) |
| gdk_device_set_timestamp (device, timestamp); |
| |
| if (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD && |
| gdk_device_get_source (device) != GDK_SOURCE_TABLET_PAD) |
| { |
| pointer_info = _gdk_display_get_pointer_info (display, device); |
| pointer_info->last_physical_device = device; |
| } |
| |
| _gdk_display_device_grab_update (display, device, serial); |
| } |
| |
| event_surface = gdk_event_get_surface (event); |
| if (!event_surface) |
| goto out; |
| |
| type = gdk_event_get_event_type (event); |
| if (type == GDK_ENTER_NOTIFY) |
| _gdk_display_set_surface_under_pointer (display, device, event_surface); |
| else if (type == GDK_LEAVE_NOTIFY) |
| _gdk_display_set_surface_under_pointer (display, device, NULL); |
| |
| if (type == GDK_BUTTON_PRESS) |
| { |
| GdkSurface *grab_surface; |
| gboolean owner_events; |
| |
| if (!gdk_device_grab_info (display, device, &grab_surface, &owner_events)) |
| { |
| _gdk_display_add_device_grab (display, |
| device, |
| event_surface, |
| FALSE, |
| GDK_ALL_EVENTS_MASK, |
| serial, |
| gdk_event_get_time (event), |
| TRUE); |
| _gdk_display_device_grab_update (display, device, serial); |
| } |
| } |
| else if (type == GDK_BUTTON_RELEASE || |
| type == GDK_TOUCH_CANCEL || |
| type == GDK_TOUCH_END) |
| { |
| if (type == GDK_BUTTON_RELEASE || |
| gdk_event_get_pointer_emulated (event)) |
| { |
| button_release_grab = |
| _gdk_display_has_device_grab (display, device, serial); |
| |
| if (button_release_grab && |
| button_release_grab->implicit && |
| (gdk_event_get_modifier_state (event) & GDK_ANY_BUTTON_MASK & ~(GDK_BUTTON1_MASK << (gdk_button_event_get_button (event) - 1))) == 0) |
| { |
| button_release_grab->serial_end = serial; |
| button_release_grab->implicit_ungrab = FALSE; |
| _gdk_display_device_grab_update (display, device, serial); |
| } |
| } |
| } |
| |
| out: |
| if (unlink_event) |
| { |
| _gdk_event_queue_remove_link (display, event_link); |
| g_list_free_1 (event_link); |
| gdk_event_unref (event); |
| } |
| |
| /* This does two things - first it sees if there are motions at the |
| * end of the queue that can be compressed. Second, if there is just |
| * a single motion that won't be dispatched because it is a compression |
| * candidate it queues up flushing the event queue. |
| */ |
| _gdk_event_queue_handle_motion_compression (display); |
| gdk_event_queue_handle_scroll_compression (display); |
| |
| if (event_surface) |
| { |
| GdkFrameClock *clock = gdk_surface_get_frame_clock (event_surface); |
| |
| if (clock) /* might be NULL if surface was destroyed */ |
| gdk_frame_clock_request_phase (clock, GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS); |
| } |
| } |
| |
| /** |
| * gdk_surface_create_similar_surface: |
| * @surface: surface to make new surface similar to |
| * @content: the content for the new surface |
| * @width: width of the new surface |
| * @height: height of the new surface |
| * |
| * Create a new Cairo surface that is as compatible as possible with the |
| * given @surface. |
| * |
| * For example the new surface will have the same fallback resolution |
| * and font options as @surface. Generally, the new surface will also |
| * use the same backend as @surface, unless that is not possible for |
| * some reason. The type of the returned surface may be examined with |
| * cairo_surface_get_type(). |
| * |
| * Initially the surface contents are all 0 (transparent if contents |
| * have transparency, black otherwise.) |
| * |
| * This function always returns a valid pointer, but it will return a |
| * pointer to a “nil” surface if @other is already in an error state |
| * or any other error occurs. |
| * |
| * Returns: a pointer to the newly allocated surface. The caller |
| * owns the surface and should call cairo_surface_destroy() when done |
| * with it. |
| */ |
| cairo_surface_t * |
| gdk_surface_create_similar_surface (GdkSurface * surface, |
| cairo_content_t content, |
| int width, |
| int height) |
| { |
| cairo_surface_t *similar_surface; |
| int scale; |
| |
| g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL); |
| |
| scale = gdk_surface_get_scale_factor (surface); |
| |
| similar_surface = cairo_image_surface_create (content == CAIRO_CONTENT_COLOR ? CAIRO_FORMAT_RGB24 : |
| content == CAIRO_CONTENT_ALPHA ? CAIRO_FORMAT_A8 : CAIRO_FORMAT_ARGB32, |
| width * scale, height * scale); |
| cairo_surface_set_device_scale (similar_surface, scale, scale); |
| |
| return similar_surface; |
| } |
| |
| /* This function is called when the XWindow is really gone. |
| */ |
| void |
| gdk_surface_destroy_notify (GdkSurface *surface) |
| { |
| GDK_SURFACE_GET_CLASS (surface)->destroy_notify (surface); |
| } |
| |
| /** |
| * gdk_drag_begin: |
| * @surface: the source surface for this drag |
| * @device: the device that controls this drag |
| * @content: (transfer none): the offered content |
| * @actions: the actions supported by this drag |
| * @dx: the x offset to @device's position where the drag nominally started |
| * @dy: the y offset to @device's position where the drag nominally started |
| * |
| * Starts a drag and creates a new drag context for it. |
| * |
| * This function is called by the drag source. After this call, you |
| * probably want to set up the drag icon using the surface returned |
| * by [method@Gdk.Drag.get_drag_surface]. |
| * |
| * This function returns a reference to the [class@Gdk.Drag] object, |
| * but GTK keeps its own reference as well, as long as the DND operation |
| * is going on. |
| * |
| * Note: if @actions include %GDK_ACTION_MOVE, you need to listen for |
| * the [signal@Gdk.Drag::dnd-finished] signal and delete the data at |
| * the source if [method@Gdk.Drag.get_selected_action] returns |
| * %GDK_ACTION_MOVE. |
| * |
| * Returns: (transfer full) (nullable): a newly created `GdkDrag` |
| */ |
| GdkDrag * |
| gdk_drag_begin (GdkSurface *surface, |
| GdkDevice *device, |
| GdkContentProvider *content, |
| GdkDragAction actions, |
| double dx, |
| double dy) |
| { |
| g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL); |
| g_return_val_if_fail (GDK_IS_DEVICE (device), NULL); |
| g_return_val_if_fail (surface->display == gdk_device_get_display (device), NULL); |
| g_return_val_if_fail (GDK_IS_CONTENT_PROVIDER (content), NULL); |
| |
| return GDK_SURFACE_GET_CLASS (surface)->drag_begin (surface, device, content, actions, dx, dy); |
| } |
| |
| static void |
| gdk_surface_ensure_motion (GdkSurface *surface) |
| { |
| GdkDisplay *display; |
| GdkSeat *seat; |
| GdkDevice *device; |
| GdkEvent *event; |
| double x, y; |
| GdkModifierType state; |
| GdkSurface *grab_surface; |
| |
| if (!surface->request_motion) |
| return; |
| |
| surface->request_motion = FALSE; |
| |
| display = gdk_surface_get_display (surface); |
| seat = gdk_display_get_default_seat (display); |
| if (!seat) |
| return; |
| |
| device = gdk_seat_get_pointer (seat); |
| |
| if (!gdk_surface_get_device_position (surface, device, &x, &y, &state)) |
| return; |
| |
| if (gdk_device_grab_info (display, device, &grab_surface, NULL)) |
| { |
| if (grab_surface != surface) |
| return; |
| } |
| |
| event = gdk_motion_event_new (surface, |
| device, |
| NULL, |
| GDK_CURRENT_TIME, |
| state, |
| x, y, |
| NULL); |
| |
| gdk_surface_handle_event (event); |
| gdk_event_unref (event); |
| } |
| |
| static void |
| gdk_surface_flush_events (GdkFrameClock *clock, |
| void *data) |
| { |
| GdkSurface *surface = GDK_SURFACE (data); |
| |
| _gdk_event_queue_flush (surface->display); |
| gdk_surface_ensure_motion (surface); |
| _gdk_display_pause_events (surface->display); |
| |
| gdk_frame_clock_request_phase (clock, GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS); |
| surface->frame_clock_events_paused = TRUE; |
| } |
| |
| static void |
| gdk_surface_resume_events (GdkFrameClock *clock, |
| void *data) |
| { |
| GdkSurface *surface = GDK_SURFACE (data); |
| |
| if (surface->frame_clock_events_paused) |
| { |
| _gdk_display_unpause_events (surface->display); |
| surface->frame_clock_events_paused = FALSE; |
| } |
| } |
| |
| static void |
| gdk_surface_set_frame_clock (GdkSurface *surface, |
| GdkFrameClock *clock) |
| { |
| g_return_if_fail (GDK_IS_SURFACE (surface)); |
| g_return_if_fail (clock == NULL || GDK_IS_FRAME_CLOCK (clock)); |
| |
| if (clock == surface->frame_clock) |
| return; |
| |
| if (clock) |
| { |
| g_object_ref (clock); |
| |
| g_signal_connect (G_OBJECT (clock), |
| "flush-events", |
| G_CALLBACK (gdk_surface_flush_events), |
| surface); |
| g_signal_connect (G_OBJECT (clock), |
| "resume-events", |
| G_CALLBACK (gdk_surface_resume_events), |
| surface); |
| g_signal_connect (G_OBJECT (clock), |
| "layout", |
| G_CALLBACK (gdk_surface_layout_on_clock), |
| surface); |
| g_signal_connect (G_OBJECT (clock), |
| "paint", |
| G_CALLBACK (gdk_surface_paint_on_clock), |
| surface); |
| |
| if (surface->update_freeze_count == 0) |
| _gdk_frame_clock_inhibit_freeze (clock); |
| } |
| |
| if (surface->frame_clock) |
| { |
| if (surface->frame_clock_events_paused) |
| gdk_surface_resume_events (surface->frame_clock, G_OBJECT (surface)); |
| |
| g_signal_handlers_disconnect_by_func (G_OBJECT (surface->frame_clock), |
| G_CALLBACK (gdk_surface_flush_events), |
| surface); |
| g_signal_handlers_disconnect_by_func (G_OBJECT (surface->frame_clock), |
| G_CALLBACK (gdk_surface_resume_events), |
| surface); |
| g_signal_handlers_disconnect_by_func (G_OBJECT (surface->frame_clock), |
| G_CALLBACK (gdk_surface_layout_on_clock), |
| surface); |
| g_signal_handlers_disconnect_by_func (G_OBJECT (surface->frame_clock), |
| G_CALLBACK (gdk_surface_paint_on_clock), |
| surface); |
| |
| if (surface->update_freeze_count == 0) |
| _gdk_frame_clock_uninhibit_freeze (surface->frame_clock); |
| |
| g_object_unref (surface->frame_clock); |
| } |
| |
| surface->frame_clock = clock; |
| } |
| |
| /** |
| * gdk_surface_get_frame_clock: (attributes org.gtk.Method.get_property=frame-clock) |
| * @surface: surface to get frame clock for |
| * |
| * Gets the frame clock for the surface. |
| * |
| * The frame clock for a surface never changes unless the surface is |
| * reparented to a new toplevel surface. |
| * |
| * Returns: (transfer none): the frame clock |
| */ |
| GdkFrameClock * |
| gdk_surface_get_frame_clock (GdkSurface *surface) |
| { |
| g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL); |
| |
| return surface->frame_clock; |
| } |
| |
| /** |
| * gdk_surface_get_scale_factor: (attributes org.gtk.Method.get_property=scale-factor) |
| * @surface: surface to get scale factor for |
| * |
| * Returns the internal scale factor that maps from surface coordinates |
| * to the actual device pixels. |
| * |
| * On traditional systems this is 1, but on very high density outputs |
| * this can be a higher value (often 2). A higher value means that drawing |
| * is automatically scaled up to a higher resolution, so any code doing |
| * drawing will automatically look nicer. However, if you are supplying |
| * pixel-based data the scale value can be used to determine whether to |
| * use a pixel resource with higher resolution data. |
| * |
| * The scale of a surface may change during runtime. |
| * |
| * Returns: the scale factor |
| */ |
| int |
| gdk_surface_get_scale_factor (GdkSurface *surface) |
| { |
| GdkSurfaceClass *class; |
| |
| g_return_val_if_fail (GDK_IS_SURFACE (surface), 1); |
| |
| if (GDK_SURFACE_DESTROYED (surface)) |
| return 1; |
| |
| class = GDK_SURFACE_GET_CLASS (surface); |
| if (class->get_scale_factor) |
| return class->get_scale_factor (surface); |
| |
| return 1; |
| } |
| |
| /** |
| * gdk_surface_set_opaque_region: |
| * @surface: a top-level `GdkSurface` |
| * @region: (nullable): a region, or %NULL to make the entire |
| * surface opaque |
| * |
| * Marks a region of the `GdkSurface` as opaque. |
| * |
| * For optimisation purposes, compositing window managers may |
| * like to not draw obscured regions of surfaces, or turn off blending |
| * during for these regions. With RGB windows with no transparency, |
| * this is just the shape of the window, but with ARGB32 windows, the |
| * compositor does not know what regions of the window are transparent |
| * or not. |
| * |
| * This function only works for toplevel surfaces. |
| * |
| * GTK will update this property automatically if the @surface background |
| * is opaque, as we know where the opaque regions are. If your surface |
| * background is not opaque, please update this property in your |
| * [vfunc@Gtk.Widget.css_changed] handler. |
| */ |
| void |
| gdk_surface_set_opaque_region (GdkSurface *surface, |
| cairo_region_t *region) |
| { |
| GdkSurfaceClass *class; |
| |
| g_return_if_fail (GDK_IS_SURFACE (surface)); |
| g_return_if_fail (!GDK_SURFACE_DESTROYED (surface)); |
| |
| if (cairo_region_equal (surface->opaque_region, region)) |
| return; |
| |
| g_clear_pointer (&surface->opaque_region, cairo_region_destroy); |
| |
| if (region != NULL) |
| surface->opaque_region = cairo_region_reference (region); |
| |
| class = GDK_SURFACE_GET_CLASS (surface); |
| if (class->set_opaque_region) |
| class->set_opaque_region (surface, region); |
| } |
| |
| void |
| gdk_surface_set_state (GdkSurface *surface, |
| GdkToplevelState new_state) |
| { |
| gboolean was_sticky, sticky; |
| g_return_if_fail (GDK_IS_SURFACE (surface)); |
| |
| if (new_state == surface->state) |
| return; /* No actual work to do, nothing changed. */ |
| |
| /* Actually update the field in GdkSurface, this is sort of an odd |
| * place to do it, but seems like the safest since it ensures we expose no |
| * inconsistent state to the user. |
| */ |
| |
| was_sticky = GDK_SURFACE_IS_STICKY (surface); |
| |
| surface->state = new_state; |
| |
| sticky = GDK_SURFACE_IS_STICKY (surface); |
| |
| if (GDK_IS_TOPLEVEL (surface)) |
| g_object_notify (G_OBJECT (surface), "state"); |
| |
| if (was_sticky != sticky) |
| g_object_notify (G_OBJECT (surface), "sticky"); |
| } |
| |
| void |
| gdk_synthesize_surface_state (GdkSurface *surface, |
| GdkToplevelState unset_flags, |
| GdkToplevelState set_flags) |
| { |
| gdk_surface_set_state (surface, (surface->state | set_flags) & ~unset_flags); |
| } |
| |
| void |
| gdk_surface_queue_state_change (GdkSurface *surface, |
| GdkToplevelState unset_flags, |
| GdkToplevelState set_flags) |
| { |
| surface->pending_unset_flags |= unset_flags; |
| surface->pending_set_flags &= ~unset_flags; |
| |
| surface->pending_set_flags |= set_flags; |
| surface->pending_unset_flags &= ~set_flags; |
| } |
| |
| void |
| gdk_surface_apply_state_change (GdkSurface *surface) |
| { |
| if (!surface->pending_unset_flags && !surface->pending_set_flags) |
| return; |
| |
| gdk_synthesize_surface_state (surface, |
| surface->pending_unset_flags, |
| surface->pending_set_flags); |
| surface->pending_unset_flags = 0; |
| surface->pending_set_flags = 0; |
| } |
| |
| static gboolean |
| set_is_mapped_idle (gpointer user_data) |
| { |
| GdkSurface *surface = GDK_SURFACE (user_data); |
| |
| surface->set_is_mapped_source_id = 0; |
| |
| g_return_val_if_fail (surface->pending_is_mapped != surface->is_mapped, |
| G_SOURCE_REMOVE); |
| |
| surface->is_mapped = surface->pending_is_mapped; |
| if (surface->is_mapped) |
| gdk_surface_invalidate_rect (surface, NULL); |
| |
| g_object_notify (G_OBJECT (surface), "mapped"); |
| |
| return G_SOURCE_REMOVE; |
| } |
| |
| void |
| gdk_surface_set_is_mapped (GdkSurface *surface, |
| gboolean is_mapped) |
| { |
| gboolean was_mapped; |
| |
| if (surface->pending_is_mapped != surface->is_mapped) |
| g_clear_handle_id (&surface->set_is_mapped_source_id, g_source_remove); |
| |
| surface->pending_is_mapped = is_mapped; |
| |
| was_mapped = surface->is_mapped; |
| surface->is_mapped = is_mapped; |
| if (surface->is_mapped) |
| gdk_surface_invalidate_rect (surface, NULL); |
| |
| if (was_mapped != is_mapped) |
| g_object_notify (G_OBJECT (surface), "mapped"); |
| } |
| |
| static void |
| gdk_surface_queue_set_is_mapped (GdkSurface *surface, |
| gboolean is_mapped) |
| { |
| if (surface->pending_is_mapped == is_mapped) |
| return; |
| |
| surface->pending_is_mapped = is_mapped; |
| |
| if (surface->is_mapped == surface->pending_is_mapped) |
| { |
| g_clear_handle_id (&surface->set_is_mapped_source_id, g_source_remove); |
| } |
| else |
| { |
| g_return_if_fail (!surface->set_is_mapped_source_id); |
| |
| surface->set_is_mapped_source_id = |
| g_idle_add_full (G_PRIORITY_HIGH - 10, set_is_mapped_idle, surface, NULL); |
| gdk_source_set_static_name_by_id (surface->set_is_mapped_source_id, "[gtk] set_is_mapped_idle"); |
| } |
| } |
| |
| static gboolean |
| check_autohide (GdkEvent *event) |
| { |
| GdkDisplay *display; |
| GdkDevice *device; |
| GdkSurface *grab_surface; |
| |
| switch ((guint) gdk_event_get_event_type (event)) |
| { |
| case GDK_BUTTON_PRESS: |
| #if 0 |
| // FIXME: we need to ignore the release that is paired |
| // with the press starting the grab - due to implicit |
| // grabs, it will be delivered to the same place as the |
| // press, and will cause the auto dismissal to be triggered. |
| case GDK_BUTTON_RELEASE: |
| #endif |
| case GDK_TOUCH_BEGIN: |
| case GDK_TOUCH_END: |
| case GDK_TOUCH_CANCEL: |
| case GDK_TOUCHPAD_SWIPE: |
| case GDK_TOUCHPAD_PINCH: |
| display = gdk_event_get_display (event); |
| device = gdk_event_get_device (event); |
| if (gdk_device_grab_info (display, device, &grab_surface, NULL)) |
| { |
| GdkSurface *event_surface; |
| |
| event_surface = gdk_event_get_surface (event); |
| |
| if (grab_surface != event_surface && |
| grab_surface != event_surface->parent && |
| grab_surface->autohide) |
| { |
| GdkSurface *surface = grab_surface; |
| |
| do |
| { |
| gdk_surface_hide (surface); |
| surface = surface->parent; |
| } |
| while (surface->autohide && surface != event_surface); |
| |
| return TRUE; |
| } |
| } |
| break; |
| default:; |
| } |
| |
| return FALSE; |
| } |
| |
| static inline void |
| add_event_mark (GdkEvent *event, |
| gint64 time, |
| gint64 end_time) |
| { |
| #ifdef HAVE_SYSPROF |
| char *message = NULL; |
| const char *kind; |
| GEnumClass *class; |
| GEnumValue *value; |
| GdkEventType event_type; |
| |
| event_type = gdk_event_get_event_type (event); |
| class = g_type_class_ref (GDK_TYPE_EVENT_TYPE); |
| value = g_enum_get_value (class, event_type); |
| g_type_class_unref (class); |
| kind = value ? value->value_nick : "event"; |
| |
| switch ((int) event_type) |
| { |
| case GDK_MOTION_NOTIFY: |
| { |
| double x, y; |
| gdk_event_get_position (event, &x, &y); |
| message = g_strdup_printf ("%s {x=%lf, y=%lf, state=0x%x}", |
| kind, |
| x, y, |
| gdk_event_get_modifier_state (event)); |
| break; |
| } |
| |
| case GDK_BUTTON_PRESS: |
| case GDK_BUTTON_RELEASE: |
| { |
| double x, y; |
| gdk_event_get_position (event, &x, &y); |
| message = g_strdup_printf ("%s {button=%u, x=%lf, y=%lf, state=0x%x}", |
| kind, |
| gdk_button_event_get_button (event), |
| x, y, |
| gdk_event_get_modifier_state (event)); |
| break; |
| } |
| |
| case GDK_KEY_PRESS: |
| case GDK_KEY_RELEASE: |
| { |
| message = g_strdup_printf ("%s {keyval=%u, state=0x%x, keycode=%u layout=%u level=%u is_modifier=%u}", |
| kind, |
| gdk_key_event_get_keyval (event), |
| gdk_event_get_modifier_state (event), |
| gdk_key_event_get_keycode (event), |
| gdk_key_event_get_layout (event), |
| gdk_key_event_get_level (event), |
| gdk_key_event_is_modifier (event)); |
| break; |
| } |
| |
| case GDK_ENTER_NOTIFY: |
| case GDK_LEAVE_NOTIFY: |
| case GDK_TOUCHPAD_SWIPE: |
| case GDK_TOUCHPAD_PINCH: |
| case GDK_SCROLL: |
| case GDK_DRAG_ENTER: |
| case GDK_DRAG_LEAVE: |
| case GDK_DRAG_MOTION: |
| case GDK_DROP_START: |
| case GDK_TOUCH_BEGIN: |
| case GDK_TOUCH_UPDATE: |
| case GDK_TOUCH_END: |
| case GDK_TOUCH_CANCEL: |
| case GDK_PAD_BUTTON_PRESS: |
| case GDK_PAD_BUTTON_RELEASE: |
| case GDK_PAD_RING: |
| case GDK_PAD_STRIP: |
| case GDK_PAD_GROUP_MODE: |
| case GDK_GRAB_BROKEN: |
| case GDK_DELETE: |
| case GDK_FOCUS_CHANGE: |
| case GDK_PROXIMITY_IN: |
| case GDK_PROXIMITY_OUT: |
| case GDK_EVENT_LAST: |
| default: |
| break; |
| } |
| |
| gdk_profiler_add_mark (time, end_time - time, "event", message ? message : kind); |
| |
| g_free (message); |
| #endif |
| } |
| |
| gboolean |
| gdk_surface_handle_event (GdkEvent *event) |
| { |
| GdkSurface *surface = gdk_event_get_surface (event); |
| gint64 begin_time = GDK_PROFILER_CURRENT_TIME; |
| gboolean handled = FALSE; |
| |
| if (check_autohide (event)) |
| return TRUE; |
| |
| |
| if (gdk_event_get_event_type (event) == GDK_MOTION_NOTIFY) |
| surface->request_motion = FALSE; |
| |
| g_signal_emit (surface, signals[EVENT], 0, event, &handled); |
| |
| if (GDK_PROFILER_IS_RUNNING) |
| add_event_mark (event, begin_time, GDK_PROFILER_CURRENT_TIME); |
| |
| return handled; |
| } |
| |
| /* |
| * gdk_surface_request_motion: |
| * @surface: a `GdkSurface` |
| * |
| * Request that the next frame cycle should deliver a motion |
| * event for @surface. |
| * |
| * The motion event will be delivered if the pointer is over the |
| * surface, regardless whether the pointer has moved or not. This |
| * is used by GTK after moving widgets around. |
| */ |
| void |
| gdk_surface_request_motion (GdkSurface *surface) |
| { |
| surface->request_motion = TRUE; |
| } |
| |
| /** |
| * gdk_surface_translate_coordinates: |
| * @from: the origin surface |
| * @to: the target surface |
| * @x: (inout): coordinates to translate |
| * @y: (inout): coordinates to translate |
| * |
| * Translates coordinates between two surfaces. |
| * |
| * Note that this only works if @to and @from are popups or |
| * transient-for to the same toplevel (directly or indirectly). |
| * |
| * Returns: %TRUE if the coordinates were successfully translated |
| */ |
| gboolean |
| gdk_surface_translate_coordinates (GdkSurface *from, |
| GdkSurface *to, |
| double *x, |
| double *y) |
| { |
| double in_x, in_y, out_x, out_y; |
| int x1, y1, x2, y2; |
| GdkSurface *f, *t; |
| |
| g_return_val_if_fail (GDK_IS_SURFACE (from), FALSE); |
| g_return_val_if_fail (GDK_IS_SURFACE (to), FALSE); |
| g_return_val_if_fail (x != NULL, FALSE); |
| g_return_val_if_fail (y != NULL, FALSE); |
| |
| in_x = *x; |
| in_y = *y; |
| |
| x1 = 0; |
| y1 = 0; |
| f = from; |
| while (f->parent) |
| { |
| x1 += f->x; |
| y1 += f->y; |
| f = f->parent; |
| } |
| |
| x2 = 0; |
| y2 = 0; |
| t = to; |
| while (t->parent) |
| { |
| x2 += t->x; |
| y2 += t->y; |
| t = t->parent; |
| } |
| |
| if (f != t) |
| return FALSE; |
| |
| out_x = in_x + (x1 - x2); |
| out_y = in_y + (y1 - y2); |
| |
| *x = out_x; |
| *y = out_y; |
| |
| return TRUE; |
| } |
| |
| GdkSeat * |
| gdk_surface_get_seat_from_event (GdkSurface *surface, |
| GdkEvent *event) |
| { |
| if (event) |
| { |
| GdkSeat *seat = NULL; |
| |
| seat = gdk_event_get_seat (event); |
| |
| if (seat) |
| return seat; |
| } |
| return gdk_display_get_default_seat (surface->display); |
| } |
| |
| void |
| gdk_surface_enter_monitor (GdkSurface *surface, |
| GdkMonitor *monitor) |
| { |
| g_signal_emit (surface, signals[ENTER_MONITOR], 0, monitor); |
| } |
| |
| void |
| gdk_surface_leave_monitor (GdkSurface *surface, |
| GdkMonitor *monitor) |
| { |
| g_signal_emit (surface, signals[LEAVE_MONITOR], 0, monitor); |
| } |