diff --git a/include/wlr/types/wlr_fractional_scale_v1.h b/include/wlr/types/wlr_fractional_scale_v1.h new file mode 100644 index 000000000..9126360d9 --- /dev/null +++ b/include/wlr/types/wlr_fractional_scale_v1.h @@ -0,0 +1,34 @@ +/* + * 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_FRACTIONAL_SCALE_V1_H +#define WLR_TYPES_WLR_FRACTIONAL_SCALE_V1_H + +#include + +struct wlr_surface; + +struct wlr_fractional_scale_manager_v1 { + struct wl_global *global; + + struct { + struct wl_signal destroy; + } events; + + // private state + + struct wl_listener display_destroy; +}; + +void wlr_fractional_scale_v1_notify_scale( + struct wlr_surface *surface, double scale); + +struct wlr_fractional_scale_manager_v1 *wlr_fractional_scale_manager_v1_create( + struct wl_display *display, uint32_t version); + +#endif diff --git a/protocol/meson.build b/protocol/meson.build index 965dc342d..f4a91b948 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -22,6 +22,7 @@ protocols = { 'drm-lease-v1': wl_protocol_dir / 'staging/drm-lease/drm-lease-v1.xml', 'ext-idle-notify-v1': wl_protocol_dir / 'staging/ext-idle-notify/ext-idle-notify-v1.xml', 'ext-session-lock-v1': wl_protocol_dir / 'staging/ext-session-lock/ext-session-lock-v1.xml', + 'fractional-scale-v1': wl_protocol_dir / 'staging/fractional-scale/fractional-scale-v1.xml', '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', diff --git a/types/meson.build b/types/meson.build index 5bc217ebb..bcf1073b4 100644 --- a/types/meson.build +++ b/types/meson.build @@ -73,6 +73,7 @@ wlr_files += files( 'wlr_shm.c', 'wlr_single_pixel_buffer_v1.c', 'wlr_subcompositor.c', + 'wlr_fractional_scale_v1.c', 'wlr_switch.c', 'wlr_tablet_pad.c', 'wlr_tablet_tool.c', diff --git a/types/wlr_fractional_scale_v1.c b/types/wlr_fractional_scale_v1.c new file mode 100644 index 000000000..d98527587 --- /dev/null +++ b/types/wlr_fractional_scale_v1.c @@ -0,0 +1,206 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include + +#include "fractional-scale-v1-protocol.h" + +#define FRACTIONAL_SCALE_VERSION 1 + +struct wlr_fractional_scale_v1 { + struct wl_resource *resource; + struct wlr_addon addon; + + // Used for dummy objects to store scale + double scale; +}; + +static const struct wp_fractional_scale_v1_interface fractional_scale_interface; + +static struct wlr_fractional_scale_v1 *fractional_scale_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &wp_fractional_scale_v1_interface, &fractional_scale_interface)); + return wl_resource_get_user_data(resource); +} + +static const struct wp_fractional_scale_manager_v1_interface scale_manager_interface; + +static void fractional_scale_destroy(struct wlr_fractional_scale_v1 *info) { + if (info == NULL) { + return; + } + + // If this is a dummy object then we do not have a wl_resource + if (info->resource != NULL) { + wl_resource_set_user_data(info->resource, NULL); + } + wlr_addon_finish(&info->addon); + free(info); +} + +static void fractional_scale_handle_resource_destroy(struct wl_resource *resource) { + struct wlr_fractional_scale_v1 *info = fractional_scale_from_resource(resource); + fractional_scale_destroy(info); +} + +static void fractional_scale_handle_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static const struct wp_fractional_scale_v1_interface fractional_scale_interface = { + .destroy = fractional_scale_handle_destroy, +}; + +static void fractional_scale_addon_destroy(struct wlr_addon *addon) { + struct wlr_fractional_scale_v1 *info = wl_container_of(addon, info, addon); + fractional_scale_destroy(info); +} + +static const struct wlr_addon_interface fractional_scale_addon_impl = { + .name = "wlr_fractional_scale_v1", + .destroy = fractional_scale_addon_destroy, +}; + +static uint32_t double_to_v120(double d) { + return round(d * 120); +} + +void wlr_fractional_scale_v1_notify_scale(struct wlr_surface *surface, double scale) { + struct wlr_addon *addon = wlr_addon_find(&surface->addons, + NULL, &fractional_scale_addon_impl); + + if (addon == NULL) { + // Create a dummy object to store the scale + struct wlr_fractional_scale_v1 *info = calloc(1, sizeof(*info)); + if (info == NULL) { + return; + } + + wlr_addon_init(&info->addon, &surface->addons, NULL, &fractional_scale_addon_impl); + info->scale = scale; + return; + } + + struct wlr_fractional_scale_v1 *info = wl_container_of(addon, info, addon); + if (info->scale == scale) { + return; + } + + info->scale = scale; + + if (!info->resource) { + // Update existing dummy object + return; + } + + wp_fractional_scale_v1_send_preferred_scale(info->resource, double_to_v120(scale)); +} + +static void handle_get_fractional_scale(struct wl_client *client, + struct wl_resource *mgr_resource, uint32_t id, + struct wl_resource *surface_resource) { + struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); + struct wlr_fractional_scale_v1 *info = NULL; + + // If no fractional_scale object had been created but scale had been set, then + // there will be a dummy object for us to fill out with a resource. Check if + // that's the case. + struct wlr_addon *addon = wlr_addon_find(&surface->addons, NULL, &fractional_scale_addon_impl); + + if (addon != NULL) { + info = wl_container_of(addon, info, addon); + if (info->resource != NULL) { + wl_resource_post_error(mgr_resource, + WP_FRACTIONAL_SCALE_MANAGER_V1_ERROR_FRACTIONAL_SCALE_EXISTS, + "a surface scale object for that surface already exists"); + return; + } + } + + if (info == NULL) { + info = calloc(1, sizeof(*info)); + if (info == NULL) { + wl_client_post_no_memory(client); + return; + } + + wlr_addon_init(&info->addon, &surface->addons, NULL, &fractional_scale_addon_impl); + } + + uint32_t version = wl_resource_get_version(mgr_resource); + info->resource = wl_resource_create(client, &wp_fractional_scale_v1_interface, version, id); + if (info->resource == NULL) { + wl_client_post_no_memory(client); + fractional_scale_destroy(info); + return; + } + wl_resource_set_implementation(info->resource, + &fractional_scale_interface, info, + fractional_scale_handle_resource_destroy); + + if (info->scale != 0) { + wp_fractional_scale_v1_send_preferred_scale(info->resource, double_to_v120(info->scale)); + } +} + +static void fractional_scale_manager_handle_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static const struct wp_fractional_scale_manager_v1_interface scale_manager_interface = { + .destroy = fractional_scale_manager_handle_destroy, + .get_fractional_scale = handle_get_fractional_scale, +}; + +static void fractional_scale_manager_bind(struct wl_client *client, void *data, + uint32_t version, uint32_t id) { + struct wlr_fractional_scale_manager_v1 *mgr = data; + + struct wl_resource *resource = wl_resource_create(client, + &wp_fractional_scale_manager_v1_interface, version, id); + if (resource == NULL) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &scale_manager_interface, mgr, NULL); +} + +static void handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_fractional_scale_manager_v1 *mgr = + wl_container_of(listener, mgr, display_destroy); + wl_signal_emit_mutable(&mgr->events.destroy, NULL); + assert(wl_list_empty(&mgr->events.destroy.listener_list)); + wl_list_remove(&mgr->display_destroy.link); + free(mgr); +} + +struct wlr_fractional_scale_manager_v1 *wlr_fractional_scale_manager_v1_create( + struct wl_display *display, uint32_t version) { + assert(version <= FRACTIONAL_SCALE_VERSION); + + struct wlr_fractional_scale_manager_v1 *mgr = calloc(1, sizeof(*mgr)); + if (mgr == NULL) { + return NULL; + } + + mgr->global = wl_global_create(display, + &wp_fractional_scale_manager_v1_interface, + version, mgr, fractional_scale_manager_bind); + if (mgr->global == NULL) { + free(mgr); + return NULL; + } + + mgr->display_destroy.notify = handle_display_destroy; + wl_display_add_destroy_listener(display, &mgr->display_destroy); + + wl_signal_init(&mgr->events.destroy); + + return mgr; +} +