forked from AuroraMiddleware/gtk
826030dcc2
The owner_events=TRUE grab makes GDK on X11 see events happening outside every client window as received on the grab window. Additionally check that the pointer is inside the grab window (i.e. it received GDK_CROSSING_NORMAL crossing events for the core pointer) in order to handle clicks happening outside client windows. These new paths are expected to be a no-op on Wayland, and to also work for touchscreen input on X11, due to emulated pointer events.
3074 lines
89 KiB
C
3074 lines
89 KiB
C
/* 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, *event_surface;
|
||
GdkEventType evtype = gdk_event_get_event_type (event);
|
||
|
||
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:
|
||
display = gdk_event_get_display (event);
|
||
device = gdk_event_get_device (event);
|
||
if (gdk_device_grab_info (display, device, &grab_surface, NULL))
|
||
{
|
||
event_surface = gdk_event_get_surface (event);
|
||
if (event_surface->autohide &&
|
||
!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_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);
|
||
}
|