forked from AuroraMiddleware/gtk
a468714849
Instead of calling gdk_surface_invalidate_region(), just gdk_surface_queue_expose() and rely on the renderer computing the diff from the previous rendering.
6213 lines
184 KiB
C
6213 lines
184 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 "gdkrectangle.h"
|
||
#include "gdkinternals.h"
|
||
#include "gdkintl.h"
|
||
#include "gdkdisplayprivate.h"
|
||
#include "gdkdeviceprivate.h"
|
||
#include "gdkframeclockidleprivate.h"
|
||
#include "gdkmarshalers.h"
|
||
#include "gdksurfaceimpl.h"
|
||
#include "gdkglcontextprivate.h"
|
||
#include "gdkdrawingcontextprivate.h"
|
||
#include "gdk-private.h"
|
||
|
||
#include <math.h>
|
||
|
||
#include <epoxy/gl.h>
|
||
|
||
/* for the use of round() */
|
||
#include "fallback-c89.c"
|
||
|
||
#ifdef GDK_WINDOWING_WAYLAND
|
||
#include "wayland/gdkwayland.h"
|
||
#endif
|
||
|
||
#undef DEBUG_SURFACE_PRINTING
|
||
|
||
|
||
/**
|
||
* SECTION:surfaces
|
||
* @Short_description: Onscreen display areas in the target window system
|
||
* @Title: Surfaces
|
||
*
|
||
* A #GdkSurface is a (usually) rectangular region on the screen.
|
||
* It’s a low-level object, used to implement high-level objects such as
|
||
* #GtkWidget and #GtkWindow on the GTK+ level. A #GtkWindow is a toplevel
|
||
* surface, the thing a user might think of as a “window” with a titlebar
|
||
* and so on; a #GtkWindow may contain many sub-GdkSurfaces.
|
||
*/
|
||
|
||
/**
|
||
* GdkSurface:
|
||
*
|
||
* The GdkSurface struct contains only private fields and
|
||
* should not be accessed directly.
|
||
*/
|
||
|
||
/* Historically a GdkSurface always matches a platform native window,
|
||
* be it a toplevel window or a child window. In this setup the
|
||
* GdkSurface (and other GdkDrawables) were platform independent classes,
|
||
* and the actual platform specific implementation was in a delegate
|
||
* object available as “impl” in the surface object.
|
||
*
|
||
* With the addition of client side windows this changes a bit. The
|
||
* application-visible GdkSurface object behaves as it did before, but
|
||
* such surfaces now don't a corresponding native window. Instead subwindows
|
||
* surfaces are “client side”, i.e. emulated by the gdk code such
|
||
* that clipping, drawing, moving, events etc work as expected.
|
||
*
|
||
* GdkSurfaces have a pointer to the “impl surface” they are in, i.e.
|
||
* the topmost GdkSurface which have the same “impl” value. This is stored
|
||
* in impl_surface, which is different from the surface itself only for client
|
||
* side surfaces.
|
||
* All GdkSurfaces (native or not) track the position of the surface in the parent
|
||
* (x, y), the size of the surface (width, height), the position of the surface
|
||
* with respect to the impl surface (abs_x, abs_y). We also track the clip
|
||
* region of the surface wrt parent surfaces, in surface-relative coordinates (clip_region).
|
||
*/
|
||
|
||
enum {
|
||
MOVED_TO_RECT,
|
||
LAST_SIGNAL
|
||
};
|
||
|
||
enum {
|
||
PROP_0,
|
||
PROP_CURSOR,
|
||
PROP_DISPLAY,
|
||
PROP_STATE,
|
||
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 gdk_surface_clear_backing_region (GdkSurface *surface);
|
||
|
||
static void recompute_visible_regions (GdkSurface *private,
|
||
gboolean recalculate_children);
|
||
static void gdk_surface_invalidate_in_parent (GdkSurface *private);
|
||
static void update_cursor (GdkDisplay *display,
|
||
GdkDevice *device);
|
||
static cairo_surface_t *gdk_surface_ref_impl_surface (GdkSurface *surface);
|
||
|
||
static void gdk_surface_set_frame_clock (GdkSurface *surface,
|
||
GdkFrameClock *clock);
|
||
|
||
|
||
static guint signals[LAST_SIGNAL] = { 0 };
|
||
static GParamSpec *properties[LAST_PROP] = { NULL, };
|
||
|
||
G_DEFINE_ABSTRACT_TYPE (GdkSurface, gdk_surface, G_TYPE_OBJECT)
|
||
|
||
#ifdef DEBUG_SURFACE_PRINTING
|
||
char *
|
||
print_region (cairo_region_t *region)
|
||
{
|
||
GString *s = g_string_new ("{");
|
||
if (cairo_region_is_empty (region))
|
||
{
|
||
g_string_append (s, "empty");
|
||
}
|
||
else
|
||
{
|
||
int num = cairo_region_num_rectangles (region);
|
||
cairo_rectangle_int_t r;
|
||
|
||
if (num == 1)
|
||
{
|
||
cairo_region_get_rectangle (region, 0, &r);
|
||
g_string_append_printf (s, "%dx%d @%d,%d", r.width, r.height, r.x, r.y);
|
||
}
|
||
else
|
||
{
|
||
int i;
|
||
cairo_region_get_extents (region, &r);
|
||
g_string_append_printf (s, "extent: %dx%d @%d,%d, details: ", r.width, r.height, r.x, r.y);
|
||
for (i = 0; i < num; i++)
|
||
{
|
||
cairo_region_get_rectangle (region, i, &r);
|
||
g_string_append_printf (s, "[%dx%d @%d,%d]", r.width, r.height, r.x, r.y);
|
||
if (i != num -1)
|
||
g_string_append (s, ", ");
|
||
}
|
||
}
|
||
}
|
||
g_string_append (s, "}");
|
||
return g_string_free (s, FALSE);
|
||
}
|
||
#endif
|
||
|
||
static GList *
|
||
list_insert_link_before (GList *list,
|
||
GList *sibling,
|
||
GList *link)
|
||
{
|
||
if (list == NULL || sibling == list)
|
||
{
|
||
link->prev = NULL;
|
||
link->next = list;
|
||
if (list)
|
||
list->prev = link;
|
||
return link;
|
||
}
|
||
else if (sibling == NULL)
|
||
{
|
||
GList *last = g_list_last (list);
|
||
|
||
last->next = link;
|
||
link->prev = last;
|
||
link->next = NULL;
|
||
|
||
return list;
|
||
}
|
||
else
|
||
{
|
||
link->next = sibling;
|
||
link->prev = sibling->prev;
|
||
sibling->prev = link;
|
||
|
||
if (link->prev)
|
||
link->prev->next = link;
|
||
|
||
return list;
|
||
}
|
||
}
|
||
|
||
static void
|
||
gdk_surface_init (GdkSurface *surface)
|
||
{
|
||
/* 0-initialization is good for all other fields. */
|
||
|
||
surface->surface_type = GDK_SURFACE_CHILD;
|
||
|
||
surface->state = GDK_SURFACE_STATE_WITHDRAWN;
|
||
surface->fullscreen_mode = GDK_FULLSCREEN_ON_CURRENT_MONITOR;
|
||
surface->width = 1;
|
||
surface->height = 1;
|
||
surface->children_list_node.data = surface;
|
||
|
||
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;
|
||
|
||
/* Properties */
|
||
|
||
/**
|
||
* GdkSurface:cursor:
|
||
*
|
||
* The mouse pointer for a #GdkSurface. See gdk_surface_set_cursor() and
|
||
* gdk_surface_get_cursor() for details.
|
||
*/
|
||
properties[PROP_CURSOR] =
|
||
g_param_spec_object ("cursor",
|
||
P_("Cursor"),
|
||
P_("Cursor"),
|
||
GDK_TYPE_CURSOR,
|
||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
|
||
|
||
/**
|
||
* GdkSurface:display:
|
||
*
|
||
* The #GdkDisplay connection of the surface. See gdk_surface_get_display()
|
||
* for details.
|
||
*/
|
||
properties[PROP_DISPLAY] =
|
||
g_param_spec_object ("display",
|
||
P_("Display"),
|
||
P_("Display"),
|
||
GDK_TYPE_DISPLAY,
|
||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
|
||
|
||
properties[PROP_STATE] =
|
||
g_param_spec_flags ("state",
|
||
P_("State"),
|
||
P_("State"),
|
||
GDK_TYPE_SURFACE_STATE, GDK_SURFACE_STATE_WITHDRAWN,
|
||
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
|
||
|
||
g_object_class_install_properties (object_class, LAST_PROP, properties);
|
||
|
||
/**
|
||
* GdkSurface::moved-to-rect:
|
||
* @surface: the #GdkSurface that moved
|
||
* @flipped_rect: (nullable): the position of @surface after any possible
|
||
* flipping or %NULL if the backend can't obtain it
|
||
* @final_rect: (nullable): the final position of @surface or %NULL if the
|
||
* backend can't obtain it
|
||
* @flipped_x: %TRUE if the anchors were flipped horizontally
|
||
* @flipped_y: %TRUE if the anchors were flipped vertically
|
||
*
|
||
* Emitted when the position of @surface is finalized after being moved to a
|
||
* destination rectangle.
|
||
*
|
||
* @surface might be flipped over the destination rectangle in order to keep
|
||
* it on-screen, in which case @flipped_x and @flipped_y will be set to %TRUE
|
||
* accordingly.
|
||
*
|
||
* @flipped_rect is the ideal position of @surface after any possible
|
||
* flipping, but before any possible sliding. @final_rect is @flipped_rect,
|
||
* but possibly translated in the case that flipping is still ineffective in
|
||
* keeping @surface on-screen.
|
||
* Stability: Private
|
||
*/
|
||
signals[MOVED_TO_RECT] =
|
||
g_signal_new (g_intern_static_string ("moved-to-rect"),
|
||
G_OBJECT_CLASS_TYPE (object_class),
|
||
G_SIGNAL_RUN_FIRST,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
_gdk_marshal_VOID__POINTER_POINTER_BOOLEAN_BOOLEAN,
|
||
G_TYPE_NONE,
|
||
4,
|
||
G_TYPE_POINTER,
|
||
G_TYPE_POINTER,
|
||
G_TYPE_BOOLEAN,
|
||
G_TYPE_BOOLEAN);
|
||
}
|
||
|
||
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);
|
||
|
||
if (surface->device_events)
|
||
g_hash_table_remove (surface->device_events, device);
|
||
}
|
||
|
||
static void
|
||
gdk_surface_finalize (GObject *object)
|
||
{
|
||
GdkSurface *surface = GDK_SURFACE (object);
|
||
|
||
g_signal_handlers_disconnect_by_func (gdk_surface_get_display (surface),
|
||
seat_removed_cb, surface);
|
||
|
||
if (!GDK_SURFACE_DESTROYED (surface))
|
||
{
|
||
if (GDK_SURFACE_TYPE (surface) != GDK_SURFACE_FOREIGN)
|
||
{
|
||
g_warning ("losing last reference to undestroyed surface");
|
||
_gdk_surface_destroy (surface, FALSE);
|
||
}
|
||
else
|
||
/* We use TRUE here, to keep us from actually calling
|
||
* XDestroyWindow() on the window
|
||
*/
|
||
_gdk_surface_destroy (surface, TRUE);
|
||
}
|
||
|
||
if (surface->impl)
|
||
{
|
||
g_object_unref (surface->impl);
|
||
surface->impl = NULL;
|
||
}
|
||
|
||
if (surface->impl_surface != surface)
|
||
{
|
||
g_object_unref (surface->impl_surface);
|
||
surface->impl_surface = NULL;
|
||
}
|
||
|
||
if (surface->input_shape)
|
||
cairo_region_destroy (surface->input_shape);
|
||
|
||
if (surface->cursor)
|
||
g_object_unref (surface->cursor);
|
||
|
||
if (surface->device_cursor)
|
||
g_hash_table_destroy (surface->device_cursor);
|
||
|
||
if (surface->device_events)
|
||
g_hash_table_destroy (surface->device_events);
|
||
|
||
if (surface->devices_inside)
|
||
g_list_free (surface->devices_inside);
|
||
|
||
g_clear_object (&surface->display);
|
||
|
||
if (surface->opaque_region)
|
||
cairo_region_destroy (surface->opaque_region);
|
||
|
||
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);
|
||
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_STATE:
|
||
g_value_set_flags (value, surface->state);
|
||
break;
|
||
|
||
default:
|
||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||
break;
|
||
}
|
||
}
|
||
|
||
static gboolean
|
||
gdk_surface_is_subsurface (GdkSurface *surface)
|
||
{
|
||
return surface->surface_type == GDK_SURFACE_SUBSURFACE;
|
||
}
|
||
|
||
static GdkSurface *
|
||
gdk_surface_get_impl_surface (GdkSurface *surface)
|
||
{
|
||
return surface->impl_surface;
|
||
}
|
||
|
||
GdkSurface *
|
||
_gdk_surface_get_impl_surface (GdkSurface *surface)
|
||
{
|
||
return gdk_surface_get_impl_surface (surface);
|
||
}
|
||
|
||
static gboolean
|
||
gdk_surface_has_impl (GdkSurface *surface)
|
||
{
|
||
return surface->impl_surface == surface;
|
||
}
|
||
|
||
static gboolean
|
||
gdk_surface_is_toplevel (GdkSurface *surface)
|
||
{
|
||
return surface->parent == NULL;
|
||
}
|
||
|
||
gboolean
|
||
_gdk_surface_has_impl (GdkSurface *surface)
|
||
{
|
||
return gdk_surface_has_impl (surface);
|
||
}
|
||
|
||
static void
|
||
remove_child_area (GdkSurface *surface,
|
||
gboolean for_input,
|
||
cairo_region_t *region)
|
||
{
|
||
GdkSurface *child;
|
||
cairo_region_t *child_region;
|
||
GdkRectangle r;
|
||
GList *l;
|
||
|
||
for (l = surface->children; l; l = l->next)
|
||
{
|
||
child = l->data;
|
||
|
||
/* If region is empty already, no need to do
|
||
anything potentially costly */
|
||
if (cairo_region_is_empty (region))
|
||
break;
|
||
|
||
if (!GDK_SURFACE_IS_MAPPED (child) || child->input_only)
|
||
continue;
|
||
|
||
r.x = child->x;
|
||
r.y = child->y;
|
||
r.width = child->width;
|
||
r.height = child->height;
|
||
|
||
/* Bail early if child totally outside region */
|
||
if (cairo_region_contains_rectangle (region, &r) == CAIRO_REGION_OVERLAP_OUT)
|
||
continue;
|
||
|
||
child_region = cairo_region_create_rectangle (&r);
|
||
|
||
if (for_input)
|
||
{
|
||
if (child->input_shape)
|
||
cairo_region_intersect (child_region, child->input_shape);
|
||
}
|
||
|
||
cairo_region_subtract (region, child_region);
|
||
cairo_region_destroy (child_region);
|
||
}
|
||
}
|
||
|
||
static void
|
||
recompute_visible_regions_internal (GdkSurface *private,
|
||
gboolean recalculate_clip,
|
||
gboolean recalculate_children)
|
||
{
|
||
GList *l;
|
||
GdkSurface *child;
|
||
gboolean abs_pos_changed;
|
||
int old_abs_x, old_abs_y;
|
||
|
||
old_abs_x = private->abs_x;
|
||
old_abs_y = private->abs_y;
|
||
|
||
/* Update absolute position */
|
||
if ((gdk_surface_has_impl (private) &&
|
||
private->surface_type != GDK_SURFACE_SUBSURFACE) ||
|
||
(gdk_surface_is_toplevel (private) &&
|
||
private->surface_type == GDK_SURFACE_SUBSURFACE))
|
||
{
|
||
/* Native surfaces and toplevel subsurfaces start here */
|
||
private->abs_x = 0;
|
||
private->abs_y = 0;
|
||
}
|
||
else
|
||
{
|
||
private->abs_x = private->parent->abs_x + private->x;
|
||
private->abs_y = private->parent->abs_y + private->y;
|
||
}
|
||
|
||
abs_pos_changed =
|
||
private->abs_x != old_abs_x ||
|
||
private->abs_y != old_abs_y;
|
||
|
||
/* Update all children, recursively */
|
||
if ((abs_pos_changed || recalculate_children))
|
||
{
|
||
for (l = private->children; l; l = l->next)
|
||
{
|
||
child = l->data;
|
||
/* Only recalculate clip if the the clip region changed, otherwise
|
||
* there is no way the child clip region could change (its has not e.g. moved)
|
||
* Except if recalculate_children is set to force child updates
|
||
*/
|
||
recompute_visible_regions_internal (child,
|
||
recalculate_clip && recalculate_children,
|
||
FALSE);
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Call this when private has changed in one or more of these ways:
|
||
* size changed
|
||
* surface moved
|
||
* new surface added
|
||
* stacking order of surface changed
|
||
* child deleted
|
||
*
|
||
* It will recalculate abs_x/y and the clip regions
|
||
*
|
||
* Unless the surface didn’t change stacking order or size/pos, pass in TRUE
|
||
* for recalculate_siblings. (Mostly used internally for the recursion)
|
||
*
|
||
* If a child surface was removed (and you can’t use that child for
|
||
* recompute_visible_regions), pass in TRUE for recalculate_children on the parent
|
||
*/
|
||
static void
|
||
recompute_visible_regions (GdkSurface *private,
|
||
gboolean recalculate_children)
|
||
{
|
||
recompute_visible_regions_internal (private,
|
||
TRUE,
|
||
recalculate_children);
|
||
}
|
||
|
||
static void
|
||
gdk_surface_clear_old_updated_area (GdkSurface *surface)
|
||
{
|
||
int i;
|
||
|
||
for (i = 0; i < 2; i++)
|
||
{
|
||
if (surface->old_updated_area[i])
|
||
{
|
||
cairo_region_destroy (surface->old_updated_area[i]);
|
||
surface->old_updated_area[i] = NULL;
|
||
}
|
||
}
|
||
}
|
||
|
||
static void
|
||
gdk_surface_append_old_updated_area (GdkSurface *surface,
|
||
cairo_region_t *region)
|
||
{
|
||
if (surface->old_updated_area[1])
|
||
cairo_region_destroy (surface->old_updated_area[1]);
|
||
surface->old_updated_area[1] = surface->old_updated_area[0];
|
||
surface->old_updated_area[0] = cairo_region_reference (region);
|
||
}
|
||
|
||
void
|
||
_gdk_surface_update_size (GdkSurface *surface)
|
||
{
|
||
gdk_surface_clear_old_updated_area (surface);
|
||
recompute_visible_regions (surface, FALSE);
|
||
}
|
||
|
||
static GdkEventMask
|
||
get_native_device_event_mask (GdkSurface *private,
|
||
GdkDevice *device)
|
||
{
|
||
GdkEventMask event_mask;
|
||
|
||
if (device)
|
||
event_mask = GPOINTER_TO_INT (g_hash_table_lookup (private->device_events, device));
|
||
else
|
||
event_mask = private->event_mask;
|
||
|
||
if (private->surface_type == GDK_SURFACE_FOREIGN)
|
||
return event_mask;
|
||
else
|
||
{
|
||
GdkEventMask mask;
|
||
|
||
mask = private->event_mask;
|
||
|
||
/* We need thse for all native surfaces so we can
|
||
emulate events on children: */
|
||
mask |=
|
||
GDK_EXPOSURE_MASK |
|
||
GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
|
||
GDK_TOUCH_MASK |
|
||
GDK_POINTER_MOTION_MASK |
|
||
GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
|
||
GDK_SCROLL_MASK;
|
||
|
||
return mask;
|
||
}
|
||
}
|
||
|
||
static GdkEventMask
|
||
get_native_event_mask (GdkSurface *private)
|
||
{
|
||
return get_native_device_event_mask (private, NULL);
|
||
}
|
||
|
||
GdkSurface*
|
||
gdk_surface_new (GdkDisplay *display,
|
||
GdkSurface *parent,
|
||
GdkSurfaceAttr *attributes)
|
||
{
|
||
GdkSurface *surface;
|
||
gboolean native;
|
||
GdkEventMask event_mask;
|
||
|
||
g_return_val_if_fail (attributes != NULL, NULL);
|
||
|
||
if (parent != NULL && GDK_SURFACE_DESTROYED (parent))
|
||
{
|
||
g_warning ("gdk_surface_new(): parent is destroyed");
|
||
return NULL;
|
||
}
|
||
|
||
surface = _gdk_display_create_surface (display);
|
||
|
||
surface->parent = parent;
|
||
|
||
surface->accept_focus = TRUE;
|
||
surface->focus_on_map = TRUE;
|
||
|
||
surface->x = attributes->x;
|
||
surface->y = attributes->y;
|
||
surface->width = (attributes->width > 1) ? (attributes->width) : (1);
|
||
surface->height = (attributes->height > 1) ? (attributes->height) : (1);
|
||
surface->alpha = 255;
|
||
|
||
if (attributes->wclass == GDK_INPUT_ONLY)
|
||
{
|
||
/* Backwards compatiblity - we've always ignored
|
||
* attributes->surface_type for input-only surfaces
|
||
* before
|
||
*/
|
||
if (parent == NULL)
|
||
surface->surface_type = GDK_SURFACE_TEMP;
|
||
else
|
||
surface->surface_type = GDK_SURFACE_CHILD;
|
||
}
|
||
else
|
||
surface->surface_type = attributes->surface_type;
|
||
|
||
/* Sanity checks */
|
||
switch (surface->surface_type)
|
||
{
|
||
case GDK_SURFACE_TOPLEVEL:
|
||
case GDK_SURFACE_TEMP:
|
||
if (parent != NULL)
|
||
g_warning (G_STRLOC "Toplevel surfaces must be created without a parent");
|
||
break;
|
||
case GDK_SURFACE_SUBSURFACE:
|
||
#ifdef GDK_WINDOWING_WAYLAND
|
||
if (!GDK_IS_WAYLAND_DISPLAY (display))
|
||
{
|
||
g_warning (G_STRLOC "Subsurface surfaces can only be used on Wayland");
|
||
return NULL;
|
||
}
|
||
#endif
|
||
break;
|
||
case GDK_SURFACE_CHILD:
|
||
if (GDK_SURFACE_TYPE (parent) == GDK_SURFACE_FOREIGN)
|
||
{
|
||
g_warning (G_STRLOC "Child surfaces must not be created as children of\n"
|
||
"a surface of type GDK_SURFACE_FOREIGN");
|
||
return NULL;
|
||
}
|
||
break;
|
||
default:
|
||
g_warning (G_STRLOC "cannot make surfaces of type %d", surface->surface_type);
|
||
return NULL;
|
||
}
|
||
|
||
surface->event_mask = GDK_ALL_EVENTS_MASK;
|
||
|
||
if (attributes->wclass == GDK_INPUT_OUTPUT)
|
||
{
|
||
surface->input_only = FALSE;
|
||
}
|
||
else
|
||
{
|
||
surface->input_only = TRUE;
|
||
}
|
||
|
||
native = FALSE;
|
||
|
||
if (surface->parent != NULL)
|
||
surface->parent->children = g_list_concat (&surface->children_list_node, surface->parent->children);
|
||
else
|
||
{
|
||
GdkFrameClock *frame_clock = g_object_new (GDK_TYPE_FRAME_CLOCK_IDLE, NULL);
|
||
gdk_surface_set_frame_clock (surface, frame_clock);
|
||
g_object_unref (frame_clock);
|
||
|
||
native = TRUE; /* Always use native surfaces for toplevels */
|
||
}
|
||
|
||
#ifdef GDK_WINDOWING_WAYLAND
|
||
if (surface->surface_type == GDK_SURFACE_SUBSURFACE)
|
||
native = TRUE; /* Always use native windows for subsurfaces as well */
|
||
#endif
|
||
|
||
if (native)
|
||
{
|
||
event_mask = get_native_event_mask (surface);
|
||
|
||
/* Create the impl */
|
||
_gdk_display_create_surface_impl (display, surface, parent, event_mask, attributes);
|
||
surface->impl_surface = surface;
|
||
}
|
||
else
|
||
{
|
||
surface->impl_surface = g_object_ref (surface->parent->impl_surface);
|
||
surface->impl = g_object_ref (surface->impl_surface->impl);
|
||
}
|
||
|
||
recompute_visible_regions (surface, FALSE);
|
||
|
||
g_signal_connect (display, "seat-removed", G_CALLBACK (seat_removed_cb), surface);
|
||
|
||
return surface;
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_new_toplevel: (constructor)
|
||
* @display: the display to create the surface on
|
||
* @width: width of new surface
|
||
* @height: height of new surface
|
||
*
|
||
* Creates a new toplevel surface. The surface will be managed by the surface
|
||
* manager.
|
||
*
|
||
* Returns: (transfer full): the new #GdkSurface
|
||
**/
|
||
GdkSurface *
|
||
gdk_surface_new_toplevel (GdkDisplay *display,
|
||
gint width,
|
||
gint height)
|
||
{
|
||
GdkSurfaceAttr attr;
|
||
|
||
g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
|
||
|
||
attr.wclass = GDK_INPUT_OUTPUT;
|
||
attr.x = 0;
|
||
attr.y = 0;
|
||
attr.width = width;
|
||
attr.height = height;
|
||
attr.surface_type = GDK_SURFACE_TOPLEVEL;
|
||
|
||
return gdk_surface_new (display, NULL, &attr);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_new_popup: (constructor)
|
||
* @display: the display to create the surface on
|
||
* @position: position of the surface on screen
|
||
*
|
||
* Creates a new toplevel popup surface. The surface will bypass surface
|
||
* management.
|
||
*
|
||
* Returns: (transfer full): the new #GdkSurface
|
||
**/
|
||
GdkSurface *
|
||
gdk_surface_new_popup (GdkDisplay *display,
|
||
const GdkRectangle *position)
|
||
{
|
||
GdkSurfaceAttr attr;
|
||
|
||
g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
|
||
g_return_val_if_fail (position != NULL, NULL);
|
||
|
||
attr.wclass = GDK_INPUT_OUTPUT;
|
||
attr.x = position->x;
|
||
attr.y = position->y;
|
||
attr.width = position->width;
|
||
attr.height = position->height;
|
||
attr.surface_type = GDK_SURFACE_TEMP;
|
||
|
||
return gdk_surface_new (display, NULL, &attr);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_new_temp: (constructor)
|
||
* @display: the display to create the surface on
|
||
*
|
||
* Creates a new toplevel temporary surface. The surface will be
|
||
* situated off-screen and not handle output.
|
||
*
|
||
* You most likely do not want to use this function.
|
||
*
|
||
* Returns: (transfer full): the new #GdkSurface
|
||
**/
|
||
GdkSurface *
|
||
gdk_surface_new_temp (GdkDisplay *display)
|
||
{
|
||
GdkSurfaceAttr attr;
|
||
|
||
g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
|
||
|
||
attr.wclass = GDK_INPUT_ONLY;
|
||
attr.x = -100;
|
||
attr.y = -100;
|
||
attr.width = 10;
|
||
attr.height = 10;
|
||
attr.surface_type = GDK_SURFACE_TEMP;
|
||
|
||
return gdk_surface_new (display, NULL, &attr);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_new_child: (constructor)
|
||
* @parent: the parent surface
|
||
* @position: placement of the surface inside @parent
|
||
*
|
||
* Creates a new client-side child surface.
|
||
*
|
||
* Returns: (transfer full): the new #GdkSurface
|
||
**/
|
||
GdkSurface *
|
||
gdk_surface_new_child (GdkSurface *parent,
|
||
const GdkRectangle *position)
|
||
{
|
||
GdkSurfaceAttr attr;
|
||
|
||
g_return_val_if_fail (GDK_IS_SURFACE (parent), NULL);
|
||
|
||
attr.wclass = GDK_INPUT_OUTPUT;
|
||
attr.x = position->x;
|
||
attr.y = position->y;
|
||
attr.width = position->width;
|
||
attr.height = position->height;
|
||
attr.surface_type = GDK_SURFACE_CHILD;
|
||
|
||
return gdk_surface_new (gdk_surface_get_display (parent), parent, &attr);
|
||
}
|
||
|
||
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);
|
||
}
|
||
|
||
static void
|
||
gdk_surface_free_current_paint (GdkSurface *surface)
|
||
{
|
||
cairo_surface_destroy (surface->current_paint.surface);
|
||
surface->current_paint.surface = NULL;
|
||
|
||
cairo_region_destroy (surface->current_paint.region);
|
||
surface->current_paint.region = NULL;
|
||
|
||
surface->current_paint.surface_needs_composite = FALSE;
|
||
}
|
||
|
||
/**
|
||
* _gdk_surface_destroy_hierarchy:
|
||
* @surface: a #GdkSurface
|
||
* @recursing: If %TRUE, then this is being called because a parent
|
||
* was destroyed.
|
||
* @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 recursing,
|
||
gboolean recursing_native,
|
||
gboolean foreign_destroy)
|
||
{
|
||
GdkSurfaceImplClass *impl_class;
|
||
GdkSurface *temp_surface;
|
||
GdkDisplay *display;
|
||
GList *tmp;
|
||
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return;
|
||
|
||
display = gdk_surface_get_display (surface);
|
||
|
||
switch (surface->surface_type)
|
||
{
|
||
default:
|
||
g_assert_not_reached ();
|
||
break;
|
||
|
||
case GDK_SURFACE_TOPLEVEL:
|
||
case GDK_SURFACE_CHILD:
|
||
case GDK_SURFACE_TEMP:
|
||
case GDK_SURFACE_FOREIGN:
|
||
case GDK_SURFACE_SUBSURFACE:
|
||
if (surface->surface_type == GDK_SURFACE_FOREIGN && !foreign_destroy)
|
||
{
|
||
}
|
||
else
|
||
{
|
||
if (surface->parent)
|
||
{
|
||
if (surface->parent->children)
|
||
surface->parent->children = g_list_remove_link (surface->parent->children, &surface->children_list_node);
|
||
|
||
if (!recursing &&
|
||
GDK_SURFACE_IS_MAPPED (surface))
|
||
{
|
||
recompute_visible_regions (surface, FALSE);
|
||
gdk_surface_invalidate_in_parent (surface);
|
||
}
|
||
}
|
||
|
||
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)
|
||
{
|
||
g_object_run_dispose (G_OBJECT (surface->frame_clock));
|
||
gdk_surface_set_frame_clock (surface, NULL);
|
||
}
|
||
|
||
gdk_surface_free_current_paint (surface);
|
||
|
||
if (surface->surface_type == GDK_SURFACE_FOREIGN)
|
||
g_assert (surface->children == NULL);
|
||
else
|
||
{
|
||
tmp = surface->children;
|
||
surface->children = NULL;
|
||
/* No need to free children list, its all made up of in-struct nodes */
|
||
|
||
while (tmp)
|
||
{
|
||
temp_surface = tmp->data;
|
||
tmp = tmp->next;
|
||
|
||
if (temp_surface)
|
||
_gdk_surface_destroy_hierarchy (temp_surface,
|
||
TRUE,
|
||
recursing_native || gdk_surface_has_impl (surface),
|
||
foreign_destroy);
|
||
}
|
||
}
|
||
|
||
_gdk_surface_clear_update_area (surface);
|
||
|
||
impl_class = GDK_SURFACE_IMPL_GET_CLASS (surface->impl);
|
||
|
||
if (gdk_surface_has_impl (surface))
|
||
impl_class->destroy (surface, recursing_native, foreign_destroy);
|
||
else
|
||
{
|
||
/* hide to make sure we repaint and break grabs */
|
||
gdk_surface_hide (surface);
|
||
}
|
||
|
||
surface->state |= GDK_SURFACE_STATE_WITHDRAWN;
|
||
surface->parent = NULL;
|
||
surface->destroyed = TRUE;
|
||
|
||
surface_remove_from_pointer_info (surface, display);
|
||
|
||
g_object_notify_by_pspec (G_OBJECT (surface), properties[PROP_STATE]);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* _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, FALSE, FALSE, 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, FALSE, FALSE);
|
||
g_object_unref (surface);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_set_user_data:
|
||
* @surface: a #GdkSurface
|
||
* @user_data: (allow-none) (type GObject.Object): user data
|
||
*
|
||
* For most purposes this function is deprecated in favor of
|
||
* g_object_set_data(). However, for historical reasons GTK+ stores
|
||
* the #GtkWidget that owns a #GdkSurface as user data on the
|
||
* #GdkSurface. So, custom widget implementations should use
|
||
* this function for that. If GTK+ receives an event for a #GdkSurface,
|
||
* and the user data for the surface is non-%NULL, GTK+ will assume the
|
||
* user data is a #GtkWidget, and forward the event to that widget.
|
||
*
|
||
**/
|
||
void
|
||
gdk_surface_set_user_data (GdkSurface *surface,
|
||
gpointer user_data)
|
||
{
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
surface->user_data = user_data;
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_get_user_data:
|
||
* @surface: a #GdkSurface
|
||
* @data: (out): return location for user data
|
||
*
|
||
* Retrieves the user data for @surface, which is normally the widget
|
||
* that @surface belongs to. See gdk_surface_set_user_data().
|
||
*
|
||
**/
|
||
void
|
||
gdk_surface_get_user_data (GdkSurface *surface,
|
||
gpointer *data)
|
||
{
|
||
*data = surface->user_data;
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_get_surface_type:
|
||
* @surface: a #GdkSurface
|
||
*
|
||
* Gets the type of the surface. See #GdkSurfaceType.
|
||
*
|
||
* Returns: type of surface
|
||
**/
|
||
GdkSurfaceType
|
||
gdk_surface_get_surface_type (GdkSurface *surface)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_SURFACE (surface), (GdkSurfaceType) -1);
|
||
|
||
return GDK_SURFACE_TYPE (surface);
|
||
}
|
||
|
||
/**
|
||
* 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_has_native:
|
||
* @surface: a #GdkSurface
|
||
*
|
||
* Checks whether the surface has a native surface or not.
|
||
*
|
||
* Returns: %TRUE if the @surface has a native surface, %FALSE otherwise.
|
||
*/
|
||
gboolean
|
||
gdk_surface_has_native (GdkSurface *surface)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_SURFACE (surface), FALSE);
|
||
|
||
return surface->parent == NULL || surface->parent->impl != surface->impl;
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_get_position:
|
||
* @surface: a #GdkSurface
|
||
* @x: (out) (allow-none): X coordinate of surface
|
||
* @y: (out) (allow-none): Y coordinate of surface
|
||
*
|
||
* Obtains the position of the surface as reported in the
|
||
* most-recently-processed #GdkEventConfigure. Contrast with
|
||
* gdk_surface_get_geometry() which queries the X server for the
|
||
* current surface position, regardless of which events have been
|
||
* received or processed.
|
||
*
|
||
* The position coordinates are relative to the surface’s parent surface.
|
||
*
|
||
**/
|
||
void
|
||
gdk_surface_get_position (GdkSurface *surface,
|
||
gint *x,
|
||
gint *y)
|
||
{
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
if (x)
|
||
*x = surface->x;
|
||
if (y)
|
||
*y = surface->y;
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_get_parent:
|
||
* @surface: a #GdkSurface
|
||
*
|
||
* Obtains the parent of @surface, as known to GDK. Does not query the
|
||
* X server; thus this returns the parent as passed to gdk_surface_new(),
|
||
* not the actual parent. This should never matter unless you’re using
|
||
* Xlib calls mixed with GDK calls on the X11 platform. It may also
|
||
* matter for toplevel windows, because the window manager may choose
|
||
* to reparent them.
|
||
*
|
||
* Returns: (transfer none): parent of @surface
|
||
**/
|
||
GdkSurface*
|
||
gdk_surface_get_parent (GdkSurface *surface)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL);
|
||
|
||
if (gdk_surface_is_subsurface (surface))
|
||
return surface->transient_for;
|
||
else
|
||
return surface->parent;
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_get_toplevel:
|
||
* @surface: a #GdkSurface
|
||
*
|
||
* Gets the toplevel surface that’s an ancestor of @surface.
|
||
*
|
||
* Any surface type but %GDK_SURFACE_CHILD is considered a
|
||
* toplevel surface, as is a %GDK_SURFACE_CHILD surface that
|
||
* has a root surface as parent.
|
||
*
|
||
* Returns: (transfer none): the toplevel surface containing @surface
|
||
**/
|
||
GdkSurface *
|
||
gdk_surface_get_toplevel (GdkSurface *surface)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL);
|
||
|
||
while (surface->surface_type == GDK_SURFACE_CHILD ||
|
||
surface->surface_type == GDK_SURFACE_SUBSURFACE)
|
||
{
|
||
if (gdk_surface_is_toplevel (surface))
|
||
break;
|
||
surface = surface->parent;
|
||
}
|
||
|
||
return surface;
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_get_children:
|
||
* @surface: a #GdkSurface
|
||
*
|
||
* Gets the list of children of @surface known to GDK.
|
||
* This function only returns children created via GDK,
|
||
* so for example it’s useless when used with the root window;
|
||
* it only returns surfaces an application created itself.
|
||
*
|
||
* The returned list must be freed, but the elements in the
|
||
* list need not be.
|
||
*
|
||
* Returns: (transfer container) (element-type GdkSurface):
|
||
* list of child surfaces inside @surface
|
||
**/
|
||
GList*
|
||
gdk_surface_get_children (GdkSurface *surface)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL);
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return NULL;
|
||
|
||
return g_list_copy (surface->children);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_peek_children:
|
||
* @surface: a #GdkSurface
|
||
*
|
||
* Like gdk_surface_get_children(), but does not copy the list of
|
||
* children, so the list does not need to be freed.
|
||
*
|
||
* Returns: (transfer none) (element-type GdkSurface):
|
||
* a reference to the list of child surfaces in @surface
|
||
**/
|
||
GList *
|
||
gdk_surface_peek_children (GdkSurface *surface)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL);
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return NULL;
|
||
|
||
return surface->children;
|
||
}
|
||
|
||
|
||
/**
|
||
* gdk_surface_get_children_with_user_data:
|
||
* @surface: a #GdkSurface
|
||
* @user_data: user data to look for
|
||
*
|
||
* Gets the list of children of @surface known to GDK with a
|
||
* particular @user_data set on it.
|
||
*
|
||
* The returned list must be freed, but the elements in the
|
||
* list need not be.
|
||
*
|
||
* The list is returned in (relative) stacking order, i.e. the
|
||
* lowest surface is first.
|
||
*
|
||
* Returns: (transfer container) (element-type GdkSurface):
|
||
* list of child surfaces inside @surface
|
||
**/
|
||
GList *
|
||
gdk_surface_get_children_with_user_data (GdkSurface *surface,
|
||
gpointer user_data)
|
||
{
|
||
GdkSurface *child;
|
||
GList *res, *l;
|
||
|
||
g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL);
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return NULL;
|
||
|
||
res = NULL;
|
||
for (l = surface->children; l != NULL; l = l->next)
|
||
{
|
||
child = l->data;
|
||
|
||
if (child->user_data == user_data)
|
||
res = g_list_prepend (res, child);
|
||
}
|
||
|
||
return res;
|
||
}
|
||
|
||
|
||
/**
|
||
* gdk_surface_is_visible:
|
||
* @surface: a #GdkSurface
|
||
*
|
||
* Checks whether the surface has been mapped (with gdk_surface_show() or
|
||
* gdk_surface_show_unraised()).
|
||
*
|
||
* Returns: %TRUE if the surface is mapped
|
||
**/
|
||
gboolean
|
||
gdk_surface_is_visible (GdkSurface *surface)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_SURFACE (surface), FALSE);
|
||
|
||
return GDK_SURFACE_IS_MAPPED (surface);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_is_viewable:
|
||
* @surface: a #GdkSurface
|
||
*
|
||
* Check if the surface and all ancestors of the surface are
|
||
* mapped. (This is not necessarily "viewable" in the X sense, since
|
||
* we only check as far as we have GDK surface parents, not to the root
|
||
* surface.)
|
||
*
|
||
* Returns: %TRUE if the surface is viewable
|
||
**/
|
||
gboolean
|
||
gdk_surface_is_viewable (GdkSurface *surface)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_SURFACE (surface), FALSE);
|
||
|
||
if (surface->destroyed)
|
||
return FALSE;
|
||
|
||
return surface->viewable;
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_get_state:
|
||
* @surface: a #GdkSurface
|
||
*
|
||
* Gets the bitwise OR of the currently active surface state flags,
|
||
* from the #GdkSurfaceState enumeration.
|
||
*
|
||
* Returns: surface state bitfield
|
||
**/
|
||
GdkSurfaceState
|
||
gdk_surface_get_state (GdkSurface *surface)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_SURFACE (surface), FALSE);
|
||
|
||
return surface->state;
|
||
}
|
||
|
||
static cairo_content_t
|
||
gdk_surface_get_content (GdkSurface *surface)
|
||
{
|
||
cairo_surface_t *cairo_surface;
|
||
cairo_content_t content;
|
||
|
||
g_return_val_if_fail (GDK_IS_SURFACE (surface), 0);
|
||
|
||
cairo_surface = gdk_surface_ref_impl_surface (surface);
|
||
content = cairo_surface_get_content (cairo_surface);
|
||
cairo_surface_destroy (cairo_surface);
|
||
|
||
return content;
|
||
}
|
||
|
||
static cairo_surface_t *
|
||
gdk_surface_ref_impl_surface (GdkSurface *surface)
|
||
{
|
||
return GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->ref_cairo_surface (gdk_surface_get_impl_surface (surface));
|
||
}
|
||
|
||
GdkGLContext *
|
||
gdk_surface_get_paint_gl_context (GdkSurface *surface,
|
||
GError **error)
|
||
{
|
||
GError *internal_error = NULL;
|
||
|
||
if (GDK_DISPLAY_DEBUG_CHECK (surface->display, GL_DISABLE))
|
||
{
|
||
g_set_error_literal (error, GDK_GL_ERROR,
|
||
GDK_GL_ERROR_NOT_AVAILABLE,
|
||
_("GL support disabled via GDK_DEBUG"));
|
||
return NULL;
|
||
}
|
||
|
||
if (surface->impl_surface->gl_paint_context == NULL)
|
||
{
|
||
GdkSurfaceImplClass *impl_class = GDK_SURFACE_IMPL_GET_CLASS (surface->impl);
|
||
|
||
if (impl_class->create_gl_context == NULL)
|
||
{
|
||
g_set_error_literal (error, GDK_GL_ERROR, GDK_GL_ERROR_NOT_AVAILABLE,
|
||
_("The current backend does not support OpenGL"));
|
||
return NULL;
|
||
}
|
||
|
||
surface->impl_surface->gl_paint_context =
|
||
impl_class->create_gl_context (surface->impl_surface,
|
||
TRUE,
|
||
NULL,
|
||
&internal_error);
|
||
}
|
||
|
||
if (internal_error != NULL)
|
||
{
|
||
g_propagate_error (error, internal_error);
|
||
g_clear_object (&(surface->impl_surface->gl_paint_context));
|
||
return NULL;
|
||
}
|
||
|
||
gdk_gl_context_realize (surface->impl_surface->gl_paint_context, &internal_error);
|
||
if (internal_error != NULL)
|
||
{
|
||
g_propagate_error (error, internal_error);
|
||
g_clear_object (&(surface->impl_surface->gl_paint_context));
|
||
return NULL;
|
||
}
|
||
|
||
return surface->impl_surface->gl_paint_context;
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_create_gl_context:
|
||
* @surface: a #GdkSurface
|
||
* @error: return location for an error
|
||
*
|
||
* Creates a new #GdkGLContext matching the
|
||
* framebuffer format to the visual of 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 gdk_gl_context_make_current() or gdk_gl_context_realize().
|
||
*
|
||
* Returns: (transfer full): the newly created #GdkGLContext, or
|
||
* %NULL on error
|
||
**/
|
||
GdkGLContext *
|
||
gdk_surface_create_gl_context (GdkSurface *surface,
|
||
GError **error)
|
||
{
|
||
GdkGLContext *paint_context;
|
||
|
||
g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL);
|
||
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
||
|
||
paint_context = gdk_surface_get_paint_gl_context (surface, error);
|
||
if (paint_context == NULL)
|
||
return NULL;
|
||
|
||
return GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->create_gl_context (surface->impl_surface,
|
||
FALSE,
|
||
paint_context,
|
||
error);
|
||
}
|
||
|
||
/**
|
||
* 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_DEBUG_CHECK (surface->display, VULKAN_DISABLE))
|
||
{
|
||
g_set_error_literal (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE,
|
||
_("Vulkan support disabled via GDK_DEBUG"));
|
||
return NULL;
|
||
}
|
||
|
||
display = gdk_surface_get_display (surface);
|
||
|
||
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);
|
||
}
|
||
|
||
static void
|
||
gdk_surface_begin_paint_internal (GdkSurface *surface,
|
||
const cairo_region_t *region)
|
||
{
|
||
GdkRectangle clip_box;
|
||
GdkSurfaceImplClass *impl_class;
|
||
double sx, sy;
|
||
gboolean needs_surface;
|
||
cairo_content_t surface_content;
|
||
|
||
if (surface->current_paint.surface != NULL)
|
||
{
|
||
g_warning ("A paint operation on the surface is alredy in progress. "
|
||
"This is not allowed.");
|
||
return;
|
||
}
|
||
|
||
impl_class = GDK_SURFACE_IMPL_GET_CLASS (surface->impl);
|
||
|
||
needs_surface = TRUE;
|
||
if (impl_class->begin_paint)
|
||
needs_surface = impl_class->begin_paint (surface);
|
||
|
||
surface->current_paint.region = cairo_region_copy (region);
|
||
cairo_region_get_extents (surface->current_paint.region, &clip_box);
|
||
|
||
surface_content = gdk_surface_get_content (surface);
|
||
|
||
if (needs_surface)
|
||
{
|
||
surface->current_paint.surface = gdk_surface_create_similar_surface (surface,
|
||
surface_content,
|
||
MAX (clip_box.width, 1),
|
||
MAX (clip_box.height, 1));
|
||
sx = sy = 1;
|
||
cairo_surface_get_device_scale (surface->current_paint.surface, &sx, &sy);
|
||
cairo_surface_set_device_offset (surface->current_paint.surface, -clip_box.x*sx, -clip_box.y*sy);
|
||
gdk_cairo_surface_mark_as_direct (surface->current_paint.surface, surface);
|
||
|
||
surface->current_paint.surface_needs_composite = TRUE;
|
||
}
|
||
else
|
||
{
|
||
surface->current_paint.surface = gdk_surface_ref_impl_surface (surface);
|
||
surface->current_paint.surface_needs_composite = FALSE;
|
||
}
|
||
|
||
if (!cairo_region_is_empty (surface->current_paint.region))
|
||
gdk_surface_clear_backing_region (surface);
|
||
}
|
||
|
||
static void
|
||
gdk_surface_end_paint_internal (GdkSurface *surface)
|
||
{
|
||
GdkSurfaceImplClass *impl_class;
|
||
cairo_t *cr;
|
||
|
||
if (surface->current_paint.surface == NULL)
|
||
{
|
||
g_warning (G_STRLOC": no preceding call to gdk_surface_begin_draw_frame(), see documentation");
|
||
return;
|
||
}
|
||
|
||
impl_class = GDK_SURFACE_IMPL_GET_CLASS (surface->impl);
|
||
|
||
if (impl_class->end_paint)
|
||
impl_class->end_paint (surface);
|
||
|
||
if (surface->current_paint.surface_needs_composite)
|
||
{
|
||
cairo_surface_t *cairo_surface;
|
||
|
||
cairo_surface = gdk_surface_ref_impl_surface (surface);
|
||
cr = cairo_create (cairo_surface);
|
||
|
||
cairo_set_source_surface (cr, surface->current_paint.surface, 0, 0);
|
||
gdk_cairo_region (cr, surface->current_paint.region);
|
||
cairo_clip (cr);
|
||
|
||
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
|
||
cairo_paint (cr);
|
||
|
||
cairo_destroy (cr);
|
||
|
||
cairo_surface_flush (cairo_surface);
|
||
cairo_surface_destroy (cairo_surface);
|
||
}
|
||
|
||
gdk_surface_free_current_paint (surface);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_begin_draw_frame:
|
||
* @surface: a #GdkSurface
|
||
* @context: (allow-none): the context used to draw the frame
|
||
* @region: a Cairo region
|
||
*
|
||
* Indicates that you are beginning the process of redrawing @region
|
||
* on @surface, and provides you with a #GdkDrawingContext.
|
||
*
|
||
* If @surface is a top level #GdkSurface, backed by a native surface
|
||
* implementation, a backing store (offscreen buffer) large enough to
|
||
* contain @region will be created. The backing store will be initialized
|
||
* with the background color or background surface for @surface. Then, all
|
||
* drawing operations performed on @surface will be diverted to the
|
||
* backing store. When you call gdk_surface_end_frame(), the contents of
|
||
* the backing store will be copied to @surface, making it visible
|
||
* on screen. Only the part of @surface contained in @region will be
|
||
* modified; that is, drawing operations are clipped to @region.
|
||
*
|
||
* The net result of all this is to remove flicker, because the user
|
||
* sees the finished product appear all at once when you call
|
||
* gdk_surface_end_draw_frame(). If you draw to @surface directly without
|
||
* calling gdk_surface_begin_draw_frame(), the user may see flicker
|
||
* as individual drawing operations are performed in sequence.
|
||
*
|
||
* When using GTK+, the widget system automatically places calls to
|
||
* gdk_surface_begin_draw_frame() and gdk_surface_end_draw_frame() around
|
||
* emissions of the `GtkWidget::draw` signal. That is, if you’re
|
||
* drawing the contents of the widget yourself, you can assume that the
|
||
* widget has a cleared background, is already set as the clip region,
|
||
* and already has a backing store. Therefore in most cases, application
|
||
* code in GTK does not need to call gdk_surface_begin_draw_frame()
|
||
* explicitly.
|
||
*
|
||
* Returns: (transfer none): a #GdkDrawingContext context that should be
|
||
* used to draw the contents of the surface; the returned context is owned
|
||
* by GDK.
|
||
*/
|
||
GdkDrawingContext *
|
||
gdk_surface_begin_draw_frame (GdkSurface *surface,
|
||
GdkDrawContext *draw_context,
|
||
const cairo_region_t *region)
|
||
{
|
||
GdkDrawingContext *context;
|
||
cairo_region_t *real_region;
|
||
|
||
g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL);
|
||
g_return_val_if_fail (gdk_surface_has_native (surface), NULL);
|
||
g_return_val_if_fail (gdk_surface_is_toplevel (surface), NULL);
|
||
g_return_val_if_fail (region != NULL, NULL);
|
||
if (draw_context != NULL)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_DRAW_CONTEXT (draw_context), NULL);
|
||
g_return_val_if_fail (gdk_draw_context_get_surface (draw_context) == surface, NULL);
|
||
}
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return NULL;
|
||
|
||
if (surface->drawing_context != NULL)
|
||
{
|
||
g_critical ("The surface %p already has a drawing context. You cannot "
|
||
"call gdk_surface_begin_draw_frame() without calling "
|
||
"gdk_surface_end_draw_frame() first.", surface);
|
||
return NULL;
|
||
}
|
||
|
||
real_region = cairo_region_copy (region);
|
||
|
||
if (draw_context)
|
||
gdk_draw_context_begin_frame (draw_context, real_region);
|
||
else
|
||
gdk_surface_begin_paint_internal (surface, real_region);
|
||
|
||
context = g_object_new (GDK_TYPE_DRAWING_CONTEXT,
|
||
"surface", surface,
|
||
"paint-context", draw_context,
|
||
"clip", real_region,
|
||
NULL);
|
||
|
||
/* Do not take a reference, to avoid creating cycles */
|
||
surface->drawing_context = context;
|
||
|
||
cairo_region_destroy (real_region);
|
||
|
||
return context;
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_end_draw_frame:
|
||
* @surface: a #GdkSurface
|
||
* @context: the #GdkDrawingContext created by gdk_surface_begin_draw_frame()
|
||
*
|
||
* Indicates that the drawing of the contents of @surface started with
|
||
* gdk_surface_begin_frame() has been completed.
|
||
*
|
||
* This function will take care of destroying the #GdkDrawingContext.
|
||
*
|
||
* It is an error to call this function without a matching
|
||
* gdk_surface_begin_frame() first.
|
||
*/
|
||
void
|
||
gdk_surface_end_draw_frame (GdkSurface *surface,
|
||
GdkDrawingContext *context)
|
||
{
|
||
GdkDrawContext *paint_context;
|
||
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
g_return_if_fail (GDK_IS_DRAWING_CONTEXT (context));
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return;
|
||
|
||
if (surface->drawing_context == NULL)
|
||
{
|
||
g_critical ("The surface %p has no drawing context. You must call "
|
||
"gdk_surface_begin_draw_frame() before calling "
|
||
"gdk_surface_end_draw_frame().", surface);
|
||
return;
|
||
}
|
||
g_return_if_fail (surface->drawing_context == context);
|
||
|
||
paint_context = gdk_drawing_context_get_paint_context (context);
|
||
if (paint_context)
|
||
{
|
||
cairo_region_t *clip = gdk_drawing_context_get_clip (context);
|
||
|
||
gdk_draw_context_end_frame (paint_context,
|
||
clip,
|
||
surface->active_update_area);
|
||
|
||
cairo_region_destroy (clip);
|
||
}
|
||
else
|
||
{
|
||
gdk_surface_end_paint_internal (surface);
|
||
}
|
||
|
||
surface->drawing_context = NULL;
|
||
|
||
g_object_unref (context);
|
||
}
|
||
|
||
/*< private >
|
||
* gdk_surface_get_current_paint_region:
|
||
* @surface: a #GdkSurface
|
||
*
|
||
* Retrieves a copy of the current paint region.
|
||
*
|
||
* Returns: (transfer full): a Cairo region
|
||
*/
|
||
cairo_region_t *
|
||
gdk_surface_get_current_paint_region (GdkSurface *surface)
|
||
{
|
||
cairo_region_t *region;
|
||
|
||
if (surface->impl_surface->current_paint.region != NULL)
|
||
{
|
||
region = cairo_region_copy (surface->impl_surface->current_paint.region);
|
||
cairo_region_translate (region, -surface->abs_x, -surface->abs_y);
|
||
}
|
||
else
|
||
{
|
||
region = cairo_region_create_rectangle (&(cairo_rectangle_int_t) { 0, 0, surface->width, surface->height });
|
||
}
|
||
|
||
return region;
|
||
}
|
||
|
||
/*< private >
|
||
* gdk_surface_get_drawing_context:
|
||
* @surface: a #GdkSurface
|
||
*
|
||
* Retrieves the #GdkDrawingContext associated to @surface by
|
||
* gdk_surface_begin_draw_frame().
|
||
*
|
||
* Returns: (transfer none) (nullable): a #GdkDrawingContext, if any is set
|
||
*/
|
||
GdkDrawingContext *
|
||
gdk_surface_get_drawing_context (GdkSurface *surface)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL);
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return NULL;
|
||
|
||
return surface->drawing_context;
|
||
}
|
||
|
||
static void
|
||
gdk_surface_clear_backing_region (GdkSurface *surface)
|
||
{
|
||
cairo_t *cr;
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return;
|
||
|
||
cr = cairo_create (surface->current_paint.surface);
|
||
|
||
cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
|
||
gdk_cairo_region (cr, surface->current_paint.region);
|
||
cairo_fill (cr);
|
||
|
||
cairo_destroy (cr);
|
||
}
|
||
|
||
/* This returns either the current working surface on the paint stack
|
||
* or the actual impl surface of the surface. This should not be used
|
||
* from very many places: be careful! */
|
||
static cairo_surface_t *
|
||
ref_surface_surface (GdkSurface *surface)
|
||
{
|
||
if (surface->impl_surface->current_paint.surface)
|
||
return cairo_surface_reference (surface->impl_surface->current_paint.surface);
|
||
else
|
||
return gdk_surface_ref_impl_surface (surface);
|
||
}
|
||
|
||
/* This is used in places like gdk_cairo_set_source_surface and
|
||
* other places to take "screenshots" of surfaces. Thus, we allow
|
||
* it to be used outside of a begin_paint / end_paint. */
|
||
cairo_surface_t *
|
||
_gdk_surface_ref_cairo_surface (GdkSurface *surface)
|
||
{
|
||
cairo_surface_t *cairo_surface;
|
||
|
||
g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL);
|
||
|
||
cairo_surface = ref_surface_surface (surface);
|
||
|
||
if (gdk_surface_has_impl (surface))
|
||
{
|
||
return cairo_surface;
|
||
}
|
||
else
|
||
{
|
||
cairo_surface_t *subsurface;
|
||
subsurface = cairo_surface_create_for_rectangle (cairo_surface,
|
||
surface->abs_x,
|
||
surface->abs_y,
|
||
surface->width,
|
||
surface->height);
|
||
cairo_surface_destroy (cairo_surface);
|
||
return subsurface;
|
||
}
|
||
}
|
||
|
||
/* Code for dirty-region queueing
|
||
*/
|
||
static GSList *update_surfaces = NULL;
|
||
|
||
static inline gboolean
|
||
gdk_surface_is_ancestor (GdkSurface *surface,
|
||
GdkSurface *ancestor)
|
||
{
|
||
while (surface)
|
||
{
|
||
GdkSurface *parent = surface->parent;
|
||
|
||
if (parent == ancestor)
|
||
return TRUE;
|
||
|
||
surface = parent;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
static void
|
||
gdk_surface_add_update_surface (GdkSurface *surface)
|
||
{
|
||
GSList *tmp;
|
||
GSList *prev = NULL;
|
||
gboolean has_ancestor_in_list = FALSE;
|
||
|
||
/* 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;
|
||
|
||
for (tmp = update_surfaces; tmp; tmp = tmp->next)
|
||
{
|
||
GdkSurface *parent = surface->parent;
|
||
|
||
/* check if tmp is an ancestor of "surface"; if it is, set a
|
||
* flag indicating that all following surfaces are either
|
||
* children of "surface" or from a differen hierarchy
|
||
*/
|
||
if (!has_ancestor_in_list && gdk_surface_is_ancestor (surface, tmp->data))
|
||
has_ancestor_in_list = TRUE;
|
||
|
||
/* insert in reverse stacking order when adding around siblings,
|
||
* so processing updates properly paints over lower stacked surfaces
|
||
*/
|
||
if (parent == GDK_SURFACE (tmp->data)->parent)
|
||
{
|
||
if (parent != NULL)
|
||
{
|
||
gint index = g_list_index (parent->children, surface);
|
||
for (; tmp && parent == GDK_SURFACE (tmp->data)->parent; tmp = tmp->next)
|
||
{
|
||
gint sibling_index = g_list_index (parent->children, tmp->data);
|
||
if (index > sibling_index)
|
||
break;
|
||
prev = tmp;
|
||
}
|
||
}
|
||
/* here, tmp got advanced past all lower stacked siblings */
|
||
tmp = g_slist_prepend (tmp, g_object_ref (surface));
|
||
if (prev)
|
||
prev->next = tmp;
|
||
else
|
||
update_surfaces = tmp;
|
||
return;
|
||
}
|
||
|
||
/* if "surface" has an ancestor in the list and tmp is one of
|
||
* "surface's" children, insert "surface" before tmp
|
||
*/
|
||
if (has_ancestor_in_list && gdk_surface_is_ancestor (tmp->data, surface))
|
||
{
|
||
tmp = g_slist_prepend (tmp, g_object_ref (surface));
|
||
|
||
if (prev)
|
||
prev->next = tmp;
|
||
else
|
||
update_surfaces = tmp;
|
||
return;
|
||
}
|
||
|
||
/* if we're at the end of the list and had an ancestor it it,
|
||
* append to the list
|
||
*/
|
||
if (! tmp->next && has_ancestor_in_list)
|
||
{
|
||
tmp = g_slist_append (tmp, g_object_ref (surface));
|
||
return;
|
||
}
|
||
|
||
prev = tmp;
|
||
}
|
||
|
||
/* if all above checks failed ("surface" is from a different
|
||
* hierarchy than what is already in the list) or the list is
|
||
* empty, prepend
|
||
*/
|
||
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)
|
||
{
|
||
GdkSurface *toplevel;
|
||
|
||
toplevel = gdk_surface_get_toplevel (surface);
|
||
|
||
return toplevel->update_and_descendants_freeze_count > 0;
|
||
}
|
||
|
||
static void
|
||
gdk_surface_schedule_update (GdkSurface *surface)
|
||
{
|
||
GdkFrameClock *frame_clock;
|
||
|
||
if (surface &&
|
||
(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_recurse (GdkSurface *surface,
|
||
cairo_region_t *expose_region)
|
||
{
|
||
GdkEvent *event;
|
||
|
||
if (surface->destroyed)
|
||
return;
|
||
|
||
/* Paint the surface before the children, clipped to the surface region */
|
||
|
||
event = gdk_event_new (GDK_EXPOSE);
|
||
event->any.surface = g_object_ref (surface);
|
||
event->any.send_event = FALSE;
|
||
event->expose.region = cairo_region_reference (expose_region);
|
||
|
||
_gdk_event_emit (event);
|
||
gdk_event_free (event);
|
||
}
|
||
|
||
/* Process and remove any invalid area on the native surface by creating
|
||
* expose events for the surface and all non-native descendants.
|
||
*/
|
||
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_viewable (surface))
|
||
{
|
||
cairo_region_t *expose_region;
|
||
|
||
expose_region = cairo_region_copy (surface->active_update_area);
|
||
|
||
gdk_surface_process_updates_recurse (surface, expose_region);
|
||
|
||
gdk_surface_append_old_updated_area (surface, surface->active_update_area);
|
||
|
||
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_paint_on_clock (GdkFrameClock *clock,
|
||
void *data)
|
||
{
|
||
GdkSurface *surface;
|
||
|
||
surface = GDK_SURFACE (data);
|
||
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
g_return_if_fail (surface->impl_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)
|
||
{
|
||
gdk_surface_process_updates_internal (surface);
|
||
gdk_surface_remove_update_surface (surface);
|
||
}
|
||
|
||
g_object_unref (surface);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_invalidate_rect:
|
||
* @surface: a #GdkSurface
|
||
* @rect: (allow-none): rectangle to invalidate or %NULL to invalidate the whole
|
||
* surface
|
||
*
|
||
* A convenience wrapper around gdk_surface_invalidate_region() which
|
||
* invalidates a rectangular region. See
|
||
* gdk_surface_invalidate_region() for details.
|
||
**/
|
||
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_DESTROYED (surface))
|
||
return;
|
||
|
||
if (surface->input_only || !surface->viewable)
|
||
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_expose:
|
||
* @surface: a #GdkSurface
|
||
*
|
||
* Forces an expose event for @surface to be scheduled.
|
||
*
|
||
* If the invalid area of @surface is empty, an expose event will
|
||
* still be emitted. Its invalid region will be empty.
|
||
*
|
||
* This function is useful for implementations that track invalid
|
||
* regions on their own.
|
||
**/
|
||
void
|
||
gdk_surface_queue_expose (GdkSurface *surface)
|
||
{
|
||
cairo_region_t *region;
|
||
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
while (!gdk_surface_has_impl (surface))
|
||
surface = surface->parent;
|
||
|
||
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.
|
||
*
|
||
* The @invalidate_children parameter controls whether the region of
|
||
* each child surface that intersects @region will also be invalidated.
|
||
* If %FALSE, then the update area for child surfaces will remain
|
||
* unaffected.
|
||
**/
|
||
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_DESTROYED (surface))
|
||
return;
|
||
|
||
if (surface->input_only ||
|
||
!surface->viewable ||
|
||
cairo_region_is_empty (region))
|
||
return;
|
||
|
||
r.x = 0;
|
||
r.y = 0;
|
||
|
||
visible_region = cairo_region_copy (region);
|
||
|
||
while (surface != NULL &&
|
||
!cairo_region_is_empty (visible_region))
|
||
{
|
||
r.width = surface->width;
|
||
r.height = surface->height;
|
||
cairo_region_intersect_rectangle (visible_region, &r);
|
||
|
||
if (gdk_surface_has_impl (surface))
|
||
{
|
||
impl_surface_add_update_area (surface, visible_region);
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
cairo_region_translate (visible_region,
|
||
surface->x, surface->y);
|
||
surface = surface->parent;
|
||
}
|
||
}
|
||
|
||
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)
|
||
{
|
||
GdkSurface *impl_surface;
|
||
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
impl_surface = gdk_surface_get_impl_surface (surface);
|
||
impl_surface->update_freeze_count++;
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_thaw_updates:
|
||
* @surface: a #GdkSurface
|
||
*
|
||
* Thaws a surface frozen with gdk_surface_freeze_updates().
|
||
**/
|
||
void
|
||
gdk_surface_thaw_updates (GdkSurface *surface)
|
||
{
|
||
GdkSurface *impl_surface;
|
||
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
impl_surface = gdk_surface_get_impl_surface (surface);
|
||
|
||
g_return_if_fail (impl_surface->update_freeze_count > 0);
|
||
|
||
if (--impl_surface->update_freeze_count == 0)
|
||
gdk_surface_schedule_update (GDK_SURFACE (impl_surface));
|
||
}
|
||
|
||
void
|
||
gdk_surface_freeze_toplevel_updates (GdkSurface *surface)
|
||
{
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
g_return_if_fail (surface->surface_type != GDK_SURFACE_CHILD);
|
||
|
||
surface->update_and_descendants_freeze_count++;
|
||
_gdk_frame_clock_freeze (gdk_surface_get_frame_clock (surface));
|
||
}
|
||
|
||
void
|
||
gdk_surface_thaw_toplevel_updates (GdkSurface *surface)
|
||
{
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
g_return_if_fail (surface->surface_type != GDK_SURFACE_CHILD);
|
||
g_return_if_fail (surface->update_and_descendants_freeze_count > 0);
|
||
|
||
surface->update_and_descendants_freeze_count--;
|
||
_gdk_frame_clock_thaw (gdk_surface_get_frame_clock (surface));
|
||
|
||
gdk_surface_schedule_update (surface);
|
||
}
|
||
|
||
/**
|
||
* 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,
|
||
gint width,
|
||
gint height,
|
||
gint *new_width,
|
||
gint *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
|
||
*/
|
||
gint min_width = 0;
|
||
gint min_height = 0;
|
||
gint base_width = 0;
|
||
gint base_height = 0;
|
||
gint xinc = 1;
|
||
gint yinc = 1;
|
||
gint max_width = G_MAXINT;
|
||
gint max_height = G_MAXINT;
|
||
|
||
#define FLOOR(value, base) ( ((gint) ((value) / (base))) * (base) )
|
||
|
||
if ((flags & GDK_HINT_BASE_SIZE) && (flags & GDK_HINT_MIN_SIZE))
|
||
{
|
||
base_width = geometry->base_width;
|
||
base_height = geometry->base_height;
|
||
min_width = geometry->min_width;
|
||
min_height = geometry->min_height;
|
||
}
|
||
else if (flags & GDK_HINT_BASE_SIZE)
|
||
{
|
||
base_width = geometry->base_width;
|
||
base_height = geometry->base_height;
|
||
min_width = geometry->base_width;
|
||
min_height = geometry->base_height;
|
||
}
|
||
else if (flags & GDK_HINT_MIN_SIZE)
|
||
{
|
||
base_width = geometry->min_width;
|
||
base_height = geometry->min_height;
|
||
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;
|
||
}
|
||
|
||
if (flags & GDK_HINT_RESIZE_INC)
|
||
{
|
||
xinc = MAX (xinc, geometry->width_inc);
|
||
yinc = MAX (yinc, geometry->height_inc);
|
||
}
|
||
|
||
/* clamp width and height to min and max values
|
||
*/
|
||
width = CLAMP (width, min_width, max_width);
|
||
height = CLAMP (height, min_height, max_height);
|
||
|
||
/* shrink to base + N * inc
|
||
*/
|
||
width = base_width + FLOOR (width - base_width, xinc);
|
||
height = base_height + FLOOR (height - base_height, yinc);
|
||
|
||
/* constrain aspect ratio, according to:
|
||
*
|
||
* width
|
||
* min_aspect <= -------- <= max_aspect
|
||
* height
|
||
*/
|
||
|
||
if (flags & GDK_HINT_ASPECT &&
|
||
geometry->min_aspect > 0 &&
|
||
geometry->max_aspect > 0)
|
||
{
|
||
gint delta;
|
||
|
||
if (geometry->min_aspect * height > width)
|
||
{
|
||
delta = FLOOR (height - width / geometry->min_aspect, yinc);
|
||
if (height - delta >= min_height)
|
||
height -= delta;
|
||
else
|
||
{
|
||
delta = FLOOR (height * geometry->min_aspect - width, xinc);
|
||
if (width + delta <= max_width)
|
||
width += delta;
|
||
}
|
||
}
|
||
|
||
if (geometry->max_aspect * height < width)
|
||
{
|
||
delta = FLOOR (width - height * geometry->max_aspect, xinc);
|
||
if (width - delta >= min_width)
|
||
width -= delta;
|
||
else
|
||
{
|
||
delta = FLOOR (width / geometry->max_aspect - height, yinc);
|
||
if (height + delta <= max_height)
|
||
height += delta;
|
||
}
|
||
}
|
||
}
|
||
|
||
#undef FLOOR
|
||
|
||
*new_width = width;
|
||
*new_height = height;
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_get_device_position_double:
|
||
* @surface: a #GdkSurface.
|
||
* @device: pointer #GdkDevice to query to.
|
||
* @x: (out) (allow-none): return location for the X coordinate of @device, or %NULL.
|
||
* @y: (out) (allow-none): return location for the Y coordinate of @device, or %NULL.
|
||
* @mask: (out) (allow-none): return location for the modifier mask, or %NULL.
|
||
*
|
||
* Obtains the current device position in doubles and modifier state.
|
||
* The position is given in coordinates relative to the upper left
|
||
* corner of @surface.
|
||
*
|
||
* Returns: (nullable) (transfer none): The surface underneath @device
|
||
* (as with gdk_device_get_surface_at_position()), or %NULL if the
|
||
* surface is not known to GDK.
|
||
**/
|
||
GdkSurface *
|
||
gdk_surface_get_device_position_double (GdkSurface *surface,
|
||
GdkDevice *device,
|
||
double *x,
|
||
double *y,
|
||
GdkModifierType *mask)
|
||
{
|
||
gdouble tmp_x, tmp_y;
|
||
GdkModifierType tmp_mask;
|
||
gboolean normal_child;
|
||
|
||
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);
|
||
|
||
tmp_x = tmp_y = 0;
|
||
tmp_mask = 0;
|
||
normal_child = GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->get_device_state (surface,
|
||
device,
|
||
&tmp_x, &tmp_y,
|
||
&tmp_mask);
|
||
/* We got the coords on the impl, convert to the surface */
|
||
tmp_x -= surface->abs_x;
|
||
tmp_y -= surface->abs_y;
|
||
|
||
if (x)
|
||
*x = tmp_x;
|
||
if (y)
|
||
*y = tmp_y;
|
||
if (mask)
|
||
*mask = tmp_mask;
|
||
|
||
if (normal_child)
|
||
return _gdk_surface_find_child_at (surface, tmp_x, tmp_y);
|
||
return NULL;
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_get_device_position:
|
||
* @surface: a #GdkSurface.
|
||
* @device: pointer #GdkDevice to query to.
|
||
* @x: (out) (allow-none): return location for the X coordinate of @device, or %NULL.
|
||
* @y: (out) (allow-none): return location for the Y coordinate of @device, or %NULL.
|
||
* @mask: (out) (allow-none): return location for the modifier mask, or %NULL.
|
||
*
|
||
* Obtains the current device position and modifier state.
|
||
* The position is given in coordinates relative to the upper left
|
||
* corner of @surface.
|
||
*
|
||
* Use gdk_surface_get_device_position_double() if you need subpixel precision.
|
||
*
|
||
* Returns: (nullable) (transfer none): The surface underneath @device
|
||
* (as with gdk_device_get_surface_at_position()), or %NULL if the
|
||
* surface is not known to GDK.
|
||
**/
|
||
GdkSurface *
|
||
gdk_surface_get_device_position (GdkSurface *surface,
|
||
GdkDevice *device,
|
||
gint *x,
|
||
gint *y,
|
||
GdkModifierType *mask)
|
||
{
|
||
gdouble tmp_x, tmp_y;
|
||
|
||
surface = gdk_surface_get_device_position_double (surface, device,
|
||
&tmp_x, &tmp_y, mask);
|
||
if (x)
|
||
*x = round (tmp_x);
|
||
if (y)
|
||
*y = round (tmp_y);
|
||
|
||
return surface;
|
||
}
|
||
|
||
static gboolean
|
||
gdk_surface_raise_internal (GdkSurface *surface)
|
||
{
|
||
GdkSurface *parent = surface->parent;
|
||
GdkSurfaceImplClass *impl_class;
|
||
gboolean did_raise = FALSE;
|
||
|
||
if (parent && parent->children->data != surface)
|
||
{
|
||
parent->children = g_list_remove_link (parent->children, &surface->children_list_node);
|
||
parent->children = g_list_concat (&surface->children_list_node, parent->children);
|
||
did_raise = TRUE;
|
||
}
|
||
|
||
impl_class = GDK_SURFACE_IMPL_GET_CLASS (surface->impl);
|
||
|
||
/* Just do native raise for toplevels */
|
||
if (gdk_surface_has_impl (surface))
|
||
impl_class->raise (surface);
|
||
|
||
return did_raise;
|
||
}
|
||
|
||
/* Returns TRUE If the native surface was mapped or unmapped */
|
||
static gboolean
|
||
set_viewable (GdkSurface *w,
|
||
gboolean val)
|
||
{
|
||
GdkSurface *child;
|
||
GList *l;
|
||
|
||
if (w->viewable == val)
|
||
return FALSE;
|
||
|
||
w->viewable = val;
|
||
|
||
if (val)
|
||
recompute_visible_regions (w, FALSE);
|
||
|
||
for (l = w->children; l != NULL; l = l->next)
|
||
{
|
||
child = l->data;
|
||
|
||
if (GDK_SURFACE_IS_MAPPED (child))
|
||
set_viewable (child, val);
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
/* Returns TRUE If the native surface was mapped or unmapped */
|
||
gboolean
|
||
_gdk_surface_update_viewable (GdkSurface *surface)
|
||
{
|
||
gboolean viewable;
|
||
|
||
if (surface->surface_type == GDK_SURFACE_FOREIGN)
|
||
viewable = TRUE;
|
||
else if (gdk_surface_is_toplevel (surface) ||
|
||
surface->parent->viewable)
|
||
viewable = GDK_SURFACE_IS_MAPPED (surface);
|
||
else
|
||
viewable = FALSE;
|
||
|
||
return set_viewable (surface, viewable);
|
||
}
|
||
|
||
static void
|
||
gdk_surface_show_internal (GdkSurface *surface, gboolean raise)
|
||
{
|
||
GdkSurfaceImplClass *impl_class;
|
||
gboolean was_mapped, was_viewable;
|
||
gboolean did_show, did_raise = FALSE;
|
||
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
if (surface->destroyed)
|
||
return;
|
||
|
||
was_mapped = GDK_SURFACE_IS_MAPPED (surface);
|
||
was_viewable = surface->viewable;
|
||
|
||
if (raise)
|
||
{
|
||
/* Keep children in (reverse) stacking order */
|
||
did_raise = gdk_surface_raise_internal (surface);
|
||
}
|
||
|
||
if (gdk_surface_has_impl (surface))
|
||
{
|
||
if (!was_mapped)
|
||
gdk_synthesize_surface_state (surface,
|
||
GDK_SURFACE_STATE_WITHDRAWN,
|
||
0);
|
||
}
|
||
else
|
||
{
|
||
surface->state = 0;
|
||
g_object_notify_by_pspec (G_OBJECT (surface), properties[PROP_STATE]);
|
||
}
|
||
|
||
did_show = _gdk_surface_update_viewable (surface);
|
||
|
||
/* If it was already viewable the backend show op won't be called, call it
|
||
again to ensure things happen right if the mapped tracking was not right
|
||
for e.g. a foreign surface.
|
||
Dunno if this is strictly needed but its what happened pre-csw.
|
||
Also show if not done by gdk_surface_update_viewable. */
|
||
if (gdk_surface_has_impl (surface) && (was_viewable || !did_show))
|
||
{
|
||
impl_class = GDK_SURFACE_IMPL_GET_CLASS (surface->impl);
|
||
impl_class->show (surface, !did_show ? was_mapped : TRUE);
|
||
}
|
||
|
||
if (!was_mapped && !gdk_surface_has_impl (surface))
|
||
{
|
||
if (surface->event_mask & GDK_STRUCTURE_MASK)
|
||
_gdk_make_event (surface, GDK_MAP, NULL, FALSE);
|
||
|
||
if (surface->parent && surface->parent->event_mask & GDK_SUBSTRUCTURE_MASK)
|
||
_gdk_make_event (surface, GDK_MAP, NULL, FALSE);
|
||
}
|
||
|
||
if (!was_mapped || did_raise)
|
||
{
|
||
recompute_visible_regions (surface, FALSE);
|
||
|
||
if (gdk_surface_is_viewable (surface))
|
||
gdk_surface_invalidate_rect (surface, NULL);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_show_unraised:
|
||
* @surface: a #GdkSurface
|
||
*
|
||
* Shows a #GdkSurface onscreen, but does not modify its stacking
|
||
* order. In contrast, gdk_surface_show() will raise the surface
|
||
* to the top of the surface stack.
|
||
*
|
||
* On the X11 platform, in Xlib terms, this function calls
|
||
* XMapWindow() (it also updates some internal GDK state, which means
|
||
* that you can’t really use XMapWindow() directly on a GDK surface).
|
||
*/
|
||
void
|
||
gdk_surface_show_unraised (GdkSurface *surface)
|
||
{
|
||
gdk_surface_show_internal (surface, FALSE);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_raise:
|
||
* @surface: a #GdkSurface
|
||
*
|
||
* Raises @surface to the top of the Z-order (stacking order), so that
|
||
* other surfaces with the same parent surface appear below @surface.
|
||
* This is true whether or not the surfaces are visible.
|
||
*
|
||
* If @surface is a toplevel, the surface manager may choose to deny the
|
||
* request to move the surface in the Z-order, gdk_surface_raise() only
|
||
* requests the restack, does not guarantee it.
|
||
*/
|
||
void
|
||
gdk_surface_raise (GdkSurface *surface)
|
||
{
|
||
gboolean did_raise;
|
||
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
if (surface->destroyed)
|
||
return;
|
||
|
||
/* Keep children in (reverse) stacking order */
|
||
did_raise = gdk_surface_raise_internal (surface);
|
||
|
||
if (did_raise &&
|
||
!gdk_surface_is_toplevel (surface) &&
|
||
gdk_surface_is_viewable (surface) &&
|
||
!surface->input_only)
|
||
gdk_surface_invalidate_rect (surface, NULL);
|
||
}
|
||
|
||
static void
|
||
gdk_surface_lower_internal (GdkSurface *surface)
|
||
{
|
||
GdkSurface *parent = surface->parent;
|
||
GdkSurfaceImplClass *impl_class;
|
||
|
||
if (parent)
|
||
{
|
||
parent->children = g_list_remove_link (parent->children, &surface->children_list_node);
|
||
parent->children = g_list_concat (parent->children, &surface->children_list_node);
|
||
}
|
||
|
||
impl_class = GDK_SURFACE_IMPL_GET_CLASS (surface->impl);
|
||
|
||
/* Just do native lower for toplevels */
|
||
if (gdk_surface_has_impl (surface))
|
||
impl_class->lower (surface);
|
||
}
|
||
|
||
static void
|
||
gdk_surface_invalidate_in_parent (GdkSurface *private)
|
||
{
|
||
GdkRectangle r, child;
|
||
|
||
if (gdk_surface_is_toplevel (private))
|
||
return;
|
||
|
||
/* get the visible rectangle of the parent */
|
||
r.x = r.y = 0;
|
||
r.width = private->parent->width;
|
||
r.height = private->parent->height;
|
||
|
||
child.x = private->x;
|
||
child.y = private->y;
|
||
child.width = private->width;
|
||
child.height = private->height;
|
||
gdk_rectangle_intersect (&r, &child, &r);
|
||
|
||
gdk_surface_invalidate_rect (private->parent, &r);
|
||
}
|
||
|
||
|
||
/**
|
||
* gdk_surface_lower:
|
||
* @surface: a #GdkSurface
|
||
*
|
||
* Lowers @surface to the bottom of the Z-order (stacking order), so that
|
||
* other surfaces with the same parent surface appear above @surface.
|
||
* This is true whether or not the other surfaces are visible.
|
||
*
|
||
* If @surface is a toplevel, the window manager may choose to deny the
|
||
* request to move the surface in the Z-order, gdk_surface_lower() only
|
||
* requests the restack, does not guarantee it.
|
||
*
|
||
* Note that gdk_surface_show() raises the surface again, so don’t call this
|
||
* function before gdk_surface_show(). (Try gdk_surface_show_unraised().)
|
||
*/
|
||
void
|
||
gdk_surface_lower (GdkSurface *surface)
|
||
{
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
if (surface->destroyed)
|
||
return;
|
||
|
||
/* Keep children in (reverse) stacking order */
|
||
gdk_surface_lower_internal (surface);
|
||
|
||
gdk_surface_invalidate_in_parent (surface);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_restack:
|
||
* @surface: a #GdkSurface
|
||
* @sibling: (allow-none): a #GdkSurface that is a sibling of @surface, or %NULL
|
||
* @above: a boolean
|
||
*
|
||
* Changes the position of @surface in the Z-order (stacking order), so that
|
||
* it is above @sibling (if @above is %TRUE) or below @sibling (if @above is
|
||
* %FALSE).
|
||
*
|
||
* If @sibling is %NULL, then this either raises (if @above is %TRUE) or
|
||
* lowers the surface.
|
||
*
|
||
* If @surface is a toplevel, the window manager may choose to deny the
|
||
* request to move the surface in the Z-order, gdk_surface_restack() only
|
||
* requests the restack, does not guarantee it.
|
||
*/
|
||
void
|
||
gdk_surface_restack (GdkSurface *surface,
|
||
GdkSurface *sibling,
|
||
gboolean above)
|
||
{
|
||
GdkSurfaceImplClass *impl_class;
|
||
GdkSurface *parent;
|
||
GList *sibling_link;
|
||
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
g_return_if_fail (sibling == NULL || GDK_IS_SURFACE (sibling));
|
||
|
||
if (surface->destroyed)
|
||
return;
|
||
|
||
if (sibling == NULL)
|
||
{
|
||
if (above)
|
||
gdk_surface_raise (surface);
|
||
else
|
||
gdk_surface_lower (surface);
|
||
return;
|
||
}
|
||
|
||
if (gdk_surface_is_toplevel (surface))
|
||
{
|
||
g_return_if_fail (gdk_surface_is_toplevel (sibling));
|
||
impl_class = GDK_SURFACE_IMPL_GET_CLASS (surface->impl);
|
||
impl_class->restack_toplevel (surface, sibling, above);
|
||
return;
|
||
}
|
||
|
||
parent = surface->parent;
|
||
if (parent)
|
||
{
|
||
sibling_link = g_list_find (parent->children, sibling);
|
||
g_return_if_fail (sibling_link != NULL);
|
||
if (sibling_link == NULL)
|
||
return;
|
||
|
||
parent->children = g_list_remove_link (parent->children, &surface->children_list_node);
|
||
if (above)
|
||
parent->children = list_insert_link_before (parent->children,
|
||
sibling_link,
|
||
&surface->children_list_node);
|
||
else
|
||
parent->children = list_insert_link_before (parent->children,
|
||
sibling_link->next,
|
||
&surface->children_list_node);
|
||
}
|
||
|
||
gdk_surface_invalidate_in_parent (surface);
|
||
}
|
||
|
||
|
||
/**
|
||
* gdk_surface_show:
|
||
* @surface: a #GdkSurface
|
||
*
|
||
* Like gdk_surface_show_unraised(), but also raises the surface to the
|
||
* top of the surface stack (moves the surface to the front of the
|
||
* Z-order).
|
||
*
|
||
* This function maps a surface so it’s visible onscreen. Its opposite
|
||
* is gdk_surface_hide().
|
||
*
|
||
* When implementing a #GtkWidget, you should call this function on the widget's
|
||
* #GdkSurface as part of the “map” method.
|
||
*/
|
||
void
|
||
gdk_surface_show (GdkSurface *surface)
|
||
{
|
||
gdk_surface_show_internal (surface, TRUE);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_hide:
|
||
* @surface: a #GdkSurface
|
||
*
|
||
* 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().
|
||
*/
|
||
void
|
||
gdk_surface_hide (GdkSurface *surface)
|
||
{
|
||
GdkSurfaceImplClass *impl_class;
|
||
gboolean was_mapped, did_hide;
|
||
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
if (surface->destroyed)
|
||
return;
|
||
|
||
was_mapped = GDK_SURFACE_IS_MAPPED (surface);
|
||
|
||
if (gdk_surface_has_impl (surface))
|
||
{
|
||
|
||
if (GDK_SURFACE_IS_MAPPED (surface))
|
||
gdk_synthesize_surface_state (surface,
|
||
0,
|
||
GDK_SURFACE_STATE_WITHDRAWN);
|
||
}
|
||
else if (was_mapped)
|
||
{
|
||
surface->state = GDK_SURFACE_STATE_WITHDRAWN;
|
||
g_object_notify_by_pspec (G_OBJECT (surface), properties[PROP_STATE]);
|
||
}
|
||
|
||
if (was_mapped)
|
||
{
|
||
GdkDisplay *display;
|
||
GdkSeat *seat;
|
||
GList *devices, *d;
|
||
|
||
/* May need to break grabs on children */
|
||
display = gdk_surface_get_display (surface);
|
||
seat = gdk_display_get_default_seat (display);
|
||
|
||
devices = gdk_seat_get_slaves (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));
|
||
|
||
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);
|
||
}
|
||
|
||
did_hide = _gdk_surface_update_viewable (surface);
|
||
|
||
/* Hide foreign surface as those are not handled by update_viewable. */
|
||
if (gdk_surface_has_impl (surface) && (!did_hide))
|
||
{
|
||
impl_class = GDK_SURFACE_IMPL_GET_CLASS (surface->impl);
|
||
impl_class->hide (surface);
|
||
}
|
||
|
||
gdk_surface_clear_old_updated_area (surface);
|
||
recompute_visible_regions (surface, FALSE);
|
||
|
||
if (was_mapped && !gdk_surface_has_impl (surface))
|
||
{
|
||
if (surface->event_mask & GDK_STRUCTURE_MASK)
|
||
_gdk_make_event (surface, GDK_UNMAP, NULL, FALSE);
|
||
|
||
if (surface->parent && surface->parent->event_mask & GDK_SUBSTRUCTURE_MASK)
|
||
_gdk_make_event (surface, GDK_UNMAP, NULL, FALSE);
|
||
}
|
||
|
||
/* Invalidate the rect */
|
||
if (was_mapped)
|
||
gdk_surface_invalidate_in_parent (surface);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_withdraw:
|
||
* @surface: a toplevel #GdkSurface
|
||
*
|
||
* Withdraws a surface (unmaps it and asks the surface manager to forget about it).
|
||
* This function is not really useful as gdk_surface_hide() automatically
|
||
* withdraws toplevel surfaces before hiding them.
|
||
**/
|
||
void
|
||
gdk_surface_withdraw (GdkSurface *surface)
|
||
{
|
||
GdkSurfaceImplClass *impl_class;
|
||
gboolean was_mapped;
|
||
GdkGLContext *current_context;
|
||
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
if (surface->destroyed)
|
||
return;
|
||
|
||
was_mapped = GDK_SURFACE_IS_MAPPED (surface);
|
||
|
||
if (gdk_surface_has_impl (surface))
|
||
{
|
||
impl_class = GDK_SURFACE_IMPL_GET_CLASS (surface->impl);
|
||
impl_class->withdraw (surface);
|
||
|
||
if (was_mapped)
|
||
{
|
||
if (surface->event_mask & GDK_STRUCTURE_MASK)
|
||
_gdk_make_event (surface, GDK_UNMAP, NULL, FALSE);
|
||
|
||
if (surface->parent && surface->parent->event_mask & GDK_SUBSTRUCTURE_MASK)
|
||
_gdk_make_event (surface, GDK_UNMAP, NULL, FALSE);
|
||
}
|
||
|
||
current_context = gdk_gl_context_get_current ();
|
||
if (current_context != NULL && gdk_gl_context_get_surface (current_context) == surface)
|
||
gdk_gl_context_clear_current ();
|
||
|
||
recompute_visible_regions (surface, FALSE);
|
||
gdk_surface_clear_old_updated_area (surface);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_set_events:
|
||
* @surface: a #GdkSurface
|
||
* @event_mask: event mask for @surface
|
||
*
|
||
* The event mask for a surface determines which events will be reported
|
||
* for that surface from all master input devices. For example, an event mask
|
||
* including #GDK_BUTTON_PRESS_MASK means the surface should report button
|
||
* press events. The event mask is the bitwise OR of values from the
|
||
* #GdkEventMask enumeration.
|
||
*
|
||
* See the [input handling overview][event-masks] for details.
|
||
**/
|
||
void
|
||
gdk_surface_set_events (GdkSurface *surface,
|
||
GdkEventMask event_mask)
|
||
{
|
||
GdkSurfaceImplClass *impl_class;
|
||
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
if (surface->destroyed)
|
||
return;
|
||
|
||
surface->event_mask = event_mask;
|
||
|
||
if (gdk_surface_has_impl (surface))
|
||
{
|
||
impl_class = GDK_SURFACE_IMPL_GET_CLASS (surface->impl);
|
||
impl_class->set_events (surface,
|
||
get_native_event_mask (surface));
|
||
}
|
||
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_get_events:
|
||
* @surface: a #GdkSurface
|
||
*
|
||
* Gets the event mask for @surface for all master input devices. See
|
||
* gdk_surface_set_events().
|
||
*
|
||
* Returns: event mask for @surface
|
||
**/
|
||
GdkEventMask
|
||
gdk_surface_get_events (GdkSurface *surface)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_SURFACE (surface), 0);
|
||
|
||
if (surface->destroyed)
|
||
return 0;
|
||
|
||
return surface->event_mask;
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_set_device_events:
|
||
* @surface: a #GdkSurface
|
||
* @device: #GdkDevice to enable events for.
|
||
* @event_mask: event mask for @surface
|
||
*
|
||
* Sets the event mask for a given device (Normally a floating device, not
|
||
* attached to any visible pointer) to @surface. For example, an event mask
|
||
* including #GDK_BUTTON_PRESS_MASK means the surface should report button
|
||
* press events. The event mask is the bitwise OR of values from the
|
||
* #GdkEventMask enumeration.
|
||
*
|
||
* See the [input handling overview][event-masks] for details.
|
||
**/
|
||
void
|
||
gdk_surface_set_device_events (GdkSurface *surface,
|
||
GdkDevice *device,
|
||
GdkEventMask event_mask)
|
||
{
|
||
GdkEventMask device_mask;
|
||
GdkSurface *native;
|
||
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
g_return_if_fail (GDK_IS_DEVICE (device));
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return;
|
||
|
||
if (G_UNLIKELY (!surface->device_events))
|
||
surface->device_events = g_hash_table_new (NULL, NULL);
|
||
|
||
if (event_mask == 0)
|
||
{
|
||
/* FIXME: unsetting events on a master device
|
||
* would restore surface->event_mask
|
||
*/
|
||
g_hash_table_remove (surface->device_events, device);
|
||
}
|
||
else
|
||
g_hash_table_insert (surface->device_events, device,
|
||
GINT_TO_POINTER (event_mask));
|
||
|
||
native = gdk_surface_get_toplevel (surface);
|
||
|
||
device_mask = get_native_device_event_mask (surface, device);
|
||
GDK_DEVICE_GET_CLASS (device)->select_surface_events (device, native, device_mask);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_get_device_events:
|
||
* @surface: a #GdkSurface.
|
||
* @device: a #GdkDevice.
|
||
*
|
||
* Returns the event mask for @surface corresponding to an specific device.
|
||
*
|
||
* Returns: device event mask for @surface
|
||
**/
|
||
GdkEventMask
|
||
gdk_surface_get_device_events (GdkSurface *surface,
|
||
GdkDevice *device)
|
||
{
|
||
GdkEventMask mask;
|
||
|
||
g_return_val_if_fail (GDK_IS_SURFACE (surface), 0);
|
||
g_return_val_if_fail (GDK_IS_DEVICE (device), 0);
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return 0;
|
||
|
||
if (!surface->device_events)
|
||
return 0;
|
||
|
||
mask = GPOINTER_TO_INT (g_hash_table_lookup (surface->device_events, device));
|
||
|
||
/* FIXME: device could be controlled by surface->event_mask */
|
||
|
||
return mask;
|
||
}
|
||
|
||
static void
|
||
gdk_surface_move_resize_toplevel (GdkSurface *surface,
|
||
gboolean with_move,
|
||
gint x,
|
||
gint y,
|
||
gint width,
|
||
gint height)
|
||
{
|
||
GdkSurfaceImplClass *impl_class;
|
||
gboolean is_resize;
|
||
|
||
is_resize = (width != -1) || (height != -1);
|
||
|
||
impl_class = GDK_SURFACE_IMPL_GET_CLASS (surface->impl);
|
||
impl_class->move_resize (surface, with_move, x, y, width, height);
|
||
|
||
/* Avoid recomputing for pure toplevel moves, for performance reasons */
|
||
if (is_resize)
|
||
recompute_visible_regions (surface, FALSE);
|
||
}
|
||
|
||
|
||
static void
|
||
gdk_surface_move_resize_internal (GdkSurface *surface,
|
||
gboolean with_move,
|
||
gint x,
|
||
gint y,
|
||
gint width,
|
||
gint height)
|
||
{
|
||
cairo_region_t *old_region, *new_region;
|
||
gboolean expose;
|
||
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
if (surface->destroyed)
|
||
return;
|
||
|
||
if (gdk_surface_is_toplevel (surface))
|
||
{
|
||
gdk_surface_move_resize_toplevel (surface, with_move, x, y, width, height);
|
||
return;
|
||
}
|
||
|
||
if (width == 0)
|
||
width = 1;
|
||
if (height == 0)
|
||
height = 1;
|
||
|
||
/* Bail early if no change */
|
||
if (surface->width == width &&
|
||
surface->height == height &&
|
||
(!with_move ||
|
||
(surface->x == x &&
|
||
surface->y == y)))
|
||
return;
|
||
|
||
/* Handle child surfaces */
|
||
|
||
expose = FALSE;
|
||
old_region = NULL;
|
||
|
||
if (gdk_surface_is_viewable (surface) &&
|
||
!surface->input_only)
|
||
{
|
||
GdkRectangle r;
|
||
|
||
expose = TRUE;
|
||
|
||
r.x = surface->x;
|
||
r.y = surface->y;
|
||
r.width = surface->width;
|
||
r.height = surface->height;
|
||
|
||
old_region = cairo_region_create_rectangle (&r);
|
||
}
|
||
|
||
/* Set the new position and size */
|
||
if (with_move)
|
||
{
|
||
surface->x = x;
|
||
surface->y = y;
|
||
}
|
||
if (!(width < 0 && height < 0))
|
||
{
|
||
surface->width = width;
|
||
surface->height = height;
|
||
}
|
||
|
||
recompute_visible_regions (surface, FALSE);
|
||
|
||
if (expose)
|
||
{
|
||
GdkRectangle r;
|
||
|
||
r.x = surface->x;
|
||
r.y = surface->y;
|
||
r.width = surface->width;
|
||
r.height = surface->height;
|
||
|
||
new_region = cairo_region_create_rectangle (&r);
|
||
|
||
cairo_region_union (new_region, old_region);
|
||
|
||
gdk_surface_invalidate_region (surface->parent, new_region);
|
||
|
||
cairo_region_destroy (old_region);
|
||
cairo_region_destroy (new_region);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/**
|
||
* gdk_surface_move:
|
||
* @surface: a #GdkSurface
|
||
* @x: X coordinate relative to surface’s parent
|
||
* @y: Y coordinate relative to surface’s parent
|
||
*
|
||
* Repositions a surface relative to its parent surface.
|
||
* For toplevel surfaces, window managers may ignore or modify the move;
|
||
* you should probably use gtk_window_move() on a #GtkWindow widget
|
||
* anyway, instead of using GDK functions. For child surfaces,
|
||
* the move will reliably succeed.
|
||
*
|
||
* If you’re also planning to resize the surface, use gdk_surface_move_resize()
|
||
* to both move and resize simultaneously, for a nicer visual effect.
|
||
**/
|
||
void
|
||
gdk_surface_move (GdkSurface *surface,
|
||
gint x,
|
||
gint y)
|
||
{
|
||
gdk_surface_move_resize_internal (surface, TRUE, x, y, -1, -1);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_resize:
|
||
* @surface: a #GdkSurface
|
||
* @width: new width of the surface
|
||
* @height: new height of the surface
|
||
*
|
||
* Resizes @surface; for toplevel surfaces, asks the window manager to resize
|
||
* the surface. The window manager may not allow the resize. When using GTK+,
|
||
* use gtk_window_resize() instead of this low-level GDK function.
|
||
*
|
||
* Surfaces may not be resized below 1x1.
|
||
*
|
||
* If you’re also planning to move the surface, use gdk_surface_move_resize()
|
||
* to both move and resize simultaneously, for a nicer visual effect.
|
||
**/
|
||
void
|
||
gdk_surface_resize (GdkSurface *surface,
|
||
gint width,
|
||
gint height)
|
||
{
|
||
gdk_surface_move_resize_internal (surface, FALSE, 0, 0, width, height);
|
||
}
|
||
|
||
|
||
/**
|
||
* gdk_surface_move_resize:
|
||
* @surface: a #GdkSurface
|
||
* @x: new X position relative to surface’s parent
|
||
* @y: new Y position relative to surface’s parent
|
||
* @width: new width
|
||
* @height: new height
|
||
*
|
||
* Equivalent to calling gdk_surface_move() and gdk_surface_resize(),
|
||
* except that both operations are performed at once, avoiding strange
|
||
* visual effects. (i.e. the user may be able to see the surface first
|
||
* move, then resize, if you don’t use gdk_surface_move_resize().)
|
||
**/
|
||
void
|
||
gdk_surface_move_resize (GdkSurface *surface,
|
||
gint x,
|
||
gint y,
|
||
gint width,
|
||
gint height)
|
||
{
|
||
gdk_surface_move_resize_internal (surface, TRUE, x, y, width, height);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_move_to_rect:
|
||
* @surface: the #GdkSurface to move
|
||
* @rect: (not nullable): the destination #GdkRectangle to align @surface with
|
||
* @rect_anchor: the point on @rect to align with @surface's anchor point
|
||
* @surface_anchor: the point on @surface to align with @rect's anchor point
|
||
* @anchor_hints: positioning hints to use when limited on space
|
||
* @rect_anchor_dx: horizontal offset to shift @surface, i.e. @rect's anchor
|
||
* point
|
||
* @rect_anchor_dy: vertical offset to shift @surface, i.e. @rect's anchor point
|
||
*
|
||
* Moves @surface to @rect, aligning their anchor points.
|
||
*
|
||
* @rect is relative to the top-left corner of the surface that @surface is
|
||
* transient for. @rect_anchor and @surface_anchor determine anchor points on
|
||
* @rect and @surface to pin together. @rect's anchor point can optionally be
|
||
* offset by @rect_anchor_dx and @rect_anchor_dy, which is equivalent to
|
||
* offsetting the position of @surface.
|
||
*
|
||
* @anchor_hints determines how @surface will be moved if the anchor points cause
|
||
* it to move off-screen. For example, %GDK_ANCHOR_FLIP_X will replace
|
||
* %GDK_GRAVITY_NORTH_WEST with %GDK_GRAVITY_NORTH_EAST and vice versa if
|
||
* @surface extends beyond the left or right edges of the monitor.
|
||
*
|
||
* Connect to the #GdkSurface::moved-to-rect signal to find out how it was
|
||
* actually positioned.
|
||
*
|
||
* Stability: Private
|
||
*/
|
||
void
|
||
gdk_surface_move_to_rect (GdkSurface *surface,
|
||
const GdkRectangle *rect,
|
||
GdkGravity rect_anchor,
|
||
GdkGravity surface_anchor,
|
||
GdkAnchorHints anchor_hints,
|
||
gint rect_anchor_dx,
|
||
gint rect_anchor_dy)
|
||
{
|
||
GdkSurfaceImplClass *impl_class;
|
||
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
g_return_if_fail (surface->transient_for);
|
||
g_return_if_fail (rect);
|
||
|
||
impl_class = GDK_SURFACE_IMPL_GET_CLASS (surface->impl);
|
||
impl_class->move_to_rect (surface,
|
||
rect,
|
||
rect_anchor,
|
||
surface_anchor,
|
||
anchor_hints,
|
||
rect_anchor_dx,
|
||
rect_anchor_dy);
|
||
}
|
||
|
||
static void
|
||
gdk_surface_set_cursor_internal (GdkSurface *surface,
|
||
GdkDevice *device,
|
||
GdkCursor *cursor)
|
||
{
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return;
|
||
|
||
g_assert (gdk_surface_get_display (surface) == gdk_device_get_display (device));
|
||
|
||
if (surface->surface_type == GDK_SURFACE_FOREIGN)
|
||
GDK_DEVICE_GET_CLASS (device)->set_surface_cursor (device, surface, cursor);
|
||
else
|
||
{
|
||
GdkPointerSurfaceInfo *pointer_info;
|
||
GdkDisplay *display;
|
||
|
||
display = gdk_surface_get_display (surface);
|
||
pointer_info = _gdk_display_get_pointer_info (display, device);
|
||
|
||
if (_gdk_surface_event_parent_of (surface, pointer_info->surface_under_pointer))
|
||
update_cursor (display, device);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_get_cursor:
|
||
* @surface: a #GdkSurface
|
||
*
|
||
* Retrieves a #GdkCursor pointer for the cursor currently set on the
|
||
* specified #GdkSurface, or %NULL. 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.
|
||
*
|
||
* Returns: (nullable) (transfer none): a #GdkCursor, or %NULL. The
|
||
* returned object is owned by the #GdkSurface and should not be
|
||
* unreferenced directly. Use gdk_surface_set_cursor() to unset the
|
||
* cursor of the surface
|
||
*/
|
||
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: (allow-none): a cursor
|
||
*
|
||
* Sets the default mouse pointer for a #GdkSurface.
|
||
*
|
||
* Note that @cursor must be for the same display as @surface.
|
||
*
|
||
* Use gdk_cursor_new_for_display() or gdk_cursor_new_from_texture() to
|
||
* create the cursor. To make the cursor invisible, use %GDK_BLANK_CURSOR.
|
||
* Passing %NULL for the @cursor argument to gdk_surface_set_cursor() means
|
||
* that @surface will use the cursor of its parent surface. Most surfaces
|
||
* should use this default.
|
||
*/
|
||
void
|
||
gdk_surface_set_cursor (GdkSurface *surface,
|
||
GdkCursor *cursor)
|
||
{
|
||
GdkDisplay *display;
|
||
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
display = gdk_surface_get_display (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 (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_slaves (s->data, GDK_SEAT_CAPABILITY_TABLET_STYLUS);
|
||
for (d = devices; d; d = d->next)
|
||
{
|
||
device = gdk_device_get_associated_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 master, pointer #GdkDevice.
|
||
*
|
||
* Retrieves a #GdkCursor pointer for the @device currently set on the
|
||
* specified #GdkSurface, or %NULL. 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.
|
||
*
|
||
* Returns: (nullable) (transfer none): a #GdkCursor, or %NULL. The
|
||
* returned object is owned by the #GdkSurface and should not be
|
||
* unreferenced directly. Use gdk_surface_set_cursor() to unset the
|
||
* cursor of the surface
|
||
**/
|
||
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);
|
||
g_return_val_if_fail (gdk_device_get_device_type (device) == GDK_DEVICE_TYPE_MASTER, NULL);
|
||
|
||
return g_hash_table_lookup (surface->device_cursor, device);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_set_device_cursor:
|
||
* @surface: a #GdkSurface
|
||
* @device: a master, pointer #GdkDevice
|
||
* @cursor: a #GdkCursor
|
||
*
|
||
* Sets a specific #GdkCursor for a given device when it gets inside @surface.
|
||
* Use gdk_cursor_new_for_display() or gdk_cursor_new_from_texture() to create
|
||
* the cursor. To make the cursor invisible, use %GDK_BLANK_CURSOR. Passing
|
||
* %NULL for the @cursor argument to gdk_surface_set_cursor() means that
|
||
* @surface will use the cursor of its parent surface. Most surfaces should
|
||
* use this default.
|
||
**/
|
||
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);
|
||
g_return_if_fail (gdk_device_get_device_type (device) == GDK_DEVICE_TYPE_MASTER);
|
||
|
||
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) (allow-none): return location for X coordinate of surface (relative to its parent)
|
||
* @y: (out) (allow-none): return location for Y coordinate of surface (relative to its parent)
|
||
* @width: (out) (allow-none): return location for width of surface
|
||
* @height: (out) (allow-none): return location for height of surface
|
||
*
|
||
* Any of the return location arguments to this function may be %NULL,
|
||
* if you aren’t interested in getting the value of that field.
|
||
*
|
||
* 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. gdk_surface_get_position() in contrast gets the
|
||
* position from the most recent configure event.
|
||
*
|
||
* Note: If @surface is not a toplevel, it is much better
|
||
* to call gdk_surface_get_position(), gdk_surface_get_width() and
|
||
* 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,
|
||
gint *x,
|
||
gint *y,
|
||
gint *width,
|
||
gint *height)
|
||
{
|
||
GdkSurface *parent;
|
||
GdkSurfaceImplClass *impl_class;
|
||
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
if (!GDK_SURFACE_DESTROYED (surface))
|
||
{
|
||
if (gdk_surface_has_impl (surface))
|
||
{
|
||
impl_class = GDK_SURFACE_IMPL_GET_CLASS (surface->impl);
|
||
impl_class->get_geometry (surface, x, y,
|
||
width, height);
|
||
/* This reports the position wrt to the native parent, we need to convert
|
||
it to be relative to the client side parent */
|
||
parent = surface->parent;
|
||
if (parent && !gdk_surface_has_impl (parent))
|
||
{
|
||
if (x)
|
||
*x -= parent->abs_x;
|
||
if (y)
|
||
*y -= parent->abs_y;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (x)
|
||
*x = surface->x;
|
||
if (y)
|
||
*y = surface->y;
|
||
if (width)
|
||
*width = surface->width;
|
||
if (height)
|
||
*height = surface->height;
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_get_width:
|
||
* @surface: a #GdkSurface
|
||
*
|
||
* Returns the width of the given @surface.
|
||
*
|
||
* On the X11 platform the returned size is the size reported in the
|
||
* most-recently-processed configure event, rather than the current
|
||
* size on the X server.
|
||
*
|
||
* 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.
|
||
*
|
||
* On the X11 platform the returned size is the size reported in the
|
||
* most-recently-processed configure event, rather than the current
|
||
* size on the X server.
|
||
*
|
||
* 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) (allow-none): return location for X coordinate
|
||
* @y: (out) (allow-none): 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.)
|
||
*
|
||
* Returns: not meaningful, ignore
|
||
*/
|
||
gint
|
||
gdk_surface_get_origin (GdkSurface *surface,
|
||
gint *x,
|
||
gint *y)
|
||
{
|
||
gint dummy_x, dummy_y;
|
||
|
||
g_return_val_if_fail (GDK_IS_SURFACE (surface), 0);
|
||
|
||
gdk_surface_get_root_coords (surface,
|
||
0, 0,
|
||
x ? x : &dummy_x,
|
||
y ? y : &dummy_y);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/**
|
||
* 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,
|
||
gint x,
|
||
gint y,
|
||
gint *root_x,
|
||
gint *root_y)
|
||
{
|
||
GdkSurfaceImplClass *impl_class;
|
||
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
{
|
||
*root_x = 0;
|
||
*root_y = 0;
|
||
return;
|
||
}
|
||
|
||
impl_class = GDK_SURFACE_IMPL_GET_CLASS (surface->impl);
|
||
impl_class->get_root_coords (surface->impl_surface,
|
||
x + surface->abs_x,
|
||
y + surface->abs_y,
|
||
root_x, root_y);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_coords_to_parent:
|
||
* @surface: a child surface
|
||
* @x: X coordinate in child’s coordinate system
|
||
* @y: Y coordinate in child’s coordinate system
|
||
* @parent_x: (out) (allow-none): return location for X coordinate
|
||
* in parent’s coordinate system, or %NULL
|
||
* @parent_y: (out) (allow-none): return location for Y coordinate
|
||
* in parent’s coordinate system, or %NULL
|
||
*
|
||
* Transforms surface coordinates from a child surface to its parent
|
||
* surface. Calling this function is equivalent to adding the return
|
||
* values of gdk_surface_get_position() to the child coordinates.
|
||
*
|
||
* See also: gdk_surface_coords_from_parent()
|
||
**/
|
||
void
|
||
gdk_surface_coords_to_parent (GdkSurface *surface,
|
||
gdouble x,
|
||
gdouble y,
|
||
gdouble *parent_x,
|
||
gdouble *parent_y)
|
||
{
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
if (parent_x)
|
||
*parent_x = x + surface->x;
|
||
|
||
if (parent_y)
|
||
*parent_y = y + surface->y;
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_coords_from_parent:
|
||
* @surface: a child surface
|
||
* @parent_x: X coordinate in parent’s coordinate system
|
||
* @parent_y: Y coordinate in parent’s coordinate system
|
||
* @x: (out) (allow-none): return location for X coordinate in child’s coordinate system
|
||
* @y: (out) (allow-none): return location for Y coordinate in child’s coordinate system
|
||
*
|
||
* Transforms surface coordinates from a parent surface to a child
|
||
* surface.
|
||
*
|
||
* Calling this function is equivalent to subtracting the return
|
||
* values of gdk_surface_get_position() from the parent coordinates.
|
||
*
|
||
* See also: gdk_surface_coords_to_parent()
|
||
**/
|
||
void
|
||
gdk_surface_coords_from_parent (GdkSurface *surface,
|
||
gdouble parent_x,
|
||
gdouble parent_y,
|
||
gdouble *x,
|
||
gdouble *y)
|
||
{
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
if (x)
|
||
*x = parent_x - surface->x;
|
||
|
||
if (y)
|
||
*y = parent_y - surface->y;
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_input_shape_combine_region:
|
||
* @surface: a #GdkSurface
|
||
* @shape_region: region of surface to be non-transparent
|
||
* @offset_x: X position of @shape_region in @surface coordinates
|
||
* @offset_y: Y position of @shape_region in @surface coordinates
|
||
*
|
||
* Like gdk_surface_shape_combine_region(), but the shape applies
|
||
* only to 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 shape 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 shape controls where the surface is
|
||
* “clickable”.
|
||
*
|
||
* On the X11 platform, this requires version 1.1 of the
|
||
* shape extension.
|
||
*
|
||
* On the Win32 platform, this functionality is not present and the
|
||
* function does nothing.
|
||
*/
|
||
void
|
||
gdk_surface_input_shape_combine_region (GdkSurface *surface,
|
||
const cairo_region_t *shape_region,
|
||
gint offset_x,
|
||
gint offset_y)
|
||
{
|
||
GdkSurfaceImplClass *impl_class;
|
||
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return;
|
||
|
||
if (surface->input_shape)
|
||
cairo_region_destroy (surface->input_shape);
|
||
|
||
if (shape_region)
|
||
{
|
||
surface->input_shape = cairo_region_copy (shape_region);
|
||
cairo_region_translate (surface->input_shape, offset_x, offset_y);
|
||
}
|
||
else
|
||
surface->input_shape = NULL;
|
||
|
||
if (gdk_surface_has_impl (surface))
|
||
{
|
||
impl_class = GDK_SURFACE_IMPL_GET_CLASS (surface->impl);
|
||
impl_class->input_shape_combine_region (surface, surface->input_shape, 0, 0);
|
||
}
|
||
}
|
||
|
||
static void
|
||
do_child_input_shapes (GdkSurface *surface,
|
||
gboolean merge)
|
||
{
|
||
GdkRectangle r;
|
||
cairo_region_t *region;
|
||
|
||
r.x = 0;
|
||
r.y = 0;
|
||
r.width = surface->width;
|
||
r.height = surface->height;
|
||
|
||
region = cairo_region_create_rectangle (&r);
|
||
remove_child_area (surface, TRUE, region);
|
||
|
||
if (merge && surface->input_shape)
|
||
cairo_region_subtract (region, surface->input_shape);
|
||
|
||
cairo_region_xor_rectangle (region, &r);
|
||
|
||
gdk_surface_input_shape_combine_region (surface, region, 0, 0);
|
||
}
|
||
|
||
|
||
/**
|
||
* gdk_surface_set_child_input_shapes:
|
||
* @surface: a #GdkSurface
|
||
*
|
||
* Sets the input shape mask of @surface to the union of input shape masks
|
||
* for all children of @surface, ignoring the input shape mask of @surface
|
||
* itself. Contrast with gdk_surface_merge_child_input_shapes() which includes
|
||
* the input shape mask of @surface in the masks to be merged.
|
||
**/
|
||
void
|
||
gdk_surface_set_child_input_shapes (GdkSurface *surface)
|
||
{
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
do_child_input_shapes (surface, FALSE);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_set_pass_through:
|
||
* @surface: a #GdkSurface
|
||
* @pass_through: a boolean
|
||
*
|
||
* Sets whether input to the surface is passed through to the surface
|
||
* below.
|
||
*
|
||
* The default value of this is %FALSE, which means that pointer
|
||
* events that happen inside the surface are send first to the surface,
|
||
* but if the event is not selected by the event mask then the event
|
||
* is sent to the parent surface, and so on up the hierarchy.
|
||
*
|
||
* If @pass_through is %TRUE then such pointer events happen as if the
|
||
* surface wasn't there at all, and thus will be sent first to any
|
||
* surfaces below @surface. This is useful if the surface is used in a
|
||
* transparent fashion. In the terminology of the web this would be called
|
||
* "pointer-events: none".
|
||
*
|
||
* Note that a surface with @pass_through %TRUE can still have a subsurface
|
||
* without pass through, so you can get events on a subset of a surface. And in
|
||
* that cases you would get the in-between related events such as the pointer
|
||
* enter/leave events on its way to the destination surface.
|
||
**/
|
||
void
|
||
gdk_surface_set_pass_through (GdkSurface *surface,
|
||
gboolean pass_through)
|
||
{
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
surface->pass_through = !!pass_through;
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_get_pass_through:
|
||
* @surface: a #GdkSurface
|
||
*
|
||
* Returns whether input to the surface is passed through to the surface
|
||
* below.
|
||
*
|
||
* See gdk_surface_set_pass_through() for details
|
||
**/
|
||
gboolean
|
||
gdk_surface_get_pass_through (GdkSurface *surface)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_SURFACE (surface), FALSE);
|
||
|
||
return surface->pass_through;
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_merge_child_input_shapes:
|
||
* @surface: a #GdkSurface
|
||
*
|
||
* Merges the input shape masks for any child surfaces into the
|
||
* input shape mask for @surface. i.e. the union of all input masks
|
||
* for @surface and its children will become the new input mask
|
||
* for @surface. See gdk_surface_input_shape_combine_region().
|
||
*
|
||
* This function is distinct from gdk_surface_set_child_input_shapes()
|
||
* because it includes @surface’s input shape mask in the set of
|
||
* shapes to be merged.
|
||
**/
|
||
void
|
||
gdk_surface_merge_child_input_shapes (GdkSurface *surface)
|
||
{
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
do_child_input_shapes (surface, TRUE);
|
||
}
|
||
|
||
|
||
/**
|
||
* gdk_surface_get_modal_hint:
|
||
* @surface: A toplevel #GdkSurface.
|
||
*
|
||
* Determines whether or not the surface manager is hinted that @surface
|
||
* has modal behaviour.
|
||
*
|
||
* Returns: whether or not the surface has the modal hint set.
|
||
*/
|
||
gboolean
|
||
gdk_surface_get_modal_hint (GdkSurface *surface)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_SURFACE (surface), FALSE);
|
||
|
||
return surface->modal_hint;
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_get_accept_focus:
|
||
* @surface: a toplevel #GdkSurface.
|
||
*
|
||
* Determines whether or not the desktop environment shuld be hinted that
|
||
* the surface does not want to receive input focus.
|
||
*
|
||
* Returns: whether or not the surface should receive input focus.
|
||
*/
|
||
gboolean
|
||
gdk_surface_get_accept_focus (GdkSurface *surface)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_SURFACE (surface), FALSE);
|
||
|
||
return surface->accept_focus;
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_get_focus_on_map:
|
||
* @surface: a toplevel #GdkSurface.
|
||
*
|
||
* Determines whether or not the desktop environment should be hinted that the
|
||
* surface does not want to receive input focus when it is mapped.
|
||
*
|
||
* Returns: whether or not the surface wants to receive input focus when
|
||
* it is mapped.
|
||
*/
|
||
gboolean
|
||
gdk_surface_get_focus_on_map (GdkSurface *surface)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_SURFACE (surface), FALSE);
|
||
|
||
return surface->focus_on_map;
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_is_input_only:
|
||
* @surface: a toplevel #GdkSurface
|
||
*
|
||
* Determines whether or not the surface is an input only surface.
|
||
*
|
||
* Returns: %TRUE if @surface is input only
|
||
*/
|
||
gboolean
|
||
gdk_surface_is_input_only (GdkSurface *surface)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_SURFACE (surface), FALSE);
|
||
|
||
return surface->input_only;
|
||
}
|
||
|
||
/* Gets the toplevel for a surface as used for events,
|
||
i.e. including offscreen parents going up to the native
|
||
toplevel */
|
||
static GdkSurface *
|
||
get_event_toplevel (GdkSurface *surface)
|
||
{
|
||
GdkSurface *parent;
|
||
|
||
while ((parent = surface->parent) != NULL)
|
||
surface = parent;
|
||
|
||
return surface;
|
||
}
|
||
|
||
gboolean
|
||
_gdk_surface_event_parent_of (GdkSurface *parent,
|
||
GdkSurface *child)
|
||
{
|
||
GdkSurface *w;
|
||
|
||
w = child;
|
||
while (w != NULL)
|
||
{
|
||
if (w == parent)
|
||
return TRUE;
|
||
|
||
w = w->parent;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
static void
|
||
update_cursor (GdkDisplay *display,
|
||
GdkDevice *device)
|
||
{
|
||
GdkSurface *cursor_surface, *parent, *toplevel;
|
||
GdkSurface *pointer_surface;
|
||
GdkPointerSurfaceInfo *pointer_info;
|
||
GdkDeviceGrabInfo *grab;
|
||
GdkCursor *cursor;
|
||
|
||
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 (/* have grab */
|
||
grab != NULL &&
|
||
/* the pointer is not in a descendant of the grab surface */
|
||
!_gdk_surface_event_parent_of (grab->surface, pointer_surface))
|
||
{
|
||
/* use the cursor from the grab surface */
|
||
cursor_surface = grab->surface;
|
||
}
|
||
else
|
||
{
|
||
/* otherwise use the cursor from the pointer surface */
|
||
cursor_surface = pointer_surface;
|
||
}
|
||
|
||
/* Find the first surface with the cursor actually set, as
|
||
the cursor is inherited from the parent */
|
||
while (cursor_surface->cursor == NULL &&
|
||
!g_hash_table_contains (cursor_surface->device_cursor, device) &&
|
||
(parent = cursor_surface->parent) != NULL)
|
||
cursor_surface = parent;
|
||
|
||
cursor = g_hash_table_lookup (cursor_surface->device_cursor, device);
|
||
|
||
if (!cursor)
|
||
cursor = cursor_surface->cursor;
|
||
|
||
/* Set all cursors on toplevel, otherwise its tricky to keep track of
|
||
* which native surface has what cursor set. */
|
||
toplevel = get_event_toplevel (pointer_surface);
|
||
GDK_DEVICE_GET_CLASS (device)->set_surface_cursor (device, toplevel, cursor);
|
||
}
|
||
|
||
static gboolean
|
||
point_in_surface (GdkSurface *surface,
|
||
gdouble x,
|
||
gdouble y)
|
||
{
|
||
return
|
||
x >= 0 && x < surface->width &&
|
||
y >= 0 && y < surface->height &&
|
||
(surface->input_shape == NULL ||
|
||
cairo_region_contains_point (surface->input_shape,
|
||
x, y));
|
||
}
|
||
|
||
/* Same as point_in_surface, except it also takes pass_through and its
|
||
interaction with child surfaces into account */
|
||
static gboolean
|
||
point_in_input_surface (GdkSurface *surface,
|
||
gdouble x,
|
||
gdouble y,
|
||
GdkSurface **input_surface,
|
||
gdouble *input_surface_x,
|
||
gdouble *input_surface_y)
|
||
{
|
||
GdkSurface *sub;
|
||
double child_x, child_y;
|
||
GList *l;
|
||
|
||
if (!point_in_surface (surface, x, y))
|
||
return FALSE;
|
||
|
||
if (!surface->pass_through)
|
||
{
|
||
if (input_surface)
|
||
{
|
||
*input_surface = surface;
|
||
*input_surface_x = x;
|
||
*input_surface_y = y;
|
||
}
|
||
return TRUE;
|
||
}
|
||
|
||
/* For pass-through, must be over a child input surface */
|
||
|
||
/* Children is ordered in reverse stack order, i.e. first is topmost */
|
||
for (l = surface->children; l != NULL; l = l->next)
|
||
{
|
||
sub = l->data;
|
||
|
||
if (!GDK_SURFACE_IS_MAPPED (sub))
|
||
continue;
|
||
|
||
gdk_surface_coords_from_parent ((GdkSurface *)sub,
|
||
x, y,
|
||
&child_x, &child_y);
|
||
if (point_in_input_surface (sub, child_x, child_y,
|
||
input_surface, input_surface_x, input_surface_y))
|
||
{
|
||
if (input_surface)
|
||
gdk_surface_coords_to_parent (sub,
|
||
*input_surface_x,
|
||
*input_surface_y,
|
||
input_surface_x,
|
||
input_surface_y);
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
GdkSurface *
|
||
_gdk_surface_find_child_at (GdkSurface *surface,
|
||
double x,
|
||
double y)
|
||
{
|
||
GdkSurface *sub;
|
||
double child_x, child_y;
|
||
GList *l;
|
||
|
||
if (point_in_surface (surface, x, y))
|
||
{
|
||
/* Children is ordered in reverse stack order, i.e. first is topmost */
|
||
for (l = surface->children; l != NULL; l = l->next)
|
||
{
|
||
sub = l->data;
|
||
|
||
if (!GDK_SURFACE_IS_MAPPED (sub))
|
||
continue;
|
||
|
||
gdk_surface_coords_from_parent ((GdkSurface *)sub,
|
||
x, y,
|
||
&child_x, &child_y);
|
||
if (point_in_input_surface (sub, child_x, child_y,
|
||
NULL, NULL, NULL))
|
||
return (GdkSurface *)sub;
|
||
}
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
GdkSurface *
|
||
_gdk_surface_find_descendant_at (GdkSurface *surface,
|
||
gdouble x,
|
||
gdouble y,
|
||
gdouble *found_x,
|
||
gdouble *found_y)
|
||
{
|
||
GdkSurface *sub, *input_surface;
|
||
gdouble child_x, child_y;
|
||
GList *l;
|
||
gboolean found;
|
||
|
||
if (point_in_surface (surface, x, y))
|
||
{
|
||
do
|
||
{
|
||
found = FALSE;
|
||
/* Children is ordered in reverse stack order, i.e. first is topmost */
|
||
for (l = surface->children; l != NULL; l = l->next)
|
||
{
|
||
sub = l->data;
|
||
|
||
if (!GDK_SURFACE_IS_MAPPED (sub))
|
||
continue;
|
||
|
||
gdk_surface_coords_from_parent ((GdkSurface *)sub,
|
||
x, y,
|
||
&child_x, &child_y);
|
||
if (point_in_input_surface (sub, child_x, child_y,
|
||
&input_surface, &child_x, &child_y))
|
||
{
|
||
x = child_x;
|
||
y = child_y;
|
||
surface = input_surface;
|
||
found = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
while (found);
|
||
}
|
||
else
|
||
{
|
||
/* Not in surface at all */
|
||
surface = NULL;
|
||
}
|
||
|
||
if (found_x)
|
||
*found_x = x;
|
||
if (found_y)
|
||
*found_y = y;
|
||
|
||
return surface;
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_beep:
|
||
* @surface: a toplevel #GdkSurface
|
||
*
|
||
* Emits a short beep associated to @surface in the appropriate
|
||
* display, if supported. Otherwise, emits a short beep on
|
||
* the display just as gdk_display_beep().
|
||
**/
|
||
void
|
||
gdk_surface_beep (GdkSurface *surface)
|
||
{
|
||
GdkDisplay *display;
|
||
GdkSurface *toplevel;
|
||
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return;
|
||
|
||
toplevel = get_event_toplevel (surface);
|
||
display = gdk_surface_get_display (surface);
|
||
|
||
if (toplevel)
|
||
{
|
||
if (GDK_SURFACE_IMPL_GET_CLASS (toplevel->impl)->beep (toplevel))
|
||
return;
|
||
}
|
||
|
||
/* If surfaces fail to beep, we beep the display. */
|
||
gdk_display_beep (display);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_set_support_multidevice:
|
||
* @surface: a #GdkSurface.
|
||
* @support_multidevice: %TRUE to enable multidevice support in @surface.
|
||
*
|
||
* This function will enable multidevice features in @surface.
|
||
*
|
||
* Multidevice aware surfaces will need to handle properly multiple,
|
||
* per device enter/leave events, device grabs and grab ownerships.
|
||
**/
|
||
void
|
||
gdk_surface_set_support_multidevice (GdkSurface *surface,
|
||
gboolean support_multidevice)
|
||
{
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return;
|
||
|
||
if (surface->support_multidevice == support_multidevice)
|
||
return;
|
||
|
||
surface->support_multidevice = support_multidevice;
|
||
|
||
/* FIXME: What to do if called when some pointers are inside the surface ? */
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_get_support_multidevice:
|
||
* @surface: a #GdkSurface.
|
||
*
|
||
* Returns %TRUE if the surface is aware of the existence of multiple
|
||
* devices.
|
||
*
|
||
* Returns: %TRUE if the surface handles multidevice features.
|
||
**/
|
||
gboolean
|
||
gdk_surface_get_support_multidevice (GdkSurface *surface)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_SURFACE (surface), FALSE);
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return FALSE;
|
||
|
||
return surface->support_multidevice;
|
||
}
|
||
|
||
/* send motion events if the right buttons are down */
|
||
|
||
GdkEvent *
|
||
_gdk_make_event (GdkSurface *surface,
|
||
GdkEventType type,
|
||
GdkEvent *event_in_queue,
|
||
gboolean before_event)
|
||
{
|
||
GdkEvent *event = gdk_event_new (type);
|
||
guint32 the_time;
|
||
GdkModifierType the_state;
|
||
|
||
the_time = gdk_event_get_time (event_in_queue);
|
||
gdk_event_get_state (event_in_queue, &the_state);
|
||
|
||
event->any.surface = g_object_ref (surface);
|
||
event->any.send_event = FALSE;
|
||
if (event_in_queue && event_in_queue->any.send_event)
|
||
event->any.send_event = TRUE;
|
||
|
||
switch ((guint) type)
|
||
{
|
||
case GDK_MOTION_NOTIFY:
|
||
event->motion.time = the_time;
|
||
event->motion.axes = NULL;
|
||
event->motion.state = the_state;
|
||
break;
|
||
|
||
case GDK_BUTTON_PRESS:
|
||
case GDK_BUTTON_RELEASE:
|
||
event->button.time = the_time;
|
||
event->button.axes = NULL;
|
||
event->button.state = the_state;
|
||
break;
|
||
|
||
case GDK_TOUCH_BEGIN:
|
||
case GDK_TOUCH_UPDATE:
|
||
case GDK_TOUCH_END:
|
||
case GDK_TOUCH_CANCEL:
|
||
event->touch.time = the_time;
|
||
event->touch.axes = NULL;
|
||
event->touch.state = the_state;
|
||
break;
|
||
|
||
case GDK_SCROLL:
|
||
event->scroll.time = the_time;
|
||
event->scroll.state = the_state;
|
||
break;
|
||
|
||
case GDK_KEY_PRESS:
|
||
case GDK_KEY_RELEASE:
|
||
event->key.time = the_time;
|
||
event->key.state = the_state;
|
||
break;
|
||
|
||
case GDK_ENTER_NOTIFY:
|
||
case GDK_LEAVE_NOTIFY:
|
||
event->crossing.time = the_time;
|
||
event->crossing.state = the_state;
|
||
break;
|
||
|
||
case GDK_PROXIMITY_IN:
|
||
case GDK_PROXIMITY_OUT:
|
||
event->proximity.time = the_time;
|
||
break;
|
||
|
||
case GDK_DRAG_ENTER:
|
||
case GDK_DRAG_LEAVE:
|
||
case GDK_DRAG_MOTION:
|
||
case GDK_DROP_START:
|
||
event->dnd.time = the_time;
|
||
break;
|
||
|
||
case GDK_TOUCHPAD_SWIPE:
|
||
event->touchpad_swipe.time = the_time;
|
||
event->touchpad_swipe.state = the_state;
|
||
break;
|
||
|
||
case GDK_TOUCHPAD_PINCH:
|
||
event->touchpad_pinch.time = the_time;
|
||
event->touchpad_pinch.state = the_state;
|
||
break;
|
||
|
||
case GDK_FOCUS_CHANGE:
|
||
case GDK_CONFIGURE:
|
||
case GDK_MAP:
|
||
case GDK_UNMAP:
|
||
case GDK_DELETE:
|
||
case GDK_DESTROY:
|
||
case GDK_EXPOSE:
|
||
default:
|
||
break;
|
||
}
|
||
|
||
if (event_in_queue)
|
||
{
|
||
if (before_event)
|
||
_gdk_event_queue_insert_before (gdk_surface_get_display (surface), event_in_queue, event);
|
||
else
|
||
_gdk_event_queue_insert_after (gdk_surface_get_display (surface), event_in_queue, event);
|
||
}
|
||
else
|
||
_gdk_event_queue_append (gdk_surface_get_display (surface), event);
|
||
|
||
return event;
|
||
}
|
||
|
||
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)
|
||
|
||
#ifdef DEBUG_SURFACE_PRINTING
|
||
|
||
#ifdef GDK_WINDOWING_X11
|
||
#include "x11/gdkx.h"
|
||
#endif
|
||
|
||
static void
|
||
gdk_surface_print (GdkSurface *surface,
|
||
int indent)
|
||
{
|
||
char *s;
|
||
const char *surface_types[] = {
|
||
"root",
|
||
"toplevel",
|
||
"child",
|
||
"dialog",
|
||
"temp",
|
||
"foreign",
|
||
"subsurface"
|
||
};
|
||
|
||
g_print ("%*s%p: [%s] %d,%d %dx%d", indent, "", surface,
|
||
surface->user_data ? g_type_name_from_instance (surface->user_data) : "no widget",
|
||
surface->x, surface->y,
|
||
surface->width, surface->height
|
||
);
|
||
|
||
if (gdk_surface_has_impl (surface))
|
||
{
|
||
#ifdef GDK_WINDOWING_X11
|
||
g_print (" impl(0x%lx)", gdk_x11_surface_get_xid (window));
|
||
#endif
|
||
}
|
||
|
||
if (surface->surface_type != GDK_SURFACE_CHILD)
|
||
g_print (" %s", surface_types[surface->surface_type]);
|
||
|
||
if (surface->input_only)
|
||
g_print (" input-only");
|
||
|
||
if (!gdk_surface_is_visible ((GdkSurface *)surface))
|
||
g_print (" hidden");
|
||
|
||
g_print (" abs[%d,%d]",
|
||
surface->abs_x, surface->abs_y);
|
||
|
||
if (surface->alpha != 255)
|
||
g_print (" alpha[%d]",
|
||
surface->alpha);
|
||
|
||
s = print_region (surface->clip_region);
|
||
g_print (" clipbox[%s]", s);
|
||
|
||
g_print ("\n");
|
||
}
|
||
|
||
|
||
static void
|
||
gdk_surface_print_tree (GdkSurface *surface,
|
||
int indent,
|
||
gboolean include_input_only)
|
||
{
|
||
GList *l;
|
||
|
||
if (surface->input_only && !include_input_only)
|
||
return;
|
||
|
||
gdk_surface_print (surface, indent);
|
||
|
||
for (l = surface->children; l != NULL; l = l->next)
|
||
gdk_surface_print_tree (l->data, indent + 4, include_input_only);
|
||
}
|
||
|
||
#endif /* DEBUG_SURFACE_PRINTING */
|
||
|
||
void
|
||
_gdk_windowing_got_event (GdkDisplay *display,
|
||
GList *event_link,
|
||
GdkEvent *event,
|
||
gulong serial)
|
||
{
|
||
GdkSurface *event_surface;
|
||
gboolean unlink_event = FALSE;
|
||
GdkDeviceGrabInfo *button_release_grab;
|
||
GdkPointerSurfaceInfo *pointer_info = NULL;
|
||
GdkDevice *device, *source_device;
|
||
|
||
_gdk_display_update_last_event (display, event);
|
||
|
||
device = gdk_event_get_device (event);
|
||
source_device = gdk_event_get_source_device (event);
|
||
|
||
if (device)
|
||
{
|
||
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);
|
||
|
||
if (source_device != pointer_info->last_slave &&
|
||
gdk_device_get_device_type (source_device) == GDK_DEVICE_TYPE_SLAVE)
|
||
pointer_info->last_slave = source_device;
|
||
else if (pointer_info->last_slave)
|
||
source_device = pointer_info->last_slave;
|
||
}
|
||
|
||
_gdk_display_device_grab_update (display, device, source_device, serial);
|
||
|
||
if (gdk_device_get_input_mode (device) == GDK_MODE_DISABLED ||
|
||
!_gdk_display_check_grab_ownership (display, device, serial))
|
||
{
|
||
/* Device events are blocked by another
|
||
* device grab, or the device is disabled
|
||
*/
|
||
unlink_event = TRUE;
|
||
goto out;
|
||
}
|
||
}
|
||
|
||
event_surface = event->any.surface;
|
||
if (!event_surface)
|
||
goto out;
|
||
|
||
#ifdef DEBUG_SURFACE_PRINTING
|
||
if (event->any.type == GDK_KEY_PRESS &&
|
||
(event->key.keyval == 0xa7 ||
|
||
event->key.keyval == 0xbd))
|
||
{
|
||
gdk_surface_print_tree (event_surface, 0, event->key.keyval == 0xbd);
|
||
}
|
||
#endif
|
||
|
||
if (event->any.type == GDK_ENTER_NOTIFY)
|
||
_gdk_display_set_surface_under_pointer (display, device, event_surface);
|
||
else if (event->any.type == GDK_LEAVE_NOTIFY)
|
||
_gdk_display_set_surface_under_pointer (display, device, NULL);
|
||
|
||
if ((event->any.type == GDK_BUTTON_RELEASE ||
|
||
event->any.type == GDK_TOUCH_CANCEL ||
|
||
event->any.type == GDK_TOUCH_END) &&
|
||
!event->any.send_event)
|
||
{
|
||
if (event->any.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 &&
|
||
(event->button.state & GDK_ANY_BUTTON_MASK & ~(GDK_BUTTON1_MASK << (event->button.button - 1))) == 0)
|
||
{
|
||
button_release_grab->serial_end = serial;
|
||
button_release_grab->implicit_ungrab = FALSE;
|
||
_gdk_display_device_grab_update (display, device, source_device, serial);
|
||
}
|
||
}
|
||
}
|
||
|
||
out:
|
||
if (unlink_event)
|
||
{
|
||
_gdk_event_queue_remove_link (display, event_link);
|
||
g_list_free_1 (event_link);
|
||
gdk_event_free (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_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 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.)
|
||
*
|
||
* Returns: a pointer to the newly allocated surface. The caller
|
||
* owns the surface and should call cairo_surface_destroy() when done
|
||
* with it.
|
||
*
|
||
* 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.
|
||
**/
|
||
cairo_surface_t *
|
||
gdk_surface_create_similar_surface (GdkSurface * surface,
|
||
cairo_content_t content,
|
||
int width,
|
||
int height)
|
||
{
|
||
cairo_surface_t *surface_surface, *similar_surface;
|
||
double sx, sy;
|
||
|
||
g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL);
|
||
|
||
surface_surface = gdk_surface_ref_impl_surface (surface);
|
||
sx = sy = 1;
|
||
cairo_surface_get_device_scale (surface_surface, &sx, &sy);
|
||
|
||
if (GDK_DISPLAY_DEBUG_CHECK (surface->display, CAIRO_IMAGE))
|
||
{
|
||
similar_surface = cairo_image_surface_create (content == CAIRO_CONTENT_COLOR ? CAIRO_FORMAT_RGB24 :
|
||
content == CAIRO_CONTENT_ALPHA ? CAIRO_FORMAT_A8 : CAIRO_FORMAT_ARGB32,
|
||
width * sx, height * sy);
|
||
cairo_surface_set_device_scale (similar_surface, sx, sy);
|
||
}
|
||
else
|
||
{
|
||
similar_surface = cairo_surface_create_similar (surface_surface,
|
||
content,
|
||
width, height);
|
||
}
|
||
|
||
cairo_surface_destroy (surface_surface);
|
||
|
||
return similar_surface;
|
||
}
|
||
|
||
|
||
/**
|
||
* gdk_surface_create_similar_image_surface:
|
||
* @surface: (nullable): surface to make new surface similar to, or
|
||
* %NULL if none
|
||
* @format: (type int): the format for the new surface
|
||
* @width: width of the new surface
|
||
* @height: height of the new surface
|
||
* @scale: the scale of the new surface, or 0 to use same as @surface
|
||
*
|
||
* Create a new image surface that is efficient to draw on the
|
||
* given @surface.
|
||
*
|
||
* Initially the surface contents are all 0 (transparent if contents
|
||
* have transparency, black otherwise.)
|
||
*
|
||
* The @width and @height of the new surface are not affected by
|
||
* the scaling factor of the @surface, or by the @scale argument; they
|
||
* are the size of the surface in device pixels. If you wish to create
|
||
* an image surface capable of holding the contents of @surface you can
|
||
* use:
|
||
*
|
||
* |[<!-- language="C" -->
|
||
* int scale = gdk_surface_get_scale_factor (surface);
|
||
* int width = gdk_surface_get_width (surface) * scale;
|
||
* int height = gdk_surface_get_height (surface) * scale;
|
||
*
|
||
* // format is set elsewhere
|
||
* cairo_surface_t *surface =
|
||
* gdk_surface_create_similar_image_surface (surface,
|
||
* format,
|
||
* width, height,
|
||
* scale);
|
||
* ]|
|
||
*
|
||
* Note that unlike cairo_surface_create_similar_image(), the new
|
||
* surface's device scale is set to @scale, or to the scale factor of
|
||
* @surface if @scale is 0.
|
||
*
|
||
* Returns: a pointer to the newly allocated surface. The caller
|
||
* owns the surface and should call cairo_surface_destroy() when done
|
||
* with it.
|
||
*
|
||
* 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.
|
||
**/
|
||
cairo_surface_t *
|
||
gdk_surface_create_similar_image_surface (GdkSurface * surface,
|
||
cairo_format_t format,
|
||
int width,
|
||
int height,
|
||
int scale)
|
||
{
|
||
cairo_surface_t *cairo_surface;
|
||
|
||
g_return_val_if_fail (surface == NULL || GDK_IS_SURFACE (surface), NULL);
|
||
|
||
if (surface == NULL)
|
||
{
|
||
cairo_surface = cairo_image_surface_create (format, width, height);
|
||
}
|
||
else if (GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->create_similar_image_surface)
|
||
{
|
||
cairo_surface =
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->create_similar_image_surface (surface, format, width, height);
|
||
}
|
||
else
|
||
{
|
||
cairo_surface_t *window_surface;
|
||
|
||
window_surface = gdk_surface_ref_impl_surface (surface);
|
||
cairo_surface =
|
||
cairo_surface_create_similar_image (window_surface,
|
||
format,
|
||
width,
|
||
height);
|
||
cairo_surface_destroy (window_surface);
|
||
}
|
||
|
||
if (scale == 0)
|
||
scale = gdk_surface_get_scale_factor (surface);
|
||
|
||
cairo_surface_set_device_scale (cairo_surface, scale, scale);
|
||
|
||
return cairo_surface;
|
||
}
|
||
|
||
|
||
/**
|
||
* gdk_surface_focus:
|
||
* @surface: a #GdkSurface
|
||
* @timestamp: timestamp of the event triggering the surface focus
|
||
*
|
||
* Sets keyboard focus to @surface. In most cases, gtk_window_present()
|
||
* should be used on a #GtkWindow, rather than calling this function.
|
||
*
|
||
**/
|
||
void
|
||
gdk_surface_focus (GdkSurface *surface,
|
||
guint32 timestamp)
|
||
{
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->focus (surface, timestamp);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_set_type_hint:
|
||
* @surface: A toplevel #GdkSurface
|
||
* @hint: A hint of the function this surface will have
|
||
*
|
||
* The application can use this call to provide a hint to the surface
|
||
* manager about the functionality of a surface. The window manager
|
||
* can use this information when determining the decoration and behaviour
|
||
* of the surface.
|
||
*
|
||
* The hint must be set before the surface is mapped.
|
||
**/
|
||
void
|
||
gdk_surface_set_type_hint (GdkSurface *surface,
|
||
GdkSurfaceTypeHint hint)
|
||
{
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->set_type_hint (surface, hint);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_get_type_hint:
|
||
* @surface: A toplevel #GdkSurface
|
||
*
|
||
* This function returns the type hint set for a surface.
|
||
*
|
||
* Returns: The type hint set for @surface
|
||
**/
|
||
GdkSurfaceTypeHint
|
||
gdk_surface_get_type_hint (GdkSurface *surface)
|
||
{
|
||
return GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->get_type_hint (surface);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_set_modal_hint:
|
||
* @surface: A toplevel #GdkSurface
|
||
* @modal: %TRUE if the surface is modal, %FALSE otherwise.
|
||
*
|
||
* The application can use this hint to tell the window manager
|
||
* that a certain surface has modal behaviour. The window manager
|
||
* can use this information to handle modal surfaces in a special
|
||
* way.
|
||
*
|
||
* You should only use this on surfaces for which you have
|
||
* previously called gdk_surface_set_transient_for()
|
||
**/
|
||
void
|
||
gdk_surface_set_modal_hint (GdkSurface *surface,
|
||
gboolean modal)
|
||
{
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->set_modal_hint (surface, modal);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_set_skip_taskbar_hint:
|
||
* @surface: a toplevel #GdkSurface
|
||
* @skips_taskbar: %TRUE to skip the taskbar
|
||
*
|
||
* Toggles whether a surface should appear in a task list or surface
|
||
* list. If a surface’s semantic type as specified with
|
||
* gdk_surface_set_type_hint() already fully describes the surface, this
|
||
* function should not be called in addition,
|
||
* instead you should allow the surface to be treated according to
|
||
* standard policy for its semantic type.
|
||
**/
|
||
void
|
||
gdk_surface_set_skip_taskbar_hint (GdkSurface *surface,
|
||
gboolean skips_taskbar)
|
||
{
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->set_skip_taskbar_hint (surface, skips_taskbar);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_set_skip_pager_hint:
|
||
* @surface: a toplevel #GdkSurface
|
||
* @skips_pager: %TRUE to skip the pager
|
||
*
|
||
* Toggles whether a surface should appear in a pager (workspace
|
||
* switcher, or other desktop utility program that displays a small
|
||
* thumbnail representation of the surfaces on the desktop). If a
|
||
* surface’s semantic type as specified with gdk_surface_set_type_hint()
|
||
* already fully describes the surface, this function should
|
||
* not be called in addition, instead you should
|
||
* allow the surface to be treated according to standard policy for
|
||
* its semantic type.
|
||
**/
|
||
void
|
||
gdk_surface_set_skip_pager_hint (GdkSurface *surface,
|
||
gboolean skips_pager)
|
||
{
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->set_skip_pager_hint (surface, skips_pager);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_set_urgency_hint:
|
||
* @surface: a toplevel #GdkSurface
|
||
* @urgent: %TRUE if the surface is urgent
|
||
*
|
||
* Toggles whether a surface needs the user's
|
||
* urgent attention.
|
||
**/
|
||
void
|
||
gdk_surface_set_urgency_hint (GdkSurface *surface,
|
||
gboolean urgent)
|
||
{
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->set_urgency_hint (surface, urgent);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_set_geometry_hints:
|
||
* @surface: a toplevel #GdkSurface
|
||
* @geometry: geometry hints
|
||
* @geom_mask: bitmask indicating fields of @geometry to pay attention to
|
||
*
|
||
* Sets the geometry hints for @surface. Hints flagged in @geom_mask
|
||
* are set, hints not flagged in @geom_mask are unset.
|
||
* To unset all hints, use a @geom_mask of 0 and a @geometry of %NULL.
|
||
*
|
||
* This function provides hints to the surfaceing system about
|
||
* acceptable sizes for a toplevel surface. The purpose of
|
||
* this is to constrain user resizing, but the windowing system
|
||
* will typically (but is not required to) also constrain the
|
||
* current size of the surface to the provided values and
|
||
* constrain programatic resizing via gdk_surface_resize() or
|
||
* gdk_surface_move_resize().
|
||
*
|
||
* Note that on X11, this effect has no effect on surfaces
|
||
* of type %GDK_SURFACE_TEMP since these surfaces are not resizable
|
||
* by the user.
|
||
*
|
||
* Since you can’t count on the windowing system doing the
|
||
* constraints for programmatic resizes, you should generally
|
||
* call gdk_surface_constrain_size() yourself to determine
|
||
* appropriate sizes.
|
||
*
|
||
**/
|
||
void
|
||
gdk_surface_set_geometry_hints (GdkSurface *surface,
|
||
const GdkGeometry *geometry,
|
||
GdkSurfaceHints geom_mask)
|
||
{
|
||
g_return_if_fail (geometry != NULL || geom_mask == 0);
|
||
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->set_geometry_hints (surface, geometry, geom_mask);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_set_title:
|
||
* @surface: a toplevel #GdkSurface
|
||
* @title: title of @surface
|
||
*
|
||
* Sets the title of a toplevel surface, to be displayed in the titlebar.
|
||
* If you haven’t explicitly set the icon name for the surface
|
||
* (using gdk_surface_set_icon_name()), the icon name will be set to
|
||
* @title as well. @title must be in UTF-8 encoding (as with all
|
||
* user-readable strings in GDK/GTK+). @title may not be %NULL.
|
||
**/
|
||
void
|
||
gdk_surface_set_title (GdkSurface *surface,
|
||
const gchar *title)
|
||
{
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->set_title (surface, title);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_set_role:
|
||
* @surface: a toplevel #GdkSurface
|
||
* @role: a string indicating its role
|
||
*
|
||
* When using GTK+, typically you should use gtk_window_set_role() instead
|
||
* of this low-level function.
|
||
*
|
||
* The window manager and session manager use a surface’s role to
|
||
* distinguish it from other kinds of surface in the same application.
|
||
* When an application is restarted after being saved in a previous
|
||
* session, all surfaces with the same title and role are treated as
|
||
* interchangeable. So if you have two surfaces with the same title
|
||
* that should be distinguished for session management purposes, you
|
||
* should set the role on those surfaces. It doesn’t matter what string
|
||
* you use for the role, as long as you have a different role for each
|
||
* non-interchangeable kind of surface.
|
||
*
|
||
**/
|
||
void
|
||
gdk_surface_set_role (GdkSurface *surface,
|
||
const gchar *role)
|
||
{
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->set_role (surface, role);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_set_startup_id:
|
||
* @surface: a toplevel #GdkSurface
|
||
* @startup_id: a string with startup-notification identifier
|
||
*
|
||
* When using GTK+, typically you should use gtk_window_set_startup_id()
|
||
* instead of this low-level function.
|
||
**/
|
||
void
|
||
gdk_surface_set_startup_id (GdkSurface *surface,
|
||
const gchar *startup_id)
|
||
{
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->set_startup_id (surface, startup_id);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_set_transient_for:
|
||
* @surface: a toplevel #GdkSurface
|
||
* @parent: another toplevel #GdkSurface
|
||
*
|
||
* Indicates to the window manager that @surface is a transient dialog
|
||
* associated with the application surface @parent. This allows the
|
||
* window manager to do things like center @surface on @parent and
|
||
* keep @surface above @parent.
|
||
*
|
||
* See gtk_window_set_transient_for() if you’re using #GtkWindow or
|
||
* #GtkDialog.
|
||
**/
|
||
void
|
||
gdk_surface_set_transient_for (GdkSurface *surface,
|
||
GdkSurface *parent)
|
||
{
|
||
surface->transient_for = parent;
|
||
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->set_transient_for (surface, parent);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_get_root_origin:
|
||
* @surface: a toplevel #GdkSurface
|
||
* @x: (out): return location for X position of surface frame
|
||
* @y: (out): return location for Y position of surface frame
|
||
*
|
||
* Obtains the top-left corner of the surface manager frame in root
|
||
* surface coordinates.
|
||
*
|
||
**/
|
||
void
|
||
gdk_surface_get_root_origin (GdkSurface *surface,
|
||
gint *x,
|
||
gint *y)
|
||
{
|
||
GdkRectangle rect;
|
||
|
||
gdk_surface_get_frame_extents (surface, &rect);
|
||
|
||
if (x)
|
||
*x = rect.x;
|
||
|
||
if (y)
|
||
*y = rect.y;
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_get_frame_extents:
|
||
* @surface: a toplevel #GdkSurface
|
||
* @rect: (out): rectangle to fill with bounding box of the surface frame
|
||
*
|
||
* Obtains the bounding box of the surface, including window manager
|
||
* titlebar/borders if any. The frame position is given in root window
|
||
* coordinates. To get the position of the surface itself (rather than
|
||
* the frame) in root window coordinates, use gdk_surface_get_origin().
|
||
*
|
||
**/
|
||
void
|
||
gdk_surface_get_frame_extents (GdkSurface *surface,
|
||
GdkRectangle *rect)
|
||
{
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->get_frame_extents (surface, rect);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_set_accept_focus:
|
||
* @surface: a toplevel #GdkSurface
|
||
* @accept_focus: %TRUE if the surface should receive input focus
|
||
*
|
||
* Setting @accept_focus to %FALSE hints the desktop environment that the
|
||
* surface doesn’t want to receive input focus.
|
||
*
|
||
* On X, it is the responsibility of the window manager to interpret this
|
||
* hint. ICCCM-compliant window manager usually respect it.
|
||
**/
|
||
void
|
||
gdk_surface_set_accept_focus (GdkSurface *surface,
|
||
gboolean accept_focus)
|
||
{
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->set_accept_focus (surface, accept_focus);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_set_focus_on_map:
|
||
* @surface: a toplevel #GdkSurface
|
||
* @focus_on_map: %TRUE if the surface should receive input focus when mapped
|
||
*
|
||
* Setting @focus_on_map to %FALSE hints the desktop environment that the
|
||
* surface doesn’t want to receive input focus when it is mapped.
|
||
* focus_on_map should be turned off for surfaces that aren’t triggered
|
||
* interactively (such as popups from network activity).
|
||
*
|
||
* On X, it is the responsibility of the window manager to interpret
|
||
* this hint. Window managers following the freedesktop.org window
|
||
* manager extension specification should respect it.
|
||
**/
|
||
void
|
||
gdk_surface_set_focus_on_map (GdkSurface *surface,
|
||
gboolean focus_on_map)
|
||
{
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->set_focus_on_map (surface, focus_on_map);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_set_icon_list:
|
||
* @surface: The #GdkSurface toplevel surface to set the icon of.
|
||
* @surfaces: (transfer none) (element-type GdkTexture):
|
||
* A list of image surfaces, of different sizes.
|
||
*
|
||
* Sets a list of icons for the surface. One of these will be used
|
||
* to represent the surface when it has been iconified. The icon is
|
||
* usually shown in an icon box or some sort of task bar. Which icon
|
||
* size is shown depends on the window manager. The window manager
|
||
* can scale the icon but setting several size icons can give better
|
||
* image quality since the window manager may only need to scale the
|
||
* icon by a small amount or not at all.
|
||
*
|
||
* Note that some platforms don't support surface icons.
|
||
*/
|
||
void
|
||
gdk_surface_set_icon_list (GdkSurface *surface,
|
||
GList *textures)
|
||
{
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->set_icon_list (surface, textures);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_set_icon_name:
|
||
* @surface: a toplevel #GdkSurface
|
||
* @name: (allow-none): name of surface while iconified (minimized)
|
||
*
|
||
* Surfaces may have a name used while minimized, distinct from the
|
||
* name they display in their titlebar. Most of the time this is a bad
|
||
* idea from a user interface standpoint. But you can set such a name
|
||
* with this function, if you like.
|
||
*
|
||
* After calling this with a non-%NULL @name, calls to gdk_surface_set_title()
|
||
* will not update the icon title.
|
||
*
|
||
* Using %NULL for @name unsets the icon title; further calls to
|
||
* gdk_surface_set_title() will again update the icon title as well.
|
||
*
|
||
* Note that some platforms don't support surface icons.
|
||
**/
|
||
void
|
||
gdk_surface_set_icon_name (GdkSurface *surface,
|
||
const gchar *name)
|
||
{
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->set_icon_name (surface, name);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_iconify:
|
||
* @surface: a toplevel #GdkSurface
|
||
*
|
||
* Asks to iconify (minimize) @surface. The window manager may choose
|
||
* to ignore the request, but normally will honor it. Using
|
||
* gtk_window_iconify() is preferred, if you have a #GtkWindow widget.
|
||
*
|
||
* This function only makes sense when @surface is a toplevel surface.
|
||
*
|
||
**/
|
||
void
|
||
gdk_surface_iconify (GdkSurface *surface)
|
||
{
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->iconify (surface);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_deiconify:
|
||
* @surface: a toplevel #GdkSurface
|
||
*
|
||
* Attempt to deiconify (unminimize) @surface. On X11 the window manager may
|
||
* choose to ignore the request to deiconify. When using GTK+,
|
||
* use gtk_window_deiconify() instead of the #GdkSurface variant. Or better yet,
|
||
* you probably want to use gtk_window_present(), which raises the surface, focuses it,
|
||
* unminimizes it, and puts it on the current desktop.
|
||
*
|
||
**/
|
||
void
|
||
gdk_surface_deiconify (GdkSurface *surface)
|
||
{
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->deiconify (surface);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_stick:
|
||
* @surface: a toplevel #GdkSurface
|
||
*
|
||
* “Pins” a surface such that it’s on all workspaces and does not scroll
|
||
* with viewports, for window managers that have scrollable viewports.
|
||
* (When using #GtkWindow, gtk_window_stick() may be more useful.)
|
||
*
|
||
* On the X11 platform, this function depends on window manager
|
||
* support, so may have no effect with many window managers. However,
|
||
* GDK will do the best it can to convince the window manager to stick
|
||
* the surface. For window managers that don’t support this operation,
|
||
* there’s nothing you can do to force it to happen.
|
||
*
|
||
**/
|
||
void
|
||
gdk_surface_stick (GdkSurface *surface)
|
||
{
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->stick (surface);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_unstick:
|
||
* @surface: a toplevel #GdkSurface
|
||
*
|
||
* Reverse operation for gdk_surface_stick(); see gdk_surface_stick(),
|
||
* and gtk_window_unstick().
|
||
*
|
||
**/
|
||
void
|
||
gdk_surface_unstick (GdkSurface *surface)
|
||
{
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->unstick (surface);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_maximize:
|
||
* @surface: a toplevel #GdkSurface
|
||
*
|
||
* Maximizes the surface. If the surface was already maximized, then
|
||
* this function does nothing.
|
||
*
|
||
* On X11, asks the window manager to maximize @surface, if the window
|
||
* manager supports this operation. Not all window managers support
|
||
* this, and some deliberately ignore it or don’t have a concept of
|
||
* “maximized”; so you can’t rely on the maximization actually
|
||
* happening. But it will happen with most standard window managers,
|
||
* and GDK makes a best effort to get it to happen.
|
||
*
|
||
* On Windows, reliably maximizes the surface.
|
||
*
|
||
**/
|
||
void
|
||
gdk_surface_maximize (GdkSurface *surface)
|
||
{
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->maximize (surface);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_unmaximize:
|
||
* @surface: a toplevel #GdkSurface
|
||
*
|
||
* Unmaximizes the surface. If the surface wasn’t maximized, then this
|
||
* function does nothing.
|
||
*
|
||
* On X11, asks the window manager to unmaximize @surface, if the
|
||
* window manager supports this operation. Not all window managers
|
||
* support this, and some deliberately ignore it or don’t have a
|
||
* concept of “maximized”; so you can’t rely on the unmaximization
|
||
* actually happening. But it will happen with most standard window
|
||
* managers, and GDK makes a best effort to get it to happen.
|
||
*
|
||
* On Windows, reliably unmaximizes the surface.
|
||
*
|
||
**/
|
||
void
|
||
gdk_surface_unmaximize (GdkSurface *surface)
|
||
{
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->unmaximize (surface);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_fullscreen:
|
||
* @surface: a toplevel #GdkSurface
|
||
*
|
||
* Moves the surface into fullscreen mode. This means the
|
||
* surface covers the entire screen and is above any panels
|
||
* or task bars.
|
||
*
|
||
* If the surface was already fullscreen, then this function does nothing.
|
||
*
|
||
* On X11, asks the window manager to put @surface in a fullscreen
|
||
* state, if the window manager supports this operation. Not all
|
||
* window managers support this, and some deliberately ignore it or
|
||
* don’t have a concept of “fullscreen”; so you can’t rely on the
|
||
* fullscreenification actually happening. But it will happen with
|
||
* most standard window managers, and GDK makes a best effort to get
|
||
* it to happen.
|
||
**/
|
||
void
|
||
gdk_surface_fullscreen (GdkSurface *surface)
|
||
{
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->fullscreen (surface);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_fullscreen_on_monitor:
|
||
* @surface: a toplevel #GdkSurface
|
||
* @monitor: Which monitor to display fullscreen on.
|
||
*
|
||
* Moves the surface into fullscreen mode on the given monitor. This means
|
||
* the surface covers the entire screen and is above any panels or task bars.
|
||
*
|
||
* If the surface was already fullscreen, then this function does nothing.
|
||
**/
|
||
void
|
||
gdk_surface_fullscreen_on_monitor (GdkSurface *surface,
|
||
GdkMonitor *monitor)
|
||
{
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
g_return_if_fail (GDK_IS_MONITOR (monitor));
|
||
g_return_if_fail (gdk_monitor_get_display (monitor) == gdk_surface_get_display (surface));
|
||
g_return_if_fail (gdk_monitor_is_valid (monitor));
|
||
|
||
if (GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->fullscreen_on_monitor != NULL)
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->fullscreen_on_monitor (surface, monitor);
|
||
else
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->fullscreen (surface);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_set_fullscreen_mode:
|
||
* @surface: a toplevel #GdkSurface
|
||
* @mode: fullscreen mode
|
||
*
|
||
* Specifies whether the @surface should span over all monitors (in a multi-head
|
||
* setup) or only the current monitor when in fullscreen mode.
|
||
*
|
||
* The @mode argument is from the #GdkFullscreenMode enumeration.
|
||
* If #GDK_FULLSCREEN_ON_ALL_MONITORS is specified, the fullscreen @surface will
|
||
* span over all monitors of the display.
|
||
*
|
||
* On X11, searches through the list of monitors display the ones
|
||
* which delimit the 4 edges of the entire display and will ask the window
|
||
* manager to span the @surface over these monitors.
|
||
*
|
||
* If the XINERAMA extension is not available or not usable, this function
|
||
* has no effect.
|
||
*
|
||
* Not all window managers support this, so you can’t rely on the fullscreen
|
||
* surface to span over the multiple monitors when #GDK_FULLSCREEN_ON_ALL_MONITORS
|
||
* is specified.
|
||
**/
|
||
void
|
||
gdk_surface_set_fullscreen_mode (GdkSurface *surface,
|
||
GdkFullscreenMode mode)
|
||
{
|
||
GdkSurfaceImplClass *impl_class;
|
||
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
if (surface->fullscreen_mode != mode)
|
||
{
|
||
surface->fullscreen_mode = mode;
|
||
|
||
impl_class = GDK_SURFACE_IMPL_GET_CLASS (surface->impl);
|
||
if (impl_class->apply_fullscreen_mode != NULL)
|
||
impl_class->apply_fullscreen_mode (surface);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_get_fullscreen_mode:
|
||
* @surface: a toplevel #GdkSurface
|
||
*
|
||
* Obtains the #GdkFullscreenMode of the @surface.
|
||
*
|
||
* Returns: The #GdkFullscreenMode applied to the surface when fullscreen.
|
||
**/
|
||
GdkFullscreenMode
|
||
gdk_surface_get_fullscreen_mode (GdkSurface *surface)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_SURFACE (surface), GDK_FULLSCREEN_ON_CURRENT_MONITOR);
|
||
|
||
return surface->fullscreen_mode;
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_unfullscreen:
|
||
* @surface: a toplevel #GdkSurface
|
||
*
|
||
* Moves the surface out of fullscreen mode. If the surface was not
|
||
* fullscreen, does nothing.
|
||
*
|
||
* On X11, asks the window manager to move @surface out of the fullscreen
|
||
* state, if the window manager supports this operation. Not all
|
||
* window managers support this, and some deliberately ignore it or
|
||
* don’t have a concept of “fullscreen”; so you can’t rely on the
|
||
* unfullscreenification actually happening. But it will happen with
|
||
* most standard window managers, and GDK makes a best effort to get
|
||
* it to happen.
|
||
**/
|
||
void
|
||
gdk_surface_unfullscreen (GdkSurface *surface)
|
||
{
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->unfullscreen (surface);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_set_keep_above:
|
||
* @surface: a toplevel #GdkSurface
|
||
* @setting: whether to keep @surface above other surfaces
|
||
*
|
||
* Set if @surface must be kept above other surfaces. If the
|
||
* surface was already above, then this function does nothing.
|
||
*
|
||
* On X11, asks the window manager to keep @surface above, if the window
|
||
* manager supports this operation. Not all window managers support
|
||
* this, and some deliberately ignore it or don’t have a concept of
|
||
* “keep above”; so you can’t rely on the surface being kept above.
|
||
* But it will happen with most standard window managers,
|
||
* and GDK makes a best effort to get it to happen.
|
||
**/
|
||
void
|
||
gdk_surface_set_keep_above (GdkSurface *surface,
|
||
gboolean setting)
|
||
{
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->set_keep_above (surface, setting);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_set_keep_below:
|
||
* @surface: a toplevel #GdkSurface
|
||
* @setting: whether to keep @surface below other surfaces
|
||
*
|
||
* Set if @surface must be kept below other surfaces. If the
|
||
* surface was already below, then this function does nothing.
|
||
*
|
||
* On X11, asks the window manager to keep @surface below, if the window
|
||
* manager supports this operation. Not all window managers support
|
||
* this, and some deliberately ignore it or don’t have a concept of
|
||
* “keep below”; so you can’t rely on the surface being kept below.
|
||
* But it will happen with most standard window managers,
|
||
* and GDK makes a best effort to get it to happen.
|
||
**/
|
||
void
|
||
gdk_surface_set_keep_below (GdkSurface *surface,
|
||
gboolean setting)
|
||
{
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->set_keep_below (surface, setting);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_get_group:
|
||
* @surface: a toplevel #GdkSurface
|
||
*
|
||
* Returns the group leader surface for @surface. See gdk_surface_set_group().
|
||
*
|
||
* Returns: (transfer none): the group leader surface for @surface
|
||
**/
|
||
GdkSurface *
|
||
gdk_surface_get_group (GdkSurface *surface)
|
||
{
|
||
return GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->get_group (surface);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_set_group:
|
||
* @surface: a toplevel #GdkSurface
|
||
* @leader: (allow-none): group leader surface, or %NULL to restore the default group leader surface
|
||
*
|
||
* Sets the group leader surface for @surface. By default,
|
||
* GDK sets the group leader for all toplevel surfaces
|
||
* to a global surface implicitly created by GDK. With this function
|
||
* you can override this default.
|
||
*
|
||
* The group leader surface allows the window manager to distinguish
|
||
* all surfaces that belong to a single application. It may for example
|
||
* allow users to minimize/unminimize all surfaces belonging to an
|
||
* application at once. You should only set a non-default group surface
|
||
* if your application pretends to be multiple applications.
|
||
**/
|
||
void
|
||
gdk_surface_set_group (GdkSurface *surface,
|
||
GdkSurface *leader)
|
||
{
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->set_group (surface, leader);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_set_decorations:
|
||
* @surface: a toplevel #GdkSurface
|
||
* @decorations: decoration hint mask
|
||
*
|
||
* “Decorations” are the features the window manager adds to a toplevel #GdkSurface.
|
||
* This function sets the traditional Motif window manager hints that tell the
|
||
* window manager which decorations you would like your surface to have.
|
||
* Usually you should use gtk_window_set_decorated() on a #GtkWindow instead of
|
||
* using the GDK function directly.
|
||
*
|
||
* The @decorations argument is the logical OR of the fields in
|
||
* the #GdkWMDecoration enumeration. If #GDK_DECOR_ALL is included in the
|
||
* mask, the other bits indicate which decorations should be turned off.
|
||
* If #GDK_DECOR_ALL is not included, then the other bits indicate
|
||
* which decorations should be turned on.
|
||
*
|
||
* Most window managers honor a decorations hint of 0 to disable all decorations,
|
||
* but very few honor all possible combinations of bits.
|
||
*
|
||
**/
|
||
void
|
||
gdk_surface_set_decorations (GdkSurface *surface,
|
||
GdkWMDecoration decorations)
|
||
{
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->set_decorations (surface, decorations);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_get_decorations:
|
||
* @surface: The toplevel #GdkSurface to get the decorations from
|
||
* @decorations: (out): The surface decorations will be written here
|
||
*
|
||
* Returns the decorations set on the GdkSurface with
|
||
* gdk_surface_set_decorations().
|
||
*
|
||
* Returns: %TRUE if the surface has decorations set, %FALSE otherwise.
|
||
**/
|
||
gboolean
|
||
gdk_surface_get_decorations (GdkSurface *surface,
|
||
GdkWMDecoration *decorations)
|
||
{
|
||
return GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->get_decorations (surface, decorations);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_set_functions:
|
||
* @surface: a toplevel #GdkSurface
|
||
* @functions: bitmask of operations to allow on @surface
|
||
*
|
||
* Sets hints about the window management functions to make available
|
||
* via buttons on the window frame.
|
||
*
|
||
* On the X backend, this function sets the traditional Motif window
|
||
* manager hint for this purpose. However, few window managers do
|
||
* anything reliable or interesting with this hint. Many ignore it
|
||
* entirely.
|
||
*
|
||
* The @functions argument is the logical OR of values from the
|
||
* #GdkWMFunction enumeration. If the bitmask includes #GDK_FUNC_ALL,
|
||
* then the other bits indicate which functions to disable; if
|
||
* it doesn’t include #GDK_FUNC_ALL, it indicates which functions to
|
||
* enable.
|
||
*
|
||
**/
|
||
void
|
||
gdk_surface_set_functions (GdkSurface *surface,
|
||
GdkWMFunction functions)
|
||
{
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->set_functions (surface, functions);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_begin_resize_drag_for_device:
|
||
* @surface: a toplevel #GdkSurface
|
||
* @edge: the edge or corner from which the drag is started
|
||
* @device: the device used for the operation
|
||
* @button: the button being used to drag, or 0 for a keyboard-initiated drag
|
||
* @root_x: root window X coordinate of mouse click that began the drag
|
||
* @root_y: root window Y coordinate of mouse click that began the drag
|
||
* @timestamp: timestamp of mouse click that began the drag (use gdk_event_get_time())
|
||
*
|
||
* Begins a surface resize operation (for a toplevel surface).
|
||
* You might use this function to implement a “window resize grip,” for
|
||
* example; in fact #GtkStatusbar uses it. The function works best
|
||
* with window managers that support the
|
||
* [Extended Window Manager Hints](http://www.freedesktop.org/Standards/wm-spec)
|
||
* but has a fallback implementation for other window managers.
|
||
*/
|
||
void
|
||
gdk_surface_begin_resize_drag_for_device (GdkSurface *surface,
|
||
GdkSurfaceEdge edge,
|
||
GdkDevice *device,
|
||
gint button,
|
||
gint root_x,
|
||
gint root_y,
|
||
guint32 timestamp)
|
||
{
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->begin_resize_drag (surface, edge, device, button, root_x, root_y, timestamp);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_begin_resize_drag:
|
||
* @surface: a toplevel #GdkSurface
|
||
* @edge: the edge or corner from which the drag is started
|
||
* @button: the button being used to drag, or 0 for a keyboard-initiated drag
|
||
* @root_x: root window X coordinate of mouse click that began the drag
|
||
* @root_y: root window Y coordinate of mouse click that began the drag
|
||
* @timestamp: timestamp of mouse click that began the drag (use gdk_event_get_time())
|
||
*
|
||
* Begins a surface resize operation (for a toplevel surface).
|
||
*
|
||
* This function assumes that the drag is controlled by the
|
||
* client pointer device, use gdk_surface_begin_resize_drag_for_device()
|
||
* to begin a drag with a different device.
|
||
*/
|
||
void
|
||
gdk_surface_begin_resize_drag (GdkSurface *surface,
|
||
GdkSurfaceEdge edge,
|
||
gint button,
|
||
gint root_x,
|
||
gint root_y,
|
||
guint32 timestamp)
|
||
{
|
||
GdkDisplay *display;
|
||
GdkDevice *device;
|
||
|
||
display = gdk_surface_get_display (surface);
|
||
device = gdk_seat_get_pointer (gdk_display_get_default_seat (display));
|
||
gdk_surface_begin_resize_drag_for_device (surface, edge,
|
||
device, button, root_x, root_y, timestamp);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_begin_move_drag_for_device:
|
||
* @surface: a toplevel #GdkSurface
|
||
* @device: the device used for the operation
|
||
* @button: the button being used to drag, or 0 for a keyboard-initiated drag
|
||
* @root_x: root window X coordinate of mouse click that began the drag
|
||
* @root_y: root window Y coordinate of mouse click that began the drag
|
||
* @timestamp: timestamp of mouse click that began the drag
|
||
*
|
||
* Begins a surface move operation (for a toplevel surface).
|
||
* You might use this function to implement a “window move grip,” for
|
||
* example. The function works best with window managers that support the
|
||
* [Extended Window Manager Hints](http://www.freedesktop.org/Standards/wm-spec)
|
||
* but has a fallback implementation for other window managers.
|
||
*/
|
||
void
|
||
gdk_surface_begin_move_drag_for_device (GdkSurface *surface,
|
||
GdkDevice *device,
|
||
gint button,
|
||
gint root_x,
|
||
gint root_y,
|
||
guint32 timestamp)
|
||
{
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->begin_move_drag (surface,
|
||
device, button, root_x, root_y, timestamp);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_begin_move_drag:
|
||
* @surface: a toplevel #GdkSurface
|
||
* @button: the button being used to drag, or 0 for a keyboard-initiated drag
|
||
* @root_x: root window X coordinate of mouse click that began the drag
|
||
* @root_y: root window Y coordinate of mouse click that began the drag
|
||
* @timestamp: timestamp of mouse click that began the drag
|
||
*
|
||
* Begins a surface move operation (for a toplevel surface).
|
||
*
|
||
* This function assumes that the drag is controlled by the
|
||
* client pointer device, use gdk_surface_begin_move_drag_for_device()
|
||
* to begin a drag with a different device.
|
||
*/
|
||
void
|
||
gdk_surface_begin_move_drag (GdkSurface *surface,
|
||
gint button,
|
||
gint root_x,
|
||
gint root_y,
|
||
guint32 timestamp)
|
||
{
|
||
GdkDisplay *display;
|
||
GdkDevice *device;
|
||
|
||
display = gdk_surface_get_display (surface);
|
||
device = gdk_seat_get_pointer (gdk_display_get_default_seat (display));
|
||
gdk_surface_begin_move_drag_for_device (surface, device, button, root_x, root_y, timestamp);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_set_opacity:
|
||
* @surface: a top-level or non-native #GdkSurface
|
||
* @opacity: opacity
|
||
*
|
||
* Set @surface to render as partially transparent,
|
||
* with opacity 0 being fully transparent and 1 fully opaque. (Values
|
||
* of the opacity parameter are clamped to the [0,1] range.)
|
||
*
|
||
* For toplevel surfaces this depends on support from the windowing system
|
||
* that may not always be there. For instance, On X11, this works only on
|
||
* X screens with a compositing manager running. On Wayland, there is no
|
||
* per-surface opacity value that the compositor would apply. Instead, use
|
||
* `gdk_surface_set_opaque_region (surface, NULL)` to tell the compositor
|
||
* that the entire surface is (potentially) non-opaque, and draw your content
|
||
* with alpha, or use gtk_widget_set_opacity() to set an overall opacity
|
||
* for your widgets.
|
||
*
|
||
* Support for non-toplevel surfaces was added in 3.8.
|
||
*/
|
||
void
|
||
gdk_surface_set_opacity (GdkSurface *surface,
|
||
gdouble opacity)
|
||
{
|
||
if (opacity < 0)
|
||
opacity = 0;
|
||
else if (opacity > 1)
|
||
opacity = 1;
|
||
|
||
surface->alpha = round (opacity * 255);
|
||
|
||
if (surface->destroyed)
|
||
return;
|
||
|
||
if (gdk_surface_has_impl (surface))
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->set_opacity (surface, opacity);
|
||
else
|
||
{
|
||
recompute_visible_regions (surface, FALSE);
|
||
gdk_surface_invalidate_rect (surface, NULL);
|
||
}
|
||
}
|
||
|
||
/* This function is called when the XWindow is really gone.
|
||
*/
|
||
void
|
||
gdk_surface_destroy_notify (GdkSurface *surface)
|
||
{
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->destroy_notify (surface);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_register_dnd:
|
||
* @surface: a #GdkSurface.
|
||
*
|
||
* Registers a surface as a potential drop destination.
|
||
*/
|
||
void
|
||
gdk_surface_register_dnd (GdkSurface *surface)
|
||
{
|
||
GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->register_dnd (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.
|
||
*
|
||
* Returns: (transfer full) (nullable): a newly created #GdkDragContext or
|
||
* %NULL on error.
|
||
*/
|
||
GdkDragContext *
|
||
gdk_drag_begin (GdkSurface *surface,
|
||
GdkDevice *device,
|
||
GdkContentProvider *content,
|
||
GdkDragAction actions,
|
||
gint dx,
|
||
gint 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 (gdk_surface_get_display (surface) == gdk_device_get_display (device), NULL);
|
||
g_return_val_if_fail (GDK_IS_CONTENT_PROVIDER (content), NULL);
|
||
|
||
return GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->drag_begin (surface, device, content, actions, dx, dy);
|
||
}
|
||
|
||
static void
|
||
gdk_surface_flush_events (GdkFrameClock *clock,
|
||
void *data)
|
||
{
|
||
GdkSurface *surface;
|
||
GdkDisplay *display;
|
||
|
||
surface = GDK_SURFACE (data);
|
||
|
||
display = gdk_surface_get_display (surface);
|
||
_gdk_event_queue_flush (display);
|
||
_gdk_display_pause_events (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;
|
||
GdkDisplay *display;
|
||
|
||
surface = GDK_SURFACE (data);
|
||
|
||
display = gdk_surface_get_display (surface);
|
||
_gdk_display_unpause_events (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));
|
||
g_return_if_fail (clock == NULL || gdk_surface_is_toplevel (surface));
|
||
|
||
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),
|
||
"paint",
|
||
G_CALLBACK (gdk_surface_paint_on_clock),
|
||
surface);
|
||
g_signal_connect (G_OBJECT (clock),
|
||
"resume-events",
|
||
G_CALLBACK (gdk_surface_resume_events),
|
||
surface);
|
||
}
|
||
|
||
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_paint_on_clock),
|
||
surface);
|
||
g_signal_handlers_disconnect_by_func (G_OBJECT (surface->frame_clock),
|
||
G_CALLBACK (gdk_surface_resume_events),
|
||
surface);
|
||
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)
|
||
{
|
||
GdkSurface *toplevel;
|
||
|
||
g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL);
|
||
|
||
toplevel = gdk_surface_get_toplevel (surface);
|
||
|
||
return toplevel->frame_clock;
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_get_scale_factor:
|
||
* @surface: surface to get scale factor for
|
||
*
|
||
* Returns the internal scale factor that maps from surface coordiantes
|
||
* 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, if this happens
|
||
* a configure event will be sent to the toplevel surface.
|
||
*
|
||
* Returns: the scale factor
|
||
*/
|
||
gint
|
||
gdk_surface_get_scale_factor (GdkSurface *surface)
|
||
{
|
||
GdkSurfaceImplClass *impl_class;
|
||
|
||
g_return_val_if_fail (GDK_IS_SURFACE (surface), 1);
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return 1;
|
||
|
||
impl_class = GDK_SURFACE_IMPL_GET_CLASS (surface->impl);
|
||
|
||
if (impl_class->get_scale_factor)
|
||
return impl_class->get_scale_factor (surface);
|
||
|
||
return 1;
|
||
}
|
||
|
||
/* Returns the *real* unscaled size, which may be a fractional size
|
||
in surface scale coordinates. We need this to properly handle GL
|
||
coordinates which are y-flipped in the real coordinates. */
|
||
void
|
||
gdk_surface_get_unscaled_size (GdkSurface *surface,
|
||
int *unscaled_width,
|
||
int *unscaled_height)
|
||
{
|
||
GdkSurfaceImplClass *impl_class;
|
||
gint scale;
|
||
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
if (surface->impl_surface == surface)
|
||
{
|
||
impl_class = GDK_SURFACE_IMPL_GET_CLASS (surface->impl);
|
||
|
||
if (impl_class->get_unscaled_size)
|
||
{
|
||
impl_class->get_unscaled_size (surface, unscaled_width, unscaled_height);
|
||
return;
|
||
}
|
||
}
|
||
|
||
scale = gdk_surface_get_scale_factor (surface);
|
||
|
||
if (unscaled_width)
|
||
*unscaled_width = surface->width * scale;
|
||
|
||
if (unscaled_height)
|
||
*unscaled_height = surface->height * scale;
|
||
}
|
||
|
||
|
||
/**
|
||
* gdk_surface_set_opaque_region:
|
||
* @surface: a top-level or non-native #GdkSurface
|
||
* @region: (allow-none): a region, or %NULL
|
||
*
|
||
* 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 #GtkWidget::style-updated handler.
|
||
*/
|
||
void
|
||
gdk_surface_set_opaque_region (GdkSurface *surface,
|
||
cairo_region_t *region)
|
||
{
|
||
GdkSurfaceImplClass *impl_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);
|
||
|
||
impl_class = GDK_SURFACE_IMPL_GET_CLASS (surface->impl);
|
||
|
||
if (impl_class->set_opaque_region)
|
||
impl_class->set_opaque_region (surface, region);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_set_shadow_width:
|
||
* @surface: a #GdkSurface
|
||
* @left: The left extent
|
||
* @right: The right extent
|
||
* @top: The top extent
|
||
* @bottom: The bottom extent
|
||
*
|
||
* Newer GTK+ windows using client-side decorations use extra geometry
|
||
* around their frames for effects like shadows and invisible borders.
|
||
* Window managers that want to maximize windows or snap to edges need
|
||
* to know where the extents of the actual frame lie, so that users
|
||
* don’t feel like windows are snapping against random invisible edges.
|
||
*
|
||
* Note that this property is automatically updated by GTK+, so this
|
||
* function should only be used by applications which do not use GTK+
|
||
* to create toplevel surfaces.
|
||
*/
|
||
void
|
||
gdk_surface_set_shadow_width (GdkSurface *surface,
|
||
gint left,
|
||
gint right,
|
||
gint top,
|
||
gint bottom)
|
||
{
|
||
GdkSurfaceImplClass *impl_class;
|
||
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
g_return_if_fail (!GDK_SURFACE_DESTROYED (surface));
|
||
g_return_if_fail (left >= 0 && right >= 0 && top >= 0 && bottom >= 0);
|
||
|
||
surface->shadow_top = top;
|
||
surface->shadow_left = left;
|
||
surface->shadow_right = right;
|
||
surface->shadow_bottom = bottom;
|
||
|
||
impl_class = GDK_SURFACE_IMPL_GET_CLASS (surface->impl);
|
||
|
||
if (impl_class->set_shadow_width)
|
||
impl_class->set_shadow_width (surface, left, right, top, bottom);
|
||
}
|
||
|
||
/**
|
||
* gdk_surface_show_window_menu:
|
||
* @surface: a #GdkSurface
|
||
* @event: a #GdkEvent to show the menu for
|
||
*
|
||
* Asks the windowing system to show the window menu. The window menu
|
||
* is the menu shown when right-clicking the titlebar on traditional
|
||
* windows managed by the window manager. This is useful for windows
|
||
* using client-side decorations, activating it with a right-click
|
||
* on the window decorations.
|
||
*
|
||
* Returns: %TRUE if the window menu was shown and %FALSE otherwise.
|
||
*/
|
||
gboolean
|
||
gdk_surface_show_window_menu (GdkSurface *surface,
|
||
GdkEvent *event)
|
||
{
|
||
GdkSurfaceImplClass *impl_class;
|
||
|
||
g_return_val_if_fail (GDK_IS_SURFACE (surface), FALSE);
|
||
g_return_val_if_fail (!GDK_SURFACE_DESTROYED (surface), FALSE);
|
||
|
||
impl_class = GDK_SURFACE_IMPL_GET_CLASS (surface->impl);
|
||
|
||
if (impl_class->show_window_menu)
|
||
return impl_class->show_window_menu (surface, event);
|
||
else
|
||
return FALSE;
|
||
}
|
||
|
||
gboolean
|
||
gdk_surface_supports_edge_constraints (GdkSurface *surface)
|
||
{
|
||
GdkSurfaceImplClass *impl_class;
|
||
|
||
g_return_val_if_fail (GDK_IS_SURFACE (surface), FALSE);
|
||
g_return_val_if_fail (!GDK_SURFACE_DESTROYED (surface), FALSE);
|
||
|
||
impl_class = GDK_SURFACE_IMPL_GET_CLASS (surface->impl);
|
||
|
||
if (impl_class->supports_edge_constraints)
|
||
return impl_class->supports_edge_constraints (surface);
|
||
else
|
||
return FALSE;
|
||
}
|
||
|
||
void
|
||
gdk_surface_set_state (GdkSurface *surface,
|
||
GdkSurfaceState new_state)
|
||
{
|
||
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.
|
||
*/
|
||
|
||
surface->state = new_state;
|
||
|
||
_gdk_surface_update_viewable (surface);
|
||
|
||
/* We only really send the event to toplevels, since
|
||
* all the surface states don't apply to non-toplevels.
|
||
* Non-toplevels do use the GDK_SURFACE_STATE_WITHDRAWN flag
|
||
* internally so we needed to update surface->state.
|
||
*/
|
||
switch (surface->surface_type)
|
||
{
|
||
case GDK_SURFACE_TOPLEVEL:
|
||
case GDK_SURFACE_TEMP: /* ? */
|
||
g_object_notify (G_OBJECT (surface), "state");
|
||
break;
|
||
case GDK_SURFACE_FOREIGN:
|
||
case GDK_SURFACE_CHILD:
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
void
|
||
gdk_synthesize_surface_state (GdkSurface *surface,
|
||
GdkSurfaceState unset_flags,
|
||
GdkSurfaceState set_flags)
|
||
{
|
||
gdk_surface_set_state (surface, (surface->state | set_flags) & ~unset_flags);
|
||
}
|