From 70f649c42490d1766a03f383859ca17d38b151db Mon Sep 17 00:00:00 2001 From: Aleksey Samoilov Date: Fri, 17 Jul 2026 18:35:41 +0400 Subject: [PATCH] [Wayland] Add support for presentation-time protocol --- cogl/cogl/winsys/cogl-winsys-egl.c | 11 + src/meson.build | 3 + .../meta-wayland-presentation-time-private.h | 68 +++ src/wayland/meta-wayland-presentation-time.c | 392 ++++++++++++++++++ src/wayland/meta-wayland-private.h | 3 + src/wayland/meta-wayland-surface.c | 30 ++ src/wayland/meta-wayland-surface.h | 9 + src/wayland/meta-wayland-versions.h | 1 + src/wayland/meta-wayland.c | 21 + src/wayland/meta-wayland.h | 6 + 10 files changed, 544 insertions(+) create mode 100644 src/wayland/meta-wayland-presentation-time-private.h create mode 100644 src/wayland/meta-wayland-presentation-time.c diff --git a/cogl/cogl/winsys/cogl-winsys-egl.c b/cogl/cogl/winsys/cogl-winsys-egl.c index 0d24824d8..87088958a 100644 --- a/cogl/cogl/winsys/cogl-winsys-egl.c +++ b/cogl/cogl/winsys/cogl-winsys-egl.c @@ -52,6 +52,7 @@ #include #include #include +#include #ifndef EGL_KHR_create_context @@ -842,6 +843,15 @@ _cogl_winsys_fence_destroy (CoglContext *context, void *fence) } #endif +static int64_t +_cogl_winsys_get_clock_time (CoglContext *context) +{ + struct timespec ts; + + clock_gettime (CLOCK_MONOTONIC, &ts); + return ts.tv_sec * G_GINT64_CONSTANT (1000000000) + ts.tv_nsec; +} + static CoglWinsysVtable _cogl_winsys_vtable = { .constraints = COGL_RENDERER_CONSTRAINT_USES_EGL, @@ -863,6 +873,7 @@ static CoglWinsysVtable _cogl_winsys_vtable = _cogl_winsys_onscreen_swap_buffers_with_damage, .onscreen_swap_region = _cogl_winsys_onscreen_swap_region, .onscreen_get_buffer_age = _cogl_winsys_onscreen_get_buffer_age, + .context_get_clock_time = _cogl_winsys_get_clock_time, #if defined(EGL_KHR_fence_sync) || defined(EGL_KHR_reusable_sync) .fence_add = _cogl_winsys_fence_add, diff --git a/src/meson.build b/src/meson.build index 796282e86..1fc1e713d 100644 --- a/src/meson.build +++ b/src/meson.build @@ -554,6 +554,8 @@ if have_wayland 'wayland/meta-wayland-pointer.h', 'wayland/meta-wayland-popup.c', 'wayland/meta-wayland-popup.h', + 'wayland/meta-wayland-presentation-time.c', + 'wayland/meta-wayland-presentation-time-private.h', 'wayland/meta-wayland-private.h', 'wayland/meta-wayland-region.c', 'wayland/meta-wayland-region.h', @@ -858,6 +860,7 @@ if have_wayland ['xapp-shell', 'private', ], ['xwayland-keyboard-grab', 'unstable', 'v1', ], ['wayland-drm', 'private', ], + ['presentation-time', 'stable', ], ] if have_wayland_eglstream wayland_eglstream_protocols_dir = wayland_eglstream_protocols_dep.get_variable(pkgconfig: 'pkgdatadir') diff --git a/src/wayland/meta-wayland-presentation-time-private.h b/src/wayland/meta-wayland-presentation-time-private.h new file mode 100644 index 000000000..4de8ce8db --- /dev/null +++ b/src/wayland/meta-wayland-presentation-time-private.h @@ -0,0 +1,68 @@ +/* + * presentation-time protocol + * + * Copyright (C) 2020 Ivan Molodetskikh + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ + +#pragma once + +#include + +#include "clutter/clutter.h" +#include "wayland/meta-wayland-types.h" + +typedef struct _CoglContext CoglContext; + +typedef struct _MetaWaylandPresentationFeedback +{ + struct wl_list link; + struct wl_resource *resource; + + MetaWaylandSurface *surface; +} MetaWaylandPresentationFeedback; + +typedef struct _MetaWaylandPresentationTime +{ + /* + * List of surfaces that have pending presentation-time feedbacks. + * Analogous to MetaWaylandCompositor.frame_callback_surfaces. + */ + GList *feedback_surfaces; + + /* + * Feedbacks collected after painting, waiting to be delivered on + * frame completion. Since Muffin has a single frame clock, we don't + * need per-view tracking - all feedbacks go into one list. + */ + struct wl_list pending_feedbacks; + + /* + * CoglContext used for converting frame timestamps from the Cogl + * clock domain to CLOCK_MONOTONIC. + */ + CoglContext *cogl_context; +} MetaWaylandPresentationTime; + +void meta_wayland_presentation_time_finalize (MetaWaylandCompositor *compositor); + +void meta_wayland_init_presentation_time (MetaWaylandCompositor *compositor); + +void meta_wayland_presentation_feedback_discard (MetaWaylandPresentationFeedback *feedback); + +void meta_wayland_surface_discard_presentation_feedback (MetaWaylandSurface *surface); + +void meta_wayland_surface_state_discard_presentation_feedback (MetaWaylandSurfaceState *state); diff --git a/src/wayland/meta-wayland-presentation-time.c b/src/wayland/meta-wayland-presentation-time.c new file mode 100644 index 000000000..22e6cb007 --- /dev/null +++ b/src/wayland/meta-wayland-presentation-time.c @@ -0,0 +1,392 @@ +/* + * presentation-time protocol + * + * Copyright (C) 2020 Ivan Molodetskikh + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ + +#include "config.h" + +#include + +#include "clutter/clutter.h" +#include "cogl/cogl.h" +#include "compositor/meta-surface-actor.h" +#include "wayland/meta-wayland-presentation-time-private.h" +#include "wayland/meta-wayland-private.h" +#include "wayland/meta-wayland-outputs.h" +#include "wayland/meta-wayland-versions.h" + +#include "presentation-time-server-protocol.h" + +static inline uint64_t +ns2s (int64_t ns) +{ + return (uint64_t) ns / 1000000000; +} + +static inline uint32_t +ns2ns_rem (int64_t ns) +{ + return (uint32_t) ((uint64_t) ns % 1000000000); +} + +void +meta_wayland_surface_state_discard_presentation_feedback (MetaWaylandSurfaceState *state) +{ + MetaWaylandPresentationFeedback *feedback, *next; + + wl_list_for_each_safe (feedback, next, + &state->presentation_feedback_list, + link) + { + meta_wayland_presentation_feedback_discard (feedback); + } +} + +void +meta_wayland_surface_discard_presentation_feedback (MetaWaylandSurface *surface) +{ + MetaWaylandPresentationFeedback *feedback, *next; + + wl_list_for_each_safe (feedback, next, + &surface->presentation_time.feedback_list, + link) + { + meta_wayland_presentation_feedback_discard (feedback); + } + + meta_wayland_compositor_remove_presentation_feedback_surface (surface->compositor, + surface); +} + +static void +wp_presentation_feedback_destructor (struct wl_resource *resource) +{ + MetaWaylandPresentationFeedback *feedback = + wl_resource_get_user_data (resource); + + wl_list_remove (&feedback->link); + g_clear_object (&feedback->surface); + g_free (feedback); +} + +static void +wp_presentation_destroy (struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy (resource); +} + +static void +wp_presentation_feedback (struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *surface_resource, + uint32_t callback_id) +{ + MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource); + MetaWaylandSurfaceState *pending; + MetaWaylandPresentationFeedback *feedback; + + feedback = g_new0 (MetaWaylandPresentationFeedback, 1); + wl_list_init (&feedback->link); + feedback->resource = wl_resource_create (client, + &wp_presentation_feedback_interface, + wl_resource_get_version (resource), + callback_id); + wl_resource_set_implementation (feedback->resource, + NULL, + feedback, + wp_presentation_feedback_destructor); + + if (surface == NULL) + { + g_warn_if_reached (); + meta_wayland_presentation_feedback_discard (feedback); + return; + } + + pending = meta_wayland_surface_get_pending_state (surface); + wl_list_insert (&pending->presentation_feedback_list, &feedback->link); + + feedback->surface = g_object_ref (surface); +} + +static const struct wp_presentation_interface +meta_wayland_presentation_interface = { + wp_presentation_destroy, + wp_presentation_feedback, +}; + +static void +wp_presentation_bind (struct wl_client *client, + void *data, + uint32_t version, + uint32_t id) +{ + struct wl_resource *resource; + + resource = wl_resource_create (client, + &wp_presentation_interface, + version, + id); + wl_resource_set_implementation (resource, + &meta_wayland_presentation_interface, + NULL, + NULL); + + wp_presentation_send_clock_id (resource, CLOCK_MONOTONIC); +} + +static void +on_after_paint (ClutterStage *stage, + MetaWaylandCompositor *compositor) +{ + GList *l; + + l = compositor->presentation_time.feedback_surfaces; + while (l) + { + MetaWaylandSurface *surface = l->data; + MetaSurfaceActor *actor; + + l = l->next; + + actor = meta_wayland_surface_get_actor (surface); + if (!actor) + { + /* Surface has no actor; discard its feedbacks */ + meta_wayland_surface_discard_presentation_feedback (surface); + } + else if (!wl_list_empty (&surface->presentation_time.feedback_list)) + { + wl_list_insert_list (&compositor->presentation_time.pending_feedbacks, + &surface->presentation_time.feedback_list); + wl_list_init (&surface->presentation_time.feedback_list); + } + + meta_wayland_compositor_remove_presentation_feedback_surface (compositor, + surface); + } +} + +static MetaWaylandOutput * +get_output_for_feedback (MetaWaylandCompositor *compositor, + MetaWaylandPresentationFeedback *feedback) +{ + GHashTableIter iter; + gpointer key, value; + + /* Find the output that the feedback's surface is on */ + g_hash_table_iter_init (&iter, compositor->outputs); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + MetaWaylandOutput *wayland_output = value; + + if (g_hash_table_contains (feedback->surface->outputs_to_destroy_notify_id, + wayland_output)) + return wayland_output; + } + + /* Fallback: return the first available output */ + g_hash_table_iter_init (&iter, compositor->outputs); + if (g_hash_table_iter_next (&iter, &key, &value)) + return value; + + return NULL; +} + +static void +on_presented (ClutterStage *stage, + CoglFrameEvent frame_event, + ClutterFrameInfo *frame_info, + MetaWaylandCompositor *compositor) +{ + MetaWaylandPresentationFeedback *feedback, *next; + gint64 presentation_time_cogl; + gint64 current_cogl_time; + gint64 current_monotonic_time; + gint64 presentation_time_us; + + if (frame_event != COGL_FRAME_EVENT_COMPLETE) + return; + + if (wl_list_empty (&compositor->presentation_time.pending_feedbacks)) + return; + + presentation_time_cogl = frame_info->presentation_time; + current_cogl_time = cogl_get_clock_time (compositor->presentation_time.cogl_context); + current_monotonic_time = g_get_monotonic_time (); + + if (presentation_time_cogl != 0) + { + presentation_time_us = + current_monotonic_time + (presentation_time_cogl - current_cogl_time) / 1000; + } + else + { + presentation_time_us = current_monotonic_time; + } + + wl_list_for_each_safe (feedback, next, + &compositor->presentation_time.pending_feedbacks, + link) + { + uint64_t time_s; + uint32_t tv_sec_hi, tv_sec_lo, tv_nsec; + uint32_t refresh_interval_ns; + uint32_t seq_hi, seq_lo; + uint32_t flags; + MetaWaylandOutput *output; + const GList *l; + + time_s = ns2s (presentation_time_us * 1000); + tv_sec_hi = (uint32_t) (time_s >> 32); + tv_sec_lo = (uint32_t) time_s; + tv_nsec = ns2ns_rem (presentation_time_us * 1000); + + if (frame_info->refresh_rate > 0) + refresh_interval_ns = + (uint32_t) (0.5 + 1000000000.0 / frame_info->refresh_rate); + else + refresh_interval_ns = 0; + + /* Use a simple monotonic sequence counter per surface */ + feedback->surface->presentation_time.sequence++; + + seq_hi = (uint32_t) (feedback->surface->presentation_time.sequence >> 32); + seq_lo = (uint32_t) (feedback->surface->presentation_time.sequence); + + flags = WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION | + WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK; + + output = get_output_for_feedback (compositor, feedback); + if (output != NULL) + { + for (l = output->resources; l; l = l->next) + { + struct wl_resource *output_resource = l->data; + + if (feedback->resource->client == output_resource->client) + { + wp_presentation_feedback_send_sync_output (feedback->resource, + output_resource); + } + } + } + + wp_presentation_feedback_send_presented (feedback->resource, + tv_sec_hi, + tv_sec_lo, + tv_nsec, + refresh_interval_ns, + seq_hi, + seq_lo, + flags); + + wl_resource_destroy (feedback->resource); + } + + wl_list_init (&compositor->presentation_time.pending_feedbacks); +} + +static void +on_monitors_changed (MetaMonitorManager *manager, + MetaWaylandCompositor *compositor) +{ + /* + * Outputs were re-created; discard all pending feedbacks since the + * output references are now invalid and the frame timing information + * no longer applies to the new output configuration. + */ + MetaWaylandPresentationFeedback *feedback, *next; + + wl_list_for_each_safe (feedback, next, + &compositor->presentation_time.pending_feedbacks, + link) + { + meta_wayland_presentation_feedback_discard (feedback); + } + + wl_list_init (&compositor->presentation_time.pending_feedbacks); +} + +void +meta_wayland_presentation_time_finalize (MetaWaylandCompositor *compositor) +{ + MetaBackend *backend = meta_get_backend (); + MetaMonitorManager *monitor_manager = + meta_backend_get_monitor_manager (backend); + ClutterActor *stage = meta_backend_get_stage (backend); + + g_signal_handlers_disconnect_by_func (stage, on_after_paint, compositor); + g_signal_handlers_disconnect_by_func (stage, on_presented, compositor); + g_signal_handlers_disconnect_by_func (monitor_manager, on_monitors_changed, + compositor); + + /* Discard any remaining pending feedbacks */ + { + MetaWaylandPresentationFeedback *feedback, *next; + + wl_list_for_each_safe (feedback, next, + &compositor->presentation_time.pending_feedbacks, + link) + { + meta_wayland_presentation_feedback_discard (feedback); + } + } + + g_list_free (compositor->presentation_time.feedback_surfaces); + compositor->presentation_time.feedback_surfaces = NULL; +} + +void +meta_wayland_init_presentation_time (MetaWaylandCompositor *compositor) +{ + MetaBackend *backend = meta_get_backend (); + MetaMonitorManager *monitor_manager = + meta_backend_get_monitor_manager (backend); + ClutterActor *stage = meta_backend_get_stage (backend); + ClutterBackend *clutter_backend = clutter_get_default_backend (); + CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend); + + wl_list_init (&compositor->presentation_time.pending_feedbacks); + compositor->presentation_time.feedback_surfaces = NULL; + compositor->presentation_time.cogl_context = cogl_context; + + g_signal_connect (monitor_manager, "monitors-changed-internal", + G_CALLBACK (on_monitors_changed), compositor); + + g_signal_connect (stage, "after-paint", + G_CALLBACK (on_after_paint), compositor); + + g_signal_connect (stage, "presented", + G_CALLBACK (on_presented), compositor); + + if (wl_global_create (compositor->wayland_display, + &wp_presentation_interface, + META_WP_PRESENTATION_VERSION, + NULL, + wp_presentation_bind) == NULL) + g_error ("Failed to register a global wp_presentation object"); +} + +void +meta_wayland_presentation_feedback_discard (MetaWaylandPresentationFeedback *feedback) +{ + wp_presentation_feedback_send_discarded (feedback->resource); + wl_resource_destroy (feedback->resource); +} diff --git a/src/wayland/meta-wayland-private.h b/src/wayland/meta-wayland-private.h index b9db986b6..617feecbd 100644 --- a/src/wayland/meta-wayland-private.h +++ b/src/wayland/meta-wayland-private.h @@ -28,6 +28,7 @@ #include "meta/meta-cursor-tracker.h" #include "wayland/meta-wayland-dma-buf.h" #include "wayland/meta-wayland-pointer-gestures.h" +#include "wayland/meta-wayland-presentation-time-private.h" #include "wayland/meta-wayland-seat.h" #include "wayland/meta-wayland-surface.h" #include "wayland/meta-wayland-tablet-manager.h" @@ -92,6 +93,8 @@ struct _MetaWaylandCompositor MetaWaylandDmaBufManager *dma_buf_manager; GHashTable *scheduled_surface_associations; + + MetaWaylandPresentationTime presentation_time; }; #define META_TYPE_WAYLAND_COMPOSITOR (meta_wayland_compositor_get_type ()) diff --git a/src/wayland/meta-wayland-surface.c b/src/wayland/meta-wayland-surface.c index 1cb39fb00..ae7979096 100644 --- a/src/wayland/meta-wayland-surface.c +++ b/src/wayland/meta-wayland-surface.c @@ -469,6 +469,7 @@ meta_wayland_surface_state_set_default (MetaWaylandSurfaceState *state) state->surface_damage = cairo_region_create (); state->buffer_damage = cairo_region_create (); wl_list_init (&state->frame_callback_list); + wl_list_init (&state->presentation_feedback_list); state->has_new_geometry = FALSE; state->has_acked_configure_serial = FALSE; @@ -498,6 +499,8 @@ meta_wayland_surface_state_clear (MetaWaylandSurfaceState *state) wl_list_for_each_safe (cb, next, &state->frame_callback_list, link) wl_resource_destroy (cb->resource); + meta_wayland_surface_state_discard_presentation_feedback (state); + if (state->subsurface_placement_ops) { g_slist_free_full ( @@ -534,6 +537,13 @@ meta_wayland_surface_state_merge_into (MetaWaylandSurfaceState *from, wl_list_insert_list (&to->frame_callback_list, &from->frame_callback_list); + /* A new commit indicates a new content update, so any previous + * content update did not go on screen and needs to be discarded. + */ + meta_wayland_surface_state_discard_presentation_feedback (to); + wl_list_insert_list (&to->presentation_feedback_list, + &from->presentation_feedback_list); + cairo_region_union (to->surface_damage, from->surface_damage); cairo_region_union (to->buffer_damage, from->buffer_damage); cairo_region_destroy (from->surface_damage); @@ -825,6 +835,21 @@ meta_wayland_surface_apply_state (MetaWaylandSurface *surface, } } + /* Move presentation feedbacks from state to surface. + * Discard any old surface feedbacks first since the new commit + * supersedes them. + */ + meta_wayland_surface_discard_presentation_feedback (surface); + if (!wl_list_empty (&state->presentation_feedback_list)) + { + wl_list_insert_list (&surface->presentation_time.feedback_list, + &state->presentation_feedback_list); + wl_list_init (&state->presentation_feedback_list); + + meta_wayland_compositor_add_presentation_feedback_surface (surface->compositor, + surface); + } + if (state->subsurface_placement_ops) { GSList *l; @@ -1429,6 +1454,9 @@ wl_surface_destructor (struct wl_resource *resource) meta_wayland_compositor_remove_frame_callback_surface (compositor, surface); + /* Clean up presentation-time feedbacks */ + meta_wayland_surface_discard_presentation_feedback (surface); + g_hash_table_foreach (surface->outputs_to_destroy_notify_id, surface_output_disconnect_signal, surface); @@ -1477,6 +1505,8 @@ meta_wayland_surface_create (MetaWaylandCompositor *compositor, wl_surface_destructor); wl_list_init (&surface->unassigned.pending_frame_callback_list); + wl_list_init (&surface->presentation_time.feedback_list); + surface->presentation_time.sequence = 0; surface->outputs_to_destroy_notify_id = g_hash_table_new (NULL, NULL); surface->shortcut_inhibited_seats = g_hash_table_new (NULL, NULL); diff --git a/src/wayland/meta-wayland-surface.h b/src/wayland/meta-wayland-surface.h index 8bb7116f8..26d663c7e 100644 --- a/src/wayland/meta-wayland-surface.h +++ b/src/wayland/meta-wayland-surface.h @@ -99,6 +99,9 @@ struct _MetaWaylandSurfaceState /* wl_surface.frame */ struct wl_list frame_callback_list; + /* wp_presentation_time feedbacks */ + struct wl_list presentation_feedback_list; + MetaRectangle new_geometry; gboolean has_new_geometry; @@ -225,6 +228,12 @@ struct _MetaWaylandSurface int dst_height; } viewport; + /* wp_presentation_time */ + struct { + struct wl_list feedback_list; + uint64_t sequence; + } presentation_time; + /* table of seats for which shortcuts are inhibited */ GHashTable *shortcut_inhibited_seats; }; diff --git a/src/wayland/meta-wayland-versions.h b/src/wayland/meta-wayland-versions.h index b4b3ef2f7..234603a75 100644 --- a/src/wayland/meta-wayland-versions.h +++ b/src/wayland/meta-wayland-versions.h @@ -68,5 +68,6 @@ #define META_ZWLR_LAYER_SHELL_V1_VERSION 4 #define META_XDG_TOPLEVEL_ICON_V1_VERSION 1 #define META_XAPP_SHELL_V1_VERSION 1 +#define META_WP_PRESENTATION_VERSION 2 #endif diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c index f0eba38ed..a92523fe9 100644 --- a/src/wayland/meta-wayland.c +++ b/src/wayland/meta-wayland.c @@ -307,6 +307,25 @@ meta_wayland_compositor_remove_frame_callback_surface (MetaWaylandCompositor *co g_list_remove (compositor->frame_callback_surfaces, surface); } +void +meta_wayland_compositor_add_presentation_feedback_surface (MetaWaylandCompositor *compositor, + MetaWaylandSurface *surface) +{ + if (g_list_find (compositor->presentation_time.feedback_surfaces, surface)) + return; + + compositor->presentation_time.feedback_surfaces = + g_list_prepend (compositor->presentation_time.feedback_surfaces, surface); +} + +void +meta_wayland_compositor_remove_presentation_feedback_surface (MetaWaylandCompositor *compositor, + MetaWaylandSurface *surface) +{ + compositor->presentation_time.feedback_surfaces = + g_list_remove (compositor->presentation_time.feedback_surfaces, surface); +} + static void set_gnome_env (const char *name, const char *value) @@ -485,6 +504,7 @@ meta_wayland_compositor_setup (MetaWaylandCompositor *wayland_compositor) meta_wayland_xapp_shell_init (compositor); meta_wayland_init_cursor_shape (compositor); meta_wayland_init_system_bell (compositor); + meta_wayland_init_presentation_time (compositor); /* The global filter restricts the Xwayland-grab protocol to Xwayland and the * input-method / virtual-keyboard protocols to the launched fcitx, so it is @@ -560,6 +580,7 @@ meta_wayland_finalize (void) compositor = meta_wayland_compositor_get_default (); + meta_wayland_presentation_time_finalize (compositor); meta_xwayland_shutdown (&compositor->xwayland_manager); meta_wayland_im_launcher_destroy (compositor->im_launcher); compositor->im_launcher = NULL; diff --git a/src/wayland/meta-wayland.h b/src/wayland/meta-wayland.h index 3549fb2d5..01dd2107a 100644 --- a/src/wayland/meta-wayland.h +++ b/src/wayland/meta-wayland.h @@ -68,6 +68,12 @@ void meta_wayland_compositor_add_frame_callback_surface (Meta void meta_wayland_compositor_remove_frame_callback_surface (MetaWaylandCompositor *compositor, MetaWaylandSurface *surface); +void meta_wayland_compositor_add_presentation_feedback_surface (MetaWaylandCompositor *compositor, + MetaWaylandSurface *surface); + +void meta_wayland_compositor_remove_presentation_feedback_surface (MetaWaylandCompositor *compositor, + MetaWaylandSurface *surface); + META_EXPORT_TEST const char *meta_wayland_get_wayland_display_name (MetaWaylandCompositor *compositor);