diff --git a/include/wlr/types/wlr_tearing_control_v1.h b/include/wlr/types/wlr_tearing_control_v1.h new file mode 100644 index 000000000..31cc5bef5 --- /dev/null +++ b/include/wlr/types/wlr_tearing_control_v1.h @@ -0,0 +1,60 @@ +/* + * This an unstable interface of wlroots. No guarantees are made regarding the + * future consistency of this API. + */ +#ifndef WLR_USE_UNSTABLE +#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" +#endif + +#ifndef WLR_TYPES_WLR_TEARING_CONTROL_MANAGER_V1_H +#define WLR_TYPES_WLR_TEARING_CONTROL_MANAGER_V1_H + +#include +#include +#include +#include + +#include "tearing-control-v1-protocol.h" + +struct wlr_tearing_control_v1 { + uint32_t hint; + struct wl_client *client; + struct wl_list link; + struct wl_resource *resource; + + struct { + struct wl_signal set_hint; + struct wl_signal destroy; + } events; + + struct wlr_surface *surface; + + struct wlr_addon addon; +}; + +struct wlr_tearing_control_manager_v1 { + struct wl_global *global; + + struct wl_list surface_hints; // wlr_tearing_control_v1.link + + struct wl_listener display_destroy; + struct { + struct wl_signal new_object; // struct wlr_tearing_control_v1* + struct wl_signal destroy; + } events; + + void *data; +}; + +struct wlr_tearing_control_manager_v1 *wlr_tearing_control_manager_v1_create( + struct wl_display *display, uint32_t version); + +/** + * Returns the tearing hint for a given surface + */ +enum wp_tearing_control_v1_presentation_hint +wlr_tearing_control_manager_v1_surface_hint_from_surface( + struct wlr_tearing_control_manager_v1 *manager, + struct wlr_surface *surface); + +#endif diff --git a/protocol/meson.build b/protocol/meson.build index 3f37f3262..ed6829e97 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -27,6 +27,7 @@ protocols = { 'single-pixel-buffer-v1': wl_protocol_dir / 'staging/single-pixel-buffer/single-pixel-buffer-v1.xml', 'xdg-activation-v1': wl_protocol_dir / 'staging/xdg-activation/xdg-activation-v1.xml', 'xwayland-shell-v1': wl_protocol_dir / 'staging/xwayland-shell/xwayland-shell-v1.xml', + 'tearing-control-v1': wl_protocol_dir / 'staging/tearing-control/tearing-control-v1.xml', # Unstable upstream protocols 'fullscreen-shell-unstable-v1': wl_protocol_dir / 'unstable/fullscreen-shell/fullscreen-shell-unstable-v1.xml', diff --git a/types/meson.build b/types/meson.build index de33a8b50..96c506c2e 100644 --- a/types/meson.build +++ b/types/meson.build @@ -91,6 +91,7 @@ wlr_files += files( 'wlr_xdg_foreign_v2.c', 'wlr_xdg_foreign_registry.c', 'wlr_xdg_output_v1.c', + 'wlr_tearing_control_v1.c', ) if features.get('drm-backend') diff --git a/types/wlr_tearing_control_v1.c b/types/wlr_tearing_control_v1.c new file mode 100644 index 000000000..d11b12800 --- /dev/null +++ b/types/wlr_tearing_control_v1.c @@ -0,0 +1,204 @@ +#define _POSIX_C_SOURCE 200809L + +#include +#include +#include +#include +#include +#include + +#include "tearing-control-v1-protocol.h" + +#define TEARING_CONTROL_MANAGER_VERSION 1 + +static const struct wp_tearing_control_manager_v1_interface tearing_impl; +static const struct wp_tearing_control_v1_interface tearing_control_impl; + +static struct wlr_tearing_control_manager_v1 *tearing_manager_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, &wp_tearing_control_manager_v1_interface, + &tearing_impl)); + return wl_resource_get_user_data(resource); +} + +static struct wlr_tearing_control_v1 *tearing_surface_hint_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, &wp_tearing_control_v1_interface, + &tearing_control_impl)); + return wl_resource_get_user_data(resource); +} + +static void destroy_tearing_hint(struct wlr_tearing_control_v1 *hint) { + if (hint == NULL) { + return; + } + + wl_signal_emit_mutable(&hint->events.destroy, NULL); + + wl_list_remove(&hint->link); + wl_resource_set_user_data(hint->resource, NULL); + + wlr_addon_finish(&hint->addon); + + free(hint); +} + +static void surface_addon_destroy(struct wlr_addon *addon) { + struct wlr_tearing_control_v1 *hint = wl_container_of(addon, hint, addon); + + destroy_tearing_hint(hint); +} + +static const struct wlr_addon_interface surface_addon_impl = { + .name = "wp_tearing_control_v1", + .destroy = surface_addon_destroy, +}; + +static void resource_handle_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static void destroy_tearing_resource_impl(struct wl_resource *resource) { + struct wlr_tearing_control_v1 *hint = tearing_surface_hint_from_resource(resource); + destroy_tearing_hint(hint); +} + +static void tearing_control_handle_presentation_hint(struct wl_client *client, + struct wl_resource *resource, uint32_t hint) { + struct wlr_tearing_control_v1 *surface_hint = + tearing_surface_hint_from_resource(resource); + + surface_hint->hint = hint; + + wl_signal_emit_mutable(&surface_hint->events.set_hint, NULL); +} + +static const struct wp_tearing_control_v1_interface tearing_control_impl = { + .destroy = resource_handle_destroy, + .set_presentation_hint = tearing_control_handle_presentation_hint +}; + +static void tearing_control_manager_handle_get_tearing_control( + struct wl_client *client, struct wl_resource *resource, uint32_t id, + struct wl_resource *surface_resource) { + struct wlr_tearing_control_manager_v1 *manager = tearing_manager_from_resource(resource); + struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); + + if (wlr_addon_find(&surface->addons, manager, &surface_addon_impl) != NULL) { + wl_resource_post_error(resource, + WP_TEARING_CONTROL_MANAGER_V1_ERROR_TEARING_CONTROL_EXISTS, + "Tearing control object already exists!"); + return; + } + + struct wlr_tearing_control_v1 *hint = calloc(1, sizeof(struct wlr_tearing_control_v1)); + + if (!hint) { + wl_client_post_no_memory(client); + return; + } + + struct wl_resource *created_resource = + wl_resource_create(client, &wp_tearing_control_v1_interface, + wl_resource_get_version(resource), id); + + if (created_resource == NULL) { + wl_resource_post_no_memory(resource); + return; + } + + wl_resource_set_implementation(created_resource, &tearing_control_impl, + hint, destroy_tearing_resource_impl); + + hint->hint = WP_TEARING_CONTROL_V1_PRESENTATION_HINT_VSYNC; + hint->client = client; + hint->resource = created_resource; + hint->surface = surface; + wlr_addon_init(&hint->addon, &hint->surface->addons, manager, &surface_addon_impl); + + wl_signal_init(&hint->events.set_hint); + wl_signal_init(&hint->events.destroy); + + wl_list_insert(&manager->surface_hints, &hint->link); + + wl_signal_emit_mutable(&manager->events.new_object, hint); +} + +static const struct wp_tearing_control_manager_v1_interface tearing_impl = { + .destroy = resource_handle_destroy, + .get_tearing_control = tearing_control_manager_handle_get_tearing_control, +}; + +static void tearing_bind(struct wl_client *wl_client, void *data, + uint32_t version, uint32_t id) { + struct wlr_tearing_control_manager_v1 *manager = data; + + struct wl_resource *wl_resource = wl_resource_create(wl_client, + &wp_tearing_control_manager_v1_interface, version, id); + if (wl_resource == NULL) { + wl_client_post_no_memory(wl_client); + return; + } + wl_resource_set_implementation(wl_resource, &tearing_impl, manager, NULL); +} + +static void handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_tearing_control_manager_v1 *manager = + wl_container_of(listener, manager, display_destroy); + + wl_signal_emit_mutable(&manager->events.destroy, NULL); + + struct wlr_tearing_control_v1 *hint; + wl_list_for_each(hint, &manager->surface_hints, link) { + destroy_tearing_hint(hint); + } + + wl_list_remove(&manager->display_destroy.link); + wl_global_destroy(manager->global); + free(manager); +} + +struct wlr_tearing_control_manager_v1 *wlr_tearing_control_manager_v1_create( + struct wl_display *display, uint32_t version) { + assert(version <= TEARING_CONTROL_MANAGER_VERSION); + + struct wlr_tearing_control_manager_v1 *manager = + calloc(1, sizeof(struct wlr_tearing_control_manager_v1)); + if (!manager) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + return NULL; + } + + wl_signal_init(&manager->events.new_object); + wl_signal_init(&manager->events.destroy); + + wl_list_init(&manager->surface_hints); + + manager->display_destroy.notify = handle_display_destroy; + wl_display_add_destroy_listener(display, &manager->display_destroy); + + manager->global = wl_global_create(display, &wp_tearing_control_manager_v1_interface, + version, manager, tearing_bind); + + if (manager->global == NULL) { + wl_list_remove(&manager->display_destroy.link); + free(manager); + return NULL; + } + return manager; +} + +enum wp_tearing_control_v1_presentation_hint +wlr_tearing_control_manager_v1_surface_hint_from_surface(struct wlr_tearing_control_manager_v1 *manager, + struct wlr_surface *surface) { + struct wlr_addon *addon = + wlr_addon_find(&surface->addons, manager, &surface_addon_impl); + if (addon == NULL) { + return WP_TEARING_CONTROL_V1_PRESENTATION_HINT_VSYNC; + } + + struct wlr_tearing_control_v1 *hint = wl_container_of(addon, hint, addon); + + return hint->hint; +}