/* 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 . */ /* * 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 #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 "gdk-private.h" #include #include /* for the use of round() */ #include "fallback-c89.c" #ifdef GDK_WINDOWING_WAYLAND #include "wayland/gdkwayland.h" #endif #undef DEBUG_SURFACE_PRINTING /** * SECTION:gdksurface * @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, SIZE_CHANGED, RENDER, EVENT, LAST_SIGNAL }; enum { PROP_0, PROP_CURSOR, PROP_DISPLAY, PROP_STATE, PROP_MAPPED, 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 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 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); properties[PROP_MAPPED] = g_param_spec_boolean ("mapped", P_("Mapped"), P_("Mapped"), FALSE, 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); /** * GdkSurface::size-changed: * @surface: the #GdkSurface * @width: the new width * @height: the new height * * Emitted when the size of @surface is changed. */ signals[SIZE_CHANGED] = g_signal_new (g_intern_static_string ("size-changed"), G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT); /** * GdkSurface::render: * @surface: the #GdkSurface * @region: the region that needs to be redrawn * * Emitted when part of the surface needs to be redrawn. * * Returns: %TRUE to indicate that the signal has been handled */ signals[RENDER] = g_signal_new (g_intern_static_string ("render"), G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, 0, g_signal_accumulator_true_handled, NULL, NULL, G_TYPE_BOOLEAN, 1, CAIRO_GOBJECT_TYPE_REGION); /** * GdkSurface::event: * @surface: the #GdkSurface * @event: an input event * * Emitted when GDK receives an input event for @surface. * * Returns: %TRUE to indicate that the event has been handled */ signals[EVENT] = g_signal_new (g_intern_static_string ("event"), G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, 0, g_signal_accumulator_true_handled, NULL, NULL, G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT); } static void seat_removed_cb (GdkDisplay *display, GdkSeat *seat, GdkSurface *surface) { GdkDevice *device = gdk_seat_get_pointer (seat); surface->devices_inside = g_list_remove (surface->devices_inside, device); g_hash_table_remove (surface->device_cursor, device); } static void gdk_surface_finalize (GObject *object) { GdkSurface *surface = GDK_SURFACE (object); g_signal_handlers_disconnect_by_func (gdk_surface_get_display (surface), seat_removed_cb, surface); if (!GDK_SURFACE_DESTROYED (surface)) { g_warning ("losing last reference to undestroyed surface"); _gdk_surface_destroy (surface, FALSE); } 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->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; case PROP_MAPPED: g_value_set_boolean (value, GDK_SURFACE_IS_MAPPED (surface)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } 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)) { /* 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); } void _gdk_surface_update_size (GdkSurface *surface) { GSList *l; for (l = surface->draw_contexts; l; l = l->next) gdk_draw_context_surface_resized (l->data); recompute_visible_regions (surface, FALSE); } GdkSurface* gdk_surface_new (GdkDisplay *display, GdkSurface *parent, GdkSurfaceAttr *attributes) { GdkSurface *surface; gboolean native; 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_CHILD: break; default: g_warning (G_STRLOC "cannot make surfaces of type %d", surface->surface_type); return NULL; } 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 */ } if (native) { /* Create the impl */ gdk_display_create_surface_impl (display, surface, parent, 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); } /** * _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: 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); } 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]); g_object_notify_by_pspec (G_OBJECT (surface), properties[PROP_MAPPED]); 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); } void gdk_surface_set_widget (GdkSurface *surface, gpointer widget) { surface->widget = widget; } gpointer gdk_surface_get_widget (GdkSurface *surface) { return surface->widget; } /** * 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); 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) { 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_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; } 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_cairo_context: * @surface: a #GdkSurface * * Creates a new #GdkCairoContext for rendering on @surface. * * Returns: (transfer full): the newly created #GdkCairoContext **/ GdkCairoContext * gdk_surface_create_cairo_context (GdkSurface *surface) { GdkDisplay *display; g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL); display = gdk_surface_get_display (surface); return g_object_new (GDK_DISPLAY_GET_CLASS (display)->cairo_context_type, "surface", surface, NULL); } /** * gdk_surface_create_vulkan_context: * @surface: a #GdkSurface * @error: return location for an error * * Creates a new #GdkVulkanContext for rendering on @surface. * * If the creation of the #GdkVulkanContext failed, @error will be set. * * Returns: (transfer full): the newly created #GdkVulkanContext, or * %NULL on error **/ GdkVulkanContext * gdk_surface_create_vulkan_context (GdkSurface *surface, GError **error) { GdkDisplay *display; g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); if (GDK_DISPLAY_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); } /* 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) { gboolean handled; if (surface->destroyed) return; /* Paint the surface before the children, clipped to the surface region */ g_signal_emit (surface, signals[RENDER], 0, expose_region, &handled); } /* 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); 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: * @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 (GdkSurface *surface, GdkDevice *device, double *x, double *y, GdkModifierType *mask) { gdouble tmp_x, tmp_y; GdkModifierType tmp_mask; 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; GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->get_device_state (surface, device, &tmp_x, &tmp_y, &tmp_mask); if (x) *x = tmp_x; if (y) *y = tmp_y; if (mask) *mask = tmp_mask; return NULL; } 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 (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]); g_object_notify_by_pspec (G_OBJECT (surface), properties[PROP_MAPPED]); } 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 || 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 window 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]); g_object_notify_by_pspec (G_OBJECT (surface), properties[PROP_MAPPED]); } 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); } recompute_visible_regions (surface, FALSE); /* Invalidate the rect */ if (was_mapped) gdk_surface_invalidate_in_parent (surface); } 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. */ 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) { GdkPointerSurfaceInfo *pointer_info; GdkDisplay *display; if (GDK_SURFACE_DESTROYED (surface)) return; g_assert (gdk_surface_get_display (surface) == gdk_device_get_display (device)); 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_from_name() 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_fromm_name() 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 window 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_DELETE: case GDK_DESTROY: 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); g_object_unref (event); } /* This does two things - first it sees if there are motions at the * end of the queue that can be compressed. Second, if there is just * a single motion that won't be dispatched because it is a compression * candidate it queues up flushing the event queue. */ _gdk_event_queue_handle_motion_compression (display); } /** * gdk_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 *similar_surface; int scale; g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL); scale = gdk_surface_get_scale_factor (surface); similar_surface = cairo_image_surface_create (content == CAIRO_CONTENT_COLOR ? CAIRO_FORMAT_RGB24 : content == CAIRO_CONTENT_ALPHA ? CAIRO_FORMAT_A8 : CAIRO_FORMAT_ARGB32, width * scale, height * scale); cairo_surface_set_device_scale (similar_surface, scale, scale); return similar_surface; } /** * 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_with_time() * 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_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 and 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_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 window 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_with_time(), 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_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 * @x: surface X coordinate of mouse click that began the drag * @y: surface 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,” */ void gdk_surface_begin_resize_drag_for_device (GdkSurface *surface, GdkSurfaceEdge edge, GdkDevice *device, gint button, gint x, gint y, guint32 timestamp) { GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->begin_resize_drag (surface, edge, device, button, x, 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 * @x: surface X coordinate of mouse click that began the drag * @y: surface 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 x, gint 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, x, 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 * @x: surface X coordinate of mouse click that began the drag * @y: surface 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). */ void gdk_surface_begin_move_drag_for_device (GdkSurface *surface, GdkDevice *device, gint button, gint x, gint y, guint32 timestamp) { GDK_SURFACE_IMPL_GET_CLASS (surface->impl)->begin_move_drag (surface, device, button, x, 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 * @x: surface X coordinate of mouse click that began the drag * @y: surface 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 x, gint 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, x, 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 #GdkDrag or * %NULL on error. */ GdkDrag * 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) { gboolean was_mapped, mapped; g_return_if_fail (GDK_IS_SURFACE (surface)); if (new_state == surface->state) return; /* No actual work to do, nothing changed. */ /* Actually update the field in GdkSurface, this is sort of an odd * place to do it, but seems like the safest since it ensures we expose no * inconsistent state to the user. */ was_mapped = GDK_SURFACE_IS_MAPPED (surface); surface->state = new_state; mapped = GDK_SURFACE_IS_MAPPED (surface); _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_by_pspec (G_OBJECT (surface), properties[PROP_STATE]); break; case GDK_SURFACE_CHILD: default: break; } if (was_mapped != mapped) g_object_notify_by_pspec (G_OBJECT (surface), properties[PROP_MAPPED]); } void gdk_synthesize_surface_state (GdkSurface *surface, GdkSurfaceState unset_flags, GdkSurfaceState set_flags) { gdk_surface_set_state (surface, (surface->state | set_flags) & ~unset_flags); } gboolean gdk_surface_handle_event (GdkEvent *event) { gboolean handled = FALSE; if (gdk_event_get_event_type (event) == GDK_CONFIGURE) { g_signal_emit (gdk_event_get_surface (event), signals[SIZE_CHANGED], 0, event->configure.width, event->configure.height); handled = TRUE; } else { g_signal_emit (gdk_event_get_surface (event), signals[EVENT], 0, event, &handled); } return handled; }