gtk/gdk/wayland/gdksurface-wayland.c

1373 lines
42 KiB
C
Raw Normal View History

2010-12-18 20:38:49 +00:00
/*
* Copyright © 2010 Intel Corporation
*
* 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
2012-02-27 13:01:10 +00:00
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
2010-12-18 20:38:49 +00:00
*/
#include "config.h"
#include "gdksurface-wayland.h"
2010-12-18 20:38:49 +00:00
#include "gdkdeviceprivate.h"
2010-12-18 20:38:49 +00:00
#include "gdkdisplay-wayland.h"
#include "gdkdragsurfaceprivate.h"
2021-09-24 19:49:38 +00:00
#include "gdkeventsprivate.h"
#include "gdkframeclockidleprivate.h"
#include "gdkglcontext-wayland.h"
#include "gdkmonitor-wayland.h"
#include "gdkpopupprivate.h"
2010-12-18 20:38:49 +00:00
#include "gdkprivate-wayland.h"
#include "gdkrectangleprivate.h"
#include "gdkseat-wayland.h"
#include "gdksurfaceprivate.h"
#include "gdktoplevelprivate.h"
#include "gdkdevice-wayland-private.h"
#include "gdkdmabuftextureprivate.h"
#include "gdksubsurfaceprivate.h"
#include "gdksubsurface-wayland-private.h"
#include <wayland/xdg-shell-unstable-v6-client-protocol.h>
#include <wayland/xdg-foreign-unstable-v2-client-protocol.h>
2010-12-18 20:38:49 +00:00
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
2010-12-18 20:38:49 +00:00
#include <netinet/in.h>
#include <unistd.h>
#include "gdksurface-wayland-private.h"
#include "gdktoplevel-wayland-private.h"
#include "linux-dmabuf-unstable-v1-client-protocol.h"
/**
* GdkWaylandSurface:
*
* The Wayland implementation of `GdkSurface`.
*
* Beyond the [class@Gdk.Surface] API, the Wayland implementation offers
* access to the Wayland `wl_surface` object with
* [method@GdkWayland.WaylandSurface.get_wl_surface].
*/
G_DEFINE_TYPE (GdkWaylandSurface, gdk_wayland_surface, GDK_TYPE_SURFACE)
static void gdk_wayland_surface_configure (GdkSurface *surface);
2023-01-06 18:44:18 +00:00
/* {{{ Utilities */
2010-12-18 20:38:49 +00:00
static void
2023-01-06 18:44:18 +00:00
fill_presentation_time_from_frame_time (GdkFrameTimings *timings,
guint32 frame_time)
{
2023-01-06 18:44:18 +00:00
/* The timestamp in a wayland frame is a msec time value that in some
* way reflects the time at which the server started drawing the frame.
* This is not useful from our perspective.
*
* However, for the DRM backend of Weston, on reasonably recent
* Linux, we know that the time is the
* clock_gettime (CLOCK_MONOTONIC) value at the vblank, and that
* backend starts drawing immediately after receiving the vblank
* notification. If we detect this, and make the assumption that the
* compositor will finish drawing before the next vblank, we can
* then determine the presentation time as the frame time we
* received plus one refresh interval.
*
* If a backend is using clock_gettime(CLOCK_MONOTONIC), but not
* picking values right at the vblank, then the presentation times
* we compute won't be accurate, but not really worse than then
* the alternative of not providing presentation times at all.
*
* The complexity here is dealing with the fact that we receive
* only the low 32 bits of the CLOCK_MONOTONIC value in milliseconds.
*/
gint64 now_monotonic = g_get_monotonic_time ();
gint64 now_monotonic_msec = now_monotonic / 1000;
uint32_t now_monotonic_low = (uint32_t)now_monotonic_msec;
if (frame_time - now_monotonic_low < 1000 ||
frame_time - now_monotonic_low > (uint32_t)-1000)
{
/* Timestamp we received is within one second of the current time.
*/
gint64 last_frame_time = now_monotonic + (gint64)1000 * (gint32)(frame_time - now_monotonic_low);
if ((gint32)now_monotonic_low < 0 && (gint32)frame_time > 0)
last_frame_time += (gint64)1000 * G_GINT64_CONSTANT(0x100000000);
else if ((gint32)now_monotonic_low > 0 && (gint32)frame_time < 0)
last_frame_time -= (gint64)1000 * G_GINT64_CONSTANT(0x100000000);
timings->presentation_time = last_frame_time + timings->refresh_interval;
}
2010-12-18 20:38:49 +00:00
}
2023-01-06 18:44:18 +00:00
static gboolean
is_realized_shell_surface (GdkWaylandSurface *impl)
{
return (impl->display_server.xdg_surface ||
impl->display_server.zxdg_surface_v6);
}
void
gdk_wayland_surface_get_window_geometry (GdkSurface *surface,
GdkRectangle *geometry)
{
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
*geometry = (GdkRectangle) {
.x = impl->shadow_left,
.y = impl->shadow_top,
.width = surface->width - (impl->shadow_left + impl->shadow_right),
.height = surface->height - (impl->shadow_top + impl->shadow_bottom)
};
}
static struct wl_region *
wl_region_from_cairo_region (GdkWaylandDisplay *display,
cairo_region_t *region)
{
struct wl_region *wl_region;
int i, n_rects;
wl_region = wl_compositor_create_region (display->compositor);
if (wl_region == NULL)
return NULL;
n_rects = cairo_region_num_rectangles (region);
for (i = 0; i < n_rects; i++)
{
cairo_rectangle_int_t rect;
cairo_region_get_rectangle (region, i, &rect);
wl_region_add (wl_region, rect.x, rect.y, rect.width, rect.height);
}
return wl_region;
}
2023-01-06 18:44:18 +00:00
/* }}} */
/* {{{ Surface implementation */
2023-01-06 18:44:18 +00:00
static void
gdk_wayland_surface_init (GdkWaylandSurface *impl)
{
impl->scale = GDK_FRACTIONAL_SCALE_INIT_INT (1);
impl->viewport_dirty = TRUE;
2023-01-06 18:44:18 +00:00
}
void
2023-01-06 18:44:18 +00:00
gdk_wayland_surface_freeze_state (GdkSurface *surface)
{
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
impl->state_freeze_count++;
}
void
2023-01-06 18:44:18 +00:00
gdk_wayland_surface_thaw_state (GdkSurface *surface)
{
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
g_assert (impl->state_freeze_count > 0);
impl->state_freeze_count--;
if (impl->state_freeze_count > 0)
return;
if (impl->pending.is_dirty)
gdk_wayland_surface_configure (surface);
}
static inline void
get_egl_window_size (GdkSurface *surface,
int *width,
int *height)
{
GdkDisplay *display = gdk_surface_get_display (surface);
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
if (GDK_DISPLAY_DEBUG_CHECK (display, GL_NO_FRACTIONAL))
{
GDK_DISPLAY_DEBUG (display, OPENGL, "Using integer scale %d for EGL window", gdk_fractional_scale_to_int (&impl->scale));
*width = surface->width * gdk_fractional_scale_to_int (&impl->scale);
*height = surface->height * gdk_fractional_scale_to_int (&impl->scale);
}
else
{
GDK_DISPLAY_DEBUG (display, OPENGL, "Using fractional scale %g for EGL window", gdk_fractional_scale_to_double (&impl->scale));
*width = gdk_fractional_scale_scale (&impl->scale, surface->width),
*height = gdk_fractional_scale_scale (&impl->scale, surface->height);
}
}
void
gdk_wayland_surface_update_size (GdkSurface *surface,
int32_t width,
int32_t height,
const GdkFractionalScale *scale)
2010-12-18 20:38:49 +00:00
{
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
gboolean width_changed, height_changed, scale_changed, scale_factor_changed;
2010-12-18 20:38:49 +00:00
width_changed = surface->width != width;
height_changed = surface->height != height;
scale_changed = !gdk_fractional_scale_equal (&impl->scale, scale);
scale_factor_changed = gdk_fractional_scale_to_int (&impl->scale) != gdk_fractional_scale_to_int (scale);
if (!width_changed && !height_changed && !scale_changed)
return;
surface->width = width;
surface->height = height;
if (scale_changed)
{
impl->scale = *scale;
impl->buffer_scale_dirty = TRUE;
impl->viewport_dirty = TRUE;
}
if (width_changed || height_changed)
impl->viewport_dirty = TRUE;
if (impl->display_server.egl_window)
{
int w, h;
get_egl_window_size (surface, &w, &h);
wl_egl_window_resize (impl->display_server.egl_window, w, h, 0, 0);
}
gdk_surface_invalidate_rect (surface, NULL);
if (width_changed)
g_object_notify (G_OBJECT (surface), "width");
if (height_changed)
g_object_notify (G_OBJECT (surface), "height");
if (scale_changed)
g_object_notify (G_OBJECT (surface), "scale");
if (scale_factor_changed)
g_object_notify (G_OBJECT (surface), "scale-factor");
_gdk_surface_update_size (surface);
2010-12-18 20:38:49 +00:00
}
void
gdk_wayland_surface_frame_callback (GdkSurface *surface,
uint32_t time)
{
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
GdkWaylandDisplay *display_wayland =
GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface));
GdkFrameClock *clock = gdk_surface_get_frame_clock (surface);
GdkFrameTimings *timings;
gdk_profiler_add_mark (GDK_PROFILER_CURRENT_TIME, 0, "Wayland frame event", NULL);
2022-09-23 00:50:05 +00:00
GDK_DISPLAY_DEBUG (GDK_DISPLAY (display_wayland), EVENTS, "frame %p", surface);
2015-02-28 05:15:13 +00:00
g_clear_pointer (&impl->frame_callback, wl_callback_destroy);
for (gsize i = 0; i < gdk_surface_get_n_subsurfaces (surface); i++)
{
GdkSubsurface *subsurface = gdk_surface_get_subsurface (surface, i);
gdk_wayland_subsurface_clear_frame_callback (subsurface);
}
2023-01-06 21:35:16 +00:00
GDK_WAYLAND_SURFACE_GET_CLASS (impl)->handle_frame (impl);
gdk/surface: Replace move_to_rect() with GdkPopupLayout based API Replace the gdk_surface_move_to_rect() API with a new GdkSurface method called gdk_surface_present_popup() taking a new GdkPopupLayout object describing how they should be laid out on screen. The layout properties provided are the same as the ones used with gdk_surface_move_to_rect(), except they are now set up using GdkPopupLayout. Calling gdk_surface_present_popup() will either show the popup at the position described using the popup layout object and a new unconstrained size, or reposition it accordingly. In some situations, such as when a popup is set to autohide, presenting may immediately fail, in case the grab was not granted by the display server. After a successful present, the result of the layout can be queried using the following methods: * gdk_surface_get_position() - to get the position relative to its parent * gdk_surface_get_width() - to get the current width * gdk_surface_get_height() - to get the current height * gdk_surface_get_rect_anchor() - to get the anchor point on the anchor rectangle the popup was effectively positioned against given constraints defined by the environment and the layout rules provided via GdkPopupLayout. * gdk_surface_get_surface_anchor() - the same as the one above but for the surface anchor. A new signal replaces the old "moved-to-rect" one - "popup-layout-changed". However, it is only intended to be emitted when the layout changes implicitly by the windowing system, for example if the monitor resolution changed, or the parent window moved.
2020-02-16 11:59:24 +00:00
if (impl->awaiting_frame_frozen)
{
impl->awaiting_frame_frozen = FALSE;
gdk_surface_thaw_updates (surface);
}
timings = gdk_frame_clock_get_timings (clock, impl->pending_frame_counter);
impl->pending_frame_counter = 0;
if (timings == NULL)
return;
timings->refresh_interval = 16667; /* default to 1/60th of a second */
if (impl->display_server.outputs)
{
/* We pick a random output out of the outputs that the surface touches
* The rate here is in milli-hertz */
int refresh_rate =
gdk_wayland_display_get_output_refresh_rate (display_wayland,
impl->display_server.outputs->data);
if (refresh_rate != 0)
timings->refresh_interval = G_GINT64_CONSTANT(1000000000) / refresh_rate;
}
fill_presentation_time_from_frame_time (timings, time);
timings->complete = TRUE;
if ((_gdk_debug_flags & GDK_DEBUG_FRAMES) != 0)
_gdk_frame_clock_debug_print_timings (clock, timings);
if (GDK_PROFILER_IS_RUNNING)
_gdk_frame_clock_add_timings_to_profiler (clock, timings);
}
static void
frame_callback (void *data,
struct wl_callback *callback,
uint32_t time)
{
GdkSurface *surface = data;
g_assert (GDK_WAYLAND_SURFACE (surface)->frame_callback == callback);
g_assert (!GDK_SURFACE_DESTROYED (surface));
gdk_wayland_surface_frame_callback (surface, time);
}
static const struct wl_callback_listener frame_listener = {
frame_callback
};
static void
on_frame_clock_before_paint (GdkFrameClock *clock,
GdkSurface *surface)
{
GdkFrameTimings *timings = gdk_frame_clock_get_current_timings (clock);
gint64 presentation_time;
gint64 refresh_interval;
if (surface->update_freeze_count > 0)
return;
gdk_frame_clock_get_refresh_info (clock,
timings->frame_time,
&refresh_interval, &presentation_time);
if (presentation_time != 0)
{
/* Assume the algorithm used by the DRM backend of Weston - it
* starts drawing at the next vblank after receiving the commit
* for this frame, and presentation occurs at the vblank
* after that.
*/
timings->predicted_presentation_time = presentation_time + refresh_interval;
}
else
{
/* As above, but we don't actually know the phase of the vblank,
* so just assume that we're half way through a refresh cycle.
*/
timings->predicted_presentation_time = timings->frame_time + refresh_interval / 2 + refresh_interval;
}
gdk_surface_apply_state_change (surface);
}
static void
gdk_wayland_surface_request_layout (GdkSurface *surface)
{
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
impl->next_layout.surface_geometry_dirty = TRUE;
}
void
gdk_wayland_surface_request_frame (GdkSurface *surface)
{
GdkWaylandSurface *self = GDK_WAYLAND_SURFACE (surface);
GdkFrameClock *clock;
if (self->frame_callback != NULL)
return;
clock = gdk_surface_get_frame_clock (surface);
self->frame_callback = wl_surface_frame (self->display_server.wl_surface);
wl_proxy_set_queue ((struct wl_proxy *) self->frame_callback, NULL);
wl_callback_add_listener (self->frame_callback, &frame_listener, surface);
for (gsize i = 0; i < gdk_surface_get_n_subsurfaces (surface); i++)
{
GdkSubsurface *subsurface = gdk_surface_get_subsurface (surface, i);
gdk_wayland_subsurface_request_frame (subsurface);
}
self->pending_frame_counter = gdk_frame_clock_get_frame_counter (clock);
}
gboolean
gdk_wayland_surface_has_surface (GdkSurface *surface)
{
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
return !!impl->display_server.wl_surface;
}
void
gdk_wayland_surface_commit (GdkSurface *surface)
{
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
wl_surface_commit (impl->display_server.wl_surface);
}
void
gdk_wayland_surface_notify_committed (GdkSurface *surface)
{
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
impl->has_uncommitted_ack_configure = FALSE;
impl->has_pending_subsurface_commits = FALSE;
}
static void
on_frame_clock_after_paint (GdkFrameClock *clock,
GdkSurface *surface)
{
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
if (surface->update_freeze_count == 0 &&
(impl->has_uncommitted_ack_configure ||
impl->has_pending_subsurface_commits))
{
gdk_wayland_surface_commit (surface);
gdk_wayland_surface_notify_committed (surface);
}
if (impl->frame_callback &&
impl->pending_frame_counter == gdk_frame_clock_get_frame_counter (clock))
{
g_assert (!impl->awaiting_frame_frozen);
impl->awaiting_frame_frozen = TRUE;
gdk_surface_freeze_updates (surface);
}
}
void
gdk_wayland_surface_update_scale (GdkSurface *surface)
{
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface));
guint32 scale;
GSList *l;
/* We can't set the scale on this surface */
if (!impl->display_server.wl_surface ||
wl_surface_get_version (impl->display_server.wl_surface) < WL_SURFACE_SET_BUFFER_SCALE_SINCE_VERSION)
return;
/* scale is tracked by the fractional scale extension */
if (impl->display_server.fractional_scale)
return;
if (!impl->display_server.outputs)
return;
scale = 1;
for (l = impl->display_server.outputs; l != NULL; l = l->next)
{
struct wl_output *output = l->data;
uint32_t output_scale;
output_scale = gdk_wayland_display_get_output_scale (display_wayland,
output);
scale = MAX (scale, output_scale);
}
/* Notify app that scale changed */
gdk_wayland_surface_update_size (surface,
surface->width, surface->height,
&GDK_FRACTIONAL_SCALE_INIT_INT (scale));
}
void
gdk_wayland_surface_attach_image (GdkSurface *surface,
cairo_surface_t *cairo_surface,
const cairo_region_t *damage)
2011-01-08 01:49:40 +00:00
{
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
cairo_rectangle_int_t rect;
uint32_t wl_surface_version;
int i, n;
2010-12-18 20:38:49 +00:00
if (GDK_SURFACE_DESTROYED (surface))
2011-01-08 01:49:40 +00:00
return;
g_assert (_gdk_wayland_is_shm_surface (cairo_surface));
wl_surface_version = wl_surface_get_version (impl->display_server.wl_surface);
/* Attach this new buffer to the surface */
wl_surface_attach (impl->display_server.wl_surface,
_gdk_wayland_shm_surface_get_wl_buffer (cairo_surface),
0, 0);
if ((impl->pending_buffer_offset_x || impl->pending_buffer_offset_y) &&
wl_surface_version >= WL_SURFACE_OFFSET_SINCE_VERSION)
wl_surface_offset (impl->display_server.wl_surface,
impl->pending_buffer_offset_x,
impl->pending_buffer_offset_y);
impl->pending_buffer_offset_x = 0;
impl->pending_buffer_offset_y = 0;
n = cairo_region_num_rectangles (damage);
for (i = 0; i < n; i++)
wayland: stage uncommitted changes to dedicated buffer Right now we use one buffer for both staged changes (freshly painted changes waiting for the frame clock to send to the compositor) and committed changes (changes actively being read by the compositor process). This creates a problem in the event we need to stage updates at the same time the compositor is processing committed updates: we can't change what the compositor is actively processing. The current solution for handling this contention is to allocate a temporary buffer on the spot at the time the updates are staged, and to copy that buffer back to the shared buffer later. The problem, though, is that the copy to the shared buffer currently happens as soon as the updates are finished being staged, not when the shared buffer is done being processed by the compositor. In order to address that problem, this commit changes the code to always stage changes to a dedicated staging buffer. The staging buffer is used exclusively by the client until the client is done with it, and then once that staging buffer is committed, the client never writes to that buffer again. If the client needs to stage new updates, it allocates a brand new staging buffer, draws to it, and back fills the undrawn parts of the buffer from a copy of the contents of the committed buffer. As an optimization, the compositor has the option of releasing the committed buffer back to the client. If it does so before the client needs to stage new updates, then the client will reuse the buffer for staging future updates. This optimization prevents having to allocate a new staging buffer and the associated cost of back filling that new buffer with a readback of the committed buffer. https://bugzilla.gnome.org/show_bug.cgi?id=761312
2016-01-29 16:19:03 +00:00
{
cairo_region_get_rectangle (damage, i, &rect);
if (wl_surface_version >= WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION)
{
float scale = gdk_surface_get_scale (surface);
gdk_rectangle_transform_affine (&rect, scale, scale, 0, 0, &rect);
wl_surface_damage_buffer (impl->display_server.wl_surface, rect.x, rect.y, rect.width, rect.height);
}
else
{
wl_surface_damage (impl->display_server.wl_surface, rect.x, rect.y, rect.width, rect.height);
}
wayland: stage uncommitted changes to dedicated buffer Right now we use one buffer for both staged changes (freshly painted changes waiting for the frame clock to send to the compositor) and committed changes (changes actively being read by the compositor process). This creates a problem in the event we need to stage updates at the same time the compositor is processing committed updates: we can't change what the compositor is actively processing. The current solution for handling this contention is to allocate a temporary buffer on the spot at the time the updates are staged, and to copy that buffer back to the shared buffer later. The problem, though, is that the copy to the shared buffer currently happens as soon as the updates are finished being staged, not when the shared buffer is done being processed by the compositor. In order to address that problem, this commit changes the code to always stage changes to a dedicated staging buffer. The staging buffer is used exclusively by the client until the client is done with it, and then once that staging buffer is committed, the client never writes to that buffer again. If the client needs to stage new updates, it allocates a brand new staging buffer, draws to it, and back fills the undrawn parts of the buffer from a copy of the contents of the committed buffer. As an optimization, the compositor has the option of releasing the committed buffer back to the client. If it does so before the client needs to stage new updates, then the client will reuse the buffer for staging future updates. This optimization prevents having to allocate a new staging buffer and the associated cost of back filling that new buffer with a readback of the committed buffer. https://bugzilla.gnome.org/show_bug.cgi?id=761312
2016-01-29 16:19:03 +00:00
}
}
static gboolean
gdk_wayland_surface_beep (GdkSurface *surface)
{
2023-01-06 18:44:18 +00:00
gdk_wayland_display_system_bell (gdk_surface_get_display (surface), surface);
return TRUE;
}
static void
gdk_wayland_surface_dispose (GObject *object)
{
GdkSurface *surface = GDK_SURFACE (object);
GdkWaylandSurface *impl;
g_return_if_fail (GDK_IS_WAYLAND_SURFACE (surface));
impl = GDK_WAYLAND_SURFACE (surface);
if (impl->event_queue)
{
GdkWaylandDisplay *display_wayland =
GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface));
display_wayland->event_queues =
g_list_remove (display_wayland->event_queues, impl->event_queue);
g_clear_pointer (&impl->event_queue, wl_event_queue_destroy);
}
G_OBJECT_CLASS (gdk_wayland_surface_parent_class)->dispose (object);
}
static void
gdk_wayland_surface_finalize (GObject *object)
{
GdkWaylandSurface *impl;
g_return_if_fail (GDK_IS_WAYLAND_SURFACE (object));
impl = GDK_WAYLAND_SURFACE (object);
g_clear_pointer (&impl->opaque_region, cairo_region_destroy);
2014-03-17 20:02:47 +00:00
g_clear_pointer (&impl->input_region, cairo_region_destroy);
G_OBJECT_CLASS (gdk_wayland_surface_parent_class)->finalize (object);
}
static void
gdk_wayland_surface_sync_shadow (GdkSurface *surface)
{
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
2023-01-06 18:44:18 +00:00
GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface));
GdkRectangle geometry;
if (!is_realized_shell_surface (impl))
return;
gdk_wayland_surface_get_window_geometry (surface, &geometry);
if (GDK_IS_WAYLAND_TOPLEVEL (impl))
{
GdkWaylandToplevel *toplevel = GDK_WAYLAND_TOPLEVEL (impl);
2023-01-06 18:44:18 +00:00
gdk_wayland_toplevel_set_geometry_hints (toplevel, NULL, 0);
}
if (gdk_rectangle_equal (&geometry, &impl->last_sent_window_geometry))
return;
switch (display_wayland->shell_variant)
{
case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL:
xdg_surface_set_window_geometry (impl->display_server.xdg_surface,
geometry.x,
geometry.y,
geometry.width,
geometry.height);
break;
case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6:
zxdg_surface_v6_set_window_geometry (impl->display_server.zxdg_surface_v6,
geometry.x,
geometry.y,
geometry.width,
geometry.height);
break;
default:
g_assert_not_reached ();
}
impl->last_sent_window_geometry = geometry;
}
static void
gdk_wayland_surface_sync_opaque_region (GdkSurface *surface)
{
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
struct wl_region *wl_region = NULL;
if (!impl->display_server.wl_surface)
return;
if (!impl->opaque_region_dirty)
return;
if (impl->opaque_region != NULL)
{
if (gdk_surface_get_n_subsurfaces (surface) > 0)
{
cairo_region_t *region = cairo_region_copy (impl->opaque_region);
for (gsize i = 0; i < gdk_surface_get_n_subsurfaces (surface); i++)
{
GdkSubsurface *subsurface = gdk_surface_get_subsurface (surface, i);
GdkWaylandSubsurface *sub = (GdkWaylandSubsurface *) subsurface;
if (subsurface->above_parent)
continue;
if (sub->texture != NULL)
cairo_region_subtract_rectangle (region, &sub->dest);
}
wl_region = wl_region_from_cairo_region (GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)),
region);
cairo_region_destroy (region);
}
else
wl_region = wl_region_from_cairo_region (GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)),
impl->opaque_region);
}
wl_surface_set_opaque_region (impl->display_server.wl_surface, wl_region);
if (wl_region != NULL)
wl_region_destroy (wl_region);
impl->opaque_region_dirty = FALSE;
}
2014-03-17 20:02:47 +00:00
static void
gdk_wayland_surface_sync_input_region (GdkSurface *surface)
2014-03-17 20:02:47 +00:00
{
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
2014-03-17 20:02:47 +00:00
struct wl_region *wl_region = NULL;
if (!impl->display_server.wl_surface)
2014-03-17 20:02:47 +00:00
return;
if (!impl->input_region_dirty)
return;
2014-03-17 20:02:47 +00:00
if (impl->input_region != NULL)
wl_region = wl_region_from_cairo_region (GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)),
2014-03-17 20:02:47 +00:00
impl->input_region);
wl_surface_set_input_region (impl->display_server.wl_surface, wl_region);
2014-03-17 20:02:47 +00:00
if (wl_region != NULL)
wl_region_destroy (wl_region);
impl->input_region_dirty = FALSE;
2014-03-17 20:02:47 +00:00
}
static void
gdk_wayland_surface_sync_buffer_scale (GdkSurface *surface)
{
GdkWaylandSurface *self = GDK_WAYLAND_SURFACE (surface);
if (!self->display_server.wl_surface)
return;
if (!self->buffer_scale_dirty)
return;
if (self->display_server.viewport)
{
/* The viewport takes care of buffer scale */
}
else if (wl_surface_get_version (self->display_server.wl_surface) >= WL_SURFACE_SET_BUFFER_SCALE_SINCE_VERSION)
{
wl_surface_set_buffer_scale (self->display_server.wl_surface,
gdk_fractional_scale_to_int (&self->scale));
}
self->buffer_scale_dirty = FALSE;
}
static void
gdk_wayland_surface_sync_viewport (GdkSurface *surface)
{
GdkWaylandSurface *self = GDK_WAYLAND_SURFACE (surface);
if (!self->display_server.viewport)
return;
if (!self->viewport_dirty)
return;
wp_viewport_set_destination (self->display_server.viewport,
surface->width,
surface->height);
self->viewport_dirty = FALSE;
}
void
gdk_wayland_surface_sync (GdkSurface *surface)
{
gdk_wayland_surface_sync_shadow (surface);
gdk_wayland_surface_sync_opaque_region (surface);
gdk_wayland_surface_sync_input_region (surface);
gdk_wayland_surface_sync_buffer_scale (surface);
gdk_wayland_surface_sync_viewport (surface);
}
gboolean
gdk_wayland_surface_needs_commit (GdkSurface *surface)
{
GdkWaylandSurface *self = GDK_WAYLAND_SURFACE (surface);
return self->has_pending_subsurface_commits ||
self->opaque_region_dirty ||
self->input_region_dirty ||
self->buffer_scale_dirty ||
self->viewport_dirty;
}
static void
gdk_wayland_surface_fractional_scale_preferred_scale_cb (void *data,
struct wp_fractional_scale_v1 *fractional_scale,
uint32_t scale)
{
GdkWaylandSurface *self = GDK_WAYLAND_SURFACE (data);
GdkSurface *surface = GDK_SURFACE (self);
/* Notify app that scale changed */
gdk_wayland_surface_update_size (surface,
surface->width, surface->height,
&GDK_FRACTIONAL_SCALE_INIT (scale));
}
static const struct wp_fractional_scale_v1_listener fractional_scale_listener = {
gdk_wayland_surface_fractional_scale_preferred_scale_cb,
};
static void
2013-09-16 21:23:29 +00:00
surface_enter (void *data,
struct wl_surface *wl_surface,
2013-09-16 21:23:29 +00:00
struct wl_output *output)
{
GdkSurface *surface = GDK_SURFACE (data);
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
GdkDisplay *display = gdk_surface_get_display (surface);
GdkMonitor *monitor;
2022-09-23 00:50:05 +00:00
GDK_DISPLAY_DEBUG(gdk_surface_get_display (surface), EVENTS,
"surface enter, surface %p output %p", surface, output);
2015-02-28 05:15:13 +00:00
impl->display_server.outputs = g_slist_prepend (impl->display_server.outputs, output);
gdk_wayland_surface_update_scale (surface);
monitor = gdk_wayland_display_get_monitor_for_output (display, output);
gdk_surface_enter_monitor (surface, monitor);
}
static void
2013-09-16 21:23:29 +00:00
surface_leave (void *data,
struct wl_surface *wl_surface,
2013-09-16 21:23:29 +00:00
struct wl_output *output)
{
GdkSurface *surface = GDK_SURFACE (data);
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
GdkDisplay *display = gdk_surface_get_display (surface);
GdkMonitor *monitor;
2022-09-23 00:50:05 +00:00
GDK_DISPLAY_DEBUG (gdk_surface_get_display (surface), EVENTS,
"surface leave, surface %p output %p", surface, output);
2015-02-28 05:15:13 +00:00
impl->display_server.outputs = g_slist_remove (impl->display_server.outputs, output);
if (impl->display_server.outputs)
gdk_wayland_surface_update_scale (surface);
monitor = gdk_wayland_display_get_monitor_for_output (display, output);
gdk_surface_leave_monitor (surface, monitor);
}
static const struct wl_surface_listener surface_listener = {
surface_enter,
surface_leave
};
static void
gdk_wayland_surface_create_wl_surface (GdkSurface *surface)
{
GdkWaylandSurface *self = GDK_WAYLAND_SURFACE (surface);
GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface));
struct wl_surface *wl_surface;
wl_surface = wl_compositor_create_surface (display_wayland->compositor);
wl_proxy_set_queue ((struct wl_proxy *) wl_surface, self->event_queue);
wl_surface_add_listener (wl_surface, &surface_listener, self);
if (display_wayland->fractional_scale)
{
self->display_server.fractional_scale =
wp_fractional_scale_manager_v1_get_fractional_scale (display_wayland->fractional_scale,
wl_surface);
wp_fractional_scale_v1_add_listener (self->display_server.fractional_scale,
&fractional_scale_listener, self);
}
if (display_wayland->viewporter)
{
self->display_server.viewport =
wp_viewporter_get_viewport (display_wayland->viewporter, wl_surface);
}
self->display_server.wl_surface = wl_surface;
}
static void
gdk_wayland_surface_constructed (GObject *object)
{
GdkSurface *surface = GDK_SURFACE (object);
GdkWaylandSurface *self = GDK_WAYLAND_SURFACE (surface);
GdkDisplay *display = gdk_surface_get_display (surface);
GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
GdkFrameClock *frame_clock = gdk_surface_get_frame_clock (surface);
self->event_queue = wl_display_create_queue (display_wayland->wl_display);
display_wayland->event_queues = g_list_prepend (display_wayland->event_queues,
self->event_queue);
/* More likely to be right than just assuming 1 */
if (wl_compositor_get_version (display_wayland->compositor) >= WL_SURFACE_SET_BUFFER_SCALE_SINCE_VERSION)
{
GdkMonitor *monitor = g_list_model_get_item (gdk_display_get_monitors (display), 0);
if (monitor)
{
guint32 monitor_scale = gdk_monitor_get_scale_factor (monitor);
if (monitor_scale != 1)
{
self->scale = GDK_FRACTIONAL_SCALE_INIT_INT (monitor_scale);
self->buffer_scale_dirty = TRUE;
}
g_object_unref (monitor);
}
}
gdk_wayland_surface_create_wl_surface (surface);
g_signal_connect (frame_clock, "before-paint", G_CALLBACK (on_frame_clock_before_paint), surface);
g_signal_connect (frame_clock, "after-paint", G_CALLBACK (on_frame_clock_after_paint), surface);
G_OBJECT_CLASS (gdk_wayland_surface_parent_class)->constructed (object);
}
static void
gdk_wayland_surface_destroy_wl_surface (GdkWaylandSurface *self)
{
if (self->display_server.egl_window)
{
gdk_surface_set_egl_native_window (GDK_SURFACE (self), NULL);
g_clear_pointer (&self->display_server.egl_window, wl_egl_window_destroy);
}
g_clear_pointer (&self->display_server.viewport, wp_viewport_destroy);
g_clear_pointer (&self->display_server.fractional_scale, wp_fractional_scale_v1_destroy);
g_clear_pointer (&self->display_server.wl_surface, wl_surface_destroy);
g_clear_pointer (&self->display_server.outputs, g_slist_free);
}
static void
2023-01-06 18:44:18 +00:00
maybe_notify_mapped (GdkSurface *surface)
{
2023-01-06 18:44:18 +00:00
if (surface->destroyed)
return;
2023-01-06 18:44:18 +00:00
if (!GDK_SURFACE_IS_MAPPED (surface))
gdk_surface_set_is_mapped (surface, TRUE);
}
static void
gdk_wayland_surface_configure (GdkSurface *surface)
{
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
if (!impl->initial_configure_received)
{
2023-01-06 18:44:18 +00:00
gdk_surface_thaw_updates (surface);
impl->initial_configure_received = TRUE;
impl->pending.is_initial_configure = TRUE;
maybe_notify_mapped (surface);
}
2023-01-06 18:44:18 +00:00
impl->has_uncommitted_ack_configure = TRUE;
2023-01-06 21:35:16 +00:00
GDK_WAYLAND_SURFACE_GET_CLASS (impl)->handle_configure (impl);
2023-01-06 18:44:18 +00:00
impl->last_configure_serial = impl->pending.serial;
2023-01-06 18:44:18 +00:00
memset (&impl->pending, 0, sizeof (impl->pending));
}
2023-01-06 18:44:18 +00:00
static void
gdk_wayland_surface_handle_configure (GdkWaylandSurface *impl,
uint32_t serial)
{
impl->pending.is_dirty = TRUE;
impl->pending.serial = serial;
2023-01-06 18:44:18 +00:00
if (impl->state_freeze_count > 0)
return;
2023-01-06 18:44:18 +00:00
gdk_wayland_surface_configure (GDK_SURFACE (impl));
}
static void
2023-01-06 18:44:18 +00:00
xdg_surface_configure (void *data,
struct xdg_surface *xdg_surface,
uint32_t serial)
{
2023-01-06 18:44:18 +00:00
gdk_wayland_surface_handle_configure (GDK_WAYLAND_SURFACE (data), serial);
}
2023-01-06 18:44:18 +00:00
static const struct xdg_surface_listener xdg_surface_listener = {
xdg_surface_configure,
};
static void
2023-01-06 18:44:18 +00:00
zxdg_surface_v6_configure (void *data,
struct zxdg_surface_v6 *xdg_surface,
uint32_t serial)
{
2023-01-06 18:44:18 +00:00
gdk_wayland_surface_handle_configure (GDK_WAYLAND_SURFACE (data), serial);
}
2023-01-06 18:44:18 +00:00
static const struct zxdg_surface_v6_listener zxdg_surface_v6_listener = {
zxdg_surface_v6_configure,
};
void
2023-01-06 18:44:18 +00:00
gdk_wayland_surface_create_xdg_surface_resources (GdkSurface *surface)
{
GdkWaylandDisplay *display =
GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface));
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
2023-01-06 18:44:18 +00:00
switch (display->shell_variant)
{
2023-01-06 18:44:18 +00:00
case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL:
impl->display_server.xdg_surface =
xdg_wm_base_get_xdg_surface (display->xdg_wm_base,
impl->display_server.wl_surface);
wl_proxy_set_queue ((struct wl_proxy *) impl->display_server.xdg_surface,
impl->event_queue);
xdg_surface_add_listener (impl->display_server.xdg_surface,
&xdg_surface_listener,
surface);
break;
case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6:
impl->display_server.zxdg_surface_v6 =
zxdg_shell_v6_get_xdg_surface (display->zxdg_shell_v6,
impl->display_server.wl_surface);
zxdg_surface_v6_add_listener (impl->display_server.zxdg_surface_v6,
&zxdg_surface_v6_listener,
surface);
break;
default:
g_assert_not_reached ();
}
2023-01-06 18:44:18 +00:00
}
2023-01-06 18:44:18 +00:00
static void
unmap_popups_for_surface (GdkSurface *surface)
{
GdkWaylandDisplay *display_wayland;
GList *l;
2023-01-06 18:44:18 +00:00
display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface));
for (l = display_wayland->current_popups; l; l = l->next)
{
2023-01-06 18:44:18 +00:00
GdkSurface *popup = l->data;
if (popup->parent == surface)
{
g_warning ("Tried to unmap the parent of a popup");
gdk_surface_hide (popup);
return;
}
}
}
void
2023-01-06 18:44:18 +00:00
gdk_wayland_surface_hide_surface (GdkSurface *surface)
{
2023-01-06 18:44:18 +00:00
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
if (!impl->mapped)
return;
2023-01-06 18:44:18 +00:00
unmap_popups_for_surface (surface);
g_clear_pointer (&impl->frame_callback, wl_callback_destroy);
if (impl->awaiting_frame_frozen)
{
impl->awaiting_frame_frozen = FALSE;
gdk_surface_thaw_updates (surface);
}
GDK_WAYLAND_SURFACE_GET_CLASS (impl)->hide_surface (impl);
if (impl->display_server.xdg_surface)
{
xdg_surface_destroy (impl->display_server.xdg_surface);
impl->display_server.xdg_surface = NULL;
if (!impl->initial_configure_received)
gdk_surface_thaw_updates (surface);
else
impl->initial_configure_received = FALSE;
gdk/surface: Replace move_to_rect() with GdkPopupLayout based API Replace the gdk_surface_move_to_rect() API with a new GdkSurface method called gdk_surface_present_popup() taking a new GdkPopupLayout object describing how they should be laid out on screen. The layout properties provided are the same as the ones used with gdk_surface_move_to_rect(), except they are now set up using GdkPopupLayout. Calling gdk_surface_present_popup() will either show the popup at the position described using the popup layout object and a new unconstrained size, or reposition it accordingly. In some situations, such as when a popup is set to autohide, presenting may immediately fail, in case the grab was not granted by the display server. After a successful present, the result of the layout can be queried using the following methods: * gdk_surface_get_position() - to get the position relative to its parent * gdk_surface_get_width() - to get the current width * gdk_surface_get_height() - to get the current height * gdk_surface_get_rect_anchor() - to get the anchor point on the anchor rectangle the popup was effectively positioned against given constraints defined by the environment and the layout rules provided via GdkPopupLayout. * gdk_surface_get_surface_anchor() - the same as the one above but for the surface anchor. A new signal replaces the old "moved-to-rect" one - "popup-layout-changed". However, it is only intended to be emitted when the layout changes implicitly by the windowing system, for example if the monitor resolution changed, or the parent window moved.
2020-02-16 11:59:24 +00:00
}
if (impl->display_server.zxdg_surface_v6)
{
g_clear_pointer (&impl->display_server.zxdg_surface_v6, zxdg_surface_v6_destroy);
if (!impl->initial_configure_received)
gdk_surface_thaw_updates (surface);
else
impl->initial_configure_received = FALSE;
}
wl_surface_attach (impl->display_server.wl_surface, NULL, 0, 0);
wl_surface_commit (impl->display_server.wl_surface);
gdk/surface: Replace move_to_rect() with GdkPopupLayout based API Replace the gdk_surface_move_to_rect() API with a new GdkSurface method called gdk_surface_present_popup() taking a new GdkPopupLayout object describing how they should be laid out on screen. The layout properties provided are the same as the ones used with gdk_surface_move_to_rect(), except they are now set up using GdkPopupLayout. Calling gdk_surface_present_popup() will either show the popup at the position described using the popup layout object and a new unconstrained size, or reposition it accordingly. In some situations, such as when a popup is set to autohide, presenting may immediately fail, in case the grab was not granted by the display server. After a successful present, the result of the layout can be queried using the following methods: * gdk_surface_get_position() - to get the position relative to its parent * gdk_surface_get_width() - to get the current width * gdk_surface_get_height() - to get the current height * gdk_surface_get_rect_anchor() - to get the anchor point on the anchor rectangle the popup was effectively positioned against given constraints defined by the environment and the layout rules provided via GdkPopupLayout. * gdk_surface_get_surface_anchor() - the same as the one above but for the surface anchor. A new signal replaces the old "moved-to-rect" one - "popup-layout-changed". However, it is only intended to be emitted when the layout changes implicitly by the windowing system, for example if the monitor resolution changed, or the parent window moved.
2020-02-16 11:59:24 +00:00
2023-01-06 18:44:18 +00:00
impl->has_uncommitted_ack_configure = FALSE;
impl->has_pending_subsurface_commits = FALSE;
2023-01-06 18:44:18 +00:00
impl->last_sent_window_geometry = (GdkRectangle) { 0 };
impl->mapped = FALSE;
}
static void
2023-01-06 18:44:18 +00:00
gdk_wayland_surface_hide (GdkSurface *surface)
{
2023-01-06 18:44:18 +00:00
GdkSeat *seat;
2023-01-06 18:44:18 +00:00
seat = gdk_display_get_default_seat (surface->display);
if (seat)
{
if (surface->autohide)
gdk_seat_ungrab (seat);
gdk_wayland_seat_clear_touchpoints (GDK_WAYLAND_SEAT (seat), surface);
}
gdk_wayland_surface_hide_surface (surface);
_gdk_surface_clear_update_area (surface);
}
void
2023-01-06 18:44:18 +00:00
gdk_wayland_surface_move_resize (GdkSurface *surface,
int x,
int y,
int width,
int height)
{
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
2023-01-06 18:44:18 +00:00
surface->x = x;
surface->y = y;
gdk_wayland_surface_update_size (surface, width, height, &impl->scale);
2023-01-06 18:44:18 +00:00
}
2023-01-06 18:44:18 +00:00
static void
gdk_wayland_surface_get_geometry (GdkSurface *surface,
int *x,
int *y,
int *width,
int *height)
{
if (!GDK_SURFACE_DESTROYED (surface))
{
2023-01-06 18:44:18 +00:00
if (x)
*x = surface->x;
if (y)
*y = surface->y;
if (width)
*width = surface->width;
if (height)
*height = surface->height;
}
}
static void
2023-01-06 18:44:18 +00:00
gdk_wayland_surface_get_root_coords (GdkSurface *surface,
int x,
int y,
int *root_x,
int *root_y)
{
2023-01-06 18:44:18 +00:00
/*
* Wayland does not have a global coordinate space shared between surfaces. In
* fact, for regular toplevels, we have no idea where our surfaces are
* positioned, relatively.
*
* However, there are some cases like popups and subsurfaces where we do have
* some amount of control over the placement of our surface, and we can
* semi-accurately control the x/y position of these surfaces, if they are
* relative to another surface.
*
* To pretend we have something called a root coordinate space, assume all
* parent-less surfaces are positioned in (0, 0), and all relative positioned
* popups and subsurfaces are placed within this fake root coordinate space.
*
* For example a 200x200 large toplevel surface will have the position (0, 0).
* If a popup positioned in the middle of the toplevel will have the fake
* position (100,100). Furthermore, if a positioned is placed in the middle
* that popup, will have the fake position (150,150), even though it has the
* relative position (50,50). These three surfaces would make up one single
* fake root coordinate space.
*/
2023-01-06 18:44:18 +00:00
if (root_x)
*root_x = surface->x + x;
gdk/surface: Replace move_to_rect() with GdkPopupLayout based API Replace the gdk_surface_move_to_rect() API with a new GdkSurface method called gdk_surface_present_popup() taking a new GdkPopupLayout object describing how they should be laid out on screen. The layout properties provided are the same as the ones used with gdk_surface_move_to_rect(), except they are now set up using GdkPopupLayout. Calling gdk_surface_present_popup() will either show the popup at the position described using the popup layout object and a new unconstrained size, or reposition it accordingly. In some situations, such as when a popup is set to autohide, presenting may immediately fail, in case the grab was not granted by the display server. After a successful present, the result of the layout can be queried using the following methods: * gdk_surface_get_position() - to get the position relative to its parent * gdk_surface_get_width() - to get the current width * gdk_surface_get_height() - to get the current height * gdk_surface_get_rect_anchor() - to get the anchor point on the anchor rectangle the popup was effectively positioned against given constraints defined by the environment and the layout rules provided via GdkPopupLayout. * gdk_surface_get_surface_anchor() - the same as the one above but for the surface anchor. A new signal replaces the old "moved-to-rect" one - "popup-layout-changed". However, it is only intended to be emitted when the layout changes implicitly by the windowing system, for example if the monitor resolution changed, or the parent window moved.
2020-02-16 11:59:24 +00:00
2023-01-06 18:44:18 +00:00
if (root_y)
*root_y = surface->y + y;
}
2023-01-06 18:44:18 +00:00
static gboolean
gdk_wayland_surface_get_device_state (GdkSurface *surface,
GdkDevice *device,
double *x,
double *y,
GdkModifierType *mask)
{
2023-01-06 18:44:18 +00:00
if (GDK_SURFACE_DESTROYED (surface))
return FALSE;
2023-01-06 18:44:18 +00:00
gdk_wayland_device_query_state (device, surface, x, y, mask);
return *x >= 0 && *y >= 0 && *x < surface->width && *y < surface->height;
}
static void
2023-01-06 18:44:18 +00:00
gdk_wayland_surface_set_input_region (GdkSurface *surface,
cairo_region_t *input_region)
{
2023-01-06 18:44:18 +00:00
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
2023-01-06 18:44:18 +00:00
if (GDK_SURFACE_DESTROYED (surface))
return;
2023-01-06 18:44:18 +00:00
g_clear_pointer (&impl->input_region, cairo_region_destroy);
2023-01-06 18:44:18 +00:00
if (input_region)
impl->input_region = cairo_region_copy (input_region);
2023-01-06 18:44:18 +00:00
impl->input_region_dirty = TRUE;
}
static void
2023-01-06 18:44:18 +00:00
gdk_wayland_surface_destroy (GdkSurface *surface,
gboolean foreign_destroy)
{
2023-01-06 18:44:18 +00:00
GdkFrameClock *frame_clock;
2023-01-06 18:44:18 +00:00
/* Wayland surfaces can't be externally destroyed; we may possibly
* eventually want to use this path at display close-down
*/
g_return_if_fail (!foreign_destroy);
2023-01-06 18:44:18 +00:00
gdk_wayland_surface_hide_surface (surface);
if (GDK_IS_TOPLEVEL (surface))
gdk_wayland_toplevel_destroy (GDK_TOPLEVEL (surface));
gdk_wayland_surface_destroy_wl_surface (GDK_WAYLAND_SURFACE (surface));
2023-01-06 18:44:18 +00:00
frame_clock = gdk_surface_get_frame_clock (surface);
g_signal_handlers_disconnect_by_func (frame_clock, on_frame_clock_before_paint, surface);
g_signal_handlers_disconnect_by_func (frame_clock, on_frame_clock_after_paint, surface);
}
static void
2023-01-06 18:44:18 +00:00
gdk_wayland_surface_destroy_notify (GdkSurface *surface)
{
2023-01-06 18:44:18 +00:00
if (!GDK_SURFACE_DESTROYED (surface))
{
2023-01-06 18:44:18 +00:00
g_warning ("GdkSurface %p unexpectedly destroyed", surface);
_gdk_surface_destroy (surface, TRUE);
}
2023-01-06 18:44:18 +00:00
g_object_unref (surface);
}
static double
gdk_wayland_surface_get_scale (GdkSurface *surface)
{
2023-01-06 18:44:18 +00:00
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
return gdk_fractional_scale_to_double (&impl->scale);
}
static void
2023-01-06 18:44:18 +00:00
gdk_wayland_surface_set_opaque_region (GdkSurface *surface,
cairo_region_t *region)
{
2023-01-06 18:44:18 +00:00
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
2023-01-06 18:44:18 +00:00
if (GDK_SURFACE_DESTROYED (surface))
return;
g_clear_pointer (&impl->opaque_region, cairo_region_destroy);
impl->opaque_region = cairo_region_reference (region);
impl->opaque_region_dirty = TRUE;
}
2023-01-06 21:35:16 +00:00
static void
gdk_wayland_surface_default_handle_configure (GdkWaylandSurface *surface)
{
}
static void
gdk_wayland_surface_default_handle_frame (GdkWaylandSurface *surface)
{
}
static void
gdk_wayland_surface_default_hide_surface (GdkWaylandSurface *surface)
{
}
static void
2023-01-06 18:44:18 +00:00
gdk_wayland_surface_class_init (GdkWaylandSurfaceClass *klass)
{
2023-01-06 18:44:18 +00:00
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GdkSurfaceClass *surface_class = GDK_SURFACE_CLASS (klass);
2023-01-06 18:44:18 +00:00
object_class->constructed = gdk_wayland_surface_constructed;
object_class->dispose = gdk_wayland_surface_dispose;
object_class->finalize = gdk_wayland_surface_finalize;
surface_class->hide = gdk_wayland_surface_hide;
surface_class->get_geometry = gdk_wayland_surface_get_geometry;
surface_class->get_root_coords = gdk_wayland_surface_get_root_coords;
surface_class->get_device_state = gdk_wayland_surface_get_device_state;
surface_class->set_input_region = gdk_wayland_surface_set_input_region;
surface_class->destroy = gdk_wayland_surface_destroy;
surface_class->beep = gdk_wayland_surface_beep;
surface_class->destroy_notify = gdk_wayland_surface_destroy_notify;
surface_class->drag_begin = _gdk_wayland_surface_drag_begin;
surface_class->get_scale = gdk_wayland_surface_get_scale;
surface_class->set_opaque_region = gdk_wayland_surface_set_opaque_region;
surface_class->request_layout = gdk_wayland_surface_request_layout;
surface_class->create_subsurface = gdk_wayland_surface_create_subsurface;
2023-01-06 21:35:16 +00:00
klass->handle_configure = gdk_wayland_surface_default_handle_configure;
klass->handle_frame = gdk_wayland_surface_default_handle_frame;
klass->hide_surface = gdk_wayland_surface_default_hide_surface;
}
2023-01-06 18:44:18 +00:00
/* }}} */
/* {{{ Private Surface API */
2023-01-06 18:44:18 +00:00
struct wl_output *
gdk_wayland_surface_get_wl_output (GdkSurface *surface)
{
2023-01-06 18:44:18 +00:00
GdkWaylandSurface *impl;
2023-01-06 18:44:18 +00:00
g_return_val_if_fail (GDK_IS_WAYLAND_SURFACE (surface), NULL);
impl = GDK_WAYLAND_SURFACE (surface);
/* We pick the head of the list as this is the last entered output */
if (impl->display_server.outputs)
return (struct wl_output *) impl->display_server.outputs->data;
return NULL;
}
2023-01-06 18:44:18 +00:00
void
_gdk_wayland_surface_offset_next_wl_buffer (GdkSurface *surface,
int x,
int y)
{
2023-01-06 18:44:18 +00:00
GdkWaylandSurface *impl;
2023-01-06 18:44:18 +00:00
g_return_if_fail (GDK_IS_WAYLAND_SURFACE (surface));
2023-01-06 18:44:18 +00:00
impl = GDK_WAYLAND_SURFACE (surface);
2023-01-06 18:44:18 +00:00
impl->pending_buffer_offset_x = x;
impl->pending_buffer_offset_y = y;
}
2023-01-06 18:44:18 +00:00
void
gdk_wayland_surface_ensure_wl_egl_window (GdkSurface *surface)
2014-02-07 22:16:40 +00:00
{
2023-01-06 18:44:18 +00:00
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
2014-02-07 22:16:40 +00:00
2023-01-06 18:44:18 +00:00
if (impl->display_server.egl_window == NULL)
{
int width, height;
get_egl_window_size (surface, &width, &height);
2023-01-06 18:44:18 +00:00
impl->display_server.egl_window =
wl_egl_window_create (impl->display_server.wl_surface, width, height);
2023-01-06 18:44:18 +00:00
gdk_surface_set_egl_native_window (surface, impl->display_server.egl_window);
}
}
2023-01-06 18:44:18 +00:00
/* }}} */
/* {{{ Surface API */
2015-02-28 05:15:13 +00:00
2023-01-06 18:44:18 +00:00
/**
* gdk_wayland_surface_get_wl_surface: (skip)
* @surface: (type GdkWaylandSurface): a `GdkSurface`
*
* Returns the Wayland `wl_surface` of a `GdkSurface`.
*
* Returns: (transfer none): a Wayland `wl_surface`
*/
struct wl_surface *
gdk_wayland_surface_get_wl_surface (GdkSurface *surface)
{
2023-01-06 18:44:18 +00:00
g_return_val_if_fail (GDK_IS_WAYLAND_SURFACE (surface), NULL);
2014-02-07 22:16:40 +00:00
2023-01-06 18:44:18 +00:00
return GDK_WAYLAND_SURFACE (surface)->display_server.wl_surface;
}
2023-01-06 18:44:18 +00:00
/* }}}} */
/* vim:set foldmethod=marker expandtab: */