/* 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 "gdkcairoprivate.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 "gdkprofilerprivate.h"
#include "gdkrectangleprivate.h"
#include "gdktoplevelprivate.h"
#include "gdkvulkancontext.h"
#include "gdksubsurfaceprivate.h"
#include "gdkseatprivate.h"
#include "gsk/gskrectprivate.h"
#include <math.h>
#ifdef HAVE_EGL
#include <epoxy/egl.h>
#endif
/**
* GdkSurface :
*
* Represents a rectangular region on the screen .
*
* It ’ s a low - level object , used to implement high - level objects
* such as [ GtkWindow ] ( . . / gtk4 / class . Window . html ) .
*
* 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
{
cairo_region_t *opaque_region;
cairo_rectangle_int_t opaque_rect; /* This is different from the region */
GdkDrawContext *attached_context;
gpointer widget;
GdkColorState *color_state;
};
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,
PROP_SCALE,
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_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;
int badness = 0 ;
int limit;
/* Try to fit without flipping */
*flipped = FALSE ;
primary = rect_pos + (1 + rect_sign) * rect_size / 2 + offset - (1 + surface_sign) * surface_size / 2 ;
if (!flip)
return primary;
/* If the position gets larger than this limit,
* the other ( right / bottom ) edge is not going to fit .
*/
limit = bounds_pos + bounds_size - surface_size;
if (primary < bounds_pos)
badness = bounds_pos - primary;
if (primary > limit)
badness = MAX (badness, primary - limit);
/* If it fit, we're done */
if (badness == 0 )
return primary;
/* See if flipping helps */
secondary = rect_pos + (1 - rect_sign) * rect_size / 2 - offset - (1 - surface_sign) * surface_size / 2 ;
if (secondary < bounds_pos && bounds_pos - secondary > badness)
return primary;
else if (secondary > limit && secondary - limit > badness)
return primary;
/* It does help */
*flipped = TRUE ;
return secondary;
}
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,
GdkSurfaceLayoutPopupHelperFlags flags,
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;
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;
if ((flags & GDK_SURFACE_LAYOUT_POPUP_HELPER_ROOT_OUT) == 0 )
{
int x, y;
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)
{
GdkSurfacePrivate *priv = gdk_surface_get_instance_private (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);
surface->subsurfaces = g_ptr_array_new ();
priv->color_state = gdk_color_state_ref (gdk_color_state_get_srgb ());
}
static double
gdk_surface_real_get_scale (GdkSurface *surface)
{
return 1 .0 ;
}
static void
gdk_surface_default_get_buffer_size (GdkSurface *surface,
GdkDrawContext *context,
guint *out_width,
guint *out_height)
{
double scale = gdk_surface_get_scale (surface);
*out_width = ceil (scale * surface->width);
*out_height = ceil (scale * surface->height);
}
static GdkSubsurface *
gdk_surface_real_create_subsurface (GdkSurface *surface)
{
GDK_DISPLAY_DEBUG (gdk_surface_get_display (surface), OFFLOAD,
"Subsurfaces not supported for %s" , G_OBJECT_TYPE_NAME (surface));
return NULL;
}
static void
gdk_surface_default_set_opaque_region (GdkSurface *surface,
cairo_region_t *region)
{
}
static void
gdk_surface_constructed (GObject *object)
{
G_GNUC_UNUSED GdkSurface *surface = GDK_SURFACE (object);
g_assert (surface->frame_clock != NULL);
G_OBJECT_CLASS (gdk_surface_parent_class)->constructed (object);
}
static void
gdk_surface_class_init (GdkSurfaceClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = gdk_surface_constructed;
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;
klass->get_scale = gdk_surface_real_get_scale;
klass->get_buffer_size = gdk_surface_default_get_buffer_size;
klass->create_subsurface = gdk_surface_real_create_subsurface;
klass->set_opaque_region = gdk_surface_default_set_opaque_region;
/**
* GdkSurface : 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_NAME);
/**
* GdkSurface : 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_NAME);
/**
* GdkSurface : 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_NAME);
/**
* GdkSurface : mapped :
*
* Whether the surface is mapped .
*/
properties[PROP_MAPPED] =
g_param_spec_boolean ("mapped" , NULL, NULL,
FALSE ,
G_PARAM_READABLE | G_PARAM_STATIC_NAME);
/**
* GdkSurface : 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_NAME);
/**
* GdkSurface : 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_NAME);
/**
* GdkSurface : scale - factor :
*
* The scale factor of the surface .
*
* The scale factor is the next larger integer ,
* compared to [ property @ Gdk . Surface : scale ] .
*/
properties[PROP_SCALE_FACTOR] =
g_param_spec_int ("scale-factor" , NULL, NULL,
1 , G_MAXINT, 1 ,
G_PARAM_READABLE | G_PARAM_STATIC_NAME);
/**
* GdkSurface : scale :
*
* The scale of the surface .
*
* Since : 4 . 12
*/
properties[PROP_SCALE] =
g_param_spec_double ("scale" , NULL, NULL,
1 ., G_MAXDOUBLE, 1 .,
G_PARAM_READABLE | G_PARAM_STATIC_NAME);
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);
GdkSurfacePrivate *priv = gdk_surface_get_instance_private (surface);
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 (&priv->opaque_region, cairo_region_destroy);
if (surface->parent)
surface->parent->children = g_list_remove (surface->parent->children, surface);
g_assert (surface->subsurfaces->len == 0 );
g_ptr_array_unref (surface->subsurfaces);
g_clear_pointer (&priv->color_state, gdk_color_state_unref);
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 ;
}
}
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 ;
case PROP_SCALE:
g_value_set_double (value, gdk_surface_get_scale (surface));
break ;
default :
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break ;
}
}
void
_gdk_surface_update_size (GdkSurface *surface)
{
GdkSurfacePrivate *priv = gdk_surface_get_instance_private (surface);
if (priv->attached_context)
gdk_draw_context_surface_resized (priv->attached_context);
g_object_notify_by_pspec (G_OBJECT (surface), properties[PROP_WIDTH]);
g_object_notify_by_pspec (G_OBJECT (surface), properties[PROP_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 g_object_new (GDK_DISPLAY_GET_CLASS (display)->toplevel_type,
"display" , display,
NULL);
}
/**
* 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;
GdkDisplay *display;
g_return_val_if_fail (GDK_IS_SURFACE (parent), NULL);
display = gdk_surface_get_display (parent);
surface = g_object_new (GDK_DISPLAY_GET_CLASS (display)->popup_type,
"display" , display,
"parent" , parent,
NULL);
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_clear_object (&pointer_info->surface_under_pointer);
}
}
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 ;
if (priv->attached_context)
{
gdk_draw_context_detach (priv->attached_context);
g_assert (priv->attached_context == NULL);
}
GDK_SURFACE_GET_CLASS (surface)->destroy (surface, foreign_destroy);
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);
// Gtk.Window wants to disconnect from the frame clock, so notify before destroying it.
g_object_ref (surface);
g_object_notify_by_pspec (G_OBJECT (surface), properties[PROP_MAPPED]);
if (surface->gl_paint_context)
{
/* Make sure to destroy if current */
g_object_run_dispose (G_OBJECT (surface->gl_paint_context));
g_clear_object (&surface->gl_paint_context);
}
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_object_unref (surface);
}
/**
* _ 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 :
* @ 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 :
* @ 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);
}
gboolean
gdk_surface_get_gl_is_srgb (GdkSurface *self)
{
return self->is_srgb;
}
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, FALSE );
}
/**
* gdk_surface_create_cairo_context :
* @ surface : a ` GdkSurface `
*
* Creates a new ` GdkCairoContext ` for rendering on @ surface .
*
* Returns : ( transfer full ) : the newly created ` GdkCairoContext `
*
* Deprecated : 4 . 18 : Drawing content with Cairo should be done via
* Cairo rendernodes , not by using the Cairo renderer .
*/
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
*
* Sets an error and returns % NULL .
*
* Returns : ( transfer full ) : % NULL
*
* Deprecated : 4 . 14 : GTK does not expose any Vulkan internals . This
* function is a leftover that was accidentally exposed .
*/
GdkVulkanContext *
gdk_surface_create_vulkan_context (GdkSurface *surface,
GError **error)
{
g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_UNSUPPORTED,
"GTK does not expose Vulkan internals." );
return FALSE ;
}
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)
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_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);
cairo_region_t *expose_region;
g_return_if_fail (GDK_IS_SURFACE (surface));
if (GDK_SURFACE_DESTROYED (surface) ||
!surface->update_area ||
surface->update_freeze_count)
return ;
surface->pending_phases &= ~GDK_FRAME_CLOCK_PHASE_PAINT;
expose_region = surface->update_area;
surface->update_area = NULL;
if (GDK_SURFACE_IS_MAPPED (surface))
{
gboolean handled;
g_object_ref (surface);
g_signal_emit (surface, signals[RENDER], 0 , expose_region, &handled);
g_object_unref (surface);
}
cairo_region_destroy (expose_region);
}
/*
* 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
{
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)
{
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));
if (GDK_DEBUG_CHECK (NO_VSYNC))
return ;
surface->update_freeze_count++;
if (surface->update_freeze_count == 1 )
gdk_frame_clock_stop (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));
if (GDK_DEBUG_CHECK (NO_VSYNC))
return ;
g_return_if_fail (surface->update_freeze_count > 0 );
if (--surface->update_freeze_count == 0 )
{
GdkFrameClock *frame_clock = surface->frame_clock;
gdk_frame_clock_start (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 [ gtk_widget_hide ( ) ] ( . . / gtk4 / method . Widget . hide . html ) .
*/
void
gdk_surface_hide (GdkSurface *surface)
{
g_return_if_fail (GDK_IS_SURFACE (surface));
if (surface->destroyed)
return ;
gdk_surface_queue_set_is_mapped (surface, FALSE );
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 :
* @ 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 :
* @ 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));
g_clear_object (&surface->cursor);
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 :
* @ 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 :
* @ 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 : ( nullable ) : region of surface to be reactive ,
* or % NULL to make the entire surface 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;
GdkCursor *cursor;
GdkSeat *seat;
g_assert (display);
g_assert (device);
pointer_info = _gdk_display_get_pointer_info (display, device);
pointer_surface = pointer_info->surface_under_pointer;
seat = gdk_device_get_seat (device);
cursor_surface = gdk_seat_get_topmost_grab_surface (seat);
if (!cursor_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);
}
}
void
_gdk_windowing_got_event (GdkDisplay *display,
GList *event_link,
GdkEvent *event,
gulong serial)
{
GdkSurface *event_surface = NULL;
gboolean unlink_event = FALSE ;
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;
}
}
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);
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 .
*
* Deprecated : 4 . 12 : Create a suitable cairo image surface yourself
*/
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 (gdk_cairo_format_for_content (content),
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 ;
grab_surface = gdk_seat_get_topmost_grab_surface (seat);
if (grab_surface && surface != grab_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 ;
}
}
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_start (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_stop (surface->frame_clock);
g_object_unref (surface->frame_clock);
}
surface->frame_clock = clock;
}
/**
* gdk_surface_get_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 :
* @ 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 factor may change during the lifetime of the surface .
*
* Returns : the scale factor
*/
int
gdk_surface_get_scale_factor (GdkSurface *surface)
{
g_return_val_if_fail (GDK_IS_SURFACE (surface), 1 );
return (int ) ceil (gdk_surface_get_scale (surface));
}
/**
* gdk_surface_get_scale :
* @ surface : surface to get scale for
*
* Returns the internal scale that maps from surface coordinates
* to the actual device pixels .
*
* When the scale is bigger than 1 , the windowing system prefers to get
* buffers with a resolution that is bigger than the surface size ( e . g .
* to show the surface on a high - resolution display , or in a magnifier ) .
*
* Compare with [ method @ Gdk . Surface . get_scale_factor ] , which returns the
* next larger integer .
*
* The scale may change during the lifetime of the surface .
*
* Returns : the scale
*
* Since : 4 . 12
*/
double
gdk_surface_get_scale (GdkSurface *surface)
{
g_return_val_if_fail (GDK_IS_SURFACE (surface), 1 .);
if (GDK_SURFACE_DESTROYED (surface))
return 1 .;
return GDK_SURFACE_GET_CLASS (surface)->get_scale (surface);
}
static void
gdk_surface_update_opaque_region (GdkSurface *self)
{
GdkSurfacePrivate *priv = gdk_surface_get_instance_private (self);
cairo_region_t *region;
if (priv->opaque_region == NULL)
{
if (priv->opaque_rect.width <= 0 )
region = NULL;
else
region = cairo_region_create_rectangle (&priv->opaque_rect);
}
else
{
if (priv->opaque_rect.width <= 0 )
region = cairo_region_reference (priv->opaque_region);
else
{
region = cairo_region_copy (priv->opaque_region);
cairo_region_union_rectangle (region, &priv->opaque_rect);
}
}
GDK_SURFACE_GET_CLASS (self)->set_opaque_region (self, region);
g_clear_pointer (®ion, cairo_region_destroy);
}
/**
* 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
* [ GtkWidgetClass . css_changed ] ( . . / gtk4 / vfunc . Widget . css_changed . html ) handler .
*
* Deprecated : 4 . 16 : GDK can figure out the opaque parts of a window itself
* by inspecting the contents that are drawn .
*/
void
gdk_surface_set_opaque_region (GdkSurface *surface,
cairo_region_t *region)
{
GdkSurfacePrivate *priv = gdk_surface_get_instance_private (surface);
g_return_if_fail (GDK_IS_SURFACE (surface));
g_return_if_fail (!GDK_SURFACE_DESTROYED (surface));
if (cairo_region_equal (priv->opaque_region, region))
return ;
g_clear_pointer (&priv->opaque_region, cairo_region_destroy);
if (region != NULL)
priv->opaque_region = cairo_region_reference (region);
gdk_surface_update_opaque_region (surface);
}
/* Sets the opaque rect from the rendernode via end_frame() */
void
gdk_surface_set_opaque_rect (GdkSurface *self,
const graphene_rect_t *rect)
{
GdkSurfacePrivate *priv = gdk_surface_get_instance_private (self);
cairo_rectangle_int_t opaque;
if (rect == NULL || !gsk_rect_to_cairo_shrink (rect, &opaque))
opaque = (cairo_rectangle_int_t) { 0 , 0 , 0 , 0 };
if (gdk_rectangle_equal (&priv->opaque_rect, &opaque))
return ;
priv->opaque_rect = opaque;
gdk_surface_update_opaque_region (self);
}
/*
* gdk_surface_is_opaque :
* @ self : a surface
*
* Checks if the whole surface is known to be opaque .
* This allows using an RGBx buffer instead of RGBA .
*
* This function works for the currently rendered frame inside
* begin_frame ( ) implementations .
*
* Returns : % TRUE if the whole surface is provably opaque
**/
gboolean
gdk_surface_is_opaque (GdkSurface *self)
{
GdkSurfacePrivate *priv = gdk_surface_get_instance_private (self);
cairo_rectangle_int_t whole = { 0 , 0 , self->width, self->height };
if (gdk_rectangle_contains (&priv->opaque_rect, &whole))
return TRUE ;
if (priv->opaque_region &&
cairo_region_contains_rectangle (priv->opaque_region, &whole) == CAIRO_REGION_OVERLAP_IN)
return TRUE ;
return FALSE ;
}
static void
gdk_surface_set_state (GdkSurface *surface,
GdkToplevelState new_state)
{
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 .
*/
surface->state = new_state;
if (GDK_IS_TOPLEVEL (surface))
/* gobject-linter-ignore-next-line: use_g_object_notify_by_pspec */
g_object_notify (G_OBJECT (surface), "state" );
}
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)
{
GdkFrameClock *frame_clock;
surface->pending_unset_flags |= unset_flags;
surface->pending_set_flags &= ~unset_flags;
surface->pending_set_flags |= set_flags;
surface->pending_unset_flags &= ~set_flags;
frame_clock = gdk_surface_get_frame_clock (surface);
if (!frame_clock || gdk_frame_clock_is_stopped (frame_clock))
gdk_surface_apply_state_change (surface);
}
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_by_pspec (G_OBJECT (surface), properties[PROP_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_by_pspec (G_OBJECT (surface), properties[PROP_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)
{
GdkSurface *grab_surface, *event_surface;
GdkEventType evtype = gdk_event_get_event_type (event);
GdkSeat *seat;
switch ((guint) evtype)
{
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:
case GDK_TOUCH_END:
case GDK_TOUCH_CANCEL:
#endif
case GDK_TOUCH_BEGIN:
case GDK_TOUCHPAD_SWIPE:
case GDK_TOUCHPAD_PINCH:
seat = gdk_event_get_seat (event);
grab_surface = gdk_seat_get_topmost_grab_surface (seat);
if (grab_surface)
{
event_surface = gdk_event_get_surface (event);
if (event_surface->autohide &&
evtype != GDK_TOUCH_BEGIN &&
!event_surface->has_pointer)
event_surface = NULL;
if (grab_surface->autohide &&
(!event_surface ||
(grab_surface != event_surface &&
grab_surface != event_surface->parent)))
{
GdkSurface *surface = grab_surface;
do
{
gdk_surface_hide (surface);
surface = surface->parent;
}
while (surface->autohide && surface != event_surface);
return TRUE ;
}
}
break ;
case GDK_ENTER_NOTIFY:
case GDK_LEAVE_NOTIFY:
event_surface = gdk_event_get_surface (event);
if (event_surface->autohide &&
gdk_crossing_event_get_mode (event) == GDK_CROSSING_NORMAL)
event_surface->has_pointer = evtype == GDK_ENTER_NOTIFY;
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_DIAL:
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);
GdkEventType evtype = gdk_event_get_event_type (event);
gint64 begin_time = GDK_PROFILER_CURRENT_TIME;
gboolean handled = FALSE ;
if (!GDK_SURFACE_IS_MAPPED (surface) &&
!(evtype == GDK_LEAVE_NOTIFY || evtype == GDK_TOUCH_CANCEL))
return FALSE ;
if (check_autohide (event))
return TRUE ;
if (evtype == 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);
}
GdkSubsurface *
gdk_surface_create_subsurface (GdkSurface *surface)
{
GdkSubsurface *subsurface;
subsurface = GDK_SURFACE_GET_CLASS (surface)->create_subsurface (surface);
if (subsurface)
{
subsurface->parent = g_object_ref (surface);
g_ptr_array_add (surface->subsurfaces, subsurface);
}
return subsurface;
}
gsize
gdk_surface_get_n_subsurfaces (GdkSurface *surface)
{
return surface->subsurfaces->len;
}
GdkSubsurface *
gdk_surface_get_subsurface (GdkSurface *surface,
gsize idx)
{
return g_ptr_array_index (surface->subsurfaces, idx);
}
GdkColorState *
gdk_surface_get_color_state (GdkSurface *surface)
{
GdkSurfacePrivate *priv = gdk_surface_get_instance_private (surface);
return priv->color_state;
}
void
gdk_surface_set_color_state (GdkSurface *surface,
GdkColorState *color_state)
{
GdkSurfacePrivate *priv = gdk_surface_get_instance_private (surface);
if (gdk_color_state_equal (priv->color_state, color_state))
return ;
gdk_color_state_unref (priv->color_state);
priv->color_state = gdk_color_state_ref (color_state);
gdk_surface_invalidate_rect (surface, NULL);
}
/*<private>
* gdk_surface_set_attached_context :
* @ self : the surface
* @ context : ( nullable ) : the context to attach
*
* This function is an implementation detail for GdkDrawContext .
*
* Use draw context functions like gdk_draw_context_attach ( ) or
* gdk_draw_context_detach ( ) .
**/
void
gdk_surface_set_attached_context (GdkSurface *self,
GdkDrawContext *context)
{
GdkSurfacePrivate *priv = gdk_surface_get_instance_private (self);
priv->attached_context = context;
}
GdkDrawContext *
gdk_surface_get_attached_context (GdkSurface *self)
{
GdkSurfacePrivate *priv = gdk_surface_get_instance_private (self);
return priv->attached_context;
}
Messung V0.5 in Prozent C=98 H=96 G=96
¤ Dauer der Verarbeitung: 0.59 Sekunden
(vorverarbeitet am 2026-07-03)
¤
*© Formatika GbR, Deutschland