From 18ebfa5361d586901f48402274492d0d36d5ba4c Mon Sep 17 00:00:00 2001 From: rewine Date: Wed, 24 Jul 2024 16:14:56 +0800 Subject: [PATCH] chore: update souce to 0.18.0 --- backend/backend.c | 189 ++- backend/drm/atomic.c | 298 +++-- backend/drm/backend.c | 112 +- backend/drm/drm.c | 759 +++++++---- backend/drm/fb.c | 258 ++++ backend/drm/legacy.c | 72 +- backend/drm/libliftoff.c | 269 ++-- backend/drm/meson.build | 4 +- backend/drm/monitor.c | 6 +- backend/drm/properties.c | 23 +- backend/drm/renderer.c | 267 +--- backend/drm/util.c | 1 - backend/headless/backend.c | 14 +- backend/headless/output.c | 5 +- backend/libinput/backend.c | 44 +- backend/libinput/events.c | 23 +- backend/libinput/keyboard.c | 4 +- backend/libinput/meson.build | 6 +- backend/libinput/pointer.c | 34 +- backend/libinput/switch.c | 4 +- backend/libinput/tablet_pad.c | 5 +- backend/libinput/tablet_tool.c | 14 +- backend/libinput/touch.c | 4 +- backend/multi/backend.c | 80 +- backend/session/session.c | 39 +- backend/wayland/backend.c | 41 +- backend/wayland/meson.build | 4 +- backend/wayland/output.c | 175 ++- backend/wayland/pointer.c | 17 +- backend/wayland/seat.c | 6 +- backend/wayland/tablet_v2.c | 10 +- backend/x11/backend.c | 36 +- backend/x11/input_device.c | 26 +- backend/x11/output.c | 29 +- docs/env_vars.md | 8 +- examples/cairo-buffer.c | 3 +- examples/embedded.c | 40 +- examples/fullscreen-shell.c | 9 +- examples/output-layers.c | 13 +- examples/output-layout.c | 7 +- examples/pointer.c | 11 +- examples/rotation.c | 4 +- examples/scene-graph.c | 12 +- examples/simple.c | 3 +- examples/tablet.c | 8 +- examples/touch.c | 4 +- include/backend/drm/drm.h | 56 +- include/backend/drm/fb.h | 24 + include/backend/drm/iface.h | 19 +- include/backend/drm/properties.h | 96 +- include/backend/drm/renderer.h | 21 +- include/backend/headless.h | 4 +- include/backend/libinput.h | 7 +- include/backend/multi.h | 2 +- include/backend/wayland.h | 24 +- include/backend/x11.h | 4 +- include/meson.build | 2 +- include/render/allocator/allocator.h | 2 +- include/render/color.h | 39 + include/render/egl.h | 15 +- include/render/gles2.h | 39 +- include/render/pass.h | 9 - include/render/pixel_format.h | 8 +- include/render/pixman.h | 3 - include/render/vulkan.h | 142 +- include/render/wlr_renderer.h | 14 - include/types/wlr_output.h | 4 +- include/types/wlr_tablet_v2.h | 2 +- include/types/wlr_xdg_shell.h | 4 +- include/util/utf8.h | 11 + include/wlr/backend.h | 33 +- include/wlr/backend/drm.h | 10 +- include/wlr/backend/headless.h | 2 +- include/wlr/backend/interface.h | 6 + include/wlr/backend/libinput.h | 3 +- include/wlr/backend/multi.h | 2 +- include/wlr/backend/session.h | 38 +- include/wlr/backend/wayland.h | 10 +- include/wlr/backend/x11.h | 2 +- include/wlr/config.h.in | 2 + include/wlr/interfaces/wlr_output.h | 13 +- include/wlr/meson.build | 2 +- include/wlr/render/allocator.h | 10 + include/wlr/render/color.h | 55 + include/wlr/render/drm_syncobj.h | 112 ++ include/wlr/render/gles2.h | 22 +- include/wlr/render/interface.h | 35 +- include/wlr/render/pass.h | 3 + include/wlr/render/pixman.h | 8 +- include/wlr/render/swapchain.h | 6 + include/wlr/render/vulkan.h | 2 - include/wlr/render/wlr_renderer.h | 92 +- include/wlr/render/wlr_texture.h | 19 + include/wlr/types/wlr_alpha_modifier_v1.h | 33 + include/wlr/types/wlr_buffer.h | 1 + include/wlr/types/wlr_compositor.h | 149 ++- include/wlr/types/wlr_cursor.h | 2 +- include/wlr/types/wlr_cursor_shape_v1.h | 2 + include/wlr/types/wlr_damage_ring.h | 25 + include/wlr/types/wlr_drm.h | 3 + .../types/wlr_ext_foreign_toplevel_list_v1.h | 67 + include/wlr/types/wlr_fullscreen_shell_v1.h | 5 +- include/wlr/types/wlr_idle_notify_v1.h | 12 +- include/wlr/types/wlr_input_device.h | 27 +- include/wlr/types/wlr_input_inhibitor.h | 37 - include/wlr/types/wlr_keyboard.h | 2 +- include/wlr/types/wlr_layer_shell_v1.h | 6 +- include/wlr/types/wlr_linux_dmabuf_v1.h | 20 +- include/wlr/types/wlr_linux_drm_syncobj_v1.h | 46 + include/wlr/types/wlr_matrix.h | 14 +- include/wlr/types/wlr_output.h | 208 +-- include/wlr/types/wlr_output_layer.h | 4 +- include/wlr/types/wlr_output_layout.h | 7 +- include/wlr/types/wlr_output_management_v1.h | 12 + .../wlr/types/wlr_output_swapchain_manager.h | 90 ++ include/wlr/types/wlr_pointer.h | 19 +- .../wlr/types/wlr_pointer_constraints_v1.h | 14 +- include/wlr/types/wlr_presentation_time.h | 8 +- include/wlr/types/wlr_region.h | 14 +- include/wlr/types/wlr_scene.h | 31 +- include/wlr/types/wlr_seat.h | 38 +- include/wlr/types/wlr_security_context_v1.h | 2 +- include/wlr/types/wlr_session_lock_v1.h | 3 + include/wlr/types/wlr_shm.h | 11 +- .../wlr/types/wlr_single_pixel_buffer_v1.h | 9 +- include/wlr/types/wlr_subcompositor.h | 16 +- include/wlr/types/wlr_switch.h | 8 +- include/wlr/types/wlr_tablet_tool.h | 12 +- include/wlr/types/wlr_tablet_v2.h | 4 +- include/wlr/types/wlr_tearing_control_v1.h | 9 +- include/wlr/types/wlr_transient_seat_v1.h | 63 + include/wlr/types/wlr_viewporter.h | 6 +- include/wlr/types/wlr_xdg_decoration_v1.h | 13 +- include/wlr/types/wlr_xdg_foreign_v1.h | 4 +- include/wlr/types/wlr_xdg_foreign_v2.h | 4 +- include/wlr/types/wlr_xdg_shell.h | 24 +- include/wlr/util/log.h | 16 +- include/wlr/util/region.h | 23 +- include/wlr/util/transform.h | 33 + include/wlr/xwayland/xwayland.h | 36 +- include/xwayland/xwm.h | 5 + meson.build | 42 +- meson.build.user | 289 ++++ meson_options.txt | 2 + protocol/meson.build | 11 +- protocol/wlr-input-inhibitor-unstable-v1.xml | 67 - render/allocator/allocator.c | 10 +- render/allocator/drm_dumb.c | 1 - render/allocator/gbm.c | 1 - render/color.c | 36 + render/color_fallback.c | 9 + render/color_lcms2.c | 119 ++ render/dmabuf.c | 1 - render/drm_format_set.c | 8 +- render/drm_syncobj.c | 212 +++ render/egl.c | 21 +- render/gles2/pass.c | 13 +- render/gles2/pixel_format.c | 41 +- render/gles2/renderer.c | 494 ++----- render/gles2/texture.c | 300 +++-- render/meson.build | 27 + render/pass.c | 148 --- render/pixel_format.c | 51 +- render/pixman/pass.c | 118 +- render/pixman/renderer.c | 316 +---- render/swapchain.c | 4 +- render/vulkan/pass.c | 380 +++++- render/vulkan/pixel_format.c | 178 ++- render/vulkan/renderer.c | 1179 ++++++----------- render/vulkan/shaders/output.frag | 35 +- render/vulkan/texture.c | 93 +- render/vulkan/vulkan.c | 15 +- render/wlr_renderer.c | 181 +-- render/wlr_texture.c | 44 + tinywl/Makefile | 25 +- tinywl/tinywl.c | 161 ++- types/buffer/buffer.c | 18 +- types/buffer/client.c | 16 + types/buffer/resource.c | 2 +- types/data_device/wlr_data_offer.c | 3 +- types/data_device/wlr_data_source.c | 1 - types/data_device/wlr_drag.c | 10 +- types/meson.build | 7 +- types/output/cursor.c | 155 +-- types/output/output.c | 270 +--- types/output/render.c | 74 +- types/scene/drag_icon.c | 22 - types/scene/layer_shell_v1.c | 36 +- types/scene/output_layout.c | 4 +- types/scene/subsurface_tree.c | 4 +- types/scene/surface.c | 30 +- types/scene/wlr_scene.c | 359 +++-- types/scene/xdg_shell.c | 30 - types/seat/wlr_seat.c | 3 +- types/seat/wlr_seat_keyboard.c | 8 +- types/seat/wlr_seat_pointer.c | 36 +- types/seat/wlr_seat_touch.c | 38 +- types/tablet_v2/wlr_tablet_v2.c | 12 +- types/tablet_v2/wlr_tablet_v2_pad.c | 6 +- types/tablet_v2/wlr_tablet_v2_tablet.c | 23 +- types/tablet_v2/wlr_tablet_v2_tool.c | 6 +- types/wlr_alpha_modifier_v1.c | 188 +++ types/wlr_compositor.c | 497 +++++-- types/wlr_content_type_v1.c | 26 +- types/wlr_cursor.c | 42 +- types/wlr_cursor_shape_v1.c | 37 +- types/wlr_damage_ring.c | 86 ++ types/wlr_data_control_v1.c | 1 - types/wlr_drm.c | 4 +- types/wlr_drm_lease_v1.c | 2 + types/wlr_ext_foreign_toplevel_list_v1.c | 265 ++++ types/wlr_foreign_toplevel_management_v1.c | 1 - types/wlr_fractional_scale_v1.c | 1 - types/wlr_gamma_control_v1.c | 7 +- types/wlr_idle_notify_v1.c | 9 - types/wlr_input_device.c | 1 - types/wlr_input_inhibitor.c | 137 -- types/wlr_input_method_v2.c | 43 +- types/wlr_keyboard.c | 2 +- types/wlr_keyboard_group.c | 1 - types/wlr_layer_shell_v1.c | 147 +- types/wlr_linux_dmabuf_v1.c | 42 +- types/wlr_linux_drm_syncobj_v1.c | 469 +++++++ types/wlr_output_layout.c | 22 +- types/wlr_output_management_v1.c | 23 + types/wlr_output_swapchain_manager.c | 251 ++++ types/wlr_pointer_constraints_v1.c | 58 +- types/wlr_pointer_gestures_v1.c | 7 +- types/wlr_presentation_time.c | 86 +- types/wlr_primary_selection_v1.c | 1 - types/wlr_region.c | 2 +- types/wlr_screencopy_v1.c | 59 +- types/wlr_security_context_v1.c | 24 +- types/wlr_session_lock_v1.c | 38 +- types/wlr_shm.c | 45 +- types/wlr_single_pixel_buffer_v1.c | 19 +- types/wlr_subcompositor.c | 93 +- types/wlr_tablet_tool.c | 4 +- types/wlr_tearing_control_v1.c | 39 +- types/wlr_text_input_v3.c | 5 +- types/wlr_transient_seat_v1.c | 160 +++ types/wlr_viewporter.c | 15 +- types/wlr_virtual_keyboard_v1.c | 1 - types/wlr_virtual_pointer_v1.c | 2 +- types/wlr_xcursor_manager.c | 1 - types/wlr_xdg_activation_v1.c | 1 - types/wlr_xdg_decoration_v1.c | 61 +- types/wlr_xdg_foreign_v1.c | 26 +- types/wlr_xdg_foreign_v2.c | 26 +- types/xdg_shell/wlr_xdg_popup.c | 87 +- types/xdg_shell/wlr_xdg_positioner.c | 5 + types/xdg_shell/wlr_xdg_shell.c | 2 + types/xdg_shell/wlr_xdg_surface.c | 97 +- types/xdg_shell/wlr_xdg_toplevel.c | 78 +- util/box.c | 20 +- util/log.c | 1 - util/meson.build | 3 +- util/shm.c | 1 - util/time.c | 1 - util/token.c | 1 - {types/output => util}/transform.c | 10 +- util/utf8.c | 66 + xcursor/wlr_xcursor.c | 3 +- xcursor/xcursor.c | 89 +- xwayland/selection/dnd.c | 5 +- xwayland/selection/incoming.c | 1 - xwayland/selection/outgoing.c | 5 +- xwayland/selection/selection.c | 1 - xwayland/server.c | 3 +- xwayland/sockets.c | 3 +- xwayland/xwayland.c | 1 - xwayland/xwm.c | 135 +- 272 files changed, 9043 insertions(+), 5692 deletions(-) create mode 100644 backend/drm/fb.c create mode 100644 include/backend/drm/fb.h create mode 100644 include/render/color.h delete mode 100644 include/render/pass.h create mode 100644 include/util/utf8.h create mode 100644 include/wlr/render/color.h create mode 100644 include/wlr/render/drm_syncobj.h create mode 100644 include/wlr/types/wlr_alpha_modifier_v1.h create mode 100644 include/wlr/types/wlr_ext_foreign_toplevel_list_v1.h delete mode 100644 include/wlr/types/wlr_input_inhibitor.h create mode 100644 include/wlr/types/wlr_linux_drm_syncobj_v1.h create mode 100644 include/wlr/types/wlr_output_swapchain_manager.h create mode 100644 include/wlr/types/wlr_transient_seat_v1.h create mode 100644 include/wlr/util/transform.h create mode 100644 meson.build.user delete mode 100644 protocol/wlr-input-inhibitor-unstable-v1.xml create mode 100644 render/color.c create mode 100644 render/color_fallback.c create mode 100644 render/color_lcms2.c create mode 100644 render/drm_syncobj.c create mode 100644 types/wlr_alpha_modifier_v1.c create mode 100644 types/wlr_ext_foreign_toplevel_list_v1.c delete mode 100644 types/wlr_input_inhibitor.c create mode 100644 types/wlr_linux_drm_syncobj_v1.c create mode 100644 types/wlr_output_swapchain_manager.c create mode 100644 types/wlr_transient_seat_v1.c rename {types/output => util}/transform.c (80%) create mode 100644 util/utf8.c diff --git a/backend/backend.c b/backend/backend.c index e5b8db1..e4e8c8d 100644 --- a/backend/backend.c +++ b/backend/backend.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -16,6 +15,7 @@ #include "backend/backend.h" #include "backend/multi.h" #include "render/allocator/allocator.h" +#include "types/wlr_output.h" #include "util/env.h" #include "util/time.h" @@ -71,9 +71,9 @@ void wlr_backend_destroy(struct wlr_backend *backend) { } } -static struct wlr_session *session_create_and_wait(struct wl_display *disp) { +static struct wlr_session *session_create_and_wait(struct wl_event_loop *loop) { #if WLR_HAS_SESSION - struct wlr_session *session = wlr_session_create(disp); + struct wlr_session *session = wlr_session_create(loop); if (!session) { wlr_log(WLR_ERROR, "Failed to start a session"); @@ -85,11 +85,9 @@ static struct wlr_session *session_create_and_wait(struct wl_display *disp) { int64_t started_at = get_current_time_msec(); int64_t timeout = WAIT_SESSION_TIMEOUT; - struct wl_event_loop *event_loop = - wl_display_get_event_loop(session->display); while (!session->active) { - int ret = wl_event_loop_dispatch(event_loop, (int)timeout); + int ret = wl_event_loop_dispatch(loop, (int)timeout); if (ret < 0) { wlr_log_errno(WLR_ERROR, "Failed to wait for session active: " "wl_event_loop_dispatch failed"); @@ -147,8 +145,55 @@ static size_t parse_outputs_env(const char *name) { return outputs; } -static struct wlr_backend *attempt_wl_backend(struct wl_display *display) { - struct wlr_backend *backend = wlr_wl_backend_create(display, NULL); +/** + * Helper to destroy the multi backend when one of its nested backends is + * destroyed. + */ +struct wlr_auto_backend_monitor { + struct wlr_backend *multi; + struct wlr_backend *primary; + + struct wl_listener multi_destroy; + struct wl_listener primary_destroy; +}; + +static void auto_backend_monitor_destroy(struct wlr_auto_backend_monitor *monitor) { + wl_list_remove(&monitor->multi_destroy.link); + wl_list_remove(&monitor->primary_destroy.link); + free(monitor); +} + +static void monitor_handle_multi_destroy(struct wl_listener *listener, void *data) { + struct wlr_auto_backend_monitor *monitor = wl_container_of(listener, monitor, multi_destroy); + auto_backend_monitor_destroy(monitor); +} + +static void monitor_handle_primary_destroy(struct wl_listener *listener, void *data) { + struct wlr_auto_backend_monitor *monitor = wl_container_of(listener, monitor, primary_destroy); + wlr_backend_destroy(monitor->multi); +} + +static struct wlr_auto_backend_monitor *auto_backend_monitor_create( + struct wlr_backend *multi, struct wlr_backend *primary) { + struct wlr_auto_backend_monitor *monitor = calloc(1, sizeof(*monitor)); + if (monitor == NULL) { + return NULL; + } + + monitor->multi = multi; + monitor->primary = primary; + + monitor->multi_destroy.notify = monitor_handle_multi_destroy; + wl_signal_add(&multi->events.destroy, &monitor->multi_destroy); + + monitor->primary_destroy.notify = monitor_handle_primary_destroy; + wl_signal_add(&primary->events.destroy, &monitor->primary_destroy); + + return monitor; +} + +static struct wlr_backend *attempt_wl_backend(struct wl_event_loop *loop) { + struct wlr_backend *backend = wlr_wl_backend_create(loop, NULL); if (backend == NULL) { return NULL; } @@ -161,10 +206,10 @@ static struct wlr_backend *attempt_wl_backend(struct wl_display *display) { return backend; } -static struct wlr_backend *attempt_x11_backend(struct wl_display *display, +static struct wlr_backend *attempt_x11_backend(struct wl_event_loop *loop, const char *x11_display) { #if WLR_HAS_X11_BACKEND - struct wlr_backend *backend = wlr_x11_backend_create(display, x11_display); + struct wlr_backend *backend = wlr_x11_backend_create(loop, x11_display); if (backend == NULL) { return NULL; } @@ -181,9 +226,8 @@ static struct wlr_backend *attempt_x11_backend(struct wl_display *display, #endif } -static struct wlr_backend *attempt_headless_backend( - struct wl_display *display) { - struct wlr_backend *backend = wlr_headless_backend_create(display); +static struct wlr_backend *attempt_headless_backend(struct wl_event_loop *loop) { + struct wlr_backend *backend = wlr_headless_backend_create(loop); if (backend == NULL) { return NULL; } @@ -196,27 +240,25 @@ static struct wlr_backend *attempt_headless_backend( return backend; } -static bool attempt_drm_backend(struct wl_display *display, - struct wlr_backend *backend, struct wlr_session *session) { +static struct wlr_backend *attempt_drm_backend(struct wlr_backend *backend, struct wlr_session *session) { #if WLR_HAS_DRM_BACKEND struct wlr_device *gpus[8]; ssize_t num_gpus = wlr_session_find_gpus(session, 8, gpus); if (num_gpus < 0) { wlr_log(WLR_ERROR, "Failed to find GPUs"); - return false; + return NULL; } if (num_gpus == 0) { wlr_log(WLR_ERROR, "Found 0 GPUs, cannot create backend"); - return false; + return NULL; } else { wlr_log(WLR_INFO, "Found %zu GPUs", num_gpus); } struct wlr_backend *primary_drm = NULL; for (size_t i = 0; i < (size_t)num_gpus; ++i) { - struct wlr_backend *drm = wlr_drm_backend_create(display, session, - gpus[i], primary_drm); + struct wlr_backend *drm = wlr_drm_backend_create(session, gpus[i], primary_drm); if (!drm) { wlr_log(WLR_ERROR, "Failed to create DRM backend"); continue; @@ -237,37 +279,36 @@ static bool attempt_drm_backend(struct wl_display *display, drm_backend_monitor_create(backend, primary_drm, session); } - return true; + return primary_drm; #else wlr_log(WLR_ERROR, "Cannot create DRM backend: disabled at compile-time"); - return false; + return NULL; #endif } -static struct wlr_backend *attempt_libinput_backend(struct wl_display *display, - struct wlr_session *session) { +static struct wlr_backend *attempt_libinput_backend(struct wlr_session *session) { #if WLR_HAS_LIBINPUT_BACKEND - return wlr_libinput_backend_create(display, session); + return wlr_libinput_backend_create(session); #else wlr_log(WLR_ERROR, "Cannot create libinput backend: disabled at compile-time"); return NULL; #endif } -static bool attempt_backend_by_name(struct wl_display *display, +static bool attempt_backend_by_name(struct wl_event_loop *loop, struct wlr_backend *multi, char *name, struct wlr_session **session_ptr) { struct wlr_backend *backend = NULL; if (strcmp(name, "wayland") == 0) { - backend = attempt_wl_backend(display); + backend = attempt_wl_backend(loop); } else if (strcmp(name, "x11") == 0) { - backend = attempt_x11_backend(display, NULL); + backend = attempt_x11_backend(loop, NULL); } else if (strcmp(name, "headless") == 0) { - backend = attempt_headless_backend(display); + backend = attempt_headless_backend(loop); } else if (strcmp(name, "drm") == 0 || strcmp(name, "libinput") == 0) { // DRM and libinput need a session if (*session_ptr == NULL) { - *session_ptr = session_create_and_wait(display); + *session_ptr = session_create_and_wait(loop); if (*session_ptr == NULL) { wlr_log(WLR_ERROR, "failed to start a session"); return false; @@ -275,10 +316,10 @@ static bool attempt_backend_by_name(struct wl_display *display, } if (strcmp(name, "libinput") == 0) { - backend = attempt_libinput_backend(display, *session_ptr); + backend = attempt_libinput_backend(*session_ptr); } else { // attempt_drm_backend() adds the multi drm backends itself - return attempt_drm_backend(display, multi, *session_ptr); + return attempt_drm_backend(multi, *session_ptr) != NULL; } } else { wlr_log(WLR_ERROR, "unrecognized backend '%s'", name); @@ -291,14 +332,14 @@ static bool attempt_backend_by_name(struct wl_display *display, return wlr_multi_backend_add(multi, backend); } -struct wlr_backend *wlr_backend_autocreate(struct wl_display *display, +struct wlr_backend *wlr_backend_autocreate(struct wl_event_loop *loop, struct wlr_session **session_ptr) { if (session_ptr != NULL) { *session_ptr = NULL; } struct wlr_session *session = NULL; - struct wlr_backend *multi = wlr_multi_backend_create(display); + struct wlr_backend *multi = wlr_multi_backend_create(loop); if (!multi) { wlr_log(WLR_ERROR, "could not allocate multibackend"); return NULL; @@ -318,7 +359,7 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display, char *saveptr; char *name = strtok_r(names, ",", &saveptr); while (name != NULL) { - if (!attempt_backend_by_name(display, multi, name, &session)) { + if (!attempt_backend_by_name(loop, multi, name, &session)) { wlr_log(WLR_ERROR, "failed to add backend '%s'", name); free(names); goto error; @@ -332,37 +373,47 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display, } if (getenv("WAYLAND_DISPLAY") || getenv("WAYLAND_SOCKET")) { - struct wlr_backend *wl_backend = attempt_wl_backend(display); + struct wlr_backend *wl_backend = attempt_wl_backend(loop); if (!wl_backend) { goto error; } - wlr_multi_backend_add(multi, wl_backend); + + if (!auto_backend_monitor_create(multi, wl_backend)) { + goto error; + } + goto success; } const char *x11_display = getenv("DISPLAY"); if (x11_display) { - struct wlr_backend *x11_backend = - attempt_x11_backend(display, x11_display); + struct wlr_backend *x11_backend = attempt_x11_backend(loop, x11_display); if (!x11_backend) { goto error; } - wlr_multi_backend_add(multi, x11_backend); + + if (!auto_backend_monitor_create(multi, x11_backend)) { + goto error; + } + goto success; } // Attempt DRM+libinput - session = session_create_and_wait(display); + session = session_create_and_wait(loop); if (!session) { wlr_log(WLR_ERROR, "Failed to start a DRM session"); goto error; } - struct wlr_backend *libinput = attempt_libinput_backend(display, session); + struct wlr_backend *libinput = attempt_libinput_backend(session); if (libinput) { wlr_multi_backend_add(multi, libinput); + if (!auto_backend_monitor_create(multi, libinput)) { + goto error; + } } else if (env_parse_bool("WLR_LIBINPUT_NO_DEVICES")) { wlr_log(WLR_INFO, "WLR_LIBINPUT_NO_DEVICES is set, " "starting without libinput backend"); @@ -372,11 +423,16 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display, goto error; } - if (!attempt_drm_backend(display, multi, session)) { + struct wlr_backend *primary_drm = attempt_drm_backend(multi, session); + if (primary_drm == NULL) { wlr_log(WLR_ERROR, "Failed to open any DRM device"); goto error; } + if (!auto_backend_monitor_create(multi, primary_drm)) { + goto error; + } + success: if (session_ptr != NULL) { *session_ptr = session; @@ -390,3 +446,52 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display, #endif return NULL; } + +bool wlr_backend_test(struct wlr_backend *backend, + const struct wlr_backend_output_state *states, size_t states_len) { + if (backend->impl->test) { + return backend->impl->test(backend, states, states_len); + } + + for (size_t i = 0; i < states_len; i++) { + const struct wlr_backend_output_state *state = &states[i]; + assert(state->output->backend == backend); + if (!wlr_output_test_state(states[i].output, &state->base)) { + return false; + } + } + + return true; +} + +bool wlr_backend_commit(struct wlr_backend *backend, + const struct wlr_backend_output_state *states, size_t states_len) { + if (!backend->impl->commit) { + for (size_t i = 0; i < states_len; i++) { + const struct wlr_backend_output_state *state = &states[i]; + if (!wlr_output_commit_state(state->output, &state->base)) { + return false; + } + } + + return true; + } + + for (size_t i = 0; i < states_len; i++) { + const struct wlr_backend_output_state *state = &states[i]; + if (!output_prepare_commit(state->output, &state->base)) { + return false; + } + } + + if (!backend->impl->commit(backend, states, states_len)) { + return false; + } + + for (size_t i = 0; i < states_len; i++) { + const struct wlr_backend_output_state *state = &states[i]; + output_apply_commit(state->output, &state->base); + } + + return true; +} diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c index 618a9f4..2649a1a 100644 --- a/backend/drm/atomic.c +++ b/backend/drm/atomic.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -6,6 +5,7 @@ #include #include #include "backend/drm/drm.h" +#include "backend/drm/fb.h" #include "backend/drm/iface.h" #include "backend/drm/util.h" @@ -60,19 +60,26 @@ static void atomic_begin(struct atomic *atom) { } } -static bool atomic_commit(struct atomic *atom, - struct wlr_drm_connector *conn, struct wlr_drm_page_flip *page_flip, - uint32_t flags) { - struct wlr_drm_backend *drm = conn->backend; +static bool atomic_commit(struct atomic *atom, struct wlr_drm_backend *drm, + const struct wlr_drm_device_state *state, + struct wlr_drm_page_flip *page_flip, uint32_t flags) { if (atom->failed) { return false; } int ret = drmModeAtomicCommit(drm->fd, atom->req, flags, page_flip); if (ret != 0) { - wlr_drm_conn_log_errno(conn, - (flags & DRM_MODE_ATOMIC_TEST_ONLY) ? WLR_DEBUG : WLR_ERROR, - "Atomic commit failed"); + enum wlr_log_importance log_level = WLR_ERROR; + if (flags & DRM_MODE_ATOMIC_TEST_ONLY) { + log_level = WLR_DEBUG; + } + + if (state->connectors_len == 1) { + struct wlr_drm_connector *conn = state->connectors[0].connector; + wlr_drm_conn_log_errno(conn, log_level, "Atomic commit failed"); + } else { + wlr_log_errno(log_level, "Atomic commit failed"); + } char *flags_str = atomic_commit_flags_str(flags); wlr_log(WLR_DEBUG, "(Atomic commit flags: %s)", flags_str ? flags_str : ""); @@ -94,15 +101,14 @@ static void atomic_add(struct atomic *atom, uint32_t id, uint32_t prop, uint64_t } } -bool create_mode_blob(struct wlr_drm_backend *drm, - struct wlr_drm_connector *conn, +static bool create_mode_blob(struct wlr_drm_connector *conn, const struct wlr_drm_connector_state *state, uint32_t *blob_id) { if (!state->active) { *blob_id = 0; return true; } - if (drmModeCreatePropertyBlob(drm->fd, &state->mode, + if (drmModeCreatePropertyBlob(conn->backend->fd, &state->mode, sizeof(drmModeModeInfo), blob_id)) { wlr_log_errno(WLR_ERROR, "Unable to create mode property blob"); return false; @@ -111,7 +117,7 @@ bool create_mode_blob(struct wlr_drm_backend *drm, return true; } -bool create_gamma_lut_blob(struct wlr_drm_backend *drm, +static bool create_gamma_lut_blob(struct wlr_drm_backend *drm, size_t size, const uint16_t *lut, uint32_t *blob_id) { if (size == 0) { *blob_id = 0; @@ -201,75 +207,41 @@ static uint64_t pick_max_bpc(struct wlr_drm_connector *conn, struct wlr_drm_fb * return target_bpc; } -static void commit_blob(struct wlr_drm_backend *drm, - uint32_t *current, uint32_t next) { - if (*current == next) { +static void destroy_blob(struct wlr_drm_backend *drm, uint32_t id) { + if (id == 0) { return; } - if (*current != 0) { - drmModeDestroyPropertyBlob(drm->fd, *current); + if (drmModeDestroyPropertyBlob(drm->fd, id) != 0) { + wlr_log_errno(WLR_ERROR, "Failed to destroy blob"); } - *current = next; } -static void rollback_blob(struct wlr_drm_backend *drm, +static void commit_blob(struct wlr_drm_backend *drm, uint32_t *current, uint32_t next) { if (*current == next) { return; } - if (next != 0) { - drmModeDestroyPropertyBlob(drm->fd, next); - } -} - -static void plane_disable(struct atomic *atom, struct wlr_drm_plane *plane) { - uint32_t id = plane->id; - const union wlr_drm_plane_props *props = &plane->props; - atomic_add(atom, id, props->fb_id, 0); - atomic_add(atom, id, props->crtc_id, 0); + destroy_blob(drm, *current); + *current = next; } -static void set_plane_props(struct atomic *atom, struct wlr_drm_backend *drm, - struct wlr_drm_plane *plane, struct wlr_drm_fb *fb, uint32_t crtc_id, - int32_t x, int32_t y) { - uint32_t id = plane->id; - const union wlr_drm_plane_props *props = &plane->props; - - if (fb == NULL) { - wlr_log(WLR_ERROR, "Failed to acquire FB for plane %"PRIu32, plane->id); - atom->failed = true; +static void rollback_blob(struct wlr_drm_backend *drm, + uint32_t *current, uint32_t next) { + if (*current == next) { return; } - - uint32_t width = fb->wlr_buf->width; - uint32_t height = fb->wlr_buf->height; - - // The src_* properties are in 16.16 fixed point - atomic_add(atom, id, props->src_x, 0); - atomic_add(atom, id, props->src_y, 0); - atomic_add(atom, id, props->src_w, (uint64_t)width << 16); - atomic_add(atom, id, props->src_h, (uint64_t)height << 16); - atomic_add(atom, id, props->crtc_w, width); - atomic_add(atom, id, props->crtc_h, height); - atomic_add(atom, id, props->fb_id, fb->id); - atomic_add(atom, id, props->crtc_id, crtc_id); - atomic_add(atom, id, props->crtc_x, (uint64_t)x); - atomic_add(atom, id, props->crtc_y, (uint64_t)y); + destroy_blob(drm, next); } -static bool atomic_crtc_commit(struct wlr_drm_connector *conn, - const struct wlr_drm_connector_state *state, - struct wlr_drm_page_flip *page_flip, uint32_t flags, bool test_only) { +bool drm_atomic_connector_prepare(struct wlr_drm_connector_state *state, bool modeset) { + struct wlr_drm_connector *conn = state->connector; struct wlr_drm_backend *drm = conn->backend; struct wlr_output *output = &conn->output; struct wlr_drm_crtc *crtc = conn->crtc; - bool modeset = state->modeset; - bool active = state->active; - uint32_t mode_id = crtc->mode_id; if (modeset) { - if (!create_mode_blob(drm, conn, state, &mode_id)) { + if (!create_mode_blob(conn, state, &mode_id)) { return false; } } @@ -304,93 +276,213 @@ static bool atomic_crtc_commit(struct wlr_drm_connector *conn, output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED; bool vrr_enabled = prev_vrr_enabled; if ((state->base->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED)) { - if (!drm_connector_supports_vrr(conn)) { + if (state->base->adaptive_sync_enabled && !output->adaptive_sync_supported) { return false; } vrr_enabled = state->base->adaptive_sync_enabled; } - if (test_only) { - flags |= DRM_MODE_ATOMIC_TEST_ONLY; - } - if (modeset) { - flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; + state->mode_id = mode_id; + state->gamma_lut = gamma_lut; + state->fb_damage_clips = fb_damage_clips; + state->vrr_enabled = vrr_enabled; + return true; +} + +void drm_atomic_connector_apply_commit(struct wlr_drm_connector_state *state) { + struct wlr_drm_connector *conn = state->connector; + struct wlr_drm_crtc *crtc = conn->crtc; + struct wlr_drm_backend *drm = conn->backend; + + if (!crtc->own_mode_id) { + crtc->mode_id = 0; // don't try to delete previous master's blobs } - if (!test_only && state->nonblock) { - flags |= DRM_MODE_ATOMIC_NONBLOCK; + crtc->own_mode_id = true; + commit_blob(drm, &crtc->mode_id, state->mode_id); + commit_blob(drm, &crtc->gamma_lut, state->gamma_lut); + + conn->output.adaptive_sync_status = state->vrr_enabled ? + WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED : WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED; + + destroy_blob(drm, state->fb_damage_clips); +} + +void drm_atomic_connector_rollback_commit(struct wlr_drm_connector_state *state) { + struct wlr_drm_connector *conn = state->connector; + struct wlr_drm_crtc *crtc = conn->crtc; + struct wlr_drm_backend *drm = conn->backend; + + rollback_blob(drm, &crtc->mode_id, state->mode_id); + rollback_blob(drm, &crtc->gamma_lut, state->gamma_lut); + + destroy_blob(drm, state->fb_damage_clips); +} + +static void plane_disable(struct atomic *atom, struct wlr_drm_plane *plane) { + uint32_t id = plane->id; + const struct wlr_drm_plane_props *props = &plane->props; + atomic_add(atom, id, props->fb_id, 0); + atomic_add(atom, id, props->crtc_id, 0); +} + +static void set_plane_props(struct atomic *atom, struct wlr_drm_backend *drm, + struct wlr_drm_plane *plane, struct wlr_drm_fb *fb, uint32_t crtc_id, + int32_t x, int32_t y) { + uint32_t id = plane->id; + const struct wlr_drm_plane_props *props = &plane->props; + + if (fb == NULL) { + wlr_log(WLR_ERROR, "Failed to acquire FB for plane %"PRIu32, plane->id); + atom->failed = true; + return; } - struct atomic atom; - atomic_begin(&atom); - atomic_add(&atom, conn->id, conn->props.crtc_id, active ? crtc->id : 0); + uint32_t width = fb->wlr_buf->width; + uint32_t height = fb->wlr_buf->height; + + // The src_* properties are in 16.16 fixed point + atomic_add(atom, id, props->src_x, 0); + atomic_add(atom, id, props->src_y, 0); + atomic_add(atom, id, props->src_w, (uint64_t)width << 16); + atomic_add(atom, id, props->src_h, (uint64_t)height << 16); + atomic_add(atom, id, props->crtc_w, width); + atomic_add(atom, id, props->crtc_h, height); + atomic_add(atom, id, props->fb_id, fb->id); + atomic_add(atom, id, props->crtc_id, crtc_id); + atomic_add(atom, id, props->crtc_x, (uint64_t)x); + atomic_add(atom, id, props->crtc_y, (uint64_t)y); +} + +static bool supports_cursor_hotspots(const struct wlr_drm_plane* plane) { + return plane->props.hotspot_x && plane->props.hotspot_y; +} + +static void atomic_connector_add(struct atomic *atom, + const struct wlr_drm_connector_state *state, bool modeset) { + struct wlr_drm_connector *conn = state->connector; + struct wlr_drm_backend *drm = conn->backend; + struct wlr_drm_crtc *crtc = conn->crtc; + bool active = state->active; + + atomic_add(atom, conn->id, conn->props.crtc_id, active ? crtc->id : 0); if (modeset && active && conn->props.link_status != 0) { - atomic_add(&atom, conn->id, conn->props.link_status, + atomic_add(atom, conn->id, conn->props.link_status, DRM_MODE_LINK_STATUS_GOOD); } if (active && conn->props.content_type != 0) { - atomic_add(&atom, conn->id, conn->props.content_type, + atomic_add(atom, conn->id, conn->props.content_type, DRM_MODE_CONTENT_TYPE_GRAPHICS); } if (modeset && active && conn->props.max_bpc != 0 && conn->max_bpc_bounds[1] != 0) { - atomic_add(&atom, conn->id, conn->props.max_bpc, pick_max_bpc(conn, state->primary_fb)); + atomic_add(atom, conn->id, conn->props.max_bpc, pick_max_bpc(conn, state->primary_fb)); } - atomic_add(&atom, crtc->id, crtc->props.mode_id, mode_id); - atomic_add(&atom, crtc->id, crtc->props.active, active); + atomic_add(atom, crtc->id, crtc->props.mode_id, state->mode_id); + atomic_add(atom, crtc->id, crtc->props.active, active); if (active) { if (crtc->props.gamma_lut != 0) { - atomic_add(&atom, crtc->id, crtc->props.gamma_lut, gamma_lut); + atomic_add(atom, crtc->id, crtc->props.gamma_lut, state->gamma_lut); } if (crtc->props.vrr_enabled != 0) { - atomic_add(&atom, crtc->id, crtc->props.vrr_enabled, vrr_enabled); + atomic_add(atom, crtc->id, crtc->props.vrr_enabled, state->vrr_enabled); } - set_plane_props(&atom, drm, crtc->primary, state->primary_fb, crtc->id, + set_plane_props(atom, drm, crtc->primary, state->primary_fb, crtc->id, 0, 0); if (crtc->primary->props.fb_damage_clips != 0) { - atomic_add(&atom, crtc->primary->id, - crtc->primary->props.fb_damage_clips, fb_damage_clips); + atomic_add(atom, crtc->primary->id, + crtc->primary->props.fb_damage_clips, state->fb_damage_clips); } if (crtc->cursor) { if (drm_connector_is_cursor_visible(conn)) { - set_plane_props(&atom, drm, crtc->cursor, get_next_cursor_fb(conn), + set_plane_props(atom, drm, crtc->cursor, state->cursor_fb, crtc->id, conn->cursor_x, conn->cursor_y); + if (supports_cursor_hotspots(crtc->cursor)) { + atomic_add(atom, crtc->cursor->id, + crtc->cursor->props.hotspot_x, conn->cursor_hotspot_x); + atomic_add(atom, crtc->cursor->id, + crtc->cursor->props.hotspot_y, conn->cursor_hotspot_y); + } } else { - plane_disable(&atom, crtc->cursor); + plane_disable(atom, crtc->cursor); } } } else { - plane_disable(&atom, crtc->primary); + plane_disable(atom, crtc->primary); if (crtc->cursor) { - plane_disable(&atom, crtc->cursor); + plane_disable(atom, crtc->cursor); } } +} - bool ok = atomic_commit(&atom, conn, page_flip, flags); - atomic_finish(&atom); +static bool atomic_device_commit(struct wlr_drm_backend *drm, + const struct wlr_drm_device_state *state, + struct wlr_drm_page_flip *page_flip, uint32_t flags, bool test_only) { + bool ok = false; + + for (size_t i = 0; i < state->connectors_len; i++) { + if (!drm_atomic_connector_prepare(&state->connectors[i], state->modeset)) { + goto out; + } + } + + struct atomic atom; + atomic_begin(&atom); + + for (size_t i = 0; i < state->connectors_len; i++) { + atomic_connector_add(&atom, &state->connectors[i], state->modeset); + } + + if (test_only) { + flags |= DRM_MODE_ATOMIC_TEST_ONLY; + } + if (state->modeset) { + flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; + } + if (!test_only && state->nonblock) { + flags |= DRM_MODE_ATOMIC_NONBLOCK; + } - if (ok && !test_only) { - commit_blob(drm, &crtc->mode_id, mode_id); - commit_blob(drm, &crtc->gamma_lut, gamma_lut); + ok = atomic_commit(&atom, drm, state, page_flip, flags); + atomic_finish(&atom); - if (vrr_enabled != prev_vrr_enabled) { - output->adaptive_sync_status = vrr_enabled ? - WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED : - WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED; - wlr_drm_conn_log(conn, WLR_DEBUG, "VRR %s", - vrr_enabled ? "enabled" : "disabled"); +out: + for (size_t i = 0; i < state->connectors_len; i++) { + struct wlr_drm_connector_state *conn_state = &state->connectors[i]; + if (ok && !test_only) { + drm_atomic_connector_apply_commit(conn_state); + } else { + drm_atomic_connector_rollback_commit(conn_state); } - } else { - rollback_blob(drm, &crtc->mode_id, mode_id); - rollback_blob(drm, &crtc->gamma_lut, gamma_lut); + } + return ok; +} + +bool drm_atomic_reset(struct wlr_drm_backend *drm) { + struct atomic atom; + atomic_begin(&atom); + + for (size_t i = 0; i < drm->num_crtcs; i++) { + struct wlr_drm_crtc *crtc = &drm->crtcs[i]; + atomic_add(&atom, crtc->id, crtc->props.mode_id, 0); + atomic_add(&atom, crtc->id, crtc->props.active, 0); } - if (fb_damage_clips != 0 && - drmModeDestroyPropertyBlob(drm->fd, fb_damage_clips) != 0) { - wlr_log_errno(WLR_ERROR, "Failed to destroy FB_DAMAGE_CLIPS property blob"); + struct wlr_drm_connector *conn; + wl_list_for_each(conn, &drm->connectors, link) { + atomic_add(&atom, conn->id, conn->props.crtc_id, 0); } + for (size_t i = 0; i < drm->num_planes; i++) { + plane_disable(&atom, &drm->planes[i]); + } + + uint32_t flags = DRM_MODE_ATOMIC_ALLOW_MODESET; + bool ok = atomic_commit(&atom, drm, NULL, NULL, flags); + atomic_finish(&atom); + return ok; } const struct wlr_drm_interface atomic_iface = { - .crtc_commit = atomic_crtc_commit, + .commit = atomic_device_commit, + .reset = drm_atomic_reset, }; diff --git a/backend/drm/backend.c b/backend/drm/backend.c index 40597da..d166f46 100644 --- a/backend/drm/backend.c +++ b/backend/drm/backend.c @@ -12,6 +12,7 @@ #include #include #include "backend/drm/drm.h" +#include "backend/drm/fb.h" struct wlr_drm_backend *get_drm_backend_from_backend( struct wlr_backend *wlr_backend) { @@ -46,7 +47,6 @@ static void backend_destroy(struct wlr_backend *backend) { wlr_backend_finish(backend); - wl_list_remove(&drm->display_destroy.link); wl_list_remove(&drm->session_destroy.link); wl_list_remove(&drm->session_active.link); wl_list_remove(&drm->parent_destroy.link); @@ -72,76 +72,56 @@ static void backend_destroy(struct wlr_backend *backend) { static int backend_get_drm_fd(struct wlr_backend *backend) { struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend); - - if (drm->parent) { - return drm->parent->fd; - } else { - return drm->fd; - } + return drm->fd; } -static uint32_t drm_backend_get_buffer_caps(struct wlr_backend *backend) { +static uint32_t backend_get_buffer_caps(struct wlr_backend *backend) { return WLR_BUFFER_CAP_DMABUF; } +static bool backend_test(struct wlr_backend *backend, + const struct wlr_backend_output_state *states, size_t states_len) { + struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend); + return commit_drm_device(drm, states, states_len, true); +} + +static bool backend_commit(struct wlr_backend *backend, + const struct wlr_backend_output_state *states, size_t states_len) { + struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend); + return commit_drm_device(drm, states, states_len, false); +} + static const struct wlr_backend_impl backend_impl = { .start = backend_start, .destroy = backend_destroy, .get_drm_fd = backend_get_drm_fd, - .get_buffer_caps = drm_backend_get_buffer_caps, + .get_buffer_caps = backend_get_buffer_caps, + .test = backend_test, + .commit = backend_commit, }; bool wlr_backend_is_drm(struct wlr_backend *b) { return b->impl == &backend_impl; } +struct wlr_backend *wlr_drm_backend_get_parent(struct wlr_backend *backend) { + struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend); + return drm->parent ? &drm->parent->backend : NULL; +} + static void handle_session_active(struct wl_listener *listener, void *data) { struct wlr_drm_backend *drm = wl_container_of(listener, drm, session_active); struct wlr_session *session = drm->session; - if (session->active) { - wlr_log(WLR_INFO, "DRM fd resumed"); - scan_drm_connectors(drm, NULL); - - // The previous DRM master leaves KMS in an undefined state. We need - // to restore out own state, but be careful to avoid invalid - // configurations. The connector/CRTC mapping may have changed, so - // first disable all CRTCs, then light up the ones we were using - // before the VT switch. - // TODO: use the atomic API to improve restoration after a VT switch - for (size_t i = 0; i < drm->num_crtcs; i++) { - struct wlr_drm_crtc *crtc = &drm->crtcs[i]; - - if (drmModeSetCrtc(drm->fd, crtc->id, 0, 0, 0, NULL, 0, NULL) != 0) { - wlr_log_errno(WLR_ERROR, "Failed to disable CRTC %"PRIu32" after VT switch", - crtc->id); - } - } + wlr_log(WLR_INFO, "DRM FD %s", session->active ? "resumed" : "paused"); - struct wlr_drm_connector *conn; - wl_list_for_each(conn, &drm->connectors, link) { - bool enabled = conn->status != DRM_MODE_DISCONNECTED && conn->output.enabled; - - struct wlr_output_state state; - wlr_output_state_init(&state); - wlr_output_state_set_enabled(&state, enabled); - if (enabled) { - if (conn->output.current_mode != NULL) { - wlr_output_state_set_mode(&state, conn->output.current_mode); - } else { - wlr_output_state_set_custom_mode(&state, - conn->output.width, conn->output.height, conn->output.refresh); - } - } - if (!drm_connector_commit_state(conn, &state)) { - wlr_drm_conn_log(conn, WLR_ERROR, "Failed to restore state after VT switch"); - } - wlr_output_state_finish(&state); - } - } else { - wlr_log(WLR_INFO, "DRM fd paused"); + if (!session->active) { + return; } + + scan_drm_connectors(drm, NULL); + restore_drm_device(drm); } static void handle_dev_change(struct wl_listener *listener, void *data) { @@ -179,26 +159,29 @@ static void handle_session_destroy(struct wl_listener *listener, void *data) { backend_destroy(&drm->backend); } -static void handle_display_destroy(struct wl_listener *listener, void *data) { - struct wlr_drm_backend *drm = - wl_container_of(listener, drm, display_destroy); - backend_destroy(&drm->backend); -} - static void handle_parent_destroy(struct wl_listener *listener, void *data) { struct wlr_drm_backend *drm = wl_container_of(listener, drm, parent_destroy); backend_destroy(&drm->backend); } -struct wlr_backend *wlr_drm_backend_create(struct wl_display *display, - struct wlr_session *session, struct wlr_device *dev, - struct wlr_backend *parent) { - assert(display && session && dev); +struct wlr_backend *wlr_drm_backend_create(struct wlr_session *session, + struct wlr_device *dev, struct wlr_backend *parent) { + assert(session && dev); assert(!parent || wlr_backend_is_drm(parent)); char *name = drmGetDeviceNameFromFd2(dev->fd); + if (name == NULL) { + wlr_log_errno(WLR_ERROR, "drmGetDeviceNameFromFd2() failed"); + return NULL; + } + drmVersion *version = drmGetVersion(dev->fd); + if (version == NULL) { + wlr_log_errno(WLR_ERROR, "drmGetVersion() failed"); + free(name); + return NULL; + } wlr_log(WLR_INFO, "Initializing DRM backend for %s (%s)", name, version->name); drmFreeVersion(version); @@ -233,10 +216,7 @@ struct wlr_backend *wlr_drm_backend_create(struct wl_display *display, drm->dev_remove.notify = handle_dev_remove; wl_signal_add(&dev->events.remove, &drm->dev_remove); - drm->display = display; - - struct wl_event_loop *event_loop = wl_display_get_event_loop(display); - drm->drm_event = wl_event_loop_add_fd(event_loop, drm->fd, + drm->drm_event = wl_event_loop_add_fd(session->event_loop, drm->fd, WL_EVENT_READABLE, handle_drm_event, drm); if (!drm->drm_event) { wlr_log(WLR_ERROR, "Failed to create DRM event source"); @@ -264,7 +244,7 @@ struct wlr_backend *wlr_drm_backend_create(struct wl_display *display, // to be able to texture from them struct wlr_renderer *renderer = drm->mgpu_renderer.wlr_rend; const struct wlr_drm_format_set *texture_formats = - wlr_renderer_get_dmabuf_texture_formats(renderer); + wlr_renderer_get_texture_formats(renderer, WLR_BUFFER_CAP_DMABUF); if (texture_formats == NULL) { wlr_log(WLR_ERROR, "Failed to query renderer texture formats"); goto error_mgpu_renderer; @@ -287,9 +267,6 @@ struct wlr_backend *wlr_drm_backend_create(struct wl_display *display, drm->session_destroy.notify = handle_session_destroy; wl_signal_add(&session->events.destroy, &drm->session_destroy); - drm->display_destroy.notify = handle_display_destroy; - wl_display_add_destroy_listener(display, &drm->display_destroy); - return &drm->backend; error_mgpu_renderer: @@ -304,6 +281,7 @@ struct wlr_backend *wlr_drm_backend_create(struct wl_display *display, wl_list_remove(&drm->dev_change.link); wl_list_remove(&drm->parent_destroy.link); wlr_session_close_file(drm->session, dev); + free(drm->name); free(drm); return NULL; } diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 07880c4..5756f28 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -1,4 +1,3 @@ -#define _XOPEN_SOURCE 700 #include #include #include @@ -16,17 +15,19 @@ #include #include #include -#include #include #include +#include #include #include #include "backend/drm/drm.h" +#include "backend/drm/fb.h" #include "backend/drm/iface.h" #include "backend/drm/util.h" #include "render/pixel_format.h" #include "render/drm_format_set.h" #include "render/wlr_renderer.h" +#include "types/wlr_output.h" #include "util/env.h" #include "config.h" @@ -110,9 +111,16 @@ bool check_drm_features(struct wlr_drm_backend *drm) { wlr_log(WLR_DEBUG, "Using atomic DRM interface"); drm->iface = &atomic_iface; } +#ifdef DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT + if (drm->iface == &atomic_iface && drmSetClientCap(drm->fd, DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT, 1) == 0) { + wlr_log(WLR_INFO, "DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT supported"); + } +#endif if (drm->iface == &legacy_iface) { drm->supports_tearing_page_flips = drmGetCap(drm->fd, DRM_CAP_ASYNC_PAGE_FLIP, &cap) == 0 && cap == 1; + } else { + drm->supports_tearing_page_flips = drmGetCap(drm->fd, DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP, &cap) == 0 && cap == 1; } if (env_parse_bool("WLR_DRM_NO_MODIFIERS")) { @@ -127,11 +135,31 @@ bool check_drm_features(struct wlr_drm_backend *drm) { return true; } +static bool init_plane_cursor_sizes(struct wlr_drm_plane *plane, + const struct drm_plane_size_hint *hints, size_t hints_len) { + assert(hints_len > 0); + plane->cursor_sizes = calloc(hints_len, sizeof(plane->cursor_sizes[0])); + if (plane->cursor_sizes == NULL) { + return false; + } + plane->cursor_sizes_len = hints_len; + + for (size_t i = 0; i < hints_len; i++) { + const struct drm_plane_size_hint hint = hints[i]; + plane->cursor_sizes[i] = (struct wlr_output_cursor_size){ + .width = hint.width, + .height = hint.height, + }; + } + + return true; +} + static bool init_plane(struct wlr_drm_backend *drm, struct wlr_drm_plane *p, const drmModePlane *drm_plane) { uint32_t id = drm_plane->plane_id; - union wlr_drm_plane_props props = {0}; + struct wlr_drm_plane_props props = {0}; if (!get_drm_plane_props(drm->fd, id, &props)) { return false; } @@ -161,13 +189,13 @@ static bool init_plane(struct wlr_drm_backend *drm, uint64_t blob_id; if (!get_drm_prop(drm->fd, p->id, p->props.in_formats, &blob_id)) { wlr_log(WLR_ERROR, "Failed to read IN_FORMATS property"); - goto error; + return false; } drmModePropertyBlobRes *blob = drmModeGetPropertyBlob(drm->fd, blob_id); if (!blob) { wlr_log(WLR_ERROR, "Failed to read IN_FORMATS blob"); - goto error; + return false; } drmModeFormatModifierIterator iter = {0}; @@ -178,6 +206,37 @@ static bool init_plane(struct wlr_drm_backend *drm, drmModeFreePropertyBlob(blob); } + uint64_t size_hints_blob_id = 0; + if (p->props.size_hints) { + if (!get_drm_prop(drm->fd, p->id, p->props.size_hints, &size_hints_blob_id)) { + wlr_log(WLR_ERROR, "Failed to read SIZE_HINTS property"); + return false; + } + } + if (size_hints_blob_id != 0) { + drmModePropertyBlobRes *blob = drmModeGetPropertyBlob(drm->fd, size_hints_blob_id); + if (!blob) { + wlr_log(WLR_ERROR, "Failed to read SIZE_HINTS blob"); + return false; + } + + const struct drm_plane_size_hint *size_hints = blob->data; + size_t size_hints_len = blob->length / sizeof(size_hints[0]); + if (!init_plane_cursor_sizes(p, size_hints, size_hints_len)) { + return false; + } + + drmModeFreePropertyBlob(blob); + } else { + const struct drm_plane_size_hint size_hint = { + .width = drm->cursor_width, + .height = drm->cursor_height, + }; + if (!init_plane_cursor_sizes(p, &size_hint, 1)) { + return false; + } + } + assert(drm->num_crtcs <= 32); for (size_t j = 0; j < drm->num_crtcs; j++) { uint32_t crtc_bit = 1 << j; @@ -197,10 +256,6 @@ static bool init_plane(struct wlr_drm_backend *drm, } return true; - -error: - free(p); - return false; } static bool init_planes(struct wlr_drm_backend *drm) { @@ -304,6 +359,17 @@ bool init_drm_resources(struct wlr_drm_backend *drm) { return false; } +static void drm_plane_finish_surface(struct wlr_drm_plane *plane) { + if (!plane) { + return; + } + + drm_fb_clear(&plane->queued_fb); + drm_fb_clear(&plane->current_fb); + + finish_drm_surface(&plane->mgpu_surf); +} + void finish_drm_resources(struct wlr_drm_backend *drm) { if (!drm) { return; @@ -316,7 +382,7 @@ void finish_drm_resources(struct wlr_drm_backend *drm) { for (size_t i = 0; i < drm->num_crtcs; ++i) { struct wlr_drm_crtc *crtc = &drm->crtcs[i]; - if (crtc->mode_id) { + if (crtc->mode_id && crtc->own_mode_id) { drmModeDestroyPropertyBlob(drm->fd, crtc->mode_id); } if (crtc->gamma_lut) { @@ -397,7 +463,7 @@ static struct wlr_drm_layer *get_or_create_layer(struct wlr_drm_backend *drm, abort(); // unreachable #endif - layer->candidate_planes = calloc(sizeof(bool), drm->num_planes); + layer->candidate_planes = calloc(drm->num_planes, sizeof(layer->candidate_planes[0])); if (layer->candidate_planes == NULL) { #if HAVE_LIBLIFTOFF liftoff_layer_destroy(layer->liftoff); @@ -412,74 +478,146 @@ static struct wlr_drm_layer *get_or_create_layer(struct wlr_drm_backend *drm, return layer; } +void drm_page_flip_destroy(struct wlr_drm_page_flip *page_flip) { + if (!page_flip) { + return; + } + + wl_list_remove(&page_flip->link); + free(page_flip->connectors); + free(page_flip); +} + +static struct wlr_drm_page_flip *drm_page_flip_create(struct wlr_drm_backend *drm, + const struct wlr_drm_device_state *state) { + struct wlr_drm_page_flip *page_flip = calloc(1, sizeof(*page_flip)); + if (page_flip == NULL) { + return NULL; + } + page_flip->connectors_len = state->connectors_len; + page_flip->connectors = + calloc(page_flip->connectors_len, sizeof(page_flip->connectors[0])); + if (page_flip->connectors == NULL) { + free(page_flip); + return NULL; + } + for (size_t i = 0; i < state->connectors_len; i++) { + struct wlr_drm_connector *conn = state->connectors[i].connector; + page_flip->connectors[i] = (struct wlr_drm_page_flip_connector){ + .connector = conn, + .crtc_id = conn->crtc->id, + }; + } + wl_list_insert(&drm->page_flips, &page_flip->link); + return page_flip; +} + +static struct wlr_drm_connector *drm_page_flip_pop( + struct wlr_drm_page_flip *page_flip, uint32_t crtc_id) { + bool found = false; + size_t i; + for (i = 0; i < page_flip->connectors_len; i++) { + if (page_flip->connectors[i].crtc_id == crtc_id) { + found = true; + break; + } + } + if (!found) { + return NULL; + } + + struct wlr_drm_connector *conn = page_flip->connectors[i].connector; + if (i != page_flip->connectors_len - 1) { + page_flip->connectors[i] = page_flip->connectors[page_flip->connectors_len - 1]; + } + page_flip->connectors_len--; + return conn; +} + static void drm_connector_set_pending_page_flip(struct wlr_drm_connector *conn, struct wlr_drm_page_flip *page_flip) { if (conn->pending_page_flip != NULL) { - conn->pending_page_flip->conn = NULL; + struct wlr_drm_page_flip *page_flip = conn->pending_page_flip; + for (size_t i = 0; i < page_flip->connectors_len; i++) { + if (page_flip->connectors[i].connector == conn) { + page_flip->connectors[i].connector = NULL; + } + } } conn->pending_page_flip = page_flip; } -void drm_page_flip_destroy(struct wlr_drm_page_flip *page_flip) { - if (!page_flip) { - return; +static void drm_connector_apply_commit(const struct wlr_drm_connector_state *state, + struct wlr_drm_page_flip *page_flip) { + struct wlr_drm_connector *conn = state->connector; + struct wlr_drm_crtc *crtc = conn->crtc; + + drm_fb_copy(&crtc->primary->queued_fb, state->primary_fb); + if (crtc->cursor != NULL) { + drm_fb_copy(&crtc->cursor->queued_fb, state->cursor_fb); } + drm_fb_clear(&conn->cursor_pending_fb); - wl_list_remove(&page_flip->link); - free(page_flip); + struct wlr_drm_layer *layer; + wl_list_for_each(layer, &crtc->layers, link) { + drm_fb_move(&layer->queued_fb, &layer->pending_fb); + } + + drm_connector_set_pending_page_flip(conn, page_flip); + + if (state->base->committed & WLR_OUTPUT_STATE_MODE) { + conn->refresh = calculate_refresh_rate(&state->mode); + } + + if (!state->active) { + drm_plane_finish_surface(crtc->primary); + drm_plane_finish_surface(crtc->cursor); + drm_fb_clear(&conn->cursor_pending_fb); + + conn->cursor_enabled = false; + conn->crtc = NULL; + } } -static bool drm_crtc_commit(struct wlr_drm_connector *conn, - const struct wlr_drm_connector_state *state, +static void drm_connector_rollback_commit(const struct wlr_drm_connector_state *state) { + struct wlr_drm_crtc *crtc = state->connector->crtc; + + // The set_cursor() hook is a bit special: it's not really synchronized + // to commit() or test(). Once set_cursor() returns true, the new + // cursor is effectively committed. So don't roll it back here, or we + // risk ending up in a state where we don't have a cursor FB but + // wlr_drm_connector.cursor_enabled is true. + // TODO: fix our output interface to avoid this issue. + + struct wlr_drm_layer *layer; + wl_list_for_each(layer, &crtc->layers, link) { + drm_fb_clear(&layer->pending_fb); + } +} + +static bool drm_commit(struct wlr_drm_backend *drm, + const struct wlr_drm_device_state *state, uint32_t flags, bool test_only) { // Disallow atomic-only flags assert((flags & ~DRM_MODE_PAGE_FLIP_FLAGS) == 0); struct wlr_drm_page_flip *page_flip = NULL; if (flags & DRM_MODE_PAGE_FLIP_EVENT) { - page_flip = calloc(1, sizeof(*page_flip)); + page_flip = drm_page_flip_create(drm, state); if (page_flip == NULL) { return false; } - page_flip->conn = conn; - wl_list_insert(&conn->backend->page_flips, &page_flip->link); } - struct wlr_drm_backend *drm = conn->backend; - struct wlr_drm_crtc *crtc = conn->crtc; - bool ok = drm->iface->crtc_commit(conn, state, page_flip, flags, test_only); + bool ok = drm->iface->commit(drm, state, page_flip, flags, test_only); if (ok && !test_only) { - drm_fb_clear(&crtc->primary->queued_fb); - if (state->primary_fb != NULL) { - crtc->primary->queued_fb = drm_fb_lock(state->primary_fb); - } - if (crtc->cursor != NULL && conn->cursor_pending_fb != NULL) { - drm_fb_move(&crtc->cursor->queued_fb, &conn->cursor_pending_fb); - } - - struct wlr_drm_layer *layer; - wl_list_for_each(layer, &crtc->layers, link) { - drm_fb_move(&layer->queued_fb, &layer->pending_fb); - } - - drm_connector_set_pending_page_flip(conn, page_flip); - - if (state->base->committed & WLR_OUTPUT_STATE_MODE) { - conn->refresh = calculate_refresh_rate(&state->mode); + for (size_t i = 0; i < state->connectors_len; i++) { + drm_connector_apply_commit(&state->connectors[i], page_flip); } } else { - // The set_cursor() hook is a bit special: it's not really synchronized - // to commit() or test(). Once set_cursor() returns true, the new - // cursor is effectively committed. So don't roll it back here, or we - // risk ending up in a state where we don't have a cursor FB but - // wlr_drm_connector.cursor_enabled is true. - // TODO: fix our output interface to avoid this issue. - - struct wlr_drm_layer *layer; - wl_list_for_each(layer, &crtc->layers, link) { - drm_fb_clear(&layer->pending_fb); + for (size_t i = 0; i < state->connectors_len; i++) { + drm_connector_rollback_commit(&state->connectors[i]); } - drm_page_flip_destroy(page_flip); } return ok; @@ -489,17 +627,9 @@ static void drm_connector_state_init(struct wlr_drm_connector_state *state, struct wlr_drm_connector *conn, const struct wlr_output_state *base) { *state = (struct wlr_drm_connector_state){ + .connector = conn, .base = base, - .modeset = base->allow_reconfiguration, - .active = (base->committed & WLR_OUTPUT_STATE_ENABLED) ? - base->enabled : conn->output.enabled, - // The wlr_output API requires non-modeset commits with a new buffer to - // wait for the frame event. However compositors often perform - // non-modesets commits without a new buffer without waiting for the - // frame event. In that case we need to make the KMS commit blocking, - // otherwise the kernel will error out with EBUSY. - .nonblock = !base->allow_reconfiguration && - (base->committed & WLR_OUTPUT_STATE_BUFFER), + .active = output_pending_enabled(&conn->output, base), }; struct wlr_output_mode *mode = conn->output.current_mode; @@ -529,18 +659,34 @@ static void drm_connector_state_init(struct wlr_drm_connector_state *state, state->mode.type = DRM_MODE_TYPE_USERDEF; } - if (conn->crtc != NULL) { + if (output_pending_enabled(&conn->output, base)) { + // The CRTC must be set up before this function is called + assert(conn->crtc != NULL); + struct wlr_drm_plane *primary = conn->crtc->primary; if (primary->queued_fb != NULL) { state->primary_fb = drm_fb_lock(primary->queued_fb); } else if (primary->current_fb != NULL) { state->primary_fb = drm_fb_lock(primary->current_fb); } + + if (conn->cursor_enabled) { + struct wlr_drm_plane *cursor = conn->crtc->cursor; + assert(cursor != NULL); + if (conn->cursor_pending_fb != NULL) { + state->cursor_fb = drm_fb_lock(conn->cursor_pending_fb); + } else if (cursor->queued_fb != NULL) { + state->cursor_fb = drm_fb_lock(cursor->queued_fb); + } else if (cursor->current_fb != NULL) { + state->cursor_fb = drm_fb_lock(cursor->current_fb); + } + } } } static void drm_connector_state_finish(struct wlr_drm_connector_state *state) { drm_fb_clear(&state->primary_fb); + drm_fb_clear(&state->cursor_fb); } static bool drm_connector_state_update_primary_fb(struct wlr_drm_connector *conn, @@ -626,13 +772,10 @@ static bool drm_connector_set_pending_layer_fbs(struct wlr_drm_connector *conn, static bool drm_connector_alloc_crtc(struct wlr_drm_connector *conn); -static bool drm_connector_test(struct wlr_output *output, - const struct wlr_output_state *state) { - struct wlr_drm_connector *conn = get_drm_connector_from_output(output); - - if (!conn->backend->session->active) { - return false; - } +static bool drm_connector_prepare(struct wlr_drm_connector_state *conn_state, bool test_only) { + const struct wlr_output_state *state = conn_state->base; + struct wlr_drm_connector *conn = conn_state->connector; + struct wlr_output *output = &conn->output; uint32_t unsupported = state->committed & ~SUPPORTED_OUTPUT_STATE; if (unsupported != 0) { @@ -641,11 +784,6 @@ static bool drm_connector_test(struct wlr_output *output, return false; } - if ((state->committed & COMMIT_OUTPUT_STATE) == 0) { - // This commit doesn't change the KMS state - return true; - } - if ((state->committed & WLR_OUTPUT_STATE_ENABLED) && state->enabled) { if (output->current_mode == NULL && !(state->committed & WLR_OUTPUT_STATE_MODE)) { @@ -655,133 +793,98 @@ static bool drm_connector_test(struct wlr_output *output, } } - bool ok = false; - struct wlr_drm_connector_state pending = {0}; - drm_connector_state_init(&pending, conn, state); - - if (pending.active) { - if ((state->committed & - (WLR_OUTPUT_STATE_ENABLED | WLR_OUTPUT_STATE_MODE)) && - !(state->committed & WLR_OUTPUT_STATE_BUFFER)) { - wlr_drm_conn_log(conn, WLR_DEBUG, - "Can't enable an output without a buffer"); - goto out; - } - - if (!drm_connector_alloc_crtc(conn)) { - wlr_drm_conn_log(conn, WLR_DEBUG, - "No CRTC available for this connector"); - goto out; - } - } - if ((state->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) && state->adaptive_sync_enabled && - !drm_connector_supports_vrr(conn)) { - goto out; + !output->adaptive_sync_supported) { + wlr_drm_conn_log(conn, WLR_DEBUG, + "Can't enable adaptive sync: connector doesn't support VRR"); + return false; } - if (conn->backend->parent) { + if (test_only && conn->backend->parent) { // If we're running as a secondary GPU, we can't perform an atomic // commit without blitting a buffer. - ok = true; - goto out; - } - - if (!conn->crtc) { - // If the output is disabled, we don't have a crtc even after - // reallocation - ok = true; - goto out; + return true; } if (state->committed & WLR_OUTPUT_STATE_BUFFER) { - if (!drm_connector_state_update_primary_fb(conn, &pending)) { - goto out; + if (!drm_connector_state_update_primary_fb(conn, conn_state)) { + return false; } - if (pending.base->tearing_page_flip && !conn->backend->supports_tearing_page_flips) { + if (conn_state->base->tearing_page_flip && !conn->backend->supports_tearing_page_flips) { wlr_log(WLR_ERROR, "Attempted to submit a tearing page flip to an unsupported backend!"); - goto out; + return false; } } if (state->committed & WLR_OUTPUT_STATE_LAYERS) { - if (!drm_connector_set_pending_layer_fbs(conn, pending.base)) { + if (!drm_connector_set_pending_layer_fbs(conn, conn_state->base)) { return false; } } - ok = drm_crtc_commit(conn, &pending, 0, true); + if (conn_state->active && !conn_state->primary_fb) { + wlr_drm_conn_log(conn, WLR_DEBUG, + "No primary frame buffer available for this connector"); + return false; + } -out: - drm_connector_state_finish(&pending); - return ok; + return true; } -bool drm_connector_supports_vrr(struct wlr_drm_connector *conn) { +static bool drm_connector_commit_state(struct wlr_drm_connector *conn, + const struct wlr_output_state *state, bool test_only) { struct wlr_drm_backend *drm = conn->backend; - struct wlr_drm_crtc *crtc = conn->crtc; - if (!crtc) { - return false; - } - - uint64_t vrr_capable; - if (conn->props.vrr_capable == 0 || - !get_drm_prop(drm->fd, conn->id, conn->props.vrr_capable, - &vrr_capable) || !vrr_capable) { - wlr_drm_conn_log(conn, WLR_DEBUG, "Failed to enable adaptive sync: " - "connector doesn't support VRR"); + if (!drm->session->active) { return false; } - if (crtc->props.vrr_enabled == 0) { - wlr_drm_conn_log(conn, WLR_DEBUG, "Failed to enable adaptive sync: " - "CRTC %"PRIu32" doesn't support VRR", crtc->id); - return false; + if (test_only && (state->committed & COMMIT_OUTPUT_STATE) == 0) { + // This commit doesn't change the KMS state + return true; } - return true; -} - -bool drm_connector_commit_state(struct wlr_drm_connector *conn, - const struct wlr_output_state *base) { - struct wlr_drm_backend *drm = conn->backend; - - if (!drm->session->active) { + if (output_pending_enabled(&conn->output, state) && !drm_connector_alloc_crtc(conn)) { + wlr_drm_conn_log(conn, WLR_DEBUG, + "No CRTC available for this connector"); return false; } bool ok = false; struct wlr_drm_connector_state pending = {0}; - drm_connector_state_init(&pending, conn, base); + drm_connector_state_init(&pending, conn, state); + struct wlr_drm_device_state pending_dev = { + .modeset = state->allow_reconfiguration, + // The wlr_output API requires non-modeset commits with a new buffer to + // wait for the frame event. However compositors often perform + // non-modesets commits without a new buffer without waiting for the + // frame event. In that case we need to make the KMS commit blocking, + // otherwise the kernel will error out with EBUSY. + .nonblock = !state->allow_reconfiguration && + (state->committed & WLR_OUTPUT_STATE_BUFFER), + .connectors = &pending, + .connectors_len = 1, + }; - if (!pending.active && conn->crtc == NULL) { - // Disabling an already-disabled connector - ok = true; + if (!drm_connector_prepare(&pending, test_only)) { goto out; } - if (pending.active) { - if (!drm_connector_alloc_crtc(conn)) { - wlr_drm_conn_log(conn, WLR_ERROR, - "No CRTC available for this connector"); - goto out; - } + if (test_only && conn->backend->parent) { + // If we're running as a secondary GPU, we can't perform an atomic + // commit without blitting a buffer. + ok = true; + goto out; } - if (pending.base->committed & WLR_OUTPUT_STATE_BUFFER) { - if (!drm_connector_state_update_primary_fb(conn, &pending)) { - goto out; - } - } - if (pending.base->committed & WLR_OUTPUT_STATE_LAYERS) { - if (!drm_connector_set_pending_layer_fbs(conn, pending.base)) { - return false; - } + if (!pending.active && conn->crtc == NULL) { + // Disabling an already-disabled connector + ok = true; + goto out; } - if (pending.modeset) { + if (!test_only && pending_dev.modeset) { if (pending.active) { wlr_drm_conn_log(conn, WLR_INFO, "Modesetting with %dx%d @ %.3f Hz", pending.mode.hdisplay, pending.mode.vdisplay, @@ -795,48 +898,37 @@ bool drm_connector_commit_state(struct wlr_drm_connector *conn, // page-flip, either a blocking modeset. When performing a blocking modeset // we'll wait for all queued page-flips to complete, so we don't need this // safeguard. - if (pending.nonblock && conn->pending_page_flip != NULL) { + if (!test_only && pending_dev.nonblock && conn->pending_page_flip != NULL) { wlr_drm_conn_log(conn, WLR_ERROR, "Failed to page-flip output: " "a page-flip is already pending"); goto out; } uint32_t flags = 0; - if (pending.active) { + if (!test_only && pending.active) { flags |= DRM_MODE_PAGE_FLIP_EVENT; } if (pending.base->tearing_page_flip) { flags |= DRM_MODE_PAGE_FLIP_ASYNC; } - ok = drm_crtc_commit(conn, &pending, flags, false); - if (!ok) { - goto out; - } - - if (!pending.active) { - drm_plane_finish_surface(conn->crtc->primary); - drm_plane_finish_surface(conn->crtc->cursor); - drm_fb_clear(&conn->cursor_pending_fb); - - conn->cursor_enabled = false; - conn->crtc = NULL; - } + ok = drm_commit(drm, &pending_dev, flags, test_only); out: drm_connector_state_finish(&pending); return ok; } -static bool drm_connector_commit(struct wlr_output *output, +static bool drm_connector_test(struct wlr_output *output, const struct wlr_output_state *state) { struct wlr_drm_connector *conn = get_drm_connector_from_output(output); + return drm_connector_commit_state(conn, state, true); +} - if (!drm_connector_test(output, state)) { - return false; - } - - return drm_connector_commit_state(conn, state); +static bool drm_connector_commit(struct wlr_output *output, + const struct wlr_output_state *state) { + struct wlr_drm_connector *conn = get_drm_connector_from_output(output); + return drm_connector_commit_state(conn, state, false); } size_t drm_crtc_get_gamma_lut_size(struct wlr_drm_backend *drm, @@ -867,19 +959,6 @@ static size_t drm_connector_get_gamma_size(struct wlr_output *output) { return drm_crtc_get_gamma_lut_size(drm, crtc); } -struct wlr_drm_fb *get_next_cursor_fb(struct wlr_drm_connector *conn) { - if (!conn->cursor_enabled || conn->crtc == NULL) { - return NULL; - } - if (conn->cursor_pending_fb != NULL) { - return conn->cursor_pending_fb; - } - if (conn->crtc->cursor->queued_fb != NULL) { - return conn->crtc->cursor->queued_fb; - } - return conn->crtc->cursor->current_fb; -} - static void realloc_crtcs(struct wlr_drm_backend *drm, struct wlr_drm_connector *want_conn); @@ -973,9 +1052,17 @@ static bool drm_connector_set_cursor(struct wlr_output *output, } conn->cursor_enabled = false; + drm_fb_clear(&conn->cursor_pending_fb); if (buffer != NULL) { - if ((uint64_t)buffer->width != drm->cursor_width || - (uint64_t)buffer->height != drm->cursor_height) { + bool found = false; + for (size_t i = 0; i < plane->cursor_sizes_len; i++) { + struct wlr_output_cursor_size size = plane->cursor_sizes[i]; + if (size.width == buffer->width && size.height == buffer->height) { + found = true; + break; + } + } + if (!found) { wlr_drm_conn_log(conn, WLR_DEBUG, "Cursor buffer size mismatch"); return false; } @@ -1101,11 +1188,18 @@ static const struct wlr_drm_format_set *drm_connector_get_cursor_formats( return &plane->formats; } -static void drm_connector_get_cursor_size(struct wlr_output *output, - int *width, int *height) { - struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend); - *width = (int)drm->cursor_width; - *height = (int)drm->cursor_height; +static const struct wlr_output_cursor_size *drm_connector_get_cursor_sizes(struct wlr_output *output, + size_t *len) { + struct wlr_drm_connector *conn = get_drm_connector_from_output(output); + if (!drm_connector_alloc_crtc(conn)) { + return NULL; + } + struct wlr_drm_plane *plane = conn->crtc->cursor; + if (!plane) { + return NULL; + } + *len = plane->cursor_sizes_len; + return plane->cursor_sizes; } static const struct wlr_drm_format_set *drm_connector_get_primary_formats( @@ -1131,7 +1225,7 @@ static const struct wlr_output_impl output_impl = { .commit = drm_connector_commit, .get_gamma_size = drm_connector_get_gamma_size, .get_cursor_formats = drm_connector_get_cursor_formats, - .get_cursor_size = drm_connector_get_cursor_size, + .get_cursor_sizes = drm_connector_get_cursor_sizes, .get_primary_formats = drm_connector_get_primary_formats, }; @@ -1195,7 +1289,7 @@ static void dealloc_crtc(struct wlr_drm_connector *conn) { struct wlr_output_state state; wlr_output_state_init(&state); wlr_output_state_set_enabled(&state, false); - if (!drm_connector_commit_state(conn, &state)) { + if (!drm_connector_commit_state(conn, &state, false)) { // On GPU unplug, disabling the CRTC can fail with EPERM wlr_drm_conn_log(conn, WLR_ERROR, "Failed to disable CRTC %"PRIu32, conn->crtc->id); @@ -1203,6 +1297,14 @@ static void dealloc_crtc(struct wlr_drm_connector *conn) { wlr_output_state_finish(&state); } +static void format_nullable_crtc(char *str, size_t size, struct wlr_drm_crtc *crtc) { + if (crtc != NULL) { + snprintf(str, size, "CRTC %"PRIu32, crtc->id); + } else { + snprintf(str, size, "no CRTC"); + } +} + static void realloc_crtcs(struct wlr_drm_backend *drm, struct wlr_drm_connector *want_conn) { assert(drm->num_crtcs > 0); @@ -1223,7 +1325,6 @@ static void realloc_crtcs(struct wlr_drm_backend *drm, previous_match[i] = UNMATCHED; } - wlr_log(WLR_DEBUG, "State before reallocation:"); size_t i = 0; struct wlr_drm_connector *conn; wl_list_for_each(conn, &drm->connectors, link) { @@ -1237,10 +1338,6 @@ static void realloc_crtcs(struct wlr_drm_backend *drm, // connector the user wants to enable bool want_crtc = conn == want_conn || conn->output.enabled; - wlr_log(WLR_DEBUG, " '%s': crtc=%d status=%s want_crtc=%d", - conn->name, conn->crtc ? (int)(conn->crtc - drm->crtcs) : -1, - drm_connector_status_str(conn->status), want_crtc); - if (conn->status == DRM_MODE_CONNECTED && want_crtc) { connector_constraints[i] = conn->possible_crtcs; } else { @@ -1255,16 +1352,37 @@ static void realloc_crtcs(struct wlr_drm_backend *drm, drm->num_crtcs, previous_match, new_match); // Converts our crtc=>connector result into a connector=>crtc one. - ssize_t connector_match[num_connectors]; + struct wlr_drm_crtc *connector_match[num_connectors]; for (size_t i = 0 ; i < num_connectors; ++i) { - connector_match[i] = -1; + connector_match[i] = NULL; } for (size_t i = 0; i < drm->num_crtcs; ++i) { if (new_match[i] != UNMATCHED) { - connector_match[new_match[i]] = i; + connector_match[new_match[i]] = &drm->crtcs[i]; } } + for (size_t i = 0; i < num_connectors; ++i) { + struct wlr_drm_connector *conn = connectors[i]; + struct wlr_drm_crtc *new_crtc = connector_match[i]; + + char old_crtc_str[16], new_crtc_str[16]; + format_nullable_crtc(old_crtc_str, sizeof(old_crtc_str), conn->crtc); + format_nullable_crtc(new_crtc_str, sizeof(new_crtc_str), new_crtc); + + char crtc_str[64]; + if (conn->crtc != new_crtc) { + snprintf(crtc_str, sizeof(crtc_str), "%s → %s", old_crtc_str, new_crtc_str); + } else { + snprintf(crtc_str, sizeof(crtc_str), "%s (no change)", new_crtc_str); + } + + wlr_log(WLR_DEBUG, " Connector %s (%s%s): %s", + conn->name, drm_connector_status_str(conn->status), + connector_constraints[i] != 0 ? ", needs CRTC" : "", + crtc_str); + } + // Refuse to remove a CRTC from an enabled connector, and refuse to // change the CRTC of an enabled connector. for (size_t i = 0; i < num_connectors; ++i) { @@ -1272,13 +1390,13 @@ static void realloc_crtcs(struct wlr_drm_backend *drm, if (conn->status != DRM_MODE_CONNECTED || !conn->output.enabled) { continue; } - if (connector_match[i] == -1) { + if (connector_match[i] == NULL) { wlr_log(WLR_DEBUG, "Could not match a CRTC for previously connected output; " "keeping old configuration"); return; } assert(conn->crtc != NULL); - if (connector_match[i] != conn->crtc - drm->crtcs) { + if (connector_match[i] != conn->crtc) { wlr_log(WLR_DEBUG, "Cannot switch CRTC for enabled output; " "keeping old configuration"); return; @@ -1286,21 +1404,17 @@ static void realloc_crtcs(struct wlr_drm_backend *drm, } // Apply new configuration - wlr_log(WLR_DEBUG, "State after reallocation:"); for (size_t i = 0; i < num_connectors; ++i) { struct wlr_drm_connector *conn = connectors[i]; - wlr_log(WLR_DEBUG, " '%s': crtc=%zd", - conn->name, connector_match[i]); - - if (conn->crtc != NULL && connector_match[i] == conn->crtc - drm->crtcs) { + if (conn->crtc != NULL && connector_match[i]) { // We don't need to change anything continue; } dealloc_crtc(conn); - if (connector_match[i] >= 0) { - conn->crtc = &drm->crtcs[connector_match[i]]; + if (connector_match[i] != NULL) { + conn->crtc = connector_match[i]; } } } @@ -1357,6 +1471,11 @@ static struct wlr_drm_connector *create_drm_connector(struct wlr_drm_backend *dr wlr_conn->status = DRM_MODE_DISCONNECTED; wlr_conn->id = drm_conn->connector_id; + if (!get_drm_connector_props(drm->fd, wlr_conn->id, &wlr_conn->props)) { + free(wlr_conn); + return NULL; + } + const char *conn_name = drmModeGetConnectorTypeName(drm_conn->connector_type); if (conn_name == NULL) { @@ -1443,6 +1562,7 @@ static bool connect_drm_connector(struct wlr_drm_connector *wlr_conn, struct wlr_drm_mode *mode = drm_mode_create(&drm_conn->modes[i]); if (!mode) { wlr_log_errno(WLR_ERROR, "Allocation failed"); + free(current_modeinfo); wlr_output_state_finish(&state); return false; } @@ -1457,6 +1577,7 @@ static bool connect_drm_connector(struct wlr_drm_connector *wlr_conn, get_drm_prop(drm->fd, wlr_conn->crtc->id, wlr_conn->crtc->props.mode_id, &mode_id); + wlr_conn->crtc->own_mode_id = false; wlr_conn->crtc->mode_id = mode_id; wlr_conn->refresh = calculate_refresh_rate(current_modeinfo); } @@ -1471,7 +1592,7 @@ static bool connect_drm_connector(struct wlr_drm_connector *wlr_conn, free(current_modeinfo); - wlr_output_init(output, &drm->backend, &output_impl, drm->display, &state); + wlr_output_init(output, &drm->backend, &output_impl, drm->session->event_loop, &state); wlr_output_state_finish(&state); // fill out the modes @@ -1489,10 +1610,6 @@ static bool connect_drm_connector(struct wlr_drm_connector *wlr_conn, wlr_log(WLR_ERROR, "Unknown subpixel value: %d", (int)drm_conn->subpixel); } - if (!get_drm_connector_props(drm->fd, wlr_conn->id, &wlr_conn->props)) { - return false; - } - uint64_t non_desktop; if (get_drm_prop(drm->fd, wlr_conn->id, wlr_conn->props.non_desktop, &non_desktop)) { @@ -1510,6 +1627,12 @@ static bool connect_drm_connector(struct wlr_drm_connector *wlr_conn, } } + uint64_t vrr_capable = 0; + if (wlr_conn->props.vrr_capable != 0) { + get_drm_prop(drm->fd, wlr_conn->id, wlr_conn->props.vrr_capable, &vrr_capable); + } + output->adaptive_sync_supported = vrr_capable; + size_t edid_len = 0; uint8_t *edid = get_drm_prop_blob(drm->fd, wlr_conn->id, wlr_conn->props.edid, &edid_len); @@ -1697,6 +1820,182 @@ void scan_drm_leases(struct wlr_drm_backend *drm) { drmFree(list); } +static void build_current_connector_state(struct wlr_output_state *state, + struct wlr_drm_connector *conn) { + bool enabled = conn->status != DRM_MODE_DISCONNECTED && conn->output.enabled; + + wlr_output_state_init(state); + wlr_output_state_set_enabled(state, enabled); + if (!enabled) { + return; + } + + if (conn->output.current_mode != NULL) { + wlr_output_state_set_mode(state, conn->output.current_mode); + } else { + wlr_output_state_set_custom_mode(state, + conn->output.width, conn->output.height, conn->output.refresh); + } +} + +/** + * Check whether we need to perform a full reset after a VT switch. + * + * If any connector or plane has a different CRTC, we need to perform a full + * reset to restore our mapping. We couldn't avoid a full reset even if we + * used a single KMS atomic commit to apply our state: the kernel rejects + * commits which migrate a plane from one CRTC to another without going through + * an intermediate state where the plane is disabled. + */ +static bool skip_reset_for_restore(struct wlr_drm_backend *drm) { + struct wlr_drm_connector *conn; + wl_list_for_each(conn, &drm->connectors, link) { + drmModeConnector *drm_conn = drmModeGetConnectorCurrent(drm->fd, conn->id); + if (drm_conn == NULL) { + return false; + } + struct wlr_drm_crtc *crtc = connector_get_current_crtc(conn, drm_conn); + drmModeFreeConnector(drm_conn); + + if (crtc != NULL && conn->crtc != crtc) { + return false; + } + } + + for (size_t i = 0; i < drm->num_planes; i++) { + struct wlr_drm_plane *plane = &drm->planes[i]; + + drmModePlane *drm_plane = drmModeGetPlane(drm->fd, plane->id); + if (drm_plane == NULL) { + return false; + } + uint32_t crtc_id = drm_plane->crtc_id; + drmModeFreePlane(drm_plane); + + struct wlr_drm_crtc *crtc = NULL; + for (size_t i = 0; i < drm->num_crtcs; i++) { + if (drm->crtcs[i].id == crtc_id) { + crtc = &drm->crtcs[i]; + break; + } + } + if (crtc == NULL) { + continue; + } + + bool ok = false; + switch (plane->type) { + case DRM_PLANE_TYPE_PRIMARY: + ok = crtc->primary == plane; + break; + case DRM_PLANE_TYPE_CURSOR: + ok = crtc->cursor == plane; + break; + } + if (!ok) { + return false; + } + } + + return true; +} + +void restore_drm_device(struct wlr_drm_backend *drm) { + // The previous DRM master leaves KMS in an undefined state. We need + // to restore our own state, but be careful to avoid invalid + // configurations. The connector/CRTC mapping may have changed, so + // first disable all CRTCs, then light up the ones we were using + // before the VT switch. + // TODO: better use the atomic API to improve restoration after a VT switch + if (!skip_reset_for_restore(drm) && !drm->iface->reset(drm)) { + wlr_log(WLR_ERROR, "Failed to reset state after VT switch"); + } + + struct wlr_drm_connector *conn; + wl_list_for_each(conn, &drm->connectors, link) { + struct wlr_output_state state; + build_current_connector_state(&state, conn); + if (!drm_connector_commit_state(conn, &state, false)) { + wlr_drm_conn_log(conn, WLR_ERROR, "Failed to restore state after VT switch"); + } + wlr_output_state_finish(&state); + } +} + +bool commit_drm_device(struct wlr_drm_backend *drm, + const struct wlr_backend_output_state *output_states, size_t output_states_len, + bool test_only) { + if (!drm->session->active) { + return false; + } + + struct wlr_drm_connector_state *conn_states = calloc(output_states_len, sizeof(conn_states[0])); + if (conn_states == NULL) { + return false; + } + + bool ok = false; + bool modeset = false; + size_t conn_states_len = 0; + for (size_t i = 0; i < output_states_len; i++) { + const struct wlr_backend_output_state *output_state = &output_states[i]; + struct wlr_output *output = output_state->output; + if (!output->enabled && !output_pending_enabled(output, &output_state->base)) { + // KMS rejects commits which disable already-disabled connectors + // and have the DRM_MODE_PAGE_FLIP_EVENT flag + continue; + } + + struct wlr_drm_connector *conn = get_drm_connector_from_output(output); + + if (output_pending_enabled(output, &output_state->base) && !drm_connector_alloc_crtc(conn)) { + wlr_drm_conn_log(conn, WLR_DEBUG, + "No CRTC available for this connector"); + goto out; + } + + struct wlr_drm_connector_state *conn_state = &conn_states[conn_states_len]; + drm_connector_state_init(conn_state, conn, &output_state->base); + conn_states_len++; + + if (!drm_connector_prepare(conn_state, test_only)) { + goto out; + } + + if (output_state->base.tearing_page_flip) { + wlr_log(WLR_DEBUG, "Tearing not supported for DRM device-wide commits"); + goto out; + } + + modeset |= output_state->base.allow_reconfiguration; + } + + if (test_only && drm->parent) { + // If we're running as a secondary GPU, we can't perform an atomic + // commit without blitting a buffer. + ok = true; + goto out; + } + + uint32_t flags = 0; + if (!test_only) { + flags |= DRM_MODE_PAGE_FLIP_EVENT; + } + struct wlr_drm_device_state dev_state = { + .modeset = modeset, + .connectors = conn_states, + .connectors_len = conn_states_len, + }; + ok = drm_commit(drm, &dev_state, flags, test_only); + +out: + for (size_t i = 0; i < conn_states_len; i++) { + drm_connector_state_finish(&conn_states[i]); + } + free(conn_states); + return ok; +} + static int mhz_to_nsec(int mhz) { return 1000000000000LL / mhz; } @@ -1705,11 +2004,13 @@ static void handle_page_flip(int fd, unsigned seq, unsigned tv_sec, unsigned tv_usec, unsigned crtc_id, void *data) { struct wlr_drm_page_flip *page_flip = data; - struct wlr_drm_connector *conn = page_flip->conn; + struct wlr_drm_connector *conn = drm_page_flip_pop(page_flip, crtc_id); if (conn != NULL) { conn->pending_page_flip = NULL; } - drm_page_flip_destroy(page_flip); + if (page_flip->connectors_len == 0) { + drm_page_flip_destroy(page_flip); + } if (conn == NULL) { return; @@ -1778,7 +2079,7 @@ int handle_drm_event(int fd, uint32_t mask, void *data) { if (drmHandleEvent(fd, &event) != 0) { wlr_log(WLR_ERROR, "drmHandleEvent failed"); - wl_display_terminate(drm->display); + wlr_backend_destroy(&drm->backend); } return 1; } diff --git a/backend/drm/fb.c b/backend/drm/fb.c new file mode 100644 index 0000000..575f32d --- /dev/null +++ b/backend/drm/fb.c @@ -0,0 +1,258 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "backend/drm/drm.h" +#include "backend/drm/fb.h" +#include "render/pixel_format.h" + +void drm_fb_clear(struct wlr_drm_fb **fb_ptr) { + if (*fb_ptr == NULL) { + return; + } + + struct wlr_drm_fb *fb = *fb_ptr; + wlr_buffer_unlock(fb->wlr_buf); // may destroy the buffer + + *fb_ptr = NULL; +} + +struct wlr_drm_fb *drm_fb_lock(struct wlr_drm_fb *fb) { + wlr_buffer_lock(fb->wlr_buf); + return fb; +} + +static void drm_fb_handle_destroy(struct wlr_addon *addon) { + struct wlr_drm_fb *fb = wl_container_of(addon, fb, addon); + drm_fb_destroy(fb); +} + +static const struct wlr_addon_interface fb_addon_impl = { + .name = "wlr_drm_fb", + .destroy = drm_fb_handle_destroy, +}; + +static uint32_t get_fb_for_bo(struct wlr_drm_backend *drm, + struct wlr_dmabuf_attributes *dmabuf, uint32_t handles[static 4]) { + uint64_t modifiers[4] = {0}; + for (int i = 0; i < dmabuf->n_planes; i++) { + // KMS requires all BO planes to have the same modifier + modifiers[i] = dmabuf->modifier; + } + + uint32_t id = 0; + if (drm->addfb2_modifiers && dmabuf->modifier != DRM_FORMAT_MOD_INVALID) { + if (drmModeAddFB2WithModifiers(drm->fd, dmabuf->width, dmabuf->height, + dmabuf->format, handles, dmabuf->stride, dmabuf->offset, + modifiers, &id, DRM_MODE_FB_MODIFIERS) != 0) { + wlr_log_errno(WLR_DEBUG, "drmModeAddFB2WithModifiers failed"); + } + } else { + if (dmabuf->modifier != DRM_FORMAT_MOD_INVALID && + dmabuf->modifier != DRM_FORMAT_MOD_LINEAR) { + wlr_log(WLR_ERROR, "Cannot import DRM framebuffer with explicit " + "modifier 0x%"PRIX64, dmabuf->modifier); + return 0; + } + + int ret = drmModeAddFB2(drm->fd, dmabuf->width, dmabuf->height, + dmabuf->format, handles, dmabuf->stride, dmabuf->offset, &id, 0); + if (ret != 0 && dmabuf->format == DRM_FORMAT_ARGB8888 && + dmabuf->n_planes == 1 && dmabuf->offset[0] == 0) { + // Some big-endian machines don't support drmModeAddFB2. Try a + // last-resort fallback for ARGB8888 buffers, like Xorg's + // modesetting driver does. + wlr_log(WLR_DEBUG, "drmModeAddFB2 failed (%s), falling back to " + "legacy drmModeAddFB", strerror(-ret)); + + uint32_t depth = 32; + uint32_t bpp = 32; + ret = drmModeAddFB(drm->fd, dmabuf->width, dmabuf->height, depth, + bpp, dmabuf->stride[0], handles[0], &id); + if (ret != 0) { + wlr_log_errno(WLR_DEBUG, "drmModeAddFB failed"); + } + } else if (ret != 0) { + wlr_log_errno(WLR_DEBUG, "drmModeAddFB2 failed"); + } + } + + return id; +} + +static void close_all_bo_handles(struct wlr_drm_backend *drm, + uint32_t handles[static 4]) { + for (int i = 0; i < 4; ++i) { + if (handles[i] == 0) { + continue; + } + + // If multiple planes share the same BO handle, avoid double-closing it + bool already_closed = false; + for (int j = 0; j < i; ++j) { + if (handles[i] == handles[j]) { + already_closed = true; + break; + } + } + if (already_closed) { + continue; + } + + if (drmCloseBufferHandle(drm->fd, handles[i]) != 0) { + wlr_log_errno(WLR_ERROR, "drmCloseBufferHandle failed"); + } + } +} + +static void drm_poisoned_fb_handle_destroy(struct wlr_addon *addon) { + wlr_addon_finish(addon); + free(addon); +} + +static const struct wlr_addon_interface poisoned_fb_addon_impl = { + .name = "wlr_drm_poisoned_fb", + .destroy = drm_poisoned_fb_handle_destroy, +}; + +static bool is_buffer_poisoned(struct wlr_drm_backend *drm, + struct wlr_buffer *buf) { + return wlr_addon_find(&buf->addons, drm, &poisoned_fb_addon_impl) != NULL; +} + +/** + * Mark the buffer as "poisoned", ie. it cannot be imported into KMS. This + * allows us to avoid repeatedly trying to import it when it's not + * scanout-capable. + */ +static void poison_buffer(struct wlr_drm_backend *drm, + struct wlr_buffer *buf) { + struct wlr_addon *addon = calloc(1, sizeof(*addon)); + if (addon == NULL) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + return; + } + wlr_addon_init(addon, &buf->addons, drm, &poisoned_fb_addon_impl); + wlr_log(WLR_DEBUG, "Poisoning buffer"); +} + +static struct wlr_drm_fb *drm_fb_create(struct wlr_drm_backend *drm, + struct wlr_buffer *buf, const struct wlr_drm_format_set *formats) { + struct wlr_dmabuf_attributes attribs; + if (!wlr_buffer_get_dmabuf(buf, &attribs)) { + wlr_log(WLR_DEBUG, "Failed to get DMA-BUF from buffer"); + return NULL; + } + + if (is_buffer_poisoned(drm, buf)) { + wlr_log(WLR_DEBUG, "Buffer is poisoned"); + return NULL; + } + + struct wlr_drm_fb *fb = calloc(1, sizeof(*fb)); + if (!fb) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + return NULL; + } + + if (formats && !wlr_drm_format_set_has(formats, attribs.format, + attribs.modifier)) { + // The format isn't supported by the plane. Try stripping the alpha + // channel, if any. + const struct wlr_pixel_format_info *info = + drm_get_pixel_format_info(attribs.format); + if (info != NULL && info->opaque_substitute != DRM_FORMAT_INVALID && + wlr_drm_format_set_has(formats, info->opaque_substitute, attribs.modifier)) { + attribs.format = info->opaque_substitute; + } else { + wlr_log(WLR_DEBUG, "Buffer format 0x%"PRIX32" with modifier " + "0x%"PRIX64" cannot be scanned out", + attribs.format, attribs.modifier); + goto error_fb; + } + } + + uint32_t handles[4] = {0}; + for (int i = 0; i < attribs.n_planes; ++i) { + int ret = drmPrimeFDToHandle(drm->fd, attribs.fd[i], &handles[i]); + if (ret != 0) { + wlr_log_errno(WLR_DEBUG, "drmPrimeFDToHandle failed"); + goto error_bo_handle; + } + } + + fb->id = get_fb_for_bo(drm, &attribs, handles); + if (!fb->id) { + wlr_log(WLR_DEBUG, "Failed to import BO in KMS"); + poison_buffer(drm, buf); + goto error_bo_handle; + } + + close_all_bo_handles(drm, handles); + + fb->backend = drm; + fb->wlr_buf = buf; + + wlr_addon_init(&fb->addon, &buf->addons, drm, &fb_addon_impl); + wl_list_insert(&drm->fbs, &fb->link); + + return fb; + +error_bo_handle: + close_all_bo_handles(drm, handles); +error_fb: + free(fb); + return NULL; +} + +void drm_fb_destroy(struct wlr_drm_fb *fb) { + struct wlr_drm_backend *drm = fb->backend; + + wl_list_remove(&fb->link); + wlr_addon_finish(&fb->addon); + + int ret = drmModeCloseFB(drm->fd, fb->id); + if (ret == -EINVAL) { + ret = drmModeRmFB(drm->fd, fb->id); + } + if (ret != 0) { + wlr_log(WLR_ERROR, "Failed to close FB: %s", strerror(-ret)); + } + + free(fb); +} + +bool drm_fb_import(struct wlr_drm_fb **fb_ptr, struct wlr_drm_backend *drm, + struct wlr_buffer *buf, const struct wlr_drm_format_set *formats) { + struct wlr_drm_fb *fb; + struct wlr_addon *addon = wlr_addon_find(&buf->addons, drm, &fb_addon_impl); + if (addon != NULL) { + fb = wl_container_of(addon, fb, addon); + } else { + fb = drm_fb_create(drm, buf, formats); + if (!fb) { + return false; + } + } + + wlr_buffer_lock(buf); + drm_fb_move(fb_ptr, &fb); + return true; +} + +void drm_fb_move(struct wlr_drm_fb **new, struct wlr_drm_fb **old) { + drm_fb_clear(new); + *new = *old; + *old = NULL; +} + +void drm_fb_copy(struct wlr_drm_fb **new, struct wlr_drm_fb *old) { + drm_fb_clear(new); + if (old != NULL) { + *new = drm_fb_lock(old); + } +} diff --git a/backend/drm/legacy.c b/backend/drm/legacy.c index 117fb9e..0c591f4 100644 --- a/backend/drm/legacy.c +++ b/backend/drm/legacy.c @@ -4,6 +4,7 @@ #include #include #include "backend/drm/drm.h" +#include "backend/drm/fb.h" #include "backend/drm/iface.h" #include "backend/drm/util.h" @@ -33,11 +34,12 @@ static bool legacy_fb_props_match(struct wlr_drm_fb *fb1, return true; } -static bool legacy_crtc_test(struct wlr_drm_connector *conn, - const struct wlr_drm_connector_state *state) { +static bool legacy_crtc_test(const struct wlr_drm_connector_state *state, + bool modeset) { + struct wlr_drm_connector *conn = state->connector; struct wlr_drm_crtc *crtc = conn->crtc; - if ((state->base->committed & WLR_OUTPUT_STATE_BUFFER) && !state->modeset) { + if ((state->base->committed & WLR_OUTPUT_STATE_BUFFER) && !modeset) { struct wlr_drm_fb *pending_fb = state->primary_fb; struct wlr_drm_fb *prev_fb = crtc->primary->queued_fb; @@ -57,16 +59,9 @@ static bool legacy_crtc_test(struct wlr_drm_connector *conn, return true; } -static bool legacy_crtc_commit(struct wlr_drm_connector *conn, - const struct wlr_drm_connector_state *state, - struct wlr_drm_page_flip *page_flip, uint32_t flags, bool test_only) { - if (!legacy_crtc_test(conn, state)) { - return false; - } - if (test_only) { - return true; - } - +static bool legacy_crtc_commit(const struct wlr_drm_connector_state *state, + struct wlr_drm_page_flip *page_flip, uint32_t flags, bool modeset) { + struct wlr_drm_connector *conn = state->connector; struct wlr_drm_backend *drm = conn->backend; struct wlr_output *output = &conn->output; struct wlr_drm_crtc *crtc = conn->crtc; @@ -82,7 +77,7 @@ static bool legacy_crtc_commit(struct wlr_drm_connector *conn, fb_id = state->primary_fb->id; } - if (state->modeset) { + if (modeset) { uint32_t *conns = NULL; size_t conns_len = 0; drmModeModeInfo *mode = NULL; @@ -115,10 +110,11 @@ static bool legacy_crtc_commit(struct wlr_drm_connector *conn, } if (state->base->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) { - if (!drm_connector_supports_vrr(conn)) { + if (state->base->adaptive_sync_enabled && !output->adaptive_sync_supported) { return false; } - if (drmModeObjectSetProperty(drm->fd, crtc->id, DRM_MODE_OBJECT_CRTC, + if (crtc->props.vrr_enabled != 0 && + drmModeObjectSetProperty(drm->fd, crtc->id, DRM_MODE_OBJECT_CRTC, crtc->props.vrr_enabled, state->base->adaptive_sync_enabled) != 0) { wlr_drm_conn_log_errno(conn, WLR_ERROR, @@ -133,7 +129,7 @@ static bool legacy_crtc_commit(struct wlr_drm_connector *conn, } if (cursor != NULL && drm_connector_is_cursor_visible(conn)) { - struct wlr_drm_fb *cursor_fb = get_next_cursor_fb(conn); + struct wlr_drm_fb *cursor_fb = state->cursor_fb; if (cursor_fb == NULL) { wlr_drm_conn_log(conn, WLR_DEBUG, "Failed to acquire cursor FB"); return false; @@ -184,6 +180,32 @@ static bool legacy_crtc_commit(struct wlr_drm_connector *conn, return true; } +static bool legacy_commit(struct wlr_drm_backend *drm, + const struct wlr_drm_device_state *state, + struct wlr_drm_page_flip *page_flip, uint32_t flags, + bool test_only) { + for (size_t i = 0; i < state->connectors_len; i++) { + const struct wlr_drm_connector_state *conn_state = &state->connectors[i]; + if (!legacy_crtc_test(conn_state, state->modeset)) { + return false; + } + } + + if (test_only) { + return true; + } + + for (size_t i = 0; i < state->connectors_len; i++) { + const struct wlr_drm_connector_state *conn_state = &state->connectors[i]; + if (!legacy_crtc_commit(conn_state, page_flip, flags, + state->modeset)) { + return false; + } + } + + return true; +} + static void fill_empty_gamma_table(size_t size, uint16_t *r, uint16_t *g, uint16_t *b) { assert(0xFFFF < UINT64_MAX / (size - 1)); @@ -226,6 +248,20 @@ bool drm_legacy_crtc_set_gamma(struct wlr_drm_backend *drm, return true; } +static bool legacy_reset(struct wlr_drm_backend *drm) { + bool ok = true; + for (size_t i = 0; i < drm->num_crtcs; i++) { + struct wlr_drm_crtc *crtc = &drm->crtcs[i]; + if (drmModeSetCrtc(drm->fd, crtc->id, 0, 0, 0, NULL, 0, NULL) != 0) { + wlr_log_errno(WLR_ERROR, "Failed to disable CRTC %"PRIu32, + crtc->id); + ok = false; + } + } + return ok; +} + const struct wlr_drm_interface legacy_iface = { - .crtc_commit = legacy_crtc_commit, + .commit = legacy_commit, + .reset = legacy_reset, }; diff --git a/backend/drm/libliftoff.c b/backend/drm/libliftoff.c index a3b6154..8505447 100644 --- a/backend/drm/libliftoff.c +++ b/backend/drm/libliftoff.c @@ -1,16 +1,38 @@ -#define _POSIX_C_SOURCE 200809L #include #include +#include +#include #include #include #include #include "backend/drm/drm.h" +#include "backend/drm/fb.h" #include "backend/drm/iface.h" +#include "config.h" + +static void log_handler(enum liftoff_log_priority priority, const char *fmt, va_list args) { + enum wlr_log_importance importance = WLR_SILENT; + switch (priority) { + case LIFTOFF_SILENT: + importance = WLR_SILENT; + break; + case LIFTOFF_ERROR: + importance = WLR_ERROR; + break; + case LIFTOFF_DEBUG: + importance = WLR_DEBUG; + break; + } + char buf[1024]; + vsnprintf(buf, sizeof(buf), fmt, args); + _wlr_log(importance, "[libliftoff] %s", buf); +} static bool init(struct wlr_drm_backend *drm) { // TODO: lower log level liftoff_log_set_priority(LIFTOFF_DEBUG); + liftoff_log_set_handler(log_handler); int drm_fd = fcntl(drm->fd, F_DUPFD_CLOEXEC, 0); if (drm_fd < 0) { @@ -126,27 +148,6 @@ static bool add_prop(drmModeAtomicReq *req, uint32_t obj, return true; } -static void commit_blob(struct wlr_drm_backend *drm, - uint32_t *current, uint32_t next) { - if (*current == next) { - return; - } - if (*current != 0) { - drmModeDestroyPropertyBlob(drm->fd, *current); - } - *current = next; -} - -static void rollback_blob(struct wlr_drm_backend *drm, - uint32_t *current, uint32_t next) { - if (*current == next) { - return; - } - if (next != 0) { - drmModeDestroyPropertyBlob(drm->fd, next); - } -} - static bool set_plane_props(struct wlr_drm_plane *plane, struct liftoff_layer *layer, struct wlr_drm_fb *fb, int32_t x, int32_t y, uint64_t zpos) { if (fb == NULL) { @@ -300,86 +301,16 @@ static void update_layer_feedback(struct wlr_drm_backend *drm, wlr_drm_format_set_finish(&formats); } -static bool crtc_commit(struct wlr_drm_connector *conn, +static bool add_connector(drmModeAtomicReq *req, const struct wlr_drm_connector_state *state, - struct wlr_drm_page_flip *page_flip, uint32_t flags, bool test_only) { - struct wlr_drm_backend *drm = conn->backend; - struct wlr_output *output = &conn->output; + bool modeset, struct wl_array *fb_damage_clips_arr) { + struct wlr_drm_connector *conn = state->connector; struct wlr_drm_crtc *crtc = conn->crtc; - - bool modeset = state->modeset; + struct wlr_drm_backend *drm = conn->backend; bool active = state->active; + bool ok = true; - if (modeset && !register_planes_for_crtc(drm, crtc)) { - return false; - } - - uint32_t mode_id = crtc->mode_id; - if (modeset) { - if (!create_mode_blob(drm, conn, state, &mode_id)) { - return false; - } - } - - uint32_t gamma_lut = crtc->gamma_lut; - if (state->base->committed & WLR_OUTPUT_STATE_GAMMA_LUT) { - // Fallback to legacy gamma interface when gamma properties are not - // available (can happen on older Intel GPUs that support gamma but not - // degamma). - if (crtc->props.gamma_lut == 0) { - if (!drm_legacy_crtc_set_gamma(drm, crtc, - state->base->gamma_lut_size, - state->base->gamma_lut)) { - return false; - } - } else { - if (!create_gamma_lut_blob(drm, state->base->gamma_lut_size, - state->base->gamma_lut, &gamma_lut)) { - return false; - } - } - } - - struct wl_array fb_damage_clips_arr = {0}; - - uint32_t primary_fb_damage_clips = 0; - if ((state->base->committed & WLR_OUTPUT_STATE_DAMAGE) && - crtc->primary->props.fb_damage_clips != 0) { - uint32_t *ptr = wl_array_add(&fb_damage_clips_arr, sizeof(primary_fb_damage_clips)); - if (ptr == NULL) { - return false; - } - create_fb_damage_clips_blob(drm, state->primary_fb->wlr_buf->width, - state->primary_fb->wlr_buf->height, &state->base->damage, - &primary_fb_damage_clips); - *ptr = primary_fb_damage_clips; - } - - bool prev_vrr_enabled = - output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED; - bool vrr_enabled = prev_vrr_enabled; - if ((state->base->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) && - drm_connector_supports_vrr(conn)) { - vrr_enabled = state->base->adaptive_sync_enabled; - } - - if (test_only) { - flags |= DRM_MODE_ATOMIC_TEST_ONLY; - } - if (modeset) { - flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; - } - if (!test_only && state->nonblock) { - flags |= DRM_MODE_ATOMIC_NONBLOCK; - } - - drmModeAtomicReq *req = drmModeAtomicAlloc(); - if (req == NULL) { - wlr_log(WLR_ERROR, "drmModeAtomicAlloc failed"); - return false; - } - - bool ok = add_prop(req, conn->id, conn->props.crtc_id, + ok = ok && add_prop(req, conn->id, conn->props.crtc_id, active ? crtc->id : 0); if (modeset && active && conn->props.link_status != 0) { ok = ok && add_prop(req, conn->id, conn->props.link_status, @@ -391,35 +322,35 @@ static bool crtc_commit(struct wlr_drm_connector *conn, } // TODO: set "max bpc" ok = ok && - add_prop(req, crtc->id, crtc->props.mode_id, mode_id) && + add_prop(req, crtc->id, crtc->props.mode_id, state->mode_id) && add_prop(req, crtc->id, crtc->props.active, active); if (active) { if (crtc->props.gamma_lut != 0) { - ok = ok && add_prop(req, crtc->id, crtc->props.gamma_lut, gamma_lut); + ok = ok && add_prop(req, crtc->id, crtc->props.gamma_lut, state->gamma_lut); } if (crtc->props.vrr_enabled != 0) { - ok = ok && add_prop(req, crtc->id, crtc->props.vrr_enabled, vrr_enabled); + ok = ok && add_prop(req, crtc->id, crtc->props.vrr_enabled, state->vrr_enabled); } ok = ok && set_plane_props(crtc->primary, crtc->primary->liftoff_layer, state->primary_fb, 0, 0, 0) && set_plane_props(crtc->primary, crtc->liftoff_composition_layer, state->primary_fb, 0, 0, 0); liftoff_layer_set_property(crtc->primary->liftoff_layer, - "FB_DAMAGE_CLIPS", primary_fb_damage_clips); + "FB_DAMAGE_CLIPS", state->fb_damage_clips); liftoff_layer_set_property(crtc->liftoff_composition_layer, - "FB_DAMAGE_CLIPS", primary_fb_damage_clips); + "FB_DAMAGE_CLIPS", state->fb_damage_clips); if (state->base->committed & WLR_OUTPUT_STATE_LAYERS) { for (size_t i = 0; i < state->base->layers_len; i++) { const struct wlr_output_layer_state *layer_state = &state->base->layers[i]; ok = ok && set_layer_props(drm, layer_state, i + 1, - &fb_damage_clips_arr); + fb_damage_clips_arr); } } if (crtc->cursor) { if (drm_connector_is_cursor_visible(conn)) { ok = ok && set_plane_props(crtc->cursor, crtc->cursor->liftoff_layer, - get_next_cursor_fb(conn), conn->cursor_x, conn->cursor_y, + state->cursor_fb, conn->cursor_x, conn->cursor_y, wl_list_length(&crtc->layers) + 1); } else { ok = ok && disable_plane(crtc->cursor); @@ -432,62 +363,105 @@ static bool crtc_commit(struct wlr_drm_connector *conn, } } - if (!ok) { - goto out; + return ok; +} + +static void connector_update_layers_feedback(const struct wlr_drm_connector_state *state) { + struct wlr_drm_backend *drm = state->connector->backend; + + if (!(state->base->committed & WLR_OUTPUT_STATE_LAYERS)) { + return; } - int ret = liftoff_output_apply(crtc->liftoff, req, flags); - if (ret != 0) { - wlr_drm_conn_log(conn, test_only ? WLR_DEBUG : WLR_ERROR, - "liftoff_output_apply failed: %s", strerror(-ret)); - ok = false; - goto out; + for (size_t i = 0; i < state->base->layers_len; i++) { + struct wlr_output_layer_state *layer_state = &state->base->layers[i]; + struct wlr_drm_layer *layer = get_drm_layer(drm, layer_state->layer); + layer_state->accepted = + !liftoff_layer_needs_composition(layer->liftoff); + if (!layer_state->accepted) { + update_layer_feedback(drm, layer); + } } +} - if (crtc->cursor && - liftoff_layer_needs_composition(crtc->cursor->liftoff_layer)) { - wlr_drm_conn_log(conn, WLR_DEBUG, "Failed to scan-out cursor plane"); - ok = false; - goto out; +static bool commit(struct wlr_drm_backend *drm, + const struct wlr_drm_device_state *state, + struct wlr_drm_page_flip *page_flip, uint32_t flags, bool test_only) { + bool ok = false; + struct wl_array fb_damage_clips_arr = {0}; + drmModeAtomicReq *req = NULL; + + if (test_only) { + flags |= DRM_MODE_ATOMIC_TEST_ONLY; + } + if (state->modeset) { + flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; + } + if (!test_only && state->nonblock) { + flags |= DRM_MODE_ATOMIC_NONBLOCK; } - ret = drmModeAtomicCommit(drm->fd, req, flags, page_flip); - if (ret != 0) { - wlr_drm_conn_log_errno(conn, test_only ? WLR_DEBUG : WLR_ERROR, - "Atomic commit failed"); - ok = false; + for (size_t i = 0; i < state->connectors_len; i++) { + struct wlr_drm_connector_state *conn_state = &state->connectors[i]; + struct wlr_drm_connector *conn = conn_state->connector; + if (state->modeset && !register_planes_for_crtc(drm, conn->crtc)) { + goto out; + } + if (!drm_atomic_connector_prepare(conn_state, state->modeset)) { + goto out; + } + } + + req = drmModeAtomicAlloc(); + if (req == NULL) { + wlr_log(WLR_ERROR, "drmModeAtomicAlloc failed"); goto out; } - if (state->base->committed & WLR_OUTPUT_STATE_LAYERS) { - for (size_t i = 0; i < state->base->layers_len; i++) { - struct wlr_output_layer_state *layer_state = &state->base->layers[i]; - struct wlr_drm_layer *layer = get_drm_layer(drm, layer_state->layer); - layer_state->accepted = - !liftoff_layer_needs_composition(layer->liftoff); - if (!test_only && !layer_state->accepted) { - update_layer_feedback(drm, layer); - } + for (size_t i = 0; i < state->connectors_len; i++) { + if (!add_connector(req, &state->connectors[i], state->modeset, &fb_damage_clips_arr)) { + goto out; } } -out: - drmModeAtomicFree(req); + for (size_t i = 0; i < state->connectors_len; i++) { + struct wlr_drm_connector *conn = state->connectors[i].connector; + struct wlr_drm_crtc *crtc = conn->crtc; - if (ok && !test_only) { - commit_blob(drm, &crtc->mode_id, mode_id); - commit_blob(drm, &crtc->gamma_lut, gamma_lut); +#if HAVE_LIBLIFTOFF_0_5 + int ret = liftoff_output_apply(crtc->liftoff, req, flags, NULL); +#else + int ret = liftoff_output_apply(crtc->liftoff, req, flags); +#endif + if (ret != 0) { + wlr_drm_conn_log(conn, test_only ? WLR_DEBUG : WLR_ERROR, + "liftoff_output_apply failed: %s", strerror(-ret)); + goto out; + } - if (vrr_enabled != prev_vrr_enabled) { - output->adaptive_sync_status = vrr_enabled ? - WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED : - WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED; - wlr_drm_conn_log(conn, WLR_DEBUG, "VRR %s", - vrr_enabled ? "enabled" : "disabled"); + if (crtc->cursor && + liftoff_layer_needs_composition(crtc->cursor->liftoff_layer)) { + wlr_drm_conn_log(conn, WLR_DEBUG, "Failed to scan-out cursor plane"); + goto out; + } + } + + ok = drmModeAtomicCommit(drm->fd, req, flags, page_flip) == 0; + if (!ok) { + wlr_log_errno(test_only ? WLR_DEBUG : WLR_ERROR, + "Atomic commit failed"); + } + +out: + drmModeAtomicFree(req); + for (size_t i = 0; i < state->connectors_len; i++) { + struct wlr_drm_connector_state *conn_state = &state->connectors[i]; + if (ok && !test_only) { + drm_atomic_connector_apply_commit(conn_state); + connector_update_layers_feedback(conn_state); + } else { + drm_atomic_connector_rollback_commit(conn_state); } - } else { - rollback_blob(drm, &crtc->mode_id, mode_id); - rollback_blob(drm, &crtc->gamma_lut, gamma_lut); } uint32_t *fb_damage_clips_ptr; @@ -504,5 +478,6 @@ static bool crtc_commit(struct wlr_drm_connector *conn, const struct wlr_drm_interface liftoff_iface = { .init = init, .finish = finish, - .crtc_commit = crtc_commit, + .commit = commit, + .reset = drm_atomic_reset, }; diff --git a/backend/drm/meson.build b/backend/drm/meson.build index 6fcb2c1..666c7cc 100644 --- a/backend/drm/meson.build +++ b/backend/drm/meson.build @@ -16,7 +16,7 @@ libliftoff = dependency( 'libliftoff', version: '>=0.4.0', fallback: 'libliftoff', - required: false, + required: get_option('libliftoff'), ) if not (hwdata.found() and libdisplay_info.found() and features['session']) @@ -38,6 +38,7 @@ wlr_files += files( 'atomic.c', 'backend.c', 'drm.c', + 'fb.c', 'legacy.c', 'monitor.c', 'properties.c', @@ -47,6 +48,7 @@ wlr_files += files( if libliftoff.found() wlr_files += files('libliftoff.c') + internal_config.set10('HAVE_LIBLIFTOFF_0_5', libliftoff.version().version_compare('>=0.5.0')) endif features += { 'drm-backend': true } diff --git a/backend/drm/monitor.c b/backend/drm/monitor.c index 4b1eeaa..efd8537 100644 --- a/backend/drm/monitor.c +++ b/backend/drm/monitor.c @@ -25,8 +25,7 @@ static void handle_add_drm_card(struct wl_listener *listener, void *data) { } wlr_log(WLR_DEBUG, "Creating DRM backend for %s after hotplug", event->path); - struct wlr_backend *child_drm = wlr_drm_backend_create( - backend_monitor->session->display, backend_monitor->session, + struct wlr_backend *child_drm = wlr_drm_backend_create(backend_monitor->session, dev, backend_monitor->primary_drm); if (!child_drm) { wlr_log(WLR_ERROR, "Failed to create DRM backend after hotplug"); @@ -64,8 +63,7 @@ static void handle_multi_destroy(struct wl_listener *listener, void *data) { } struct wlr_drm_backend_monitor *drm_backend_monitor_create( - struct wlr_backend *multi, - struct wlr_backend *primary_drm, + struct wlr_backend *multi, struct wlr_backend *primary_drm, struct wlr_session *session) { struct wlr_drm_backend_monitor *monitor = calloc(1, sizeof(*monitor)); if (!monitor) { diff --git a/backend/drm/properties.c b/backend/drm/properties.c index 4f49517..d78bfaf 100644 --- a/backend/drm/properties.c +++ b/backend/drm/properties.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -20,7 +19,7 @@ struct prop_info { }; static const struct prop_info connector_info[] = { -#define INDEX(name) (offsetof(union wlr_drm_connector_props, name) / sizeof(uint32_t)) +#define INDEX(name) (offsetof(struct wlr_drm_connector_props, name) / sizeof(uint32_t)) { "CRTC_ID", INDEX(crtc_id) }, { "DPMS", INDEX(dpms) }, { "EDID", INDEX(edid) }, @@ -36,7 +35,7 @@ static const struct prop_info connector_info[] = { }; static const struct prop_info crtc_info[] = { -#define INDEX(name) (offsetof(union wlr_drm_crtc_props, name) / sizeof(uint32_t)) +#define INDEX(name) (offsetof(struct wlr_drm_crtc_props, name) / sizeof(uint32_t)) { "ACTIVE", INDEX(active) }, { "GAMMA_LUT", INDEX(gamma_lut) }, { "GAMMA_LUT_SIZE", INDEX(gamma_lut_size) }, @@ -46,7 +45,7 @@ static const struct prop_info crtc_info[] = { }; static const struct prop_info plane_info[] = { -#define INDEX(name) (offsetof(union wlr_drm_plane_props, name) / sizeof(uint32_t)) +#define INDEX(name) (offsetof(struct wlr_drm_plane_props, name) / sizeof(uint32_t)) { "CRTC_H", INDEX(crtc_h) }, { "CRTC_ID", INDEX(crtc_id) }, { "CRTC_W", INDEX(crtc_w) }, @@ -54,7 +53,10 @@ static const struct prop_info plane_info[] = { { "CRTC_Y", INDEX(crtc_y) }, { "FB_DAMAGE_CLIPS", INDEX(fb_damage_clips) }, { "FB_ID", INDEX(fb_id) }, + { "HOTSPOT_X", INDEX(hotspot_x) }, + { "HOTSPOT_Y", INDEX(hotspot_y) }, { "IN_FORMATS", INDEX(in_formats) }, + { "SIZE_HINTS", INDEX(size_hints) }, { "SRC_H", INDEX(src_h) }, { "SRC_W", INDEX(src_w) }, { "SRC_X", INDEX(src_x) }, @@ -99,19 +101,18 @@ static bool scan_properties(int fd, uint32_t id, uint32_t type, uint32_t *result return true; } -bool get_drm_connector_props(int fd, uint32_t id, - union wlr_drm_connector_props *out) { - return scan_properties(fd, id, DRM_MODE_OBJECT_CONNECTOR, out->props, +bool get_drm_connector_props(int fd, uint32_t id, struct wlr_drm_connector_props *out) { + return scan_properties(fd, id, DRM_MODE_OBJECT_CONNECTOR, (uint32_t *)out, connector_info, sizeof(connector_info) / sizeof(connector_info[0])); } -bool get_drm_crtc_props(int fd, uint32_t id, union wlr_drm_crtc_props *out) { - return scan_properties(fd, id, DRM_MODE_OBJECT_CRTC, out->props, +bool get_drm_crtc_props(int fd, uint32_t id, struct wlr_drm_crtc_props *out) { + return scan_properties(fd, id, DRM_MODE_OBJECT_CRTC, (uint32_t *)out, crtc_info, sizeof(crtc_info) / sizeof(crtc_info[0])); } -bool get_drm_plane_props(int fd, uint32_t id, union wlr_drm_plane_props *out) { - return scan_properties(fd, id, DRM_MODE_OBJECT_PLANE, out->props, +bool get_drm_plane_props(int fd, uint32_t id, struct wlr_drm_plane_props *out) { + return scan_properties(fd, id, DRM_MODE_OBJECT_PLANE, (uint32_t *)out, plane_info, sizeof(plane_info) / sizeof(plane_info[0])); } diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c index f98cc86..e4aadc1 100644 --- a/backend/drm/renderer.c +++ b/backend/drm/renderer.c @@ -1,18 +1,12 @@ -#define _POSIX_C_SOURCE 200809L #include #include -#include -#include -#include -#include -#include -#include #include #include -#include #include #include "backend/drm/drm.h" -#include "backend/drm/util.h" +#include "backend/drm/fb.h" +#include "backend/drm/renderer.h" +#include "backend/backend.h" #include "render/drm_format_set.h" #include "render/allocator/allocator.h" #include "render/pixel_format.h" @@ -20,15 +14,14 @@ bool init_drm_renderer(struct wlr_drm_backend *drm, struct wlr_drm_renderer *renderer) { - renderer->backend = drm; - renderer->wlr_rend = renderer_autocreate_with_drm_fd(drm->fd); if (!renderer->wlr_rend) { wlr_log(WLR_ERROR, "Failed to create renderer"); return false; } - renderer->allocator = allocator_autocreate_with_drm_fd(&drm->backend, + uint32_t backend_caps = backend_get_buffer_caps(&drm->backend); + renderer->allocator = allocator_autocreate_with_drm_fd(backend_caps, renderer->wlr_rend, drm->fd); if (renderer->allocator == NULL) { wlr_log(WLR_ERROR, "Failed to create allocator"); @@ -48,7 +41,7 @@ void finish_drm_renderer(struct wlr_drm_renderer *renderer) { wlr_renderer_destroy(renderer->wlr_rend); } -static void finish_drm_surface(struct wlr_drm_surface *surf) { +void finish_drm_surface(struct wlr_drm_surface *surf) { if (!surf || !surf->renderer) { return; } @@ -128,18 +121,6 @@ struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf, return NULL; } - -void drm_plane_finish_surface(struct wlr_drm_plane *plane) { - if (!plane) { - return; - } - - drm_fb_clear(&plane->queued_fb); - drm_fb_clear(&plane->current_fb); - - finish_drm_surface(&plane->mgpu_surf); -} - bool drm_plane_pick_render_format(struct wlr_drm_plane *plane, struct wlr_drm_format *fmt, struct wlr_drm_renderer *renderer) { const struct wlr_drm_format_set *render_formats = @@ -189,239 +170,3 @@ bool drm_plane_pick_render_format(struct wlr_drm_plane *plane, return true; } - -void drm_fb_clear(struct wlr_drm_fb **fb_ptr) { - if (*fb_ptr == NULL) { - return; - } - - struct wlr_drm_fb *fb = *fb_ptr; - wlr_buffer_unlock(fb->wlr_buf); // may destroy the buffer - - *fb_ptr = NULL; -} - -struct wlr_drm_fb *drm_fb_lock(struct wlr_drm_fb *fb) { - wlr_buffer_lock(fb->wlr_buf); - return fb; -} - -static void drm_fb_handle_destroy(struct wlr_addon *addon) { - struct wlr_drm_fb *fb = wl_container_of(addon, fb, addon); - drm_fb_destroy(fb); -} - -static const struct wlr_addon_interface fb_addon_impl = { - .name = "wlr_drm_fb", - .destroy = drm_fb_handle_destroy, -}; - -static uint32_t get_fb_for_bo(struct wlr_drm_backend *drm, - struct wlr_dmabuf_attributes *dmabuf, uint32_t handles[static 4]) { - uint64_t modifiers[4] = {0}; - for (int i = 0; i < dmabuf->n_planes; i++) { - // KMS requires all BO planes to have the same modifier - modifiers[i] = dmabuf->modifier; - } - - uint32_t id = 0; - if (drm->addfb2_modifiers && dmabuf->modifier != DRM_FORMAT_MOD_INVALID) { - if (drmModeAddFB2WithModifiers(drm->fd, dmabuf->width, dmabuf->height, - dmabuf->format, handles, dmabuf->stride, dmabuf->offset, - modifiers, &id, DRM_MODE_FB_MODIFIERS) != 0) { - wlr_log_errno(WLR_DEBUG, "drmModeAddFB2WithModifiers failed"); - } - } else { - if (dmabuf->modifier != DRM_FORMAT_MOD_INVALID && - dmabuf->modifier != DRM_FORMAT_MOD_LINEAR) { - wlr_log(WLR_ERROR, "Cannot import DRM framebuffer with explicit " - "modifier 0x%"PRIX64, dmabuf->modifier); - return 0; - } - - int ret = drmModeAddFB2(drm->fd, dmabuf->width, dmabuf->height, - dmabuf->format, handles, dmabuf->stride, dmabuf->offset, &id, 0); - if (ret != 0 && dmabuf->format == DRM_FORMAT_ARGB8888 && - dmabuf->n_planes == 1 && dmabuf->offset[0] == 0) { - // Some big-endian machines don't support drmModeAddFB2. Try a - // last-resort fallback for ARGB8888 buffers, like Xorg's - // modesetting driver does. - wlr_log(WLR_DEBUG, "drmModeAddFB2 failed (%s), falling back to " - "legacy drmModeAddFB", strerror(-ret)); - - uint32_t depth = 32; - uint32_t bpp = 32; - ret = drmModeAddFB(drm->fd, dmabuf->width, dmabuf->height, depth, - bpp, dmabuf->stride[0], handles[0], &id); - if (ret != 0) { - wlr_log_errno(WLR_DEBUG, "drmModeAddFB failed"); - } - } else if (ret != 0) { - wlr_log_errno(WLR_DEBUG, "drmModeAddFB2 failed"); - } - } - - return id; -} - -static void close_all_bo_handles(struct wlr_drm_backend *drm, - uint32_t handles[static 4]) { - for (int i = 0; i < 4; ++i) { - if (handles[i] == 0) { - continue; - } - - // If multiple planes share the same BO handle, avoid double-closing it - bool already_closed = false; - for (int j = 0; j < i; ++j) { - if (handles[i] == handles[j]) { - already_closed = true; - break; - } - } - if (already_closed) { - continue; - } - - if (drmCloseBufferHandle(drm->fd, handles[i]) != 0) { - wlr_log_errno(WLR_ERROR, "drmCloseBufferHandle failed"); - } - } -} - -static void drm_poisoned_fb_handle_destroy(struct wlr_addon *addon) { - wlr_addon_finish(addon); - free(addon); -} - -static const struct wlr_addon_interface poisoned_fb_addon_impl = { - .name = "wlr_drm_poisoned_fb", - .destroy = drm_poisoned_fb_handle_destroy, -}; - -static bool is_buffer_poisoned(struct wlr_drm_backend *drm, - struct wlr_buffer *buf) { - return wlr_addon_find(&buf->addons, drm, &poisoned_fb_addon_impl) != NULL; -} - -/** - * Mark the buffer as "poisoned", ie. it cannot be imported into KMS. This - * allows us to avoid repeatedly trying to import it when it's not - * scanout-capable. - */ -static void poison_buffer(struct wlr_drm_backend *drm, - struct wlr_buffer *buf) { - struct wlr_addon *addon = calloc(1, sizeof(*addon)); - if (addon == NULL) { - wlr_log_errno(WLR_ERROR, "Allocation failed"); - return; - } - wlr_addon_init(addon, &buf->addons, drm, &poisoned_fb_addon_impl); - wlr_log(WLR_DEBUG, "Poisoning buffer"); -} - -static struct wlr_drm_fb *drm_fb_create(struct wlr_drm_backend *drm, - struct wlr_buffer *buf, const struct wlr_drm_format_set *formats) { - struct wlr_dmabuf_attributes attribs; - if (!wlr_buffer_get_dmabuf(buf, &attribs)) { - wlr_log(WLR_DEBUG, "Failed to get DMA-BUF from buffer"); - return NULL; - } - - if (is_buffer_poisoned(drm, buf)) { - wlr_log(WLR_DEBUG, "Buffer is poisoned"); - return NULL; - } - - struct wlr_drm_fb *fb = calloc(1, sizeof(*fb)); - if (!fb) { - wlr_log_errno(WLR_ERROR, "Allocation failed"); - return NULL; - } - - if (formats && !wlr_drm_format_set_has(formats, attribs.format, - attribs.modifier)) { - // The format isn't supported by the plane. Try stripping the alpha - // channel, if any. - const struct wlr_pixel_format_info *info = - drm_get_pixel_format_info(attribs.format); - if (info != NULL && info->opaque_substitute != DRM_FORMAT_INVALID && - wlr_drm_format_set_has(formats, info->opaque_substitute, attribs.modifier)) { - attribs.format = info->opaque_substitute; - } else { - wlr_log(WLR_DEBUG, "Buffer format 0x%"PRIX32" with modifier " - "0x%"PRIX64" cannot be scanned out", - attribs.format, attribs.modifier); - goto error_fb; - } - } - - uint32_t handles[4] = {0}; - for (int i = 0; i < attribs.n_planes; ++i) { - int ret = drmPrimeFDToHandle(drm->fd, attribs.fd[i], &handles[i]); - if (ret != 0) { - wlr_log_errno(WLR_DEBUG, "drmPrimeFDToHandle failed"); - goto error_bo_handle; - } - } - - fb->id = get_fb_for_bo(drm, &attribs, handles); - if (!fb->id) { - wlr_log(WLR_DEBUG, "Failed to import BO in KMS"); - poison_buffer(drm, buf); - goto error_bo_handle; - } - - close_all_bo_handles(drm, handles); - - fb->backend = drm; - fb->wlr_buf = buf; - - wlr_addon_init(&fb->addon, &buf->addons, drm, &fb_addon_impl); - wl_list_insert(&drm->fbs, &fb->link); - - return fb; - -error_bo_handle: - close_all_bo_handles(drm, handles); -error_fb: - free(fb); - return NULL; -} - -void drm_fb_destroy(struct wlr_drm_fb *fb) { - struct wlr_drm_backend *drm = fb->backend; - - wl_list_remove(&fb->link); - wlr_addon_finish(&fb->addon); - - if (drmModeRmFB(drm->fd, fb->id) != 0) { - wlr_log(WLR_ERROR, "drmModeRmFB failed"); - } - - free(fb); -} - -bool drm_fb_import(struct wlr_drm_fb **fb_ptr, struct wlr_drm_backend *drm, - struct wlr_buffer *buf, const struct wlr_drm_format_set *formats) { - struct wlr_drm_fb *fb; - struct wlr_addon *addon = wlr_addon_find(&buf->addons, drm, &fb_addon_impl); - if (addon != NULL) { - fb = wl_container_of(addon, fb, addon); - } else { - fb = drm_fb_create(drm, buf, formats); - if (!fb) { - return false; - } - } - - wlr_buffer_lock(buf); - drm_fb_move(fb_ptr, &fb); - return true; -} - -void drm_fb_move(struct wlr_drm_fb **new, struct wlr_drm_fb **old) { - drm_fb_clear(new); - *new = *old; - *old = NULL; -} diff --git a/backend/drm/util.c b/backend/drm/util.c index 0e7f3f8..a14e5e1 100644 --- a/backend/drm/util.c +++ b/backend/drm/util.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include diff --git a/backend/headless/backend.c b/backend/headless/backend.c index 8ab7d03..e643a06 100644 --- a/backend/headless/backend.c +++ b/backend/headless/backend.c @@ -40,7 +40,7 @@ static void backend_destroy(struct wlr_backend *wlr_backend) { wlr_output_destroy(&output->wlr_output); } - wl_list_remove(&backend->display_destroy.link); + wl_list_remove(&backend->event_loop_destroy.link); free(backend); } @@ -57,13 +57,13 @@ static const struct wlr_backend_impl backend_impl = { .get_buffer_caps = get_buffer_caps, }; -static void handle_display_destroy(struct wl_listener *listener, void *data) { +static void handle_event_loop_destroy(struct wl_listener *listener, void *data) { struct wlr_headless_backend *backend = - wl_container_of(listener, backend, display_destroy); + wl_container_of(listener, backend, event_loop_destroy); backend_destroy(&backend->backend); } -struct wlr_backend *wlr_headless_backend_create(struct wl_display *display) { +struct wlr_backend *wlr_headless_backend_create(struct wl_event_loop *loop) { wlr_log(WLR_INFO, "Creating headless backend"); struct wlr_headless_backend *backend = calloc(1, sizeof(*backend)); @@ -74,11 +74,11 @@ struct wlr_backend *wlr_headless_backend_create(struct wl_display *display) { wlr_backend_init(&backend->backend, &backend_impl); - backend->display = display; + backend->event_loop = loop; wl_list_init(&backend->outputs); - backend->display_destroy.notify = handle_display_destroy; - wl_display_add_destroy_listener(display, &backend->display_destroy); + backend->event_loop_destroy.notify = handle_event_loop_destroy; + wl_event_loop_add_destroy_listener(loop, &backend->event_loop_destroy); return &backend->backend; } diff --git a/backend/headless/output.c b/backend/headless/output.c index 406959b..5aaf1bd 100644 --- a/backend/headless/output.c +++ b/backend/headless/output.c @@ -119,7 +119,7 @@ struct wlr_output *wlr_headless_add_output(struct wlr_backend *wlr_backend, wlr_output_state_init(&state); wlr_output_state_set_custom_mode(&state, width, height, 0); - wlr_output_init(wlr_output, &backend->backend, &output_impl, backend->display, &state); + wlr_output_init(wlr_output, &backend->backend, &output_impl, backend->event_loop, &state); wlr_output_state_finish(&state); output_update_refresh(output, 0); @@ -134,8 +134,7 @@ struct wlr_output *wlr_headless_add_output(struct wlr_backend *wlr_backend, snprintf(description, sizeof(description), "Headless output %zu", output_num); wlr_output_set_description(wlr_output, description); - struct wl_event_loop *ev = wl_display_get_event_loop(backend->display); - output->frame_timer = wl_event_loop_add_timer(ev, signal_frame, output); + output->frame_timer = wl_event_loop_add_timer(backend->event_loop, signal_frame, output); wl_list_insert(&backend->outputs, &output->link); diff --git a/backend/libinput/backend.c b/backend/libinput/backend.c index cbdf8b7..121731b 100644 --- a/backend/libinput/backend.c +++ b/backend/libinput/backend.c @@ -51,7 +51,7 @@ static int handle_libinput_readable(int fd, uint32_t mask, void *_backend) { int ret = libinput_dispatch(backend->libinput_context); if (ret != 0) { wlr_log(WLR_ERROR, "Failed to dispatch libinput: %s", strerror(-ret)); - wl_display_terminate(backend->display); + wlr_backend_destroy(&backend->backend); return 0; } struct libinput_event *event; @@ -106,21 +106,17 @@ static bool backend_start(struct wlr_backend *wlr_backend) { int libinput_fd = libinput_get_fd(backend->libinput_context); + handle_libinput_readable(libinput_fd, WL_EVENT_READABLE, backend); if (!env_parse_bool("WLR_LIBINPUT_NO_DEVICES") && wl_list_empty(&backend->devices)) { - handle_libinput_readable(libinput_fd, WL_EVENT_READABLE, backend); - if (wl_list_empty(&backend->devices)) { - wlr_log(WLR_ERROR, "libinput initialization failed, no input devices"); - wlr_log(WLR_ERROR, "Set WLR_LIBINPUT_NO_DEVICES=1 to suppress this check"); - return false; - } + wlr_log(WLR_ERROR, "libinput initialization failed, no input devices"); + wlr_log(WLR_ERROR, "Set WLR_LIBINPUT_NO_DEVICES=1 to suppress this check"); + return false; } - struct wl_event_loop *event_loop = - wl_display_get_event_loop(backend->display); if (backend->input_event) { wl_event_source_remove(backend->input_event); } - backend->input_event = wl_event_loop_add_fd(event_loop, libinput_fd, + backend->input_event = wl_event_loop_add_fd(backend->session->event_loop, libinput_fd, WL_EVENT_READABLE, handle_libinput_readable, backend); if (!backend->input_event) { wlr_log(WLR_ERROR, "Failed to create input event on event loop"); @@ -144,7 +140,6 @@ static void backend_destroy(struct wlr_backend *wlr_backend) { wlr_backend_finish(wlr_backend); - wl_list_remove(&backend->display_destroy.link); wl_list_remove(&backend->session_destroy.link); wl_list_remove(&backend->session_signal.link); @@ -186,14 +181,7 @@ static void handle_session_destroy(struct wl_listener *listener, void *data) { backend_destroy(&backend->backend); } -static void handle_display_destroy(struct wl_listener *listener, void *data) { - struct wlr_libinput_backend *backend = - wl_container_of(listener, backend, display_destroy); - backend_destroy(&backend->backend); -} - -struct wlr_backend *wlr_libinput_backend_create(struct wl_display *display, - struct wlr_session *session) { +struct wlr_backend *wlr_libinput_backend_create(struct wlr_session *session) { struct wlr_libinput_backend *backend = calloc(1, sizeof(*backend)); if (!backend) { wlr_log(WLR_ERROR, "Allocation failed: %s", strerror(errno)); @@ -204,7 +192,6 @@ struct wlr_backend *wlr_libinput_backend_create(struct wl_display *display, wl_list_init(&backend->devices); backend->session = session; - backend->display = display; backend->session_signal.notify = session_signal; wl_signal_add(&session->events.active, &backend->session_signal); @@ -212,9 +199,6 @@ struct wlr_backend *wlr_libinput_backend_create(struct wl_display *display, backend->session_destroy.notify = handle_session_destroy; wl_signal_add(&session->events.destroy, &backend->session_destroy); - backend->display_destroy.notify = handle_display_destroy; - wl_display_add_destroy_listener(display, &backend->display_destroy); - return &backend->backend; } @@ -234,16 +218,28 @@ struct libinput_device *wlr_libinput_get_device_handle( case WLR_INPUT_DEVICE_TOUCH: dev = device_from_touch(wlr_touch_from_input_device(wlr_dev)); break; - case WLR_INPUT_DEVICE_TABLET_TOOL: + case WLR_INPUT_DEVICE_TABLET: dev = device_from_tablet(wlr_tablet_from_input_device(wlr_dev)); break; case WLR_INPUT_DEVICE_TABLET_PAD: dev = device_from_tablet_pad(wlr_tablet_pad_from_input_device(wlr_dev)); break; } + + assert(dev); return dev->handle; } uint32_t usec_to_msec(uint64_t usec) { return (uint32_t)(usec / 1000); } + +const char *get_libinput_device_name(struct libinput_device *device) { + // libinput guarantees that the name is non-NULL, and an empty string if + // unset. However wlroots uses NULL to indicate that the name is unset. + const char *name = libinput_device_get_name(device); + if (name[0] == '\0') { + return NULL; + } + return name; +} diff --git a/backend/libinput/events.c b/backend/libinput/events.c index 4726e32..6cfe34e 100644 --- a/backend/libinput/events.c +++ b/backend/libinput/events.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include @@ -48,8 +47,8 @@ bool wlr_input_device_is_libinput(struct wlr_input_device *wlr_dev) { case WLR_INPUT_DEVICE_TOUCH: return wlr_touch_from_input_device(wlr_dev)->impl == &libinput_touch_impl; - case WLR_INPUT_DEVICE_TABLET_TOOL: - return wlr_tablet_from_input_device(wlr_dev)-> impl == + case WLR_INPUT_DEVICE_TABLET: + return wlr_tablet_from_input_device(wlr_dev)->impl == &libinput_tablet_impl; case WLR_INPUT_DEVICE_TABLET_PAD: return wlr_tablet_pad_from_input_device(wlr_dev)->impl == @@ -124,11 +123,6 @@ static void handle_device_added(struct wlr_libinput_backend *backend, wl_signal_emit_mutable(&backend->backend.events.new_input, &dev->tablet_pad.base); } - - if (libinput_device_has_capability( - libinput_dev, LIBINPUT_DEVICE_CAP_GESTURE)) { - wlr_log(WLR_DEBUG, "libinput gesture not handled"); - } } static void handle_device_removed(struct wlr_libinput_backend *backend, @@ -173,25 +167,20 @@ void handle_libinput_event(struct wlr_libinput_backend *backend, handle_pointer_button(event, &dev->pointer); break; case LIBINPUT_EVENT_POINTER_AXIS: -#if !HAVE_LIBINPUT_SCROLL_VALUE120 /* This event must be ignored in favour of the SCROLL_* events */ - handle_pointer_axis(event, &dev->pointer); -#endif break; -#if HAVE_LIBINPUT_SCROLL_VALUE120 case LIBINPUT_EVENT_POINTER_SCROLL_WHEEL: handle_pointer_axis_value120(event, &dev->pointer, - WLR_AXIS_SOURCE_WHEEL); + WL_POINTER_AXIS_SOURCE_WHEEL); break; case LIBINPUT_EVENT_POINTER_SCROLL_FINGER: handle_pointer_axis_value120(event, &dev->pointer, - WLR_AXIS_SOURCE_FINGER); + WL_POINTER_AXIS_SOURCE_FINGER); break; case LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS: handle_pointer_axis_value120(event, &dev->pointer, - WLR_AXIS_SOURCE_CONTINUOUS); + WL_POINTER_AXIS_SOURCE_CONTINUOUS); break; -#endif case LIBINPUT_EVENT_TOUCH_DOWN: handle_touch_down(event, &dev->touch); break; @@ -249,14 +238,12 @@ void handle_libinput_event(struct wlr_libinput_backend *backend, case LIBINPUT_EVENT_GESTURE_PINCH_END: handle_pointer_pinch_end(event, &dev->pointer); break; -#if HAVE_LIBINPUT_HOLD_GESTURES case LIBINPUT_EVENT_GESTURE_HOLD_BEGIN: handle_pointer_hold_begin(event, &dev->pointer); break; case LIBINPUT_EVENT_GESTURE_HOLD_END: handle_pointer_hold_end(event, &dev->pointer); break; -#endif default: wlr_log(WLR_DEBUG, "Unknown libinput event %d", event_type); break; diff --git a/backend/libinput/keyboard.c b/backend/libinput/keyboard.c index 2e5e6ef..7518453 100644 --- a/backend/libinput/keyboard.c +++ b/backend/libinput/keyboard.c @@ -23,11 +23,9 @@ const struct wlr_keyboard_impl libinput_keyboard_impl = { }; void init_device_keyboard(struct wlr_libinput_input_device *dev) { - const char *name = libinput_device_get_name(dev->handle); + const char *name = get_libinput_device_name(dev->handle); struct wlr_keyboard *wlr_kb = &dev->keyboard; wlr_keyboard_init(wlr_kb, &libinput_keyboard_impl, name); - wlr_kb->base.vendor = libinput_device_get_id_vendor(dev->handle); - wlr_kb->base.product = libinput_device_get_id_product(dev->handle); libinput_device_led_update(dev->handle, 0); } diff --git a/backend/libinput/meson.build b/backend/libinput/meson.build index f459289..c244eb7 100644 --- a/backend/libinput/meson.build +++ b/backend/libinput/meson.build @@ -5,7 +5,7 @@ endif libinput = dependency( 'libinput', - version: '>=1.14.0', + version: '>=1.19.0', required: 'libinput' in backends, not_found_message: '\n'.join(msg), ) @@ -28,6 +28,4 @@ wlr_files += files( features += { 'libinput-backend': true } wlr_deps += libinput -# libinput hold gestures and high resolution scroll are available since 1.19.0 -internal_config.set10('HAVE_LIBINPUT_HOLD_GESTURES', libinput.version().version_compare('>=1.19.0')) -internal_config.set10('HAVE_LIBINPUT_SCROLL_VALUE120', libinput.version().version_compare('>=1.19.0')) +internal_config.set10('HAVE_LIBINPUT_BUSTYPE', libinput.version().version_compare('>=1.26.0')) diff --git a/backend/libinput/pointer.c b/backend/libinput/pointer.c index 711474f..1fe3d47 100644 --- a/backend/libinput/pointer.c +++ b/backend/libinput/pointer.c @@ -8,11 +8,9 @@ const struct wlr_pointer_impl libinput_pointer_impl = { }; void init_device_pointer(struct wlr_libinput_input_device *dev) { - const char *name = libinput_device_get_name(dev->handle); + const char *name = get_libinput_device_name(dev->handle); struct wlr_pointer *wlr_pointer = &dev->pointer; wlr_pointer_init(wlr_pointer, &libinput_pointer_impl, name); - wlr_pointer->base.vendor = libinput_device_get_id_vendor(dev->handle); - wlr_pointer->base.product = libinput_device_get_id_product(dev->handle); } struct wlr_libinput_input_device *device_from_pointer( @@ -69,13 +67,13 @@ void handle_pointer_button(struct libinput_event *event, uint32_t seat_count = libinput_event_pointer_get_seat_button_count(pevent); switch (libinput_event_pointer_get_button_state(pevent)) { case LIBINPUT_BUTTON_STATE_PRESSED: - wlr_event.state = WLR_BUTTON_PRESSED; + wlr_event.state = WL_POINTER_BUTTON_STATE_PRESSED; if (seat_count != 1) { return; } break; case LIBINPUT_BUTTON_STATE_RELEASED: - wlr_event.state = WLR_BUTTON_RELEASED; + wlr_event.state = WL_POINTER_BUTTON_STATE_RELEASED; if (seat_count != 0) { return; } @@ -95,16 +93,16 @@ void handle_pointer_axis(struct libinput_event *event, }; switch (libinput_event_pointer_get_axis_source(pevent)) { case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL: - wlr_event.source = WLR_AXIS_SOURCE_WHEEL; + wlr_event.source = WL_POINTER_AXIS_SOURCE_WHEEL; break; case LIBINPUT_POINTER_AXIS_SOURCE_FINGER: - wlr_event.source = WLR_AXIS_SOURCE_FINGER; + wlr_event.source = WL_POINTER_AXIS_SOURCE_FINGER; break; case LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS: - wlr_event.source = WLR_AXIS_SOURCE_CONTINUOUS; + wlr_event.source = WL_POINTER_AXIS_SOURCE_CONTINUOUS; break; case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL_TILT: - wlr_event.source = WLR_AXIS_SOURCE_WHEEL_TILT; + wlr_event.source = WL_POINTER_AXIS_SOURCE_WHEEL_TILT; break; } const enum libinput_pointer_axis axes[] = { @@ -118,10 +116,10 @@ void handle_pointer_axis(struct libinput_event *event, switch (axes[i]) { case LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL: - wlr_event.orientation = WLR_AXIS_ORIENTATION_VERTICAL; + wlr_event.orientation = WL_POINTER_AXIS_VERTICAL_SCROLL; break; case LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL: - wlr_event.orientation = WLR_AXIS_ORIENTATION_HORIZONTAL; + wlr_event.orientation = WL_POINTER_AXIS_HORIZONTAL_SCROLL; break; } wlr_event.delta = @@ -129,14 +127,17 @@ void handle_pointer_axis(struct libinput_event *event, wlr_event.delta_discrete = libinput_event_pointer_get_axis_value_discrete(pevent, axes[i]); wlr_event.delta_discrete *= WLR_POINTER_AXIS_DISCRETE_STEP; + wlr_event.relative_direction = WL_POINTER_AXIS_RELATIVE_DIRECTION_IDENTICAL; + if (libinput_device_config_scroll_get_natural_scroll_enabled(libinput_event_get_device(event))) { + wlr_event.relative_direction = WL_POINTER_AXIS_RELATIVE_DIRECTION_INVERTED; + } wl_signal_emit_mutable(&pointer->events.axis, &wlr_event); } wl_signal_emit_mutable(&pointer->events.frame, pointer); } -#if HAVE_LIBINPUT_SCROLL_VALUE120 void handle_pointer_axis_value120(struct libinput_event *event, - struct wlr_pointer *pointer, enum wlr_axis_source source) { + struct wlr_pointer *pointer, enum wl_pointer_axis_source source) { struct libinput_event_pointer *pevent = libinput_event_get_pointer_event(event); struct wlr_pointer_axis_event wlr_event = { @@ -155,15 +156,15 @@ void handle_pointer_axis_value120(struct libinput_event *event, } switch (axes[i]) { case LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL: - wlr_event.orientation = WLR_AXIS_ORIENTATION_VERTICAL; + wlr_event.orientation = WL_POINTER_AXIS_VERTICAL_SCROLL; break; case LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL: - wlr_event.orientation = WLR_AXIS_ORIENTATION_HORIZONTAL; + wlr_event.orientation = WL_POINTER_AXIS_HORIZONTAL_SCROLL; break; } wlr_event.delta = libinput_event_pointer_get_scroll_value(pevent, axes[i]); - if (source == WLR_AXIS_SOURCE_WHEEL) { + if (source == WL_POINTER_AXIS_SOURCE_WHEEL) { wlr_event.delta_discrete = libinput_event_pointer_get_scroll_value_v120(pevent, axes[i]); } @@ -171,7 +172,6 @@ void handle_pointer_axis_value120(struct libinput_event *event, } wl_signal_emit_mutable(&pointer->events.frame, pointer); } -#endif void handle_pointer_swipe_begin(struct libinput_event *event, struct wlr_pointer *pointer) { diff --git a/backend/libinput/switch.c b/backend/libinput/switch.c index 61bb887..abeec86 100644 --- a/backend/libinput/switch.c +++ b/backend/libinput/switch.c @@ -8,11 +8,9 @@ const struct wlr_switch_impl libinput_switch_impl = { }; void init_device_switch(struct wlr_libinput_input_device *dev) { - const char *name = libinput_device_get_name(dev->handle); + const char *name = get_libinput_device_name(dev->handle); struct wlr_switch *wlr_switch = &dev->switch_device; wlr_switch_init(wlr_switch, &libinput_switch_impl, name); - wlr_switch->base.vendor = libinput_device_get_id_vendor(dev->handle); - wlr_switch->base.product = libinput_device_get_id_product(dev->handle); } struct wlr_libinput_input_device *device_from_switch( diff --git a/backend/libinput/tablet_pad.c b/backend/libinput/tablet_pad.c index e532752..98b3e4f 100644 --- a/backend/libinput/tablet_pad.c +++ b/backend/libinput/tablet_pad.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -91,11 +90,9 @@ static void add_pad_group_from_libinput(struct wlr_tablet_pad *pad, void init_device_tablet_pad(struct wlr_libinput_input_device *dev) { struct libinput_device *handle = dev->handle; - const char *name = libinput_device_get_name(handle); + const char *name = get_libinput_device_name(handle); struct wlr_tablet_pad *wlr_tablet_pad = &dev->tablet_pad; wlr_tablet_pad_init(wlr_tablet_pad, &libinput_tablet_pad_impl, name); - wlr_tablet_pad->base.vendor = libinput_device_get_id_vendor(handle); - wlr_tablet_pad->base.product = libinput_device_get_id_product(handle); wlr_tablet_pad->button_count = libinput_device_tablet_pad_get_num_buttons(handle); diff --git a/backend/libinput/tablet_tool.c b/backend/libinput/tablet_tool.c index 1c85d08..cc9e609 100644 --- a/backend/libinput/tablet_tool.c +++ b/backend/libinput/tablet_tool.c @@ -1,7 +1,7 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include +#include #include #include #include @@ -18,11 +18,17 @@ const struct wlr_tablet_impl libinput_tablet_impl = { }; void init_device_tablet(struct wlr_libinput_input_device *dev) { - const char *name = libinput_device_get_name(dev->handle); + const char *name = get_libinput_device_name(dev->handle); struct wlr_tablet *wlr_tablet = &dev->tablet; wlr_tablet_init(wlr_tablet, &libinput_tablet_impl, name); - wlr_tablet->base.vendor = libinput_device_get_id_vendor(dev->handle); - wlr_tablet->base.product = libinput_device_get_id_product(dev->handle); + +#if HAVE_LIBINPUT_BUSTYPE + if (libinput_device_get_id_bustype(dev->handle) == BUS_USB) +#endif + { + wlr_tablet->usb_vendor_id = libinput_device_get_id_vendor(dev->handle); + wlr_tablet->usb_product_id = libinput_device_get_id_product(dev->handle); + } libinput_device_get_size(dev->handle, &wlr_tablet->width_mm, &wlr_tablet->height_mm); diff --git a/backend/libinput/touch.c b/backend/libinput/touch.c index 14713b5..077cdf2 100644 --- a/backend/libinput/touch.c +++ b/backend/libinput/touch.c @@ -8,11 +8,9 @@ const struct wlr_touch_impl libinput_touch_impl = { }; void init_device_touch(struct wlr_libinput_input_device *dev) { - const char *name = libinput_device_get_name(dev->handle); + const char *name = get_libinput_device_name(dev->handle); struct wlr_touch *wlr_touch = &dev->touch; wlr_touch_init(wlr_touch, &libinput_touch_impl, name); - wlr_touch->base.vendor = libinput_device_get_id_vendor(dev->handle); - wlr_touch->base.product = libinput_device_get_id_product(dev->handle); libinput_device_get_size(dev->handle, &wlr_touch->width_mm, &wlr_touch->height_mm); diff --git a/backend/multi/backend.c b/backend/multi/backend.c index 8c36834..7dc9f54 100644 --- a/backend/multi/backend.c +++ b/backend/multi/backend.c @@ -1,10 +1,10 @@ -#define _POSIX_C_SOURCE 200112L #include #include #include #include #include #include +#include #include #include "backend/backend.h" #include "backend/multi.h" @@ -48,7 +48,9 @@ static void subbackend_state_destroy(struct subbackend_state *sub) { static void multi_backend_destroy(struct wlr_backend *wlr_backend) { struct wlr_multi_backend *backend = multi_backend_from_backend(wlr_backend); - wl_list_remove(&backend->display_destroy.link); + wl_list_remove(&backend->event_loop_destroy.link); + + wlr_backend_finish(wlr_backend); // Some backends may depend on other backends, ie. destroying a backend may // also destroy other backends @@ -58,8 +60,6 @@ static void multi_backend_destroy(struct wlr_backend *wlr_backend) { wlr_backend_destroy(sub->backend); } - // Destroy this backend only after removing all sub-backends - wlr_backend_finish(wlr_backend); free(backend); } @@ -98,20 +98,82 @@ static uint32_t multi_backend_get_buffer_caps(struct wlr_backend *backend) { return caps; } +static int compare_output_state_backend(const void *data_a, const void *data_b) { + const struct wlr_backend_output_state *a = data_a; + const struct wlr_backend_output_state *b = data_b; + + uintptr_t ptr_a = (uintptr_t)a->output->backend; + uintptr_t ptr_b = (uintptr_t)b->output->backend; + + if (ptr_a == ptr_b) { + return 0; + } else if (ptr_a < ptr_b) { + return -1; + } else { + return 1; + } +} + +static bool commit(struct wlr_backend *backend, + const struct wlr_backend_output_state *states, size_t states_len, + bool test_only) { + // Group states by backend, then perform one commit per backend + struct wlr_backend_output_state *by_backend = malloc(states_len * sizeof(by_backend[0])); + if (by_backend == NULL) { + return false; + } + memcpy(by_backend, states, states_len * sizeof(by_backend[0])); + qsort(by_backend, states_len, sizeof(by_backend[0]), compare_output_state_backend); + + bool ok = true; + for (size_t i = 0; i < states_len; i++) { + struct wlr_backend *sub = by_backend[i].output->backend; + + size_t j = i; + while (j < states_len && by_backend[j].output->backend == sub) { + j++; + } + + if (test_only) { + ok = wlr_backend_test(sub, &by_backend[i], j - i); + } else { + ok = wlr_backend_commit(sub, &by_backend[i], j - i); + } + if (!ok) { + break; + } + } + + free(by_backend); + return ok; +} + +static bool multi_backend_test(struct wlr_backend *backend, + const struct wlr_backend_output_state *states, size_t states_len) { + return commit(backend, states, states_len, true); +} + +static bool multi_backend_commit(struct wlr_backend *backend, + const struct wlr_backend_output_state *states, size_t states_len) { + return commit(backend, states, states_len, false); +} + static const struct wlr_backend_impl backend_impl = { .start = multi_backend_start, .destroy = multi_backend_destroy, .get_drm_fd = multi_backend_get_drm_fd, .get_buffer_caps = multi_backend_get_buffer_caps, + .test = multi_backend_test, + .commit = multi_backend_commit, }; -static void handle_display_destroy(struct wl_listener *listener, void *data) { +static void handle_event_loop_destroy(struct wl_listener *listener, void *data) { struct wlr_multi_backend *backend = - wl_container_of(listener, backend, display_destroy); + wl_container_of(listener, backend, event_loop_destroy); multi_backend_destroy((struct wlr_backend*)backend); } -struct wlr_backend *wlr_multi_backend_create(struct wl_display *display) { +struct wlr_backend *wlr_multi_backend_create(struct wl_event_loop *loop) { struct wlr_multi_backend *backend = calloc(1, sizeof(*backend)); if (!backend) { wlr_log(WLR_ERROR, "Backend allocation failed"); @@ -124,8 +186,8 @@ struct wlr_backend *wlr_multi_backend_create(struct wl_display *display) { wl_signal_init(&backend->events.backend_add); wl_signal_init(&backend->events.backend_remove); - backend->display_destroy.notify = handle_display_destroy; - wl_display_add_destroy_listener(display, &backend->display_destroy); + backend->event_loop_destroy.notify = handle_event_loop_destroy; + wl_event_loop_add_destroy_listener(loop, &backend->event_loop_destroy); return &backend->backend; } diff --git a/backend/session/session.c b/backend/session/session.c index 7b8b0bc..6254a73 100644 --- a/backend/session/session.c +++ b/backend/session/session.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -39,7 +38,7 @@ static int libseat_event(int fd, uint32_t mask, void *data) { struct wlr_session *session = data; if (libseat_dispatch(session->seat_handle, 0) == -1) { wlr_log_errno(WLR_ERROR, "Failed to dispatch libseat"); - wl_display_terminate(session->display); + wlr_session_destroy(session); } return 1; } @@ -71,7 +70,8 @@ static void log_libseat(enum libseat_log_level level, _wlr_vlog(importance, wlr_fmt, args); } -static int libseat_session_init(struct wlr_session *session, struct wl_display *disp) { +static int libseat_session_init(struct wlr_session *session, + struct wl_event_loop *event_loop) { libseat_set_log_handler(log_libseat); libseat_set_log_level(LIBSEAT_LOG_LEVEL_INFO); @@ -91,7 +91,6 @@ static int libseat_session_init(struct wlr_session *session, struct wl_display * } snprintf(session->seat, sizeof(session->seat), "%s", seat_name); - struct wl_event_loop *event_loop = wl_display_get_event_loop(disp); session->libseat_event = wl_event_loop_add_fd(event_loop, libseat_get_fd(session->seat_handle), WL_EVENT_READABLE, libseat_event, session); if (session->libseat_event == NULL) { @@ -226,25 +225,26 @@ static int handle_udev_event(int fd, uint32_t mask, void *data) { return 1; } -static void handle_display_destroy(struct wl_listener *listener, void *data) { +static void handle_event_loop_destroy(struct wl_listener *listener, void *data) { struct wlr_session *session = - wl_container_of(listener, session, display_destroy); + wl_container_of(listener, session, event_loop_destroy); wlr_session_destroy(session); } -struct wlr_session *wlr_session_create(struct wl_display *disp) { +struct wlr_session *wlr_session_create(struct wl_event_loop *event_loop) { struct wlr_session *session = calloc(1, sizeof(*session)); if (!session) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return NULL; } + session->event_loop = event_loop; wl_signal_init(&session->events.active); wl_signal_init(&session->events.add_drm_card); wl_signal_init(&session->events.destroy); wl_list_init(&session->devices); - if (libseat_session_init(session, disp) == -1) { + if (libseat_session_init(session, event_loop) == -1) { wlr_log(WLR_ERROR, "Failed to load session backend"); goto error_open; } @@ -264,7 +264,6 @@ struct wlr_session *wlr_session_create(struct wl_display *disp) { udev_monitor_filter_add_match_subsystem_devtype(session->mon, "drm", NULL); udev_monitor_enable_receiving(session->mon); - struct wl_event_loop *event_loop = wl_display_get_event_loop(disp); int fd = udev_monitor_get_fd(session->mon); session->udev_event = wl_event_loop_add_fd(event_loop, fd, @@ -274,10 +273,8 @@ struct wlr_session *wlr_session_create(struct wl_display *disp) { goto error_mon; } - session->display = disp; - - session->display_destroy.notify = handle_display_destroy; - wl_display_add_destroy_listener(disp, &session->display_destroy); + session->event_loop_destroy.notify = handle_event_loop_destroy; + wl_event_loop_add_destroy_listener(event_loop, &session->event_loop_destroy); return session; @@ -298,7 +295,7 @@ void wlr_session_destroy(struct wlr_session *session) { } wl_signal_emit_mutable(&session->events.destroy, session); - wl_list_remove(&session->display_destroy.link); + wl_list_remove(&session->event_loop_destroy.link); wl_event_source_remove(session->udev_event); udev_monitor_unref(session->mon); @@ -407,7 +404,7 @@ static ssize_t explicit_find_gpus(struct wlr_session *session, ret[i] = session_open_if_kms(session, ptr); if (!ret[i]) { - wlr_log(WLR_ERROR, "Unable to open %s as DRM device", ptr); + wlr_log(WLR_ERROR, "Unable to open %s as KMS device", ptr); } else { ++i; } @@ -447,13 +444,11 @@ static void find_gpus_handle_add(struct wl_listener *listener, void *data) { handler->added = true; } -/* Tries to find the primary GPU by checking for the "boot_vga" attribute. - * If it's not found, it returns the first valid GPU it finds. - */ ssize_t wlr_session_find_gpus(struct wlr_session *session, size_t ret_len, struct wlr_device **ret) { const char *explicit = getenv("WLR_DRM_DEVICES"); if (explicit) { + wlr_log(WLR_INFO, "Opening fixed list of KMS devices from WLR_DRM_DEVICES: %s", explicit); return explicit_find_gpus(session, ret_len, ret, explicit); } @@ -464,7 +459,7 @@ ssize_t wlr_session_find_gpus(struct wlr_session *session, if (udev_enumerate_get_list_entry(en) == NULL) { udev_enumerate_unref(en); - wlr_log(WLR_INFO, "Waiting for a DRM card device"); + wlr_log(WLR_INFO, "Waiting for a KMS device"); struct find_gpus_add_handler handler = {0}; handler.listener.notify = find_gpus_handle_add; @@ -472,12 +467,10 @@ ssize_t wlr_session_find_gpus(struct wlr_session *session, int64_t started_at = get_current_time_msec(); int64_t timeout = WAIT_GPU_TIMEOUT; - struct wl_event_loop *event_loop = - wl_display_get_event_loop(session->display); while (!handler.added) { - int ret = wl_event_loop_dispatch(event_loop, (int)timeout); + int ret = wl_event_loop_dispatch(session->event_loop, (int)timeout); if (ret < 0) { - wlr_log_errno(WLR_ERROR, "Failed to wait for DRM card device: " + wlr_log_errno(WLR_ERROR, "Failed to wait for KMS device: " "wl_event_loop_dispatch failed"); udev_enumerate_unref(en); return -1; diff --git a/backend/wayland/backend.c b/backend/wayland/backend.c index 9f78b6f..c2b2f2f 100644 --- a/backend/wayland/backend.c +++ b/backend/wayland/backend.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -21,13 +20,13 @@ #include "render/pixel_format.h" #include "drm-client-protocol.h" -#include "linux-dmabuf-unstable-v1-client-protocol.h" +#include "linux-dmabuf-v1-client-protocol.h" #include "pointer-gestures-unstable-v1-client-protocol.h" #include "presentation-time-client-protocol.h" #include "xdg-activation-v1-client-protocol.h" #include "xdg-decoration-unstable-v1-client-protocol.h" #include "xdg-shell-client-protocol.h" -#include "tablet-unstable-v2-client-protocol.h" +#include "tablet-v2-client-protocol.h" #include "relative-pointer-unstable-v1-client-protocol.h" #include "viewporter-client-protocol.h" @@ -59,7 +58,7 @@ static int dispatch_events(int fd, uint32_t mask, void *data) { if (mask & WL_EVENT_ERROR) { wlr_log(WLR_ERROR, "Failed to read from remote Wayland display"); } - wl_display_terminate(wl->local_display); + wlr_backend_destroy(&wl->backend); return 0; } @@ -77,7 +76,7 @@ static int dispatch_events(int fd, uint32_t mask, void *data) { if (count < 0) { wlr_log(WLR_ERROR, "Failed to dispatch remote Wayland display"); - wl_display_terminate(wl->local_display); + wlr_backend_destroy(&wl->backend); return 0; } return count; @@ -355,8 +354,8 @@ static void registry_global(void *data, struct wl_registry *registry, if (version < 5) { target_version = 5; } - if (version > 8) { - target_version = 8; + if (version > 9) { + target_version = 9; } struct wl_seat *wl_seat = wl_registry_bind(registry, name, &wl_seat_interface, target_version); @@ -394,7 +393,11 @@ static void registry_global(void *data, struct wl_registry *registry, wl->legacy_drm = wl_registry_bind(registry, name, &wl_drm_interface, 1); wl_drm_add_listener(wl->legacy_drm, &legacy_drm_listener, wl); } else if (strcmp(iface, wl_shm_interface.name) == 0) { - wl->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); + uint32_t target_version = version; + if (version > 2) { + target_version = 2; + } + wl->shm = wl_registry_bind(registry, name, &wl_shm_interface, target_version); wl_shm_add_listener(wl->shm, &shm_listener, wl); } else if (strcmp(iface, xdg_activation_v1_interface.name) == 0) { wl->activation_v1 = wl_registry_bind(registry, name, @@ -480,7 +483,7 @@ static void backend_destroy(struct wlr_backend *backend) { wlr_backend_finish(backend); - wl_list_remove(&wl->local_display_destroy.link); + wl_list_remove(&wl->event_loop_destroy.link); wl_event_source_remove(wl->remote_display_src); @@ -516,7 +519,11 @@ static void backend_destroy(struct wlr_backend *backend) { wl_drm_destroy(wl->legacy_drm); } if (wl->shm) { - wl_shm_destroy(wl->shm); + if (wl_shm_get_version(wl->shm) >= WL_SHM_RELEASE_SINCE_VERSION) { + wl_shm_release(wl->shm); + } else { + wl_shm_destroy(wl->shm); + } } if (wl->zwp_relative_pointer_manager_v1) { zwp_relative_pointer_manager_v1_destroy(wl->zwp_relative_pointer_manager_v1); @@ -561,13 +568,12 @@ bool wlr_backend_is_wl(struct wlr_backend *b) { return b->impl == &backend_impl; } -static void handle_display_destroy(struct wl_listener *listener, void *data) { - struct wlr_wl_backend *wl = - wl_container_of(listener, wl, local_display_destroy); +static void handle_event_loop_destroy(struct wl_listener *listener, void *data) { + struct wlr_wl_backend *wl = wl_container_of(listener, wl, event_loop_destroy); backend_destroy(&wl->backend); } -struct wlr_backend *wlr_wl_backend_create(struct wl_display *display, +struct wlr_backend *wlr_wl_backend_create(struct wl_event_loop *loop, struct wl_display *remote_display) { wlr_log(WLR_INFO, "Creating wayland backend"); @@ -579,7 +585,7 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display, wlr_backend_init(&wl->backend, &backend_impl); - wl->local_display = display; + wl->event_loop = loop; wl_list_init(&wl->outputs); wl_list_init(&wl->seats); wl_list_init(&wl->buffers); @@ -644,7 +650,6 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display, zwp_linux_dmabuf_feedback_v1_destroy(linux_dmabuf_feedback_v1); } - struct wl_event_loop *loop = wl_display_get_event_loop(wl->local_display); int fd = wl_display_get_fd(wl->remote_display); wl->remote_display_src = wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE, dispatch_events, wl); @@ -666,8 +671,8 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display, wl->drm_fd = -1; } - wl->local_display_destroy.notify = handle_display_destroy; - wl_display_add_destroy_listener(display, &wl->local_display_destroy); + wl->event_loop_destroy.notify = handle_event_loop_destroy; + wl_event_loop_add_destroy_listener(loop, &wl->event_loop_destroy); const char *token = getenv("XDG_ACTIVATION_TOKEN"); if (token != NULL) { diff --git a/backend/wayland/meson.build b/backend/wayland/meson.build index 0fd528c..fcf9fdd 100644 --- a/backend/wayland/meson.build +++ b/backend/wayland/meson.build @@ -14,11 +14,11 @@ wlr_files += files( client_protos = [ 'drm', - 'linux-dmabuf-unstable-v1', + 'linux-dmabuf-v1', 'pointer-gestures-unstable-v1', 'presentation-time', 'relative-pointer-unstable-v1', - 'tablet-unstable-v2', + 'tablet-v2', 'viewporter', 'xdg-activation-v1', 'xdg-decoration-unstable-v1', diff --git a/backend/wayland/output.c b/backend/wayland/output.c index bc15084..39803ea 100644 --- a/backend/wayland/output.c +++ b/backend/wayland/output.c @@ -8,20 +8,18 @@ #include #include -#include +#include #include #include -#include #include #include #include "backend/wayland.h" #include "render/pixel_format.h" -#include "render/wlr_renderer.h" #include "types/wlr_output.h" -#include "linux-dmabuf-unstable-v1-client-protocol.h" +#include "linux-dmabuf-v1-client-protocol.h" #include "presentation-time-client-protocol.h" #include "viewporter-client-protocol.h" #include "xdg-activation-v1-client-protocol.h" @@ -264,6 +262,40 @@ static struct wlr_wl_buffer *get_or_create_wl_buffer(struct wlr_wl_backend *wl, return create_wl_buffer(wl, wlr_buffer); } +static bool update_title(struct wlr_wl_output *output, const char *title) { + struct wlr_output *wlr_output = &output->wlr_output; + + char default_title[64]; + if (title == NULL) { + snprintf(default_title, sizeof(default_title), "wlroots - %s", wlr_output->name); + title = default_title; + } + + char *wl_title = strdup(title); + if (wl_title == NULL) { + return false; + } + + free(output->title); + output->title = wl_title; + return true; +} + +static bool update_app_id(struct wlr_wl_output *output, const char *app_id) { + if (app_id == NULL) { + app_id = "wlroots"; + } + + char *wl_app_id = strdup(app_id); + if (wl_app_id == NULL) { + return false; + } + + free(output->app_id); + output->app_id = wl_app_id; + return true; +} + static bool output_test(struct wlr_output *wlr_output, const struct wlr_output_state *state) { struct wlr_wl_output *output = @@ -522,6 +554,17 @@ static bool commit_layers(struct wlr_wl_output *output, return true; } +static void unmap_callback_handle_done(void *data, struct wl_callback *callback, + uint32_t cb_data) { + struct wlr_wl_output *output = data; + output->unmap_callback = NULL; + wl_callback_destroy(callback); +} + +static const struct wl_callback_listener unmap_callback_listener = { + .done = unmap_callback_handle_done, +}; + static bool output_commit(struct wlr_output *wlr_output, const struct wlr_output_state *state) { struct wlr_wl_output *output = @@ -531,9 +574,37 @@ static bool output_commit(struct wlr_output *wlr_output, return false; } - if ((state->committed & WLR_OUTPUT_STATE_ENABLED) && !state->enabled) { + bool pending_enabled = output_pending_enabled(wlr_output, state); + + if (wlr_output->enabled && !pending_enabled) { + if (output->own_surface) { + output->unmap_callback = wl_display_sync(output->backend->remote_display); + if (output->unmap_callback == NULL) { + return false; + } + wl_callback_add_listener(output->unmap_callback, &unmap_callback_listener, output); + } + wl_surface_attach(output->surface, NULL, 0, 0); wl_surface_commit(output->surface); + + output->initialized = false; + output->configured = false; + output->has_configure_serial = false; + output->requested_width = output->requested_height = 0; + } else if (output->own_surface && pending_enabled && !output->initialized) { + xdg_toplevel_set_title(output->xdg_toplevel, output->title); + xdg_toplevel_set_app_id(output->xdg_toplevel, output->app_id); + wl_surface_commit(output->surface); + output->initialized = true; + + wl_display_flush(output->backend->remote_display); + while (!output->configured) { + if (wl_display_dispatch(output->backend->remote_display) == -1) { + wlr_log(WLR_ERROR, "wl_display_dispatch() failed"); + return false; + } + } } if (state->committed & WLR_OUTPUT_STATE_BUFFER) { @@ -558,7 +629,7 @@ static bool output_commit(struct wlr_output *wlr_output, return false; } - if (output_pending_enabled(wlr_output, state)) { + if (pending_enabled) { if (output->frame_callback != NULL) { wl_callback_destroy(output->frame_callback); } @@ -571,6 +642,11 @@ static bool output_commit(struct wlr_output *wlr_output, output->surface); } + if (output->has_configure_serial) { + xdg_surface_ack_configure(output->xdg_surface, output->configure_serial); + output->has_configure_serial = false; + } + wl_surface_commit(output->surface); if (wp_feedback != NULL) { @@ -668,6 +744,10 @@ static void output_destroy(struct wlr_output *wlr_output) { presentation_feedback_destroy(feedback); } + if (output->unmap_callback) { + wl_callback_destroy(output->unmap_callback); + } + if (output->zxdg_toplevel_decoration_v1) { zxdg_toplevel_decoration_v1_destroy(output->zxdg_toplevel_decoration_v1); } @@ -681,6 +761,10 @@ static void output_destroy(struct wlr_output *wlr_output) { wl_surface_destroy(output->surface); } wl_display_flush(output->backend->remote_display); + + free(output->title); + free(output->app_id); + free(output); } @@ -721,10 +805,30 @@ static void xdg_surface_handle_configure(void *data, struct wlr_wl_output *output = data; assert(output && output->xdg_surface == xdg_surface); + int32_t req_width = output->wlr_output.width; + int32_t req_height = output->wlr_output.height; + if (output->requested_width > 0) { + req_width = output->requested_width; + output->requested_width = 0; + } + if (output->requested_height > 0) { + req_height = output->requested_height; + output->requested_height = 0; + } + + if (output->unmap_callback != NULL) { + return; + } + output->configured = true; - xdg_surface_ack_configure(xdg_surface, serial); + output->has_configure_serial = true; + output->configure_serial = serial; - // nothing else? + struct wlr_output_state state; + wlr_output_state_init(&state); + wlr_output_state_set_custom_mode(&state, req_width, req_height, 0); + wlr_output_send_request_state(&output->wlr_output, &state); + wlr_output_state_finish(&state); } static const struct xdg_surface_listener xdg_surface_listener = { @@ -737,15 +841,12 @@ static void xdg_toplevel_handle_configure(void *data, struct wlr_wl_output *output = data; assert(output && output->xdg_toplevel == xdg_toplevel); - if (width == 0 || height == 0) { - return; + if (width > 0) { + output->requested_width = width; + } + if (height > 0) { + output->requested_height = height; } - - struct wlr_output_state state; - wlr_output_state_init(&state); - wlr_output_state_set_custom_mode(&state, width, height, 0); - wlr_output_send_request_state(&output->wlr_output, &state); - wlr_output_state_finish(&state); } static void xdg_toplevel_handle_close(void *data, @@ -775,7 +876,7 @@ static struct wlr_wl_output *output_create(struct wlr_wl_backend *backend, wlr_output_state_set_custom_mode(&state, 1280, 720, 0); wlr_output_init(wlr_output, &backend->backend, &output_impl, - backend->local_display, &state); + backend->event_loop, &state); wlr_output_state_finish(&state); wlr_output->adaptive_sync_status = WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED; @@ -863,23 +964,21 @@ struct wlr_output *wlr_wl_output_create(struct wlr_backend *wlr_backend) { ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); } - wlr_wl_output_set_title(&output->wlr_output, NULL); + if (!update_title(output, NULL)) { + wlr_log_errno(WLR_ERROR, "Could not allocate xdg toplevel title"); + goto error; + } + if (!update_app_id(output, NULL)) { + wlr_log_errno(WLR_ERROR, "Could not allocate xdg toplevel app_id"); + goto error; + } - xdg_toplevel_set_app_id(output->xdg_toplevel, "wlroots"); xdg_surface_add_listener(output->xdg_surface, &xdg_surface_listener, output); xdg_toplevel_add_listener(output->xdg_toplevel, &xdg_toplevel_listener, output); - wl_surface_commit(output->surface); - struct wl_event_loop *event_loop = wl_display_get_event_loop(backend->local_display); - while (!output->configured) { - int ret = wl_event_loop_dispatch(event_loop, -1); - if (ret < 0) { - wlr_log(WLR_ERROR, "wl_event_loop_dispatch() failed"); - goto error; - } - } + wl_display_flush(backend->remote_display); output_start(output); @@ -916,16 +1015,20 @@ void wlr_wl_output_set_title(struct wlr_output *output, const char *title) { struct wlr_wl_output *wl_output = get_wl_output_from_output(output); assert(wl_output->xdg_toplevel != NULL); - char wl_title[32]; - if (title == NULL) { - if (snprintf(wl_title, sizeof(wl_title), "wlroots - %s", output->name) <= 0) { - return; - } - title = wl_title; + if (update_title(wl_output, title) && wl_output->initialized) { + xdg_toplevel_set_title(wl_output->xdg_toplevel, wl_output->title); + wl_display_flush(wl_output->backend->remote_display); } +} - xdg_toplevel_set_title(wl_output->xdg_toplevel, title); - wl_display_flush(wl_output->backend->remote_display); +void wlr_wl_output_set_app_id(struct wlr_output *output, const char *app_id) { + struct wlr_wl_output *wl_output = get_wl_output_from_output(output); + assert(wl_output->xdg_toplevel != NULL); + + if (update_app_id(wl_output, app_id) && wl_output->initialized) { + xdg_toplevel_set_app_id(wl_output->xdg_toplevel, wl_output->app_id); + wl_display_flush(wl_output->backend->remote_display); + } } struct wl_surface *wlr_wl_output_get_surface(struct wlr_output *output) { diff --git a/backend/wayland/pointer.c b/backend/wayland/pointer.c index 39fb337..87383fa 100644 --- a/backend/wayland/pointer.c +++ b/backend/wayland/pointer.c @@ -1,8 +1,6 @@ -#define _POSIX_C_SOURCE 200809L - #include #include -#include +#include #include #include @@ -132,6 +130,7 @@ static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer, .orientation = axis, .time_msec = time, .source = pointer->axis_source, + .relative_direction = pointer->axis_relative_direction, }; wl_signal_emit_mutable(&pointer->wlr_pointer.events.axis, &event); @@ -201,6 +200,17 @@ static void pointer_handle_axis_value120(void *data, pointer->axis_discrete = value120; } +static void pointer_handle_axis_relative_direction(void *data, + struct wl_pointer *wl_pointer, uint32_t axis, uint32_t direction) { + struct wlr_wl_seat *seat = data; + struct wlr_wl_pointer *pointer = seat->active_pointer; + if (pointer == NULL) { + return; + } + + pointer->axis_relative_direction = direction; +} + static const struct wl_pointer_listener pointer_listener = { .enter = pointer_handle_enter, .leave = pointer_handle_leave, @@ -212,6 +222,7 @@ static const struct wl_pointer_listener pointer_listener = { .axis_stop = pointer_handle_axis_stop, .axis_discrete = pointer_handle_axis_discrete, .axis_value120 = pointer_handle_axis_value120, + .axis_relative_direction = pointer_handle_axis_relative_direction, }; static void gesture_swipe_begin(void *data, diff --git a/backend/wayland/seat.c b/backend/wayland/seat.c index d88a370..b812b53 100644 --- a/backend/wayland/seat.c +++ b/backend/wayland/seat.c @@ -1,5 +1,3 @@ -#define _POSIX_C_SOURCE 200809L - #include #include #include @@ -7,7 +5,7 @@ #include #include -#include +#include #include #include @@ -314,7 +312,7 @@ bool wlr_input_device_is_wl(struct wlr_input_device *dev) { return wlr_pointer_from_input_device(dev)->impl == &wl_pointer_impl; case WLR_INPUT_DEVICE_TOUCH: return wlr_touch_from_input_device(dev)->impl == &touch_impl; - case WLR_INPUT_DEVICE_TABLET_TOOL: + case WLR_INPUT_DEVICE_TABLET: return wlr_tablet_from_input_device(dev)-> impl == &wl_tablet_impl; case WLR_INPUT_DEVICE_TABLET_PAD: return wlr_tablet_pad_from_input_device(dev)->impl == &wl_tablet_pad_impl; diff --git a/backend/wayland/tablet_v2.c b/backend/wayland/tablet_v2.c index a3604b5..3f6a16e 100644 --- a/backend/wayland/tablet_v2.c +++ b/backend/wayland/tablet_v2.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include @@ -11,7 +10,7 @@ #include "backend/wayland.h" #include "util/time.h" -#include "tablet-unstable-v2-client-protocol.h" +#include "tablet-v2-client-protocol.h" struct tablet_tool { /* static */ @@ -405,6 +404,7 @@ static void handle_pad_added(void *data, struct wlr_wl_seat *seat = data; if (seat->zwp_tablet_pad_v2 != NULL) { wlr_log(WLR_ERROR, "zwp_tablet_pad_v2 is already present"); + zwp_tablet_pad_v2_destroy(zwp_tablet_pad_v2); return; } @@ -780,6 +780,7 @@ static void handle_tool_added(void *data, struct wlr_wl_seat *seat = data; if (seat->zwp_tablet_tool_v2 != NULL) { wlr_log(WLR_ERROR, "zwp_tablet_tool_v2 already present"); + zwp_tablet_tool_v2_destroy(zwp_tablet_tool_v2); return; } @@ -814,8 +815,8 @@ static void handle_tablet_id(void *data, struct zwp_tablet_v2 *zwp_tablet_v2, struct wlr_wl_seat *seat = data; struct wlr_tablet *tablet = &seat->wlr_tablet; - tablet->base.vendor = vid; - tablet->base.product = pid; + tablet->usb_vendor_id = vid; + tablet->usb_product_id = pid; } static void handle_tablet_path(void *data, struct zwp_tablet_v2 *zwp_tablet_v2, @@ -861,6 +862,7 @@ static void handle_tab_added(void *data, struct wlr_wl_seat *seat = data; if (seat->zwp_tablet_v2 != NULL) { wlr_log(WLR_ERROR, "zwp_tablet_v2 already present"); + zwp_tablet_v2_destroy(zwp_tablet_v2); return; } diff --git a/backend/x11/backend.c b/backend/x11/backend.c index fcaab61..9c8ea32 100644 --- a/backend/x11/backend.c +++ b/backend/x11/backend.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -132,7 +131,7 @@ static int x11_event(int fd, uint32_t mask, void *data) { if (mask & WL_EVENT_ERROR) { wlr_log(WLR_ERROR, "Failed to read from X11 server"); } - wl_display_terminate(x11->wl_display); + wlr_backend_destroy(&x11->backend); return 0; } @@ -145,7 +144,7 @@ static int x11_event(int fd, uint32_t mask, void *data) { int ret = xcb_connection_has_error(x11->xcb); if (ret != 0) { wlr_log(WLR_ERROR, "X11 connection error (%d)", ret); - wl_display_terminate(x11->wl_display); + wlr_backend_destroy(&x11->backend); } return 0; @@ -192,7 +191,7 @@ static void backend_destroy(struct wlr_backend *backend) { if (x11->event_source) { wl_event_source_remove(x11->event_source); } - wl_list_remove(&x11->display_destroy.link); + wl_list_remove(&x11->event_loop_destroy.link); wlr_drm_format_set_finish(&x11->primary_dri3_formats); wlr_drm_format_set_finish(&x11->primary_shm_formats); @@ -230,9 +229,8 @@ bool wlr_backend_is_x11(struct wlr_backend *backend) { return backend->impl == &backend_impl; } -static void handle_display_destroy(struct wl_listener *listener, void *data) { - struct wlr_x11_backend *x11 = - wl_container_of(listener, x11, display_destroy); +static void handle_event_loop_destroy(struct wl_listener *listener, void *data) { + struct wlr_x11_backend *x11 = wl_container_of(listener, x11, event_loop_destroy); backend_destroy(&x11->backend); } @@ -263,11 +261,13 @@ static int query_dri3_drm_fd(struct wlr_x11_backend *x11) { xcb_dri3_open_reply_t *open_reply = xcb_dri3_open_reply(x11->xcb, open_cookie, NULL); if (open_reply == NULL) { + wlr_log(WLR_ERROR, "Failed to open DRI3"); return -1; } int *open_fds = xcb_dri3_open_reply_fds(x11->xcb, open_reply); if (open_fds == NULL) { + wlr_log(WLR_ERROR, "xcb_dri3_open_reply_fds() failed"); free(open_reply); return -1; } @@ -279,10 +279,12 @@ static int query_dri3_drm_fd(struct wlr_x11_backend *x11) { int flags = fcntl(drm_fd, F_GETFD); if (flags < 0) { + wlr_log_errno(WLR_ERROR, "Failed to get DRM FD flags"); close(drm_fd); return -1; } if (fcntl(drm_fd, F_SETFD, flags | FD_CLOEXEC) < 0) { + wlr_log_errno(WLR_ERROR, "Failed to set DRM FD flags"); close(drm_fd); return -1; } @@ -290,6 +292,7 @@ static int query_dri3_drm_fd(struct wlr_x11_backend *x11) { if (drmGetNodeTypeFromFd(drm_fd) != DRM_NODE_RENDER) { char *render_name = drmGetRenderDeviceNameFromFd(drm_fd); if (render_name == NULL) { + wlr_log(WLR_ERROR, "Failed to get DRM render node name from DRM FD"); close(drm_fd); return -1; } @@ -297,6 +300,7 @@ static int query_dri3_drm_fd(struct wlr_x11_backend *x11) { close(drm_fd); drm_fd = open(render_name, O_RDWR | O_CLOEXEC); if (drm_fd < 0) { + wlr_log_errno(WLR_ERROR, "Failed to open DRM render node '%s'", render_name); free(render_name); return -1; } @@ -391,7 +395,7 @@ static void x11_get_argb32(struct wlr_x11_backend *x11) { free(reply); } -struct wlr_backend *wlr_x11_backend_create(struct wl_display *display, +struct wlr_backend *wlr_x11_backend_create(struct wl_event_loop *loop, const char *x11_display) { wlr_log(WLR_INFO, "Creating X11 backend"); @@ -401,7 +405,7 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display, } wlr_backend_init(&x11->backend, &backend_impl); - x11->wl_display = display; + x11->event_loop = loop; wl_list_init(&x11->outputs); x11->xcb = xcb_connect(x11_display, NULL); @@ -561,9 +565,8 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display, free(xi_reply); int fd = xcb_get_file_descriptor(x11->xcb); - struct wl_event_loop *ev = wl_display_get_event_loop(display); uint32_t events = WL_EVENT_READABLE | WL_EVENT_ERROR | WL_EVENT_HANGUP; - x11->event_source = wl_event_loop_add_fd(ev, fd, events, x11_event, x11); + x11->event_source = wl_event_loop_add_fd(loop, fd, events, x11_event, x11); if (!x11->event_source) { wlr_log(WLR_ERROR, "Could not create event source"); goto error_display; @@ -600,7 +603,7 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display, if (!query_formats(x11)) { wlr_log(WLR_ERROR, "Failed to query supported DRM formats"); - return false; + goto error_event; } x11->drm_fd = -1; @@ -610,7 +613,8 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display, x11->drm_fd = query_dri3_drm_fd(x11); if (x11->drm_fd < 0) { wlr_log(WLR_ERROR, "Failed to query DRI3 DRM FD"); - goto error_event; + wlr_log(WLR_INFO, "Disabling DMA-BUF support"); + x11->have_dri3 = false; } } @@ -637,15 +641,15 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display, #if HAVE_XCB_ERRORS if (xcb_errors_context_new(x11->xcb, &x11->errors_context) != 0) { wlr_log(WLR_ERROR, "Failed to create error context"); - return false; + goto error_event; } #endif wlr_keyboard_init(&x11->keyboard, &x11_keyboard_impl, x11_keyboard_impl.name); - x11->display_destroy.notify = handle_display_destroy; - wl_display_add_destroy_listener(display, &x11->display_destroy); + x11->event_loop_destroy.notify = handle_event_loop_destroy; + wl_event_loop_add_destroy_listener(loop, &x11->event_loop_destroy); // Create an empty pixmap to be used as the cursor. The // default GC foreground is 0, and that is what it will be diff --git a/backend/x11/input_device.c b/backend/x11/input_device.c index b3bd77d..c2ed6fd 100644 --- a/backend/x11/input_device.c +++ b/backend/x11/input_device.c @@ -29,7 +29,7 @@ static void send_key_event(struct wlr_x11_backend *x11, uint32_t key, } static void send_button_event(struct wlr_x11_output *output, uint32_t key, - enum wlr_button_state st, xcb_timestamp_t time) { + enum wl_pointer_button_state st, xcb_timestamp_t time) { struct wlr_pointer_button_event ev = { .pointer = &output->pointer, .time_msec = time, @@ -45,8 +45,8 @@ static void send_axis_event(struct wlr_x11_output *output, int32_t delta, struct wlr_pointer_axis_event ev = { .pointer = &output->pointer, .time_msec = time, - .source = WLR_AXIS_SOURCE_WHEEL, - .orientation = WLR_AXIS_ORIENTATION_VERTICAL, + .source = WL_POINTER_AXIS_SOURCE_WHEEL, + .orientation = WL_POINTER_AXIS_VERTICAL_SCROLL, // Most mice use a 15 degree angle per scroll click .delta = delta * 15, .delta_discrete = delta * WLR_POINTER_AXIS_DISCRETE_STEP, @@ -129,7 +129,7 @@ void handle_x11_xinput_event(struct wlr_x11_backend *x11, } wlr_keyboard_notify_modifiers(&x11->keyboard, ev->mods.base, - ev->mods.latched, ev->mods.locked, ev->mods.effective); + ev->mods.latched, ev->mods.locked, ev->group.effective); send_key_event(x11, ev->detail - 8, WL_KEYBOARD_KEY_STATE_PRESSED, ev->time); x11->time = ev->time; break; @@ -139,7 +139,7 @@ void handle_x11_xinput_event(struct wlr_x11_backend *x11, (xcb_input_key_release_event_t *)event; wlr_keyboard_notify_modifiers(&x11->keyboard, ev->mods.base, - ev->mods.latched, ev->mods.locked, ev->mods.effective); + ev->mods.latched, ev->mods.locked, ev->group.effective); send_key_event(x11, ev->detail - 8, WL_KEYBOARD_KEY_STATE_RELEASED, ev->time); x11->time = ev->time; break; @@ -155,15 +155,15 @@ void handle_x11_xinput_event(struct wlr_x11_backend *x11, switch (ev->detail) { case XCB_BUTTON_INDEX_1: - send_button_event(output, BTN_LEFT, WLR_BUTTON_PRESSED, + send_button_event(output, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED, ev->time); break; case XCB_BUTTON_INDEX_2: - send_button_event(output, BTN_MIDDLE, WLR_BUTTON_PRESSED, + send_button_event(output, BTN_MIDDLE, WL_POINTER_BUTTON_STATE_PRESSED, ev->time); break; case XCB_BUTTON_INDEX_3: - send_button_event(output, BTN_RIGHT, WLR_BUTTON_PRESSED, + send_button_event(output, BTN_RIGHT, WL_POINTER_BUTTON_STATE_PRESSED, ev->time); break; case XCB_BUTTON_INDEX_4: @@ -188,15 +188,15 @@ void handle_x11_xinput_event(struct wlr_x11_backend *x11, switch (ev->detail) { case XCB_BUTTON_INDEX_1: - send_button_event(output, BTN_LEFT, WLR_BUTTON_RELEASED, + send_button_event(output, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED, ev->time); break; case XCB_BUTTON_INDEX_2: - send_button_event(output, BTN_MIDDLE, WLR_BUTTON_RELEASED, + send_button_event(output, BTN_MIDDLE, WL_POINTER_BUTTON_STATE_RELEASED, ev->time); break; case XCB_BUTTON_INDEX_3: - send_button_event(output, BTN_RIGHT, WLR_BUTTON_RELEASED, + send_button_event(output, BTN_RIGHT, WL_POINTER_BUTTON_STATE_RELEASED, ev->time); break; } @@ -233,6 +233,10 @@ void handle_x11_xinput_event(struct wlr_x11_backend *x11, } struct wlr_x11_touchpoint *touchpoint = calloc(1, sizeof(*touchpoint)); + if (!touchpoint) { + return; + } + touchpoint->x11_id = ev->detail; touchpoint->wayland_id = id; wl_list_init(&touchpoint->link); diff --git a/backend/x11/output.c b/backend/x11/output.c index 2521795..51a8459 100644 --- a/backend/x11/output.c +++ b/backend/x11/output.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include @@ -17,7 +16,6 @@ #include #include #include -#include #include #include "backend/x11.h" @@ -449,25 +447,26 @@ static bool output_cursor_to_picture(struct wlr_x11_output *output, return true; } - int depth = 32; - int stride = buffer->width * 4; - - uint8_t *data = malloc(buffer->height * stride); - if (data == NULL) { + struct wlr_texture *texture = wlr_texture_from_buffer(renderer, buffer); + if (!texture) { return false; } - if (!wlr_renderer_begin_with_buffer(renderer, buffer)) { - free(data); + int depth = 32; + int stride = texture->width * 4; + uint8_t *data = malloc(texture->height * stride); + if (data == NULL) { + wlr_texture_destroy(texture); return false; } - bool result = wlr_renderer_read_pixels( - renderer, DRM_FORMAT_ARGB8888, - stride, buffer->width, buffer->height, 0, 0, 0, 0, - data); + bool result = wlr_texture_read_pixels(texture, &(struct wlr_texture_read_pixels_options) { + .format = DRM_FORMAT_ARGB8888, + .stride = stride, + .data = data, + }); - wlr_renderer_end(renderer); + wlr_texture_destroy(texture); if (!result) { free(data); @@ -575,7 +574,7 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) { wlr_output_state_init(&state); wlr_output_state_set_custom_mode(&state, 1024, 768, 0); - wlr_output_init(wlr_output, &x11->backend, &output_impl, x11->wl_display, &state); + wlr_output_init(wlr_output, &x11->backend, &output_impl, x11->event_loop, &state); wlr_output_state_finish(&state); size_t output_num = ++last_output_num; diff --git a/docs/env_vars.md b/docs/env_vars.md index e36cdc7..a2a86c8 100644 --- a/docs/env_vars.md +++ b/docs/env_vars.md @@ -57,7 +57,13 @@ wlroots reads these environment variables * *WLR_SCENE_DISABLE_DIRECT_SCANOUT*: disables direct scan-out for debugging. * *WLR_SCENE_DISABLE_VISIBILITY*: If set to 1, the visibility of all scene nodes will be considered to be the full node. Intelligent visibility canculations will - be disabled. + be disabled. Note that direct scanout will not work for most cases when this + option is set as surfaces that don't contribute to the rendered output will now + bail direct scanout (desktop background / black rect underneath). +* *WLR_SCENE_HIGHLIGHT_TRANSPARENT_REGION*: Highlights regions of scene buffers + that are advertised as transparent through wlr_scene_buffer_set_opaque_region(). + This can be used to debug issues with clients advertizing bogus opaque regions + with scene based compositors. # Generic diff --git a/examples/cairo-buffer.c b/examples/cairo-buffer.c index d92a2f1..7de4a05 100644 --- a/examples/cairo-buffer.c +++ b/examples/cairo-buffer.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200112L #include #include @@ -125,7 +124,7 @@ int main(void) { struct server server = {0}; server.display = wl_display_create(); - server.backend = wlr_backend_autocreate(server.display, NULL); + server.backend = wlr_backend_autocreate(wl_display_get_event_loop(server.display), NULL); server.scene = wlr_scene_create(); server.renderer = wlr_renderer_autocreate(server.backend); diff --git a/examples/embedded.c b/examples/embedded.c index 1e52e89..f0f6611 100644 --- a/examples/embedded.c +++ b/examples/embedded.c @@ -1,13 +1,12 @@ -#define _POSIX_C_SOURCE 200112L #include #include #include #include #include #include -#include +#include #include -#include +#include #include #include #include @@ -17,6 +16,12 @@ #include "xdg-shell-client-protocol.h" +struct surface { + struct wlr_surface *wlr; + struct wl_listener commit; + struct wl_listener destroy; +}; + static struct wl_display *remote_display = NULL; static struct wl_compositor *compositor = NULL; static struct wl_subcompositor *subcompositor = NULL; @@ -120,8 +125,34 @@ static void output_handle_frame(struct wl_listener *listener, void *data) { wlr_scene_output_send_frame_done(scene_output, &now); } +static void surface_handle_commit(struct wl_listener *listener, void *data) { + struct surface *surface = wl_container_of(listener, surface, commit); + struct wlr_xdg_toplevel *xdg_toplevel = + wlr_xdg_toplevel_try_from_wlr_surface(surface->wlr); + if (xdg_toplevel != NULL && xdg_toplevel->base->initial_commit) { + wlr_xdg_toplevel_set_size(xdg_toplevel, 0, 0); + } +} + +static void surface_handle_destroy(struct wl_listener *listener, void *data) { + struct surface *surface = wl_container_of(listener, surface, destroy); + wl_list_remove(&surface->commit.link); + wl_list_remove(&surface->destroy.link); + free(surface); +} + static void handle_new_surface(struct wl_listener *listener, void *data) { struct wlr_surface *wlr_surface = data; + + struct surface *surface = calloc(1, sizeof(*surface)); + surface->wlr = wlr_surface; + + surface->commit.notify = surface_handle_commit; + wl_signal_add(&wlr_surface->events.commit, &surface->commit); + + surface->destroy.notify = surface_handle_destroy; + wl_signal_add(&wlr_surface->events.destroy, &surface->destroy); + wlr_scene_surface_create(&scene->tree, wlr_surface); } @@ -159,7 +190,8 @@ int main(int argc, char *argv[]) { init_egl(remote_display); struct wl_display *local_display = wl_display_create(); - struct wlr_backend *backend = wlr_wl_backend_create(local_display, remote_display); + struct wl_event_loop *event_loop = wl_display_get_event_loop(local_display); + struct wlr_backend *backend = wlr_wl_backend_create(event_loop, remote_display); struct wlr_renderer *renderer = wlr_renderer_autocreate(backend); wlr_renderer_init_wl_display(renderer, local_display); struct wlr_allocator *allocator = wlr_allocator_autocreate(backend, renderer); diff --git a/examples/fullscreen-shell.c b/examples/fullscreen-shell.c index 70b7f67..c12f126 100644 --- a/examples/fullscreen-shell.c +++ b/examples/fullscreen-shell.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200112L #include #include #include @@ -11,11 +10,11 @@ #include #include #include -#include #include #include #include #include +#include /** * A minimal fullscreen-shell server. It only supports rendering. @@ -162,7 +161,7 @@ static void server_handle_new_output(struct wl_listener *listener, void *data) { wl_list_insert(&server->outputs, &output->link); wlr_output_layout_add_auto(server->output_layout, wlr_output); - wlr_output_create_global(wlr_output); + wlr_output_create_global(wlr_output, server->wl_display); struct wlr_output_state state; wlr_output_state_init(&state); @@ -212,7 +211,7 @@ int main(int argc, char *argv[]) { struct fullscreen_server server = {0}; server.wl_display = wl_display_create(); - server.backend = wlr_backend_autocreate(server.wl_display, NULL); + server.backend = wlr_backend_autocreate(wl_display_get_event_loop(server.wl_display), NULL); server.renderer = wlr_renderer_autocreate(server.backend); wlr_renderer_init_wl_display(server.renderer, server.wl_display); server.allocator = wlr_allocator_autocreate(server.backend, @@ -220,7 +219,7 @@ int main(int argc, char *argv[]) { wlr_compositor_create(server.wl_display, 5, server.renderer); - server.output_layout = wlr_output_layout_create(); + server.output_layout = wlr_output_layout_create(server.wl_display); wl_list_init(&server.outputs); server.new_output.notify = server_handle_new_output; diff --git a/examples/output-layers.c b/examples/output-layers.c index cec3b50..5089c6a 100644 --- a/examples/output-layers.c +++ b/examples/output-layers.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200112L #include #include #include @@ -184,7 +183,7 @@ static void server_handle_new_output(struct wl_listener *listener, void *data) { wlr_output_commit_state(wlr_output, &state); wlr_output_state_finish(&state); - wlr_output_create_global(wlr_output); + wlr_output_create_global(wlr_output, server->wl_display); } static void output_surface_handle_destroy(struct wl_listener *listener, @@ -205,6 +204,12 @@ static void output_surface_handle_commit(struct wl_listener *listener, struct output_surface *output_surface = wl_container_of(listener, output_surface, commit); + struct wlr_xdg_toplevel *xdg_toplevel = + wlr_xdg_toplevel_try_from_wlr_surface(output_surface->wlr_surface); + if (xdg_toplevel != NULL && xdg_toplevel->base->initial_commit) { + wlr_xdg_toplevel_set_size(xdg_toplevel, 0, 0); + } + struct wlr_buffer *buffer = NULL; if (output_surface->wlr_surface->buffer != NULL) { buffer = wlr_buffer_lock(&output_surface->wlr_surface->buffer->base); @@ -286,12 +291,12 @@ int main(int argc, char *argv[]) { struct server server = {0}; server.wl_display = wl_display_create(); - server.backend = wlr_backend_autocreate(server.wl_display, NULL); + server.backend = wlr_backend_autocreate(wl_display_get_event_loop(server.wl_display), NULL); server.renderer = wlr_renderer_autocreate(server.backend); wlr_renderer_init_wl_shm(server.renderer, server.wl_display); - if (wlr_renderer_get_dmabuf_texture_formats(server.renderer) != NULL) { + if (wlr_renderer_get_texture_formats(server.renderer, WLR_BUFFER_CAP_DMABUF) != NULL) { wlr_drm_create(server.wl_display, server.renderer); server.linux_dmabuf_v1 = wlr_linux_dmabuf_v1_create_with_renderer( server.wl_display, 4, server.renderer); diff --git a/examples/output-layout.c b/examples/output-layout.c index de5217e..3a3eeb2 100644 --- a/examples/output-layout.c +++ b/examples/output-layout.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200112L #include #include #include @@ -14,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -273,10 +271,10 @@ int main(int argc, char *argv[]) { .display = display, }; - state.layout = wlr_output_layout_create(); + state.layout = wlr_output_layout_create(display); clock_gettime(CLOCK_MONOTONIC, &state.ts_last); - struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL); + struct wlr_backend *wlr = wlr_backend_autocreate(wl_display_get_event_loop(display), NULL); if (!wlr) { exit(1); } @@ -303,5 +301,4 @@ int main(int argc, char *argv[]) { wlr_texture_destroy(state.cat_texture); wl_display_destroy(state.display); - wlr_output_layout_destroy(state.layout); } diff --git a/examples/pointer.c b/examples/pointer.c index 5d9de33..fed37a5 100644 --- a/examples/pointer.c +++ b/examples/pointer.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200112L #include #include #include @@ -13,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -146,7 +144,7 @@ static void handle_cursor_button(struct wl_listener *listener, void *data) { struct wlr_pointer_button_event *event = data; float (*color)[4]; - if (event->state == WLR_BUTTON_RELEASED) { + if (event->state == WL_POINTER_BUTTON_STATE_RELEASED) { color = &sample->default_color; memcpy(&sample->clear_color, color, sizeof(*color)); } else { @@ -300,7 +298,7 @@ static void new_input_notify(struct wl_listener *listener, void *data) { switch (device->type) { case WLR_INPUT_DEVICE_POINTER: case WLR_INPUT_DEVICE_TOUCH: - case WLR_INPUT_DEVICE_TABLET_TOOL: + case WLR_INPUT_DEVICE_TABLET: wlr_cursor_attach_input_device(state->cursor, device); break; @@ -342,7 +340,7 @@ int main(int argc, char *argv[]) { .display = display }; - struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL); + struct wlr_backend *wlr = wlr_backend_autocreate(wl_display_get_event_loop(display), NULL); if (!wlr) { exit(1); } @@ -351,7 +349,7 @@ int main(int argc, char *argv[]) { state.allocator = wlr_allocator_autocreate(wlr, state.renderer); state.cursor = wlr_cursor_create(); - state.layout = wlr_output_layout_create(); + state.layout = wlr_output_layout_create(display); wlr_cursor_attach_output_layout(state.cursor, state.layout); //wlr_cursor_map_to_region(state.cursor, state.config->cursor.mapped_box); wl_list_init(&state.devices); @@ -415,5 +413,4 @@ int main(int argc, char *argv[]) { wlr_xcursor_manager_destroy(state.xcursor_manager); wlr_cursor_destroy(state.cursor); - wlr_output_layout_destroy(state.layout); } diff --git a/examples/rotation.c b/examples/rotation.c index 203b208..351973f 100644 --- a/examples/rotation.c +++ b/examples/rotation.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200112L #include #include #include @@ -15,7 +14,6 @@ #include #include #include -#include #include #include #include "cat.h" @@ -251,7 +249,7 @@ int main(int argc, char *argv[]) { }; wl_list_init(&state.outputs); - struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL); + struct wlr_backend *wlr = wlr_backend_autocreate(wl_display_get_event_loop(display), NULL); if (!wlr) { exit(1); } diff --git a/examples/scene-graph.c b/examples/scene-graph.c index 03ad814..4219865 100644 --- a/examples/scene-graph.c +++ b/examples/scene-graph.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200112L #include #include #include @@ -90,14 +89,21 @@ static void server_handle_new_output(struct wl_listener *listener, void *data) { wlr_output_commit_state(wlr_output, &state); wlr_output_state_finish(&state); - wlr_output_create_global(wlr_output); + wlr_output_create_global(wlr_output, server->display); } static void surface_handle_commit(struct wl_listener *listener, void *data) { struct surface *surface = wl_container_of(listener, surface, commit); + wlr_scene_rect_set_size(surface->border, surface->wlr->current.width + 2 * border_width, surface->wlr->current.height + 2 * border_width); + + struct wlr_xdg_toplevel *xdg_toplevel = + wlr_xdg_toplevel_try_from_wlr_surface(surface->wlr); + if (xdg_toplevel != NULL && xdg_toplevel->base->initial_commit) { + wlr_xdg_toplevel_set_size(xdg_toplevel, 0, 0); + } } static void surface_handle_destroy(struct wl_listener *listener, void *data) { @@ -160,7 +166,7 @@ int main(int argc, char *argv[]) { struct server server = {0}; server.surface_offset = 0; server.display = wl_display_create(); - server.backend = wlr_backend_autocreate(server.display, NULL); + server.backend = wlr_backend_autocreate(wl_display_get_event_loop(server.display), NULL); server.scene = wlr_scene_create(); server.renderer = wlr_renderer_autocreate(server.backend); diff --git a/examples/simple.c b/examples/simple.c index 8fe4946..81ed2e0 100644 --- a/examples/simple.c +++ b/examples/simple.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200112L #include #include #include @@ -181,7 +180,7 @@ int main(void) { .last_frame = { 0 }, .display = display }; - struct wlr_backend *backend = wlr_backend_autocreate(display, NULL); + struct wlr_backend *backend = wlr_backend_autocreate(wl_display_get_event_loop(display), NULL); if (!backend) { exit(1); } diff --git a/examples/tablet.c b/examples/tablet.c index c253526..8f0754d 100644 --- a/examples/tablet.c +++ b/examples/tablet.c @@ -1,4 +1,5 @@ -#define _XOPEN_SOURCE 600 +#undef _POSIX_C_SOURCE +#define _XOPEN_SOURCE 600 // for M_PI #include #include #include @@ -10,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -351,7 +351,7 @@ static void new_input_notify(struct wl_listener *listener, void *data) { wl_signal_add(&pstate->wlr_tablet_pad->events.ring, &pstate->ring); wl_list_insert(&sample->tablet_pads, &pstate->link); break; - case WLR_INPUT_DEVICE_TABLET_TOOL:; + case WLR_INPUT_DEVICE_TABLET:; struct wlr_tablet *tablet = wlr_tablet_from_input_device(device); sample->width_mm = tablet->width_mm == 0 ? 20 : tablet->width_mm; @@ -386,7 +386,7 @@ int main(int argc, char *argv[]) { }; wl_list_init(&state.tablet_pads); wl_list_init(&state.tablet_tools); - struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL); + struct wlr_backend *wlr = wlr_backend_autocreate(wl_display_get_event_loop(display), NULL); if (!wlr) { exit(1); } diff --git a/examples/touch.c b/examples/touch.c index 53def62..e1bd738 100644 --- a/examples/touch.c +++ b/examples/touch.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200112L #include #include #include @@ -15,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -275,7 +273,7 @@ int main(int argc, char *argv[]) { wl_list_init(&state.touch_points); wl_list_init(&state.touch); - struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL); + struct wlr_backend *wlr = wlr_backend_autocreate(wl_display_get_event_loop(display), NULL); if (!wlr) { exit(1); } diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h index 39a247b..1106e9f 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -29,7 +29,10 @@ struct wlr_drm_plane { struct wlr_drm_format_set formats; - union wlr_drm_plane_props props; + struct wlr_output_cursor_size *cursor_sizes; + size_t cursor_sizes_len; + + struct wlr_drm_plane_props props; uint32_t initial_crtc_id; struct liftoff_plane *liftoff; @@ -61,6 +64,7 @@ struct wlr_drm_crtc { struct wl_list layers; // wlr_drm_layer.link // Atomic modesetting only + bool own_mode_id; uint32_t mode_id; uint32_t gamma_lut; @@ -70,7 +74,7 @@ struct wlr_drm_crtc { struct wlr_drm_plane *primary; struct wlr_drm_plane *cursor; - union wlr_drm_crtc_props props; + struct wlr_drm_crtc_props props; }; struct wlr_drm_backend { @@ -91,10 +95,8 @@ struct wlr_drm_backend { size_t num_planes; struct wlr_drm_plane *planes; - struct wl_display *display; struct wl_event_source *drm_event; - struct wl_listener display_destroy; struct wl_listener session_destroy; struct wl_listener session_active; struct wl_listener parent_destroy; @@ -123,13 +125,27 @@ struct wlr_drm_mode { drmModeModeInfo drm_mode; }; -struct wlr_drm_connector_state { - const struct wlr_output_state *base; +struct wlr_drm_device_state { bool modeset; bool nonblock; + + struct wlr_drm_connector_state *connectors; + size_t connectors_len; +}; + +struct wlr_drm_connector_state { + struct wlr_drm_connector *connector; + const struct wlr_output_state *base; bool active; drmModeModeInfo mode; struct wlr_drm_fb *primary_fb; + struct wlr_drm_fb *cursor_fb; + + // used by atomic + uint32_t mode_id; + uint32_t gamma_lut; + uint32_t fb_damage_clips; + bool vrr_enabled; }; /** @@ -146,7 +162,13 @@ struct wlr_drm_connector_state { */ struct wlr_drm_page_flip { struct wl_list link; // wlr_drm_connector.page_flips - struct wlr_drm_connector *conn; + struct wlr_drm_page_flip_connector *connectors; + size_t connectors_len; +}; + +struct wlr_drm_page_flip_connector { + uint32_t crtc_id; + struct wlr_drm_connector *connector; // may be NULL }; struct wlr_drm_connector { @@ -162,7 +184,7 @@ struct wlr_drm_connector { struct wlr_drm_crtc *crtc; uint32_t possible_crtcs; - union wlr_drm_connector_props props; + struct wlr_drm_connector_props props; bool cursor_enabled; int cursor_x, cursor_y; @@ -187,24 +209,34 @@ void finish_drm_resources(struct wlr_drm_backend *drm); void scan_drm_connectors(struct wlr_drm_backend *state, struct wlr_device_hotplug_event *event); void scan_drm_leases(struct wlr_drm_backend *drm); +bool commit_drm_device(struct wlr_drm_backend *drm, + const struct wlr_backend_output_state *states, size_t states_len, bool test_only); +void restore_drm_device(struct wlr_drm_backend *drm); int handle_drm_event(int fd, uint32_t mask, void *data); void destroy_drm_connector(struct wlr_drm_connector *conn); -bool drm_connector_commit_state(struct wlr_drm_connector *conn, - const struct wlr_output_state *state); bool drm_connector_is_cursor_visible(struct wlr_drm_connector *conn); -bool drm_connector_supports_vrr(struct wlr_drm_connector *conn); size_t drm_crtc_get_gamma_lut_size(struct wlr_drm_backend *drm, struct wlr_drm_crtc *crtc); void drm_lease_destroy(struct wlr_drm_lease *lease); void drm_page_flip_destroy(struct wlr_drm_page_flip *page_flip); -struct wlr_drm_fb *get_next_cursor_fb(struct wlr_drm_connector *conn); struct wlr_drm_layer *get_drm_layer(struct wlr_drm_backend *drm, struct wlr_output_layer *layer); +#if __STDC_VERSION__ >= 202311L + +#define wlr_drm_conn_log(conn, verb, fmt, ...) \ + wlr_log(verb, "connector %s: " fmt, conn->name __VA_OPT__(,) __VA_ARGS__) +#define wlr_drm_conn_log_errno(conn, verb, fmt, ...) \ + wlr_log_errno(verb, "connector %s: " fmt, conn->name __VA_OPT__(,) __VA_ARGS__) + +#else + #define wlr_drm_conn_log(conn, verb, fmt, ...) \ wlr_log(verb, "connector %s: " fmt, conn->name, ##__VA_ARGS__) #define wlr_drm_conn_log_errno(conn, verb, fmt, ...) \ wlr_log_errno(verb, "connector %s: " fmt, conn->name, ##__VA_ARGS__) #endif + +#endif diff --git a/include/backend/drm/fb.h b/include/backend/drm/fb.h new file mode 100644 index 0000000..91d0fff --- /dev/null +++ b/include/backend/drm/fb.h @@ -0,0 +1,24 @@ +#ifndef BACKEND_DRM_FB_H +#define BACKEND_DRM_FB_H + +#include + +struct wlr_drm_fb { + struct wlr_buffer *wlr_buf; + struct wlr_addon addon; + struct wlr_drm_backend *backend; + struct wl_list link; // wlr_drm_backend.fbs + + uint32_t id; +}; + +bool drm_fb_import(struct wlr_drm_fb **fb, struct wlr_drm_backend *drm, + struct wlr_buffer *buf, const struct wlr_drm_format_set *formats); +void drm_fb_destroy(struct wlr_drm_fb *fb); + +void drm_fb_clear(struct wlr_drm_fb **fb); +void drm_fb_copy(struct wlr_drm_fb **new, struct wlr_drm_fb *old); +void drm_fb_move(struct wlr_drm_fb **new, struct wlr_drm_fb **old); +struct wlr_drm_fb *drm_fb_lock(struct wlr_drm_fb *fb); + +#endif diff --git a/include/backend/drm/iface.h b/include/backend/drm/iface.h index dbe4c90..1ac1095 100644 --- a/include/backend/drm/iface.h +++ b/include/backend/drm/iface.h @@ -10,6 +10,7 @@ struct wlr_drm_backend; struct wlr_drm_connector; struct wlr_drm_crtc; +struct wlr_drm_device_state; struct wlr_drm_connector_state; struct wlr_drm_fb; struct wlr_drm_page_flip; @@ -18,10 +19,11 @@ struct wlr_drm_page_flip; struct wlr_drm_interface { bool (*init)(struct wlr_drm_backend *drm); void (*finish)(struct wlr_drm_backend *drm); - // Commit all pending changes on a CRTC. - bool (*crtc_commit)(struct wlr_drm_connector *conn, - const struct wlr_drm_connector_state *state, + bool (*commit)(struct wlr_drm_backend *drm, + const struct wlr_drm_device_state *state, struct wlr_drm_page_flip *page_flip, uint32_t flags, bool test_only); + // Turn off everything + bool (*reset)(struct wlr_drm_backend *drm); }; extern const struct wlr_drm_interface atomic_iface; @@ -31,12 +33,13 @@ extern const struct wlr_drm_interface liftoff_iface; bool drm_legacy_crtc_set_gamma(struct wlr_drm_backend *drm, struct wlr_drm_crtc *crtc, size_t size, uint16_t *lut); -bool create_mode_blob(struct wlr_drm_backend *drm, - struct wlr_drm_connector *conn, - const struct wlr_drm_connector_state *state, uint32_t *blob_id); -bool create_gamma_lut_blob(struct wlr_drm_backend *drm, - size_t size, const uint16_t *lut, uint32_t *blob_id); bool create_fb_damage_clips_blob(struct wlr_drm_backend *drm, int width, int height, const pixman_region32_t *damage, uint32_t *blob_id); +bool drm_atomic_reset(struct wlr_drm_backend *drm); + +bool drm_atomic_connector_prepare(struct wlr_drm_connector_state *state, + bool modeset); +void drm_atomic_connector_apply_commit(struct wlr_drm_connector_state *state); +void drm_atomic_connector_rollback_commit(struct wlr_drm_connector_state *state); #endif diff --git a/include/backend/drm/properties.h b/include/backend/drm/properties.h index 103d125..5707370 100644 --- a/include/backend/drm/properties.h +++ b/include/backend/drm/properties.h @@ -7,72 +7,66 @@ /* * These types contain the property ids for several DRM objects. - * See https://01.org/linuxgraphics/gfx-docs/drm/gpu/drm-kms.html#kms-properties - * for more details. + * For more details, see: + * https://dri.freedesktop.org/docs/drm/gpu/drm-kms.html#kms-properties */ -union wlr_drm_connector_props { - struct { - uint32_t edid; - uint32_t dpms; - uint32_t link_status; // not guaranteed to exist - uint32_t path; - uint32_t vrr_capable; // not guaranteed to exist - uint32_t subconnector; // not guaranteed to exist - uint32_t non_desktop; - uint32_t panel_orientation; // not guaranteed to exist - uint32_t content_type; // not guaranteed to exist - uint32_t max_bpc; // not guaranteed to exist +struct wlr_drm_connector_props { + uint32_t edid; + uint32_t dpms; + uint32_t link_status; // not guaranteed to exist + uint32_t path; + uint32_t vrr_capable; // not guaranteed to exist + uint32_t subconnector; // not guaranteed to exist + uint32_t non_desktop; + uint32_t panel_orientation; // not guaranteed to exist + uint32_t content_type; // not guaranteed to exist + uint32_t max_bpc; // not guaranteed to exist - // atomic-modesetting only + // atomic-modesetting only - uint32_t crtc_id; - }; - uint32_t props[4]; + uint32_t crtc_id; }; -union wlr_drm_crtc_props { - struct { - // Neither of these are guaranteed to exist - uint32_t vrr_enabled; - uint32_t gamma_lut; - uint32_t gamma_lut_size; +struct wlr_drm_crtc_props { + // Neither of these are guaranteed to exist + uint32_t vrr_enabled; + uint32_t gamma_lut; + uint32_t gamma_lut_size; - // atomic-modesetting only + // atomic-modesetting only - uint32_t active; - uint32_t mode_id; - }; - uint32_t props[6]; + uint32_t active; + uint32_t mode_id; }; -union wlr_drm_plane_props { - struct { - uint32_t type; - uint32_t rotation; // Not guaranteed to exist - uint32_t in_formats; // Not guaranteed to exist +struct wlr_drm_plane_props { + uint32_t type; + uint32_t rotation; // Not guaranteed to exist + uint32_t in_formats; // Not guaranteed to exist + uint32_t size_hints; // Not guaranteed to exist - // atomic-modesetting only + // atomic-modesetting only - uint32_t src_x; - uint32_t src_y; - uint32_t src_w; - uint32_t src_h; - uint32_t crtc_x; - uint32_t crtc_y; - uint32_t crtc_w; - uint32_t crtc_h; - uint32_t fb_id; - uint32_t crtc_id; - uint32_t fb_damage_clips; - }; - uint32_t props[14]; + uint32_t src_x; + uint32_t src_y; + uint32_t src_w; + uint32_t src_h; + uint32_t crtc_x; + uint32_t crtc_y; + uint32_t crtc_w; + uint32_t crtc_h; + uint32_t fb_id; + uint32_t crtc_id; + uint32_t fb_damage_clips; + uint32_t hotspot_x; + uint32_t hotspot_y; }; bool get_drm_connector_props(int fd, uint32_t id, - union wlr_drm_connector_props *out); -bool get_drm_crtc_props(int fd, uint32_t id, union wlr_drm_crtc_props *out); -bool get_drm_plane_props(int fd, uint32_t id, union wlr_drm_plane_props *out); + struct wlr_drm_connector_props *out); +bool get_drm_crtc_props(int fd, uint32_t id, struct wlr_drm_crtc_props *out); +bool get_drm_plane_props(int fd, uint32_t id, struct wlr_drm_plane_props *out); bool get_drm_prop(int fd, uint32_t obj, uint32_t prop, uint64_t *ret); void *get_drm_prop_blob(int fd, uint32_t obj, uint32_t prop, size_t *ret_len); diff --git a/include/backend/drm/renderer.h b/include/backend/drm/renderer.h index d29fe2a..f53f720 100644 --- a/include/backend/drm/renderer.h +++ b/include/backend/drm/renderer.h @@ -13,8 +13,6 @@ struct wlr_drm_plane; struct wlr_buffer; struct wlr_drm_renderer { - struct wlr_drm_backend *backend; - struct wlr_renderer *wlr_rend; struct wlr_allocator *allocator; }; @@ -24,15 +22,6 @@ struct wlr_drm_surface { struct wlr_swapchain *swapchain; }; -struct wlr_drm_fb { - struct wlr_buffer *wlr_buf; - struct wlr_addon addon; - struct wlr_drm_backend *backend; - struct wl_list link; // wlr_drm_backend.fbs - - uint32_t id; -}; - bool init_drm_renderer(struct wlr_drm_backend *drm, struct wlr_drm_renderer *renderer); void finish_drm_renderer(struct wlr_drm_renderer *renderer); @@ -40,20 +29,12 @@ void finish_drm_renderer(struct wlr_drm_renderer *renderer); bool init_drm_surface(struct wlr_drm_surface *surf, struct wlr_drm_renderer *renderer, int width, int height, const struct wlr_drm_format *drm_format); - -bool drm_fb_import(struct wlr_drm_fb **fb, struct wlr_drm_backend *drm, - struct wlr_buffer *buf, const struct wlr_drm_format_set *formats); -void drm_fb_destroy(struct wlr_drm_fb *fb); - -void drm_fb_clear(struct wlr_drm_fb **fb); -void drm_fb_move(struct wlr_drm_fb **new, struct wlr_drm_fb **old); -struct wlr_drm_fb *drm_fb_lock(struct wlr_drm_fb *fb); +void finish_drm_surface(struct wlr_drm_surface *surf); struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf, struct wlr_buffer *buffer); bool drm_plane_pick_render_format(struct wlr_drm_plane *plane, struct wlr_drm_format *fmt, struct wlr_drm_renderer *renderer); -void drm_plane_finish_surface(struct wlr_drm_plane *plane); #endif diff --git a/include/backend/headless.h b/include/backend/headless.h index 4c71fb6..21df86e 100644 --- a/include/backend/headless.h +++ b/include/backend/headless.h @@ -8,9 +8,9 @@ struct wlr_headless_backend { struct wlr_backend backend; - struct wl_display *display; + struct wl_event_loop *event_loop; struct wl_list outputs; - struct wl_listener display_destroy; + struct wl_listener event_loop_destroy; bool started; }; diff --git a/include/backend/libinput.h b/include/backend/libinput.h index 73a5275..cf685f0 100644 --- a/include/backend/libinput.h +++ b/include/backend/libinput.h @@ -18,12 +18,10 @@ struct wlr_libinput_backend { struct wlr_backend backend; struct wlr_session *session; - struct wl_display *display; struct libinput *libinput_context; struct wl_event_source *input_event; - struct wl_listener display_destroy; struct wl_listener session_destroy; struct wl_listener session_signal; @@ -50,6 +48,7 @@ void handle_libinput_event(struct wlr_libinput_backend *state, struct libinput_event *event); void destroy_libinput_input_device(struct wlr_libinput_input_device *dev); +const char *get_libinput_device_name(struct libinput_device *device); extern const struct wlr_keyboard_impl libinput_keyboard_impl; extern const struct wlr_pointer_impl libinput_pointer_impl; @@ -72,10 +71,8 @@ void handle_pointer_button(struct libinput_event *event, struct wlr_pointer *pointer); void handle_pointer_axis(struct libinput_event *event, struct wlr_pointer *pointer); -#if HAVE_LIBINPUT_SCROLL_VALUE120 void handle_pointer_axis_value120(struct libinput_event *event, - struct wlr_pointer *pointer, enum wlr_axis_source source); -#endif + struct wlr_pointer *pointer, enum wl_pointer_axis_source source); void handle_pointer_swipe_begin(struct libinput_event *event, struct wlr_pointer *pointer); void handle_pointer_swipe_update(struct libinput_event *event, diff --git a/include/backend/multi.h b/include/backend/multi.h index 20ad9db..3ffd814 100644 --- a/include/backend/multi.h +++ b/include/backend/multi.h @@ -10,7 +10,7 @@ struct wlr_multi_backend { struct wl_list backends; - struct wl_listener display_destroy; + struct wl_listener event_loop_destroy; struct { struct wl_signal backend_add; diff --git a/include/backend/wayland.h b/include/backend/wayland.h index d6796b4..aa266ee 100644 --- a/include/backend/wayland.h +++ b/include/backend/wayland.h @@ -3,7 +3,7 @@ #include -#include +#include #include #include @@ -20,12 +20,12 @@ struct wlr_wl_backend { /* local state */ bool started; - struct wl_display *local_display; + struct wl_event_loop *event_loop; struct wl_list outputs; int drm_fd; struct wl_list buffers; // wlr_wl_buffer.link size_t requested_outputs; - struct wl_listener local_display_destroy; + struct wl_listener event_loop_destroy; char *activation_token; /* remote state */ @@ -90,7 +90,22 @@ struct wlr_wl_output { struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1; struct wl_list presentation_feedbacks; + char *title; + char *app_id; + + // 0 if not requested + int32_t requested_width, requested_height; + + uint32_t configure_serial; + bool has_configure_serial; bool configured; + + bool initialized; + + // If not NULL, the host compositor hasn't acknowledged the unmapping yet; + // ignore all configure events + struct wl_callback *unmap_callback; + uint32_t enter_serial; struct { @@ -106,9 +121,10 @@ struct wlr_wl_pointer { struct wlr_wl_seat *seat; struct wlr_wl_output *output; - enum wlr_axis_source axis_source; + enum wl_pointer_axis_source axis_source; int32_t axis_discrete; uint32_t fingers; // trackpad gesture + enum wl_pointer_axis_relative_direction axis_relative_direction; struct wl_listener output_destroy; diff --git a/include/backend/x11.h b/include/backend/x11.h index 9eb208b..fcea4d2 100644 --- a/include/backend/x11.h +++ b/include/backend/x11.h @@ -62,7 +62,7 @@ struct wlr_x11_touchpoint { struct wlr_x11_backend { struct wlr_backend backend; - struct wl_display *wl_display; + struct wl_event_loop *event_loop; bool started; xcb_connection_t *xcb; @@ -108,7 +108,7 @@ struct wlr_x11_backend { uint8_t present_opcode; uint8_t xinput_opcode; - struct wl_listener display_destroy; + struct wl_listener event_loop_destroy; }; struct wlr_x11_buffer { diff --git a/include/meson.build b/include/meson.build index e669800..165166c 100644 --- a/include/meson.build +++ b/include/meson.build @@ -25,7 +25,7 @@ if not features.get('session') endif install_subdir('wlr', - install_dir: get_option('includedir'), + install_dir: get_option('includedir') / versioned_name, exclude_files: exclude_files, ) diff --git a/include/render/allocator/allocator.h b/include/render/allocator/allocator.h index 2abdd43..5f8bd2a 100644 --- a/include/render/allocator/allocator.h +++ b/include/render/allocator/allocator.h @@ -4,6 +4,6 @@ #include struct wlr_allocator *allocator_autocreate_with_drm_fd( - struct wlr_backend *backend, struct wlr_renderer *renderer, int drm_fd); + uint32_t backend_caps, struct wlr_renderer *renderer, int drm_fd); #endif diff --git a/include/render/color.h b/include/render/color.h new file mode 100644 index 0000000..e18f12e --- /dev/null +++ b/include/render/color.h @@ -0,0 +1,39 @@ +#ifndef RENDER_COLOR_H +#define RENDER_COLOR_H + +#include +#include + +/** + * The formula is approximated via a 3D look-up table. A 3D LUT is a + * three-dimensional array where each element is an RGB triplet. The flat lut_3d + * array has a length of dim_len³. + * + * Color channel values in the range [0.0, 1.0] are mapped linearly to + * 3D LUT indices such that 0.0 maps exactly to the first element and 1.0 maps + * exactly to the last element in each dimension. + * + * The offset of the RGB triplet given red, green and blue indices r_index, + * g_index and b_index is: + * + * offset = 3 * (r_index + dim_len * g_index + dim_len * dim_len * b_index) + */ +struct wlr_color_transform_lut3d { + float *lut_3d; + size_t dim_len; +}; + +enum wlr_color_transform_type { + COLOR_TRANSFORM_SRGB, + COLOR_TRANSFORM_LUT_3D, +}; + +struct wlr_color_transform { + int ref_count; + struct wlr_addon_set addons; // per-renderer helper state + + enum wlr_color_transform_type type; + struct wlr_color_transform_lut3d lut3d; +}; + +#endif diff --git a/include/render/egl.h b/include/render/egl.h index e8b8596..0765cce 100644 --- a/include/render/egl.h +++ b/include/render/egl.h @@ -90,13 +90,6 @@ bool wlr_egl_destroy_image(struct wlr_egl *egl, EGLImageKHR image); int wlr_egl_dup_drm_fd(struct wlr_egl *egl); -/** - * Save the current EGL context to the structure provided in the argument. - * - * This includes display, context, draw surface and read surface. - */ -void wlr_egl_save_context(struct wlr_egl_context *context); - /** * Restore EGL context that was previously saved using wlr_egl_save_current(). */ @@ -105,13 +98,11 @@ bool wlr_egl_restore_context(struct wlr_egl_context *context); /** * Make the EGL context current. * - * Callers are expected to clear the current context when they are done by - * calling wlr_egl_unset_current(). + * The old EGL context is saved. Callers are expected to clear the current + * context when they are done by calling wlr_egl_restore_context(). */ -bool wlr_egl_make_current(struct wlr_egl *egl); +bool wlr_egl_make_current(struct wlr_egl *egl, struct wlr_egl_context *save_context); bool wlr_egl_unset_current(struct wlr_egl *egl); -bool wlr_egl_is_current(struct wlr_egl *egl); - #endif diff --git a/include/render/gles2.h b/include/render/gles2.h index 930cf07..a472ee9 100644 --- a/include/render/gles2.h +++ b/include/render/gles2.h @@ -15,6 +15,8 @@ #include #include +#include "render/egl.h" + // mesa ships old GL headers that don't include this type, so for distros that use headers from // mesa we need to def it ourselves until they update. // https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/23144 @@ -25,7 +27,6 @@ struct wlr_gles2_pixel_format { // optional field, if empty then internalformat = format GLint gl_internalformat; GLint gl_format, gl_type; - bool has_alpha; }; struct wlr_gles2_tex_shader { @@ -40,10 +41,11 @@ struct wlr_gles2_tex_shader { struct wlr_gles2_renderer { struct wlr_renderer wlr_renderer; - float projection[9]; struct wlr_egl *egl; int drm_fd; + struct wlr_drm_format_set shm_texture_formats; + const char *exts_str; struct { bool EXT_read_format_bgra; @@ -86,9 +88,6 @@ struct wlr_gles2_renderer { struct wl_list buffers; // wlr_gles2_buffer.link struct wl_list textures; // wlr_gles2_texture.link - - struct wlr_gles2_buffer *current_buffer; - uint32_t viewport_width, viewport_height; }; struct wlr_gles2_render_timer { @@ -104,10 +103,12 @@ struct wlr_gles2_buffer { struct wlr_buffer *buffer; struct wlr_gles2_renderer *renderer; struct wl_list link; // wlr_gles2_renderer.buffers + bool external_only; EGLImageKHR image; GLuint rbo; GLuint fbo; + GLuint tex; struct wlr_addon addon; }; @@ -117,27 +118,25 @@ struct wlr_gles2_texture { struct wlr_gles2_renderer *renderer; struct wl_list link; // wlr_gles2_renderer.textures - // Basically: - // GL_TEXTURE_2D == mutable - // GL_TEXTURE_EXTERNAL_OES == immutable GLenum target; - GLuint tex; - EGLImageKHR image; + // If this texture is imported from a buffer, the texture is does not own + // these states. These cannot be destroyed along with the texture in this + // case. + GLuint tex; + GLuint fbo; bool has_alpha; - // Only affects target == GL_TEXTURE_2D - uint32_t drm_format; // used to interpret upload data - // If imported from a wlr_buffer - struct wlr_buffer *buffer; - struct wlr_addon buffer_addon; + uint32_t drm_format; // for mutable textures only, used to interpret upload data + struct wlr_gles2_buffer *buffer; // for DMA-BUF imports only }; struct wlr_gles2_render_pass { struct wlr_render_pass base; struct wlr_gles2_buffer *buffer; float projection_matrix[9]; + struct wlr_egl_context prev_ctx; struct wlr_gles2_render_timer *timer; }; @@ -146,8 +145,10 @@ bool is_gles2_pixel_format_supported(const struct wlr_gles2_renderer *renderer, const struct wlr_gles2_pixel_format *get_gles2_format_from_drm(uint32_t fmt); const struct wlr_gles2_pixel_format *get_gles2_format_from_gl( GLint gl_format, GLint gl_type, bool alpha); -const uint32_t *get_gles2_shm_formats(const struct wlr_gles2_renderer *renderer, - size_t *len); +void get_gles2_shm_formats(const struct wlr_gles2_renderer *renderer, + struct wlr_drm_format_set *out); + +GLuint gles2_buffer_get_fbo(struct wlr_gles2_buffer *buffer); struct wlr_gles2_renderer *gles2_get_renderer( struct wlr_renderer *wlr_renderer); @@ -155,6 +156,8 @@ struct wlr_gles2_render_timer *gles2_get_render_timer( struct wlr_render_timer *timer); struct wlr_gles2_texture *gles2_get_texture( struct wlr_texture *wlr_texture); +struct wlr_gles2_buffer *gles2_buffer_get_or_create(struct wlr_gles2_renderer *renderer, + struct wlr_buffer *wlr_buffer); struct wlr_texture *gles2_texture_from_buffer(struct wlr_renderer *wlr_renderer, struct wlr_buffer *buffer); @@ -166,6 +169,6 @@ void push_gles2_debug_(struct wlr_gles2_renderer *renderer, void pop_gles2_debug(struct wlr_gles2_renderer *renderer); struct wlr_gles2_render_pass *begin_gles2_buffer_pass(struct wlr_gles2_buffer *buffer, - struct wlr_gles2_render_timer *timer); + struct wlr_egl_context *prev_ctx, struct wlr_gles2_render_timer *timer); #endif diff --git a/include/render/pass.h b/include/render/pass.h deleted file mode 100644 index 2cbd6db..0000000 --- a/include/render/pass.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef RENDER_PASS_H -#define RENDER_PASS_H - -#include - -struct wlr_render_pass *begin_legacy_buffer_render_pass(struct wlr_renderer *renderer, - struct wlr_buffer *buffer); - -#endif diff --git a/include/render/pixel_format.h b/include/render/pixel_format.h index a024ff9..e0b500c 100644 --- a/include/render/pixel_format.h +++ b/include/render/pixel_format.h @@ -27,9 +27,6 @@ struct wlr_pixel_format_info { uint32_t bytes_per_block; /* Size of a block in pixels (zero for 1×1) */ uint32_t block_width, block_height; - - /* True if the format has an alpha channel */ - bool has_alpha; }; /** @@ -61,4 +58,9 @@ uint32_t convert_wl_shm_format_to_drm(enum wl_shm_format fmt); */ enum wl_shm_format convert_drm_format_to_wl_shm(uint32_t fmt); +/** + * Return true if the DRM FourCC fmt has an alpha channel, false otherwise. + */ +bool pixel_format_has_alpha(uint32_t fmt); + #endif diff --git a/include/render/pixman.h b/include/render/pixman.h index 24a2705..0984214 100644 --- a/include/render/pixman.h +++ b/include/render/pixman.h @@ -20,9 +20,6 @@ struct wlr_pixman_renderer { struct wl_list buffers; // wlr_pixman_buffer.link struct wl_list textures; // wlr_pixman_texture.link - struct wlr_pixman_buffer *current_buffer; - int32_t width, height; - struct wlr_drm_format_set drm_formats; }; diff --git a/include/render/vulkan.h b/include/render/vulkan.h index 3e0c395..29403f0 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -60,11 +60,7 @@ struct wlr_vk_device { struct wlr_vk_format_props *format_props; struct wlr_drm_format_set dmabuf_render_formats; struct wlr_drm_format_set dmabuf_texture_formats; - - // supported formats for textures (contains only those formats - // that support everything we need for textures) - uint32_t shm_format_count; - uint32_t *shm_formats; // to implement vulkan_get_shm_texture_formats + struct wlr_drm_format_set shm_texture_formats; }; // Tries to find the VkPhysicalDevice for the given drm fd. @@ -87,7 +83,7 @@ int vulkan_find_mem_type(struct wlr_vk_device *device, struct wlr_vk_format { uint32_t drm; VkFormat vk; - bool is_srgb; + VkFormat vk_srgb; // sRGB version of the format, or 0 if nonexistent bool is_ycbcr; }; @@ -101,6 +97,7 @@ const struct wlr_vk_format *vulkan_get_format_from_drm(uint32_t drm_format); struct wlr_vk_format_modifier_props { VkDrmFormatModifierPropertiesEXT props; VkExtent2D max_extent; + bool has_mutable_srgb; }; struct wlr_vk_format_props { @@ -109,6 +106,7 @@ struct wlr_vk_format_props { struct { VkExtent2D max_extent; VkFormatFeatureFlags features; + bool has_mutable_srgb; } shm; struct { @@ -123,7 +121,7 @@ struct wlr_vk_format_props { void vulkan_format_props_query(struct wlr_vk_device *dev, const struct wlr_vk_format *format); const struct wlr_vk_format_modifier_props *vulkan_format_props_find_modifier( - struct wlr_vk_format_props *props, uint64_t mod, bool render); + const struct wlr_vk_format_props *props, uint64_t mod, bool render); void vulkan_format_props_finish(struct wlr_vk_format_props *props); struct wlr_vk_pipeline_layout_key { @@ -159,6 +157,13 @@ enum wlr_vk_shader_source { WLR_VK_SHADER_SOURCE_SINGLE_COLOR, }; +// Constants used to pick the color transform for the blend-to-output +// fragment shader. Must match those in shaders/output.frag +enum wlr_vk_output_transform { + WLR_VK_OUTPUT_TRANSFORM_INVERSE_SRGB = 0, + WLR_VK_OUTPUT_TRANSFORM_LUT3D = 1, +}; + struct wlr_vk_pipeline_key { struct wlr_vk_pipeline_layout_key layout; enum wlr_vk_shader_source source; @@ -182,9 +187,11 @@ struct wlr_vk_pipeline { struct wlr_vk_render_format_setup { struct wl_list link; // wlr_vk_renderer.render_format_setups const struct wlr_vk_format *render_format; // used in renderpass + bool use_blending_buffer; VkRenderPass render_pass; - VkPipeline output_pipe; + VkPipeline output_pipe_srgb; + VkPipeline output_pipe_lut3d; struct wlr_vk_renderer *renderer; struct wl_list pipelines; // struct wlr_vk_pipeline.link @@ -195,24 +202,44 @@ struct wlr_vk_render_buffer { struct wlr_buffer *wlr_buffer; struct wlr_addon addon; struct wlr_vk_renderer *renderer; - struct wlr_vk_render_format_setup *render_setup; struct wl_list link; // wlr_vk_renderer.buffers - VkImage image; - VkImageView image_view; - VkFramebuffer framebuffer; - uint32_t mem_count; VkDeviceMemory memories[WLR_DMABUF_MAX_PLANES]; - bool transitioned; - - VkImage blend_image; - VkImageView blend_image_view; - VkDeviceMemory blend_memory; - VkDescriptorSet blend_descriptor_set; - struct wlr_vk_descriptor_pool *blend_attachment_pool; - bool blend_transitioned; + uint32_t mem_count; + VkImage image; + + // Framebuffer and image view for rendering directly onto the buffer image. + // This requires that the image support an _SRGB VkFormat, and does + // not work with color transforms. + struct { + struct wlr_vk_render_format_setup *render_setup; + VkImageView image_view; + VkFramebuffer framebuffer; + bool transitioned; + } srgb; + + // Framebuffer, image view, and blending image to render indirectly + // onto the buffer image. This works for general image types and permits + // color transforms. + struct { + struct wlr_vk_render_format_setup *render_setup; + + VkImageView image_view; + VkFramebuffer framebuffer; + bool transitioned; + + VkImage blend_image; + VkImageView blend_image_view; + VkDeviceMemory blend_memory; + VkDescriptorSet blend_descriptor_set; + struct wlr_vk_descriptor_pool *blend_attachment_pool; + bool blend_transitioned; + } plain; }; +bool vulkan_setup_plain_framebuffer(struct wlr_vk_render_buffer *buffer, + const struct wlr_dmabuf_attributes *dmabuf); + struct wlr_vk_command_buffer { VkCommandBuffer vk; bool recording; @@ -221,6 +248,8 @@ struct wlr_vk_command_buffer { struct wl_list destroy_textures; // wlr_vk_texture.destroy_link // Staging shared buffers to release after the command buffer completes struct wl_list stage_buffers; // wlr_vk_shared_buffer.link + // Color transform to unref after the command buffer completes + struct wlr_color_transform *color_transform; // For DMA-BUF implicit sync interop, may be NULL VkSemaphore binary_semaphore; @@ -245,24 +274,25 @@ struct wlr_vk_renderer { // for blend->output subpass VkPipelineLayout output_pipe_layout; - VkDescriptorSetLayout output_ds_layout; + VkDescriptorSetLayout output_ds_srgb_layout; + VkDescriptorSetLayout output_ds_lut3d_layout; + VkSampler output_sampler_lut3d; + // descriptor set indicating dummy 1x1x1 image, for use in the lut3d slot + VkDescriptorSet output_ds_lut3d_dummy; + struct wlr_vk_descriptor_pool *output_ds_lut3d_dummy_pool; + size_t last_output_pool_size; struct wl_list output_descriptor_pools; // wlr_vk_descriptor_pool.link + // dummy sampler to bind when output shader is not using a lookup table + VkImage dummy3d_image; + VkDeviceMemory dummy3d_mem; + VkImageView dummy3d_image_view; + bool dummy3d_image_transitioned; + VkSemaphore timeline_semaphore; uint64_t timeline_point; - struct wlr_vk_render_buffer *current_render_buffer; - struct wlr_vk_command_buffer *current_command_buffer; - - VkRect2D scissor; // needed for clearing - - VkPipeline bound_pipe; - - uint32_t render_width; - uint32_t render_height; - float projection[9]; - size_t last_pool_size; struct wl_list descriptor_pools; // wlr_vk_descriptor_pool.link struct wl_list render_format_setups; // wlr_vk_render_format_setup.link @@ -274,6 +304,8 @@ struct wlr_vk_renderer { struct wl_list render_buffers; // wlr_vk_render_buffer.link + struct wl_list color_transforms; // wlr_vk_color_transform.link + // Pool of command buffers struct wlr_vk_command_buffer command_buffers[VULKAN_COMMAND_BUFFERS_CAP]; @@ -299,6 +331,11 @@ struct wlr_vk_vert_pcr_data { float uv_size[2]; }; +struct wlr_vk_frag_output_pcr_data { + float lut_3d_offset; + float lut_3d_scale; +}; + struct wlr_vk_texture_view { struct wl_list link; // struct wlr_vk_texture.views const struct wlr_vk_pipeline_layout *layout; @@ -339,10 +376,12 @@ struct wlr_vk_render_pass { VkPipeline bound_pipeline; float projection[9]; bool failed; + bool srgb_pathway; // if false, rendering via intermediate blending buffer + struct wlr_color_transform *color_transform; }; struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *renderer, - struct wlr_vk_render_buffer *buffer); + struct wlr_vk_render_buffer *buffer, const struct wlr_buffer_pass_options *options); // Suballocates a buffer span with the given size that can be mapped // and used as staging buffer. The allocation is implicitly released when the @@ -383,6 +422,12 @@ bool vulkan_sync_render_buffer(struct wlr_vk_renderer *renderer, struct wlr_vk_render_buffer *render_buffer, struct wlr_vk_command_buffer *cb); bool vulkan_sync_foreign_texture(struct wlr_vk_texture *texture); +bool vulkan_read_pixels(struct wlr_vk_renderer *vk_renderer, + VkFormat src_format, VkImage src_image, + uint32_t drm_format, uint32_t stride, + uint32_t width, uint32_t height, uint32_t src_x, uint32_t src_y, + uint32_t dst_x, uint32_t dst_y, void *data); + // State (e.g. image texture) associated with a surface. struct wlr_vk_texture { struct wlr_texture wlr_texture; @@ -397,6 +442,7 @@ struct wlr_vk_texture { bool owned; // if dmabuf_imported: whether we have ownership of the image bool transitioned; // if dma_imported: whether we transitioned it away from preinit bool has_alpha; // whether the image is has alpha channel + bool using_mutable_srgb; // is this accessed through _SRGB format view struct wl_list foreign_link; // wlr_vk_renderer.foreign_textures struct wl_list destroy_link; // wlr_vk_command_buffer.destroy_textures struct wl_list link; // wlr_vk_renderer.textures @@ -414,7 +460,7 @@ struct wlr_vk_texture *vulkan_get_texture(struct wlr_texture *wlr_texture); VkImage vulkan_import_dmabuf(struct wlr_vk_renderer *renderer, const struct wlr_dmabuf_attributes *attribs, VkDeviceMemory mems[static WLR_DMABUF_MAX_PLANES], uint32_t *n_mems, - bool for_render); + bool for_render, bool *using_mutable_srgb); struct wlr_texture *vulkan_texture_from_buffer( struct wlr_renderer *wlr_renderer, struct wlr_buffer *buffer); void vulkan_texture_destroy(struct wlr_vk_texture *texture); @@ -437,6 +483,7 @@ struct wlr_vk_shared_buffer { VkBuffer buffer; VkDeviceMemory memory; VkDeviceSize buf_size; + void *cpu_mapping; struct wl_array allocs; // struct wlr_vk_allocation }; @@ -446,13 +493,38 @@ struct wlr_vk_buffer_span { struct wlr_vk_allocation alloc; }; + +// Lookup table for a color transform +struct wlr_vk_color_transform { + struct wlr_addon addon; // owned by: wlr_vk_renderer + struct wl_list link; // wlr_vk_renderer, list of all color transforms + + struct { + VkImage image; + VkImageView image_view; + VkDeviceMemory memory; + VkDescriptorSet ds; + struct wlr_vk_descriptor_pool *ds_pool; + } lut_3d; +}; +void vk_color_transform_destroy(struct wlr_addon *addon); + // util const char *vulkan_strerror(VkResult err); void vulkan_change_layout(VkCommandBuffer cb, VkImage img, VkImageLayout ol, VkPipelineStageFlags srcs, VkAccessFlags srca, VkImageLayout nl, VkPipelineStageFlags dsts, VkAccessFlags dsta); +#if __STDC_VERSION__ >= 202311L + +#define wlr_vk_error(fmt, res, ...) wlr_log(WLR_ERROR, fmt ": %s (%d)", \ + vulkan_strerror(res), res __VA_OPT__(,) __VA_ARGS__) + +#else + #define wlr_vk_error(fmt, res, ...) wlr_log(WLR_ERROR, fmt ": %s (%d)", \ vulkan_strerror(res), res, ##__VA_ARGS__) +#endif + #endif // RENDER_VULKAN_H diff --git a/include/render/wlr_renderer.h b/include/render/wlr_renderer.h index c55b3ac..d9823dc 100644 --- a/include/render/wlr_renderer.h +++ b/include/render/wlr_renderer.h @@ -7,25 +7,11 @@ * Automatically select and create a renderer suitable for the DRM FD. */ struct wlr_renderer *renderer_autocreate_with_drm_fd(int drm_fd); -/** - * Bind a buffer to the renderer. - * - * All subsequent rendering operations will operate on the supplied buffer. - * After rendering operations are done, the caller must unbind a buffer by - * calling renderer_bind_buffer with a NULL buffer. - */ -bool renderer_bind_buffer(struct wlr_renderer *r, struct wlr_buffer *buffer); /** * Get the supported render formats. Buffers allocated with a format from this * list may be attached via wlr_renderer_begin_with_buffer. */ const struct wlr_drm_format_set *wlr_renderer_get_render_formats( struct wlr_renderer *renderer); -/** - * Get the supported buffer capabilities. - * - * This functions returns a bitfield of supported wlr_buffer_cap. - */ -uint32_t renderer_get_render_buffer_caps(struct wlr_renderer *renderer); #endif diff --git a/include/types/wlr_output.h b/include/types/wlr_output.h index e1aea1b..09be355 100644 --- a/include/types/wlr_output.h +++ b/include/types/wlr_output.h @@ -12,7 +12,6 @@ bool output_pending_enabled(struct wlr_output *output, bool output_pick_format(struct wlr_output *output, const struct wlr_drm_format_set *display_formats, struct wlr_drm_format *format, uint32_t fmt); -void output_clear_back_buffer(struct wlr_output *output); bool output_ensure_buffer(struct wlr_output *output, struct wlr_output_state *state, bool *new_back_buffer); @@ -23,4 +22,7 @@ bool output_cursor_set_texture(struct wlr_output_cursor *cursor, void output_defer_present(struct wlr_output *output, struct wlr_output_event_present event); +bool output_prepare_commit(struct wlr_output *output, const struct wlr_output_state *state); +void output_apply_commit(struct wlr_output *output, const struct wlr_output_state *state); + #endif diff --git a/include/types/wlr_tablet_v2.h b/include/types/wlr_tablet_v2.h index 2e54adc..af6a258 100644 --- a/include/types/wlr_tablet_v2.h +++ b/include/types/wlr_tablet_v2.h @@ -1,7 +1,7 @@ #ifndef TYPES_WLR_TABLET_V2_H #define TYPES_WLR_TABLET_V2_H -#include "tablet-unstable-v2-protocol.h" +#include "tablet-v2-protocol.h" #include #include diff --git a/include/types/wlr_xdg_shell.h b/include/types/wlr_xdg_shell.h index 34dec2a..1b4c89c 100644 --- a/include/types/wlr_xdg_shell.h +++ b/include/types/wlr_xdg_shell.h @@ -20,7 +20,7 @@ void create_xdg_popup(struct wlr_xdg_surface *surface, struct wlr_xdg_positioner *positioner, uint32_t id); void reset_xdg_popup(struct wlr_xdg_popup *popup); void destroy_xdg_popup(struct wlr_xdg_popup *popup); -void handle_xdg_popup_committed(struct wlr_xdg_popup *popup); +void handle_xdg_popup_client_commit(struct wlr_xdg_popup *popup); struct wlr_xdg_popup_configure *send_xdg_popup_configure( struct wlr_xdg_popup *popup); void handle_xdg_popup_ack_configure(struct wlr_xdg_popup *popup, @@ -30,7 +30,7 @@ void create_xdg_toplevel(struct wlr_xdg_surface *surface, uint32_t id); void reset_xdg_toplevel(struct wlr_xdg_toplevel *toplevel); void destroy_xdg_toplevel(struct wlr_xdg_toplevel *toplevel); -void handle_xdg_toplevel_committed(struct wlr_xdg_toplevel *toplevel); +void handle_xdg_toplevel_client_commit(struct wlr_xdg_toplevel *toplevel); struct wlr_xdg_toplevel_configure *send_xdg_toplevel_configure( struct wlr_xdg_toplevel *toplevel); void handle_xdg_toplevel_ack_configure(struct wlr_xdg_toplevel *toplevel, diff --git a/include/util/utf8.h b/include/util/utf8.h new file mode 100644 index 0000000..4d5172c --- /dev/null +++ b/include/util/utf8.h @@ -0,0 +1,11 @@ +#ifndef UTIL_UTF8_H +#define UTIL_UTF8_H + +#include + +/** + * Return true if and only if the string is a valid UTF-8 sequence. + */ +bool is_utf8(const char *string); + +#endif diff --git a/include/wlr/backend.h b/include/wlr/backend.h index 9a8a2d8..6e22695 100644 --- a/include/wlr/backend.h +++ b/include/wlr/backend.h @@ -10,10 +10,19 @@ #define WLR_BACKEND_H #include +#include struct wlr_session; struct wlr_backend_impl; +/** + * Per-output state for wlr_backend_test() and wlr_backend_commit(). + */ +struct wlr_backend_output_state { + struct wlr_output *output; + struct wlr_output_state base; +}; + /** * A backend provides a set of input and output devices. */ @@ -37,8 +46,11 @@ struct wlr_backend { * * If session_ptr is not NULL, it's populated with the session which has been * created with the backend, if any. + * + * The multi-backend will be destroyed if one of the primary underlying + * backends is destroyed (e.g. if the primary DRM device is unplugged). */ -struct wlr_backend *wlr_backend_autocreate(struct wl_display *display, +struct wlr_backend *wlr_backend_autocreate(struct wl_event_loop *loop, struct wlr_session **session_ptr); /** * Start the backend. This may signal new_input or new_output immediately, but @@ -59,4 +71,23 @@ void wlr_backend_destroy(struct wlr_backend *backend); */ int wlr_backend_get_drm_fd(struct wlr_backend *backend); +/** + * Atomically test a new configuration for multiple outputs. + * + * Some backends (e.g. DRM) have global backend-wide limitations. This function + * can be used to check whether changes across multiple outputs are supported by + * the backend. + */ +bool wlr_backend_test(struct wlr_backend *backend, + const struct wlr_backend_output_state *states, size_t states_len); +/** + * Atomically apply a new configuration for multiple outputs. + * + * There is no guarantee that the changes will be applied atomically. Users + * should call wlr_backend_test() first to check that the new state is supported + * by the backend. + */ +bool wlr_backend_commit(struct wlr_backend *backend, + const struct wlr_backend_output_state *states, size_t states_len); + #endif diff --git a/include/wlr/backend/drm.h b/include/wlr/backend/drm.h index e4fdf65..3ca6390 100644 --- a/include/wlr/backend/drm.h +++ b/include/wlr/backend/drm.h @@ -36,13 +36,17 @@ struct wlr_drm_lease { * To slave this to another DRM backend, pass it as the parent (which _must_ be * a DRM backend, other kinds of backends raise SIGABRT). */ -struct wlr_backend *wlr_drm_backend_create(struct wl_display *display, - struct wlr_session *session, struct wlr_device *dev, - struct wlr_backend *parent); +struct wlr_backend *wlr_drm_backend_create(struct wlr_session *session, + struct wlr_device *dev, struct wlr_backend *parent); bool wlr_backend_is_drm(struct wlr_backend *backend); bool wlr_output_is_drm(struct wlr_output *output); +/** + * Get the parent DRM backend, if any. + */ +struct wlr_backend *wlr_drm_backend_get_parent(struct wlr_backend *backend); + /** * Get the KMS connector object ID. */ diff --git a/include/wlr/backend/headless.h b/include/wlr/backend/headless.h index 2516f3a..126f1ff 100644 --- a/include/wlr/backend/headless.h +++ b/include/wlr/backend/headless.h @@ -16,7 +16,7 @@ * Creates a headless backend. A headless backend has no outputs or inputs by * default. */ -struct wlr_backend *wlr_headless_backend_create(struct wl_display *display); +struct wlr_backend *wlr_headless_backend_create(struct wl_event_loop *loop); /** * Create a new headless output. * diff --git a/include/wlr/backend/interface.h b/include/wlr/backend/interface.h index da57cae..938ca73 100644 --- a/include/wlr/backend/interface.h +++ b/include/wlr/backend/interface.h @@ -12,11 +12,17 @@ #include #include +struct wlr_output_state; + struct wlr_backend_impl { bool (*start)(struct wlr_backend *backend); void (*destroy)(struct wlr_backend *backend); int (*get_drm_fd)(struct wlr_backend *backend); uint32_t (*get_buffer_caps)(struct wlr_backend *backend); + bool (*test)(struct wlr_backend *backend, + const struct wlr_backend_output_state *states, size_t states_len); + bool (*commit)(struct wlr_backend *backend, + const struct wlr_backend_output_state *states, size_t states_len); }; /** diff --git a/include/wlr/backend/libinput.h b/include/wlr/backend/libinput.h index 83d281f..663f71a 100644 --- a/include/wlr/backend/libinput.h +++ b/include/wlr/backend/libinput.h @@ -16,8 +16,7 @@ struct wlr_input_device; -struct wlr_backend *wlr_libinput_backend_create(struct wl_display *display, - struct wlr_session *session); +struct wlr_backend *wlr_libinput_backend_create(struct wlr_session *session); /** * Gets the underlying struct libinput_device handle for the given input device. */ diff --git a/include/wlr/backend/multi.h b/include/wlr/backend/multi.h index 09000ac..c4322d9 100644 --- a/include/wlr/backend/multi.h +++ b/include/wlr/backend/multi.h @@ -15,7 +15,7 @@ * Creates a multi-backend. Multi-backends wrap an arbitrary number of backends * and aggregate their new_output/new_input signals. */ -struct wlr_backend *wlr_multi_backend_create(struct wl_display *display); +struct wlr_backend *wlr_multi_backend_create(struct wl_event_loop *loop); /** * Adds the given backend to the multi backend. This should be done before the * new backend is started. diff --git a/include/wlr/backend/session.h b/include/wlr/backend/session.h index 3583b31..3179d1f 100644 --- a/include/wlr/backend/session.h +++ b/include/wlr/backend/session.h @@ -7,11 +7,14 @@ struct libseat; +/** + * An opened physical device. + */ struct wlr_device { int fd; int device_id; dev_t dev; - struct wl_list link; + struct wl_list link; // wlr_session.devices struct { struct wl_signal change; // struct wlr_device_change_event @@ -19,6 +22,20 @@ struct wlr_device { } events; }; +/** + * A session manages access to physical devices (such as GPUs and input + * devices). + * + * A session is only required when running on bare metal (e.g. with the KMS or + * libinput backends). + * + * The session listens for device hotplug events, and relays that information + * via the add_drm_card event and the change/remove events on struct wlr_device. + * The session provides functions to gain access to physical device (which is a + * privileged operation), see wlr_session_open_file(). The session also keeps + * track of the virtual terminal state (allowing users to switch between + * compositors or TTYs), see wlr_session_change_vt() and the active event. + */ struct wlr_session { /* * Signal for when the session becomes active/inactive. @@ -40,10 +57,10 @@ struct wlr_session { struct libseat *seat_handle; struct wl_event_source *libseat_event; - struct wl_list devices; + struct wl_list devices; // wlr_device.link - struct wl_display *display; - struct wl_listener display_destroy; + struct wl_event_loop *event_loop; + struct wl_listener event_loop_destroy; struct { struct wl_signal active; @@ -80,7 +97,7 @@ struct wlr_device_change_event { * * Returns NULL on error. */ -struct wlr_session *wlr_session_create(struct wl_display *disp); +struct wlr_session *wlr_session_create(struct wl_event_loop *loop); /* * Closes a previously opened session and restores the virtual terminal. @@ -114,6 +131,17 @@ void wlr_session_close_file(struct wlr_session *session, */ bool wlr_session_change_vt(struct wlr_session *session, unsigned vt); +/** + * Enumerate and open KMS devices. + * + * ret is filled with up to ret_len devices. The number of devices ret has been + * filled with is returned on success. If more devices than ret_len are probed, + * the extraneous ones are ignored. If there is no KMS device, the function + * will block until such device is detected up to a timeout. The first device + * returned is the default device (marked as "boot_vga" by the kernel). + * + * On error, or if no device was found, -1 is returned. + */ ssize_t wlr_session_find_gpus(struct wlr_session *session, size_t ret_len, struct wlr_device **ret); diff --git a/include/wlr/backend/wayland.h b/include/wlr/backend/wayland.h index a30bac2..c732de6 100644 --- a/include/wlr/backend/wayland.h +++ b/include/wlr/backend/wayland.h @@ -1,7 +1,8 @@ #ifndef WLR_BACKEND_WAYLAND_H #define WLR_BACKEND_WAYLAND_H + #include -#include +#include #include #include #include @@ -15,7 +16,7 @@ struct wlr_input_device; * The remote_display argument is an existing libwayland-client struct wl_display * to use. Leave it NULL to create a new connection to the compositor. */ -struct wlr_backend *wlr_wl_backend_create(struct wl_display *display, +struct wlr_backend *wlr_wl_backend_create(struct wl_event_loop *loop, struct wl_display *remote_display); /** @@ -62,6 +63,11 @@ bool wlr_output_is_wl(struct wlr_output *output); */ void wlr_wl_output_set_title(struct wlr_output *output, const char *title); +/** + * Sets the app_id of a struct wlr_output which is a Wayland toplevel. + */ +void wlr_wl_output_set_app_id(struct wlr_output *output, const char *app_id); + /** * Returns the remote struct wl_surface used by the Wayland output. */ diff --git a/include/wlr/backend/x11.h b/include/wlr/backend/x11.h index 54d91c7..1791041 100644 --- a/include/wlr/backend/x11.h +++ b/include/wlr/backend/x11.h @@ -17,7 +17,7 @@ struct wlr_input_device; * The `x11_display` argument is the name of the X Display socket. Set * to NULL for the default behaviour of XOpenDisplay(). */ -struct wlr_backend *wlr_x11_backend_create(struct wl_display *display, +struct wlr_backend *wlr_x11_backend_create(struct wl_event_loop *loop, const char *x11_display); /** diff --git a/include/wlr/config.h.in b/include/wlr/config.h.in index 6a53d21..e03049d 100644 --- a/include/wlr/config.h.in +++ b/include/wlr/config.h.in @@ -14,4 +14,6 @@ #mesondefine WLR_HAS_SESSION +#mesondefine WLR_HAS_COLOR_MANAGEMENT + #endif diff --git a/include/wlr/interfaces/wlr_output.h b/include/wlr/interfaces/wlr_output.h index a513129..2a40a0f 100644 --- a/include/wlr/interfaces/wlr_output.h +++ b/include/wlr/interfaces/wlr_output.h @@ -25,6 +25,10 @@ WLR_OUTPUT_STATE_SUBPIXEL | \ WLR_OUTPUT_STATE_LAYERS) +struct wlr_output_cursor_size { + int width, height; +}; + /** * A backend implementation of struct wlr_output. * @@ -82,10 +86,11 @@ struct wlr_output_impl { const struct wlr_drm_format_set *(*get_cursor_formats)( struct wlr_output *output, uint32_t buffer_caps); /** - * Get the size suitable for the cursor buffer. Attempts to use a different - * size for the cursor may fail. + * Get the list of sizes suitable for the cursor buffer. Attempts to use a + * different size for the cursor may fail. */ - void (*get_cursor_size)(struct wlr_output *output, int *width, int *height); + const struct wlr_output_cursor_size *(*get_cursor_sizes)(struct wlr_output *output, + size_t *len); /** * Get the list of DRM formats suitable for the primary buffer, * assuming a buffer with the specified capabilities. @@ -101,7 +106,7 @@ struct wlr_output_impl { * Initialize a new output. */ void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend, - const struct wlr_output_impl *impl, struct wl_display *display, + const struct wlr_output_impl *impl, struct wl_event_loop *event_loop, const struct wlr_output_state *state); /** * Notify compositors that they need to submit a new frame in order to apply diff --git a/include/wlr/meson.build b/include/wlr/meson.build index f7ca413..5d25a57 100644 --- a/include/wlr/meson.build +++ b/include/wlr/meson.build @@ -22,4 +22,4 @@ ver_h = configure_file( configuration: version_data, ) -install_headers(conf_h, ver_h, subdir: 'wlr') +install_headers(conf_h, ver_h, subdir: versioned_name / 'wlr') diff --git a/include/wlr/render/allocator.h b/include/wlr/render/allocator.h index f5bb752..8715919 100644 --- a/include/wlr/render/allocator.h +++ b/include/wlr/render/allocator.h @@ -25,6 +25,16 @@ struct wlr_allocator_interface { void wlr_allocator_init(struct wlr_allocator *alloc, const struct wlr_allocator_interface *impl, uint32_t buffer_caps); +/** + * An allocator is responsible for allocating memory for pixel buffers. + * + * Each allocator may return buffers with different capabilities (shared + * memory, DMA-BUF, memory mapping, etc), placement (main memory, VRAM on a + * GPU, etc) and properties (possible usage, access performance, etc). See + * struct wlr_buffer. + * + * An allocator can be passed to a struct wlr_swapchain for multiple buffering. + */ struct wlr_allocator { const struct wlr_allocator_interface *impl; diff --git a/include/wlr/render/color.h b/include/wlr/render/color.h new file mode 100644 index 0000000..ddd4408 --- /dev/null +++ b/include/wlr/render/color.h @@ -0,0 +1,55 @@ +/* + * 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_RENDER_COLOR_H +#define WLR_RENDER_COLOR_H + +#include +#include + +/** + * A color transformation formula, which maps a linear color space with + * sRGB primaries to an output color space. + * + * For ease of use, this type is heap allocated and reference counted. + * Use wlr_color_transform_ref()/wlr_color_transform_unref(). The initial reference + * count after creation is 1. + * + * Color transforms are immutable; their type/parameters should not be changed, + * and this API provides no functions to modify them after creation. + * + * This formula may be implemented using a 3d look-up table, or some other + * means. + */ +struct wlr_color_transform; + +/** + * Initialize a color transformation to convert linear + * (with sRGB(?) primaries) to an ICC profile. Returns NULL on failure. + */ +struct wlr_color_transform *wlr_color_transform_init_linear_to_icc( + const void *data, size_t size); + +/** + * Initialize a color transformation to apply sRGB encoding. + * Returns NULL on failure. + */ +struct wlr_color_transform *wlr_color_transform_init_srgb(void); + +/** + * Increase the reference count of the color transform by 1. + */ +void wlr_color_transform_ref(struct wlr_color_transform *tr); + +/** + * Reduce the reference count of the color transform by 1; freeing it and + * all associated resources when the reference count hits zero. + */ +void wlr_color_transform_unref(struct wlr_color_transform *tr); + +#endif diff --git a/include/wlr/render/drm_syncobj.h b/include/wlr/render/drm_syncobj.h new file mode 100644 index 0000000..be3dce2 --- /dev/null +++ b/include/wlr/render/drm_syncobj.h @@ -0,0 +1,112 @@ +#ifndef WLR_RENDER_DRM_SYNCOBJ_H +#define WLR_RENDER_DRM_SYNCOBJ_H + +#include +#include +#include + +/** + * A synchronization timeline. + * + * Timelines are used to synchronize accesses to buffers. Given a producer + * (writing contents to a buffer) and a consumer (reading from the buffer), the + * compositor needs to synchronize back-and-forth between these two users. The + * consumer needs to wait for the producer to signal that they're done with the + * writes, and the producer needs to wait for the consumer to signal that + * they're done with the reads. + * + * Timelines provide synchronization points in the form of monotonically + * increasing 64-bit integer values. + * + * wlroots timelines are designed after Vulkan timeline semaphores. For more + * information on the Vulkan APIs, see: + * https://www.khronos.org/blog/vulkan-timeline-semaphores + * + * wlroots timelines are powered by DRM synchronization objects (drm_syncobj): + * https://dri.freedesktop.org/docs/drm/gpu/drm-mm.html#drm-sync-objects + */ +struct wlr_drm_syncobj_timeline { + int drm_fd; + uint32_t handle; + + // private state + + size_t n_refs; +}; + +struct wlr_drm_syncobj_timeline_waiter { + struct { + struct wl_signal ready; + } events; + + // private state + + int ev_fd; + struct wl_event_source *event_source; +}; + +/** + * Create a new synchronization timeline. + */ +struct wlr_drm_syncobj_timeline *wlr_drm_syncobj_timeline_create(int drm_fd); +/** + * Import a timeline from a drm_syncobj FD. + */ +struct wlr_drm_syncobj_timeline *wlr_drm_syncobj_timeline_import(int drm_fd, + int drm_syncobj_fd); +/** + * Reference a synchronization timeline. + */ +struct wlr_drm_syncobj_timeline *wlr_drm_syncobj_timeline_ref(struct wlr_drm_syncobj_timeline *timeline); +/** + * Unreference a synchronization timeline. + */ +void wlr_drm_syncobj_timeline_unref(struct wlr_drm_syncobj_timeline *timeline); +/** + * Check if a timeline point has been signalled or has materialized. + * + * Flags can be: + * + * - DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT to wait for the point to be + * signalled + * - DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE to only wait for a fence to + * materialize + */ +bool wlr_drm_syncobj_timeline_check(struct wlr_drm_syncobj_timeline *timeline, + uint64_t point, uint32_t flags, bool *result); +/** + * Asynchronously wait for a timeline point. + * + * See wlr_drm_syncobj_timeline_check() for a definition of flags. + */ +bool wlr_drm_syncobj_timeline_waiter_init(struct wlr_drm_syncobj_timeline_waiter *waiter, + struct wlr_drm_syncobj_timeline *timeline, uint64_t point, uint32_t flags, + struct wl_event_loop *loop); +/** + * Cancel a timeline waiter. + */ +void wlr_drm_syncobj_timeline_waiter_finish(struct wlr_drm_syncobj_timeline_waiter *waiter); +/** + * Export a timeline point as a sync_file FD. + * + * The returned sync_file will be signalled when the provided point is + * signalled on the timeline. + * + * This allows inter-operation with other APIs which don't support drm_syncobj + * yet. The synchronization point needs to have already materialized: + * wait-before-signal is not supported. + */ +int wlr_drm_syncobj_timeline_export_sync_file(struct wlr_drm_syncobj_timeline *timeline, + uint64_t src_point); +/** + * Import a timeline point from a sync_file FD. + * + * The provided timeline point will be signalled when the provided sync_file is. + * + * This allows inter-operation with other APIs which don't support drm_syncobj + * yet. + */ +bool wlr_drm_syncobj_timeline_import_sync_file(struct wlr_drm_syncobj_timeline *timeline, + uint64_t dst_point, int sync_file_fd); + +#endif diff --git a/include/wlr/render/gles2.h b/include/wlr/render/gles2.h index daf6aab..454e7eb 100644 --- a/include/wlr/render/gles2.h +++ b/include/wlr/render/gles2.h @@ -10,20 +10,30 @@ #define WLR_RENDER_GLES2_H #include + #include struct wlr_egl; +/** + * OpenGL ES 2 renderer. + * + * Care must be taken to avoid stepping each other's toes with EGL contexts: + * the current EGL is global state. The GLES2 renderer operations will save + * and restore any previous EGL context when called. A render pass is seen as + * a single operation. + * + * The GLES2 renderer doesn't support arbitrarily nested render passes. It + * supports a subset only: after a nested render pass is created, any parent + * render pass can't be used before the nested render pass is submitted. + */ + struct wlr_renderer *wlr_gles2_renderer_create_with_drm_fd(int drm_fd); struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl); struct wlr_egl *wlr_gles2_renderer_get_egl(struct wlr_renderer *renderer); -bool wlr_gles2_renderer_check_ext(struct wlr_renderer *renderer, - const char *ext); -/** - * Returns the OpenGL FBO of current buffer. - */ -GLuint wlr_gles2_renderer_get_current_fbo(struct wlr_renderer *wlr_renderer); +bool wlr_gles2_renderer_check_ext(struct wlr_renderer *renderer, const char *ext); +GLuint wlr_gles2_renderer_get_buffer_fbo(struct wlr_renderer *renderer, struct wlr_buffer *buffer); struct wlr_gles2_texture_attribs { GLenum target; /* either GL_TEXTURE_2D or GL_TEXTURE_EXTERNAL_OES */ diff --git a/include/wlr/render/interface.h b/include/wlr/render/interface.h index 06f4083..89f6de9 100644 --- a/include/wlr/render/interface.h +++ b/include/wlr/render/interface.h @@ -20,32 +20,12 @@ struct wlr_box; struct wlr_fbox; struct wlr_renderer_impl { - bool (*bind_buffer)(struct wlr_renderer *renderer, - struct wlr_buffer *buffer); - bool (*begin)(struct wlr_renderer *renderer, uint32_t width, - uint32_t height); - void (*end)(struct wlr_renderer *renderer); - void (*clear)(struct wlr_renderer *renderer, const float color[static 4]); - void (*scissor)(struct wlr_renderer *renderer, struct wlr_box *box); - bool (*render_subtexture_with_matrix)(struct wlr_renderer *renderer, - struct wlr_texture *texture, const struct wlr_fbox *box, - const float matrix[static 9], float alpha); - void (*render_quad_with_matrix)(struct wlr_renderer *renderer, - const float color[static 4], const float matrix[static 9]); - const uint32_t *(*get_shm_texture_formats)( - struct wlr_renderer *renderer, size_t *len); - const struct wlr_drm_format_set *(*get_dmabuf_texture_formats)( - struct wlr_renderer *renderer); + const struct wlr_drm_format_set *(*get_texture_formats)( + struct wlr_renderer *renderer, uint32_t buffer_caps); const struct wlr_drm_format_set *(*get_render_formats)( struct wlr_renderer *renderer); - uint32_t (*preferred_read_format)(struct wlr_renderer *renderer); - bool (*read_pixels)(struct wlr_renderer *renderer, uint32_t fmt, - uint32_t stride, uint32_t width, uint32_t height, - uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y, - void *data); void (*destroy)(struct wlr_renderer *renderer); int (*get_drm_fd)(struct wlr_renderer *renderer); - uint32_t (*get_render_buffer_caps)(struct wlr_renderer *renderer); struct wlr_texture *(*texture_from_buffer)(struct wlr_renderer *renderer, struct wlr_buffer *buffer); struct wlr_render_pass *(*begin_buffer_pass)(struct wlr_renderer *renderer, @@ -54,11 +34,14 @@ struct wlr_renderer_impl { }; void wlr_renderer_init(struct wlr_renderer *renderer, - const struct wlr_renderer_impl *impl); + const struct wlr_renderer_impl *impl, uint32_t render_buffer_caps); struct wlr_texture_impl { bool (*update_from_buffer)(struct wlr_texture *texture, struct wlr_buffer *buffer, const pixman_region32_t *damage); + bool (*read_pixels)(struct wlr_texture *texture, + const struct wlr_texture_read_pixels_options *options); + uint32_t (*preferred_read_format)(struct wlr_texture *texture); void (*destroy)(struct wlr_texture *texture); }; @@ -98,4 +81,10 @@ float wlr_render_texture_options_get_alpha(const struct wlr_render_texture_optio void wlr_render_rect_options_get_box(const struct wlr_render_rect_options *options, const struct wlr_buffer *buffer, struct wlr_box *box); +void wlr_texture_read_pixels_options_get_src_box( + const struct wlr_texture_read_pixels_options *options, + const struct wlr_texture *texture, struct wlr_box *box); +void *wlr_texture_read_pixel_options_get_data( + const struct wlr_texture_read_pixels_options *options); + #endif diff --git a/include/wlr/render/pass.h b/include/wlr/render/pass.h index d1b3eb1..66480f7 100644 --- a/include/wlr/render/pass.h +++ b/include/wlr/render/pass.h @@ -30,6 +30,9 @@ struct wlr_render_timer; struct wlr_buffer_pass_options { /* Timer to measure the duration of the render pass */ struct wlr_render_timer *timer; + /* Color transform to apply to the output of the render pass, + * leave NULL to indicate sRGB/no custom transform */ + struct wlr_color_transform *color_transform; }; /** diff --git a/include/wlr/render/pixman.h b/include/wlr/render/pixman.h index 7e19fa4..206dd78 100644 --- a/include/wlr/render/pixman.h +++ b/include/wlr/render/pixman.h @@ -13,14 +13,12 @@ #include struct wlr_renderer *wlr_pixman_renderer_create(void); -/** - * Returns the image of current buffer. - */ -pixman_image_t *wlr_pixman_renderer_get_current_image( - struct wlr_renderer *wlr_renderer); bool wlr_renderer_is_pixman(struct wlr_renderer *wlr_renderer); bool wlr_texture_is_pixman(struct wlr_texture *texture); + +pixman_image_t *wlr_pixman_renderer_get_buffer_image( + struct wlr_renderer *wlr_renderer, struct wlr_buffer *wlr_buffer); pixman_image_t *wlr_pixman_texture_get_image(struct wlr_texture *wlr_texture); #endif diff --git a/include/wlr/render/swapchain.h b/include/wlr/render/swapchain.h index 8bb8afc..cb3aee9 100644 --- a/include/wlr/render/swapchain.h +++ b/include/wlr/render/swapchain.h @@ -38,6 +38,12 @@ void wlr_swapchain_destroy(struct wlr_swapchain *swapchain); */ struct wlr_buffer *wlr_swapchain_acquire(struct wlr_swapchain *swapchain, int *age); +/** + * Returns true if this buffer has been created by this swapchain, and false + * otherwise. + */ +bool wlr_swapchain_has_buffer(struct wlr_swapchain *swapchain, + struct wlr_buffer *buffer); /** * Mark the buffer as submitted for presentation. This needs to be called by * swap chain users on frame boundaries. diff --git a/include/wlr/render/vulkan.h b/include/wlr/render/vulkan.h index 04c877e..50f8c55 100644 --- a/include/wlr/render/vulkan.h +++ b/include/wlr/render/vulkan.h @@ -24,8 +24,6 @@ VkInstance wlr_vk_renderer_get_instance(struct wlr_renderer *renderer); VkPhysicalDevice wlr_vk_renderer_get_physical_device(struct wlr_renderer *renderer); VkDevice wlr_vk_renderer_get_device(struct wlr_renderer *renderer); uint32_t wlr_vk_renderer_get_queue_family(struct wlr_renderer *renderer); -void wlr_vk_renderer_get_current_image_attribs(struct wlr_renderer *renderer, - struct wlr_vk_image_attribs *attribs); bool wlr_renderer_is_vk(struct wlr_renderer *wlr_renderer); bool wlr_texture_is_vk(struct wlr_texture *texture); diff --git a/include/wlr/render/wlr_renderer.h b/include/wlr/render/wlr_renderer.h index 450d61e..bb9a55f 100644 --- a/include/wlr/render/wlr_renderer.h +++ b/include/wlr/render/wlr_renderer.h @@ -26,6 +26,10 @@ struct wlr_fbox; * A renderer for basic 2D operations. */ struct wlr_renderer { + // Capabilities required for the buffer used as a render target (bitmask of + // enum wlr_buffer_cap) + uint32_t render_buffer_caps; + struct { struct wl_signal destroy; /** @@ -36,12 +40,16 @@ struct wlr_renderer { struct wl_signal lost; } events; + struct { + /** + * Does the renderer support color transforms on its output? + */ + bool output_color_transform; + } features; + // private state const struct wlr_renderer_impl *impl; - - bool rendering; - bool rendering_with_buffer; }; /** @@ -53,81 +61,15 @@ struct wlr_renderer { struct wlr_renderer *wlr_renderer_autocreate(struct wlr_backend *backend); /** - * Start a render pass with the provided viewport. + * Get the formats supporting sampling usage. * - * This should be called after wlr_output_attach_render(). Compositors must call - * wlr_renderer_end() when they are done. - * - * Returns false on failure, in which case compositors shouldn't try rendering. - */ -bool wlr_renderer_begin(struct wlr_renderer *r, uint32_t width, uint32_t height); -/** - * Start a render pass on the provided struct wlr_buffer. + * The buffer capabilities must be passed in. * - * Compositors must call wlr_renderer_end() when they are done. - */ -bool wlr_renderer_begin_with_buffer(struct wlr_renderer *r, - struct wlr_buffer *buffer); -/** - * End a render pass. - */ -void wlr_renderer_end(struct wlr_renderer *r); -/** - * Clear the viewport with the provided color. - */ -void wlr_renderer_clear(struct wlr_renderer *r, const float color[static 4]); -/** - * Defines a scissor box. Only pixels that lie within the scissor box can be - * modified by drawing functions. Providing a NULL `box` disables the scissor - * box. - */ -void wlr_renderer_scissor(struct wlr_renderer *r, struct wlr_box *box); -/** - * Renders the requested texture. - */ -bool wlr_render_texture(struct wlr_renderer *r, struct wlr_texture *texture, - const float projection[static 9], int x, int y, float alpha); -/** - * Renders the requested texture using the provided matrix. - */ -bool wlr_render_texture_with_matrix(struct wlr_renderer *r, - struct wlr_texture *texture, const float matrix[static 9], float alpha); -/** - * Renders the requested texture using the provided matrix, after cropping it - * to the provided rectangle. - */ -bool wlr_render_subtexture_with_matrix(struct wlr_renderer *r, - struct wlr_texture *texture, const struct wlr_fbox *box, - const float matrix[static 9], float alpha); -/** - * Renders a solid rectangle in the specified color. - */ -void wlr_render_rect(struct wlr_renderer *r, const struct wlr_box *box, - const float color[static 4], const float projection[static 9]); -/** - * Renders a solid quadrangle in the specified color with the specified matrix. - */ -void wlr_render_quad_with_matrix(struct wlr_renderer *r, - const float color[static 4], const float matrix[static 9]); -/** - * Get the shared-memory formats supporting import usage. Buffers allocated - * with a format from this list may be imported via wlr_texture_from_pixels(). - */ -const uint32_t *wlr_renderer_get_shm_texture_formats( - struct wlr_renderer *r, size_t *len); -/** - * Get the DMA-BUF formats supporting sampling usage. Buffers allocated with - * a format from this list may be imported via wlr_texture_from_dmabuf(). - */ -const struct wlr_drm_format_set *wlr_renderer_get_dmabuf_texture_formats( - struct wlr_renderer *renderer); -/** - * Reads out of pixels of the currently bound surface into data. `stride` is in - * bytes. + * Buffers allocated with a format from this list may be passed to + * wlr_texture_from_buffer(). */ -bool wlr_renderer_read_pixels(struct wlr_renderer *r, uint32_t fmt, - uint32_t stride, uint32_t width, uint32_t height, - uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y, void *data); +const struct wlr_drm_format_set *wlr_renderer_get_texture_formats( + struct wlr_renderer *r, uint32_t buffer_caps); /** * Initializes wl_shm, linux-dmabuf and other buffer factory protocols. diff --git a/include/wlr/render/wlr_texture.h b/include/wlr/render/wlr_texture.h index 87ed993..1e352c6 100644 --- a/include/wlr/render/wlr_texture.h +++ b/include/wlr/render/wlr_texture.h @@ -13,6 +13,7 @@ #include #include #include +#include struct wlr_buffer; struct wlr_renderer; @@ -25,6 +26,24 @@ struct wlr_texture { struct wlr_renderer *renderer; }; +struct wlr_texture_read_pixels_options { + /** Memory location to read pixels into */ + void *data; + /** Format used for writing the pixel data */ + uint32_t format; + /** Stride in bytes for the data */ + uint32_t stride; + /** Destination offsets */ + uint32_t dst_x, dst_y; + /** Source box of the texture to read from. If empty, the full texture is assumed. */ + const struct wlr_box src_box; +}; + +bool wlr_texture_read_pixels(struct wlr_texture *texture, + const struct wlr_texture_read_pixels_options *options); + +uint32_t wlr_texture_preferred_read_format(struct wlr_texture *texture); + /** * Create a new texture from raw pixel data. `stride` is in bytes. The returned * texture is mutable. diff --git a/include/wlr/types/wlr_alpha_modifier_v1.h b/include/wlr/types/wlr_alpha_modifier_v1.h new file mode 100644 index 0000000..e400f4e --- /dev/null +++ b/include/wlr/types/wlr_alpha_modifier_v1.h @@ -0,0 +1,33 @@ +/* + * 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_ALPHA_MODIFIER_V1_H +#define WLR_TYPES_WLR_ALPHA_MODIFIER_V1_H + +#include + +struct wlr_surface; + +struct wlr_alpha_modifier_surface_v1_state { + double multiplier; // between 0 and 1 +}; + +struct wlr_alpha_modifier_v1 { + struct wl_global *global; + + // private state + + struct wl_listener display_destroy; +}; + +struct wlr_alpha_modifier_v1 *wlr_alpha_modifier_v1_create(struct wl_display *display); + +const struct wlr_alpha_modifier_surface_v1_state *wlr_alpha_modifier_v1_get_surface_state( + struct wlr_surface *surface); + +#endif diff --git a/include/wlr/types/wlr_buffer.h b/include/wlr/types/wlr_buffer.h index de3aeec..9a655dd 100644 --- a/include/wlr/types/wlr_buffer.h +++ b/include/wlr/types/wlr_buffer.h @@ -151,6 +151,7 @@ struct wlr_client_buffer { // private state struct wl_listener source_destroy; + struct wl_listener renderer_destroy; size_t n_ignore_locks; }; diff --git a/include/wlr/types/wlr_compositor.h b/include/wlr/types/wlr_compositor.h index 53e663f..fa9e354 100644 --- a/include/wlr/types/wlr_compositor.h +++ b/include/wlr/types/wlr_compositor.h @@ -69,6 +69,9 @@ struct wlr_surface_state { // Number of locks that prevent this surface state from being committed. size_t cached_state_locks; struct wl_list cached_state_link; // wlr_surface.cached + + // Sync'ed object states, one per struct wlr_surface_synced + struct wl_array synced; // void * }; struct wlr_surface_role { @@ -78,6 +81,15 @@ struct wlr_surface_role { * For example, this applies to cursor surfaces. */ bool no_object; + /** + * Called when the client sends the wl_surface.commit request. May be NULL. + * Typically used to check that the pending state is valid, and send + * protocol errors if not. + * + * If the role is represented by an object, this is only called if + * such object exists. + */ + void (*client_commit)(struct wlr_surface *surface); /** * Called when a new surface state is committed. May be NULL. * @@ -109,7 +121,7 @@ struct wlr_surface_output { struct wlr_surface { struct wl_resource *resource; - struct wlr_renderer *renderer; // may be NULL + struct wlr_compositor *compositor; /** * The surface's buffer, if any. A surface has an attached buffer when it * commits with a non-null buffer in its pending state. A surface will not @@ -129,11 +141,6 @@ struct wlr_surface { * positions need to be damaged. */ pixman_region32_t buffer_damage; - /** - * The last commit's damage caused by surface and its subsurfaces' - * movement, in surface-local coordinates. - */ - pixman_region32_t external_damage; /** * The current opaque region, in surface-local coordinates. It is clipped to * the surface bounds. If the surface's buffer is using a fully opaque @@ -174,7 +181,6 @@ struct wlr_surface { struct { struct wl_signal client_commit; - struct wl_signal precommit; // const struct wlr_surface_state * struct wl_signal commit; /** @@ -190,7 +196,12 @@ struct wlr_surface { */ struct wl_signal unmap; - struct wl_signal new_subsurface; + /** + * Note: unlike other new_* signals, new_subsurface is emitted when + * the subsurface is added to the parent surface's current state, + * not when the object is created. + */ + struct wl_signal new_subsurface; // struct wlr_subsurface struct wl_signal destroy; } events; @@ -201,7 +212,6 @@ struct wlr_surface { // private state - struct wl_listener renderer_destroy; struct wl_listener role_resource_destroy; struct { @@ -214,11 +224,19 @@ struct wlr_surface { bool unmap_commit; bool opaque; - bool has_buffer; + + bool handling_commit; + bool pending_rejected; int32_t preferred_buffer_scale; bool preferred_buffer_transform_sent; enum wl_output_transform preferred_buffer_transform; + + struct wl_list synced; // wlr_surface_synced.link + size_t synced_len; + + struct wl_resource *pending_buffer_resource; + struct wl_listener pending_buffer_resource_destroy; }; struct wlr_renderer; @@ -228,6 +246,7 @@ struct wlr_compositor { struct wlr_renderer *renderer; // may be NULL struct wl_listener display_destroy; + struct wl_listener renderer_destroy; struct { struct wl_signal new_surface; @@ -272,14 +291,36 @@ void wlr_surface_map(struct wlr_surface *surface); */ void wlr_surface_unmap(struct wlr_surface *surface); +/** + * Mark the pending state of a surface as rejected due to a protocol violation, + * preventing it from being cached or committed. + * + * This function must only be used while processing a commit request. + */ +void wlr_surface_reject_pending(struct wlr_surface *surface, struct wl_resource *resource, + uint32_t code, const char *msg, ...); + /** * Whether or not this surface currently has an attached buffer. A surface has * an attached buffer when it commits with a non-null buffer in its pending - * state. A surface will not have a buffer if it has never committed one, has - * committed a null buffer, or something went wrong with uploading the buffer. + * state. A surface will not have a buffer if it has never committed one or has + * committed a null buffer. */ bool wlr_surface_has_buffer(struct wlr_surface *surface); +/** + * Check whether this surface state has an attached buffer. + * + * A surface has an attached buffer when the client commits with a non-null + * buffer. A surface will not have a buffer if the client never committed one, + * or committed a null buffer. + * + * Note that wlr_surface_state.buffer may be NULL even if this function returns + * true: the buffer field is reset after commit, to allow the buffer to be + * released to the client. Additionally, the buffer import or upload may fail. + */ +bool wlr_surface_state_has_buffer(const struct wlr_surface_state *state); + /** * Get the texture of the buffer currently attached to this surface. Returns * NULL if no buffer is currently attached or if something went wrong with @@ -288,8 +329,9 @@ bool wlr_surface_has_buffer(struct wlr_surface *surface); struct wlr_texture *wlr_surface_get_texture(struct wlr_surface *surface); /** - * Get the root of the subsurface tree for this surface. Can return NULL if - * a surface in the tree has been destroyed. + * Get the root of the subsurface tree for this surface. + * May return the same surface passed if that surface is the root. + * Never returns NULL. */ struct wlr_surface *wlr_surface_get_root_surface(struct wlr_surface *surface); @@ -358,10 +400,7 @@ void wlr_surface_for_each_surface(struct wlr_surface *surface, wlr_surface_iterator_func_t iterator, void *user_data); /** - * Get the effective surface damage in surface-local coordinate space. Besides - * buffer damage, this includes damage induced by resizing and moving the - * surface and its subsurfaces. The resulting damage is not expected to be - * bounded by the surface itself. + * Get the effective surface damage in surface-local coordinate space. */ void wlr_surface_get_effective_damage(struct wlr_surface *surface, pixman_region32_t *damage); @@ -414,6 +453,68 @@ void wlr_surface_set_preferred_buffer_scale(struct wlr_surface *surface, void wlr_surface_set_preferred_buffer_transform(struct wlr_surface *surface, enum wl_output_transform transform); +/** + * Implementation for struct wlr_surface_synced. + * + * struct wlr_surface takes care of allocating the sync'ed object state. + * + * The only mandatory field is state_size. + */ +struct wlr_surface_synced_impl { + // Size in bytes of the state struct. + size_t state_size; + // Initialize a state. If NULL, this is a no-op. + void (*init_state)(void *state); + // Finish a state. If NULL, this is a no-op. + void (*finish_state)(void *state); + // Move a state. If NULL, memcpy() is used. + void (*move_state)(void *dst, void *src); +}; + +/** + * An object synchronized with a surface. + * + * This is typically used by surface add-ons which integrate with the surface + * commit mechanism. + * + * A sync'ed object maintains state whose lifecycle is managed by + * struct wlr_surface_synced_impl. Clients make requests to mutate the pending + * state, then clients commit the pending state via wl_surface.commit. The + * pending state may become cached, then becomes current when it's applied. + */ +struct wlr_surface_synced { + struct wlr_surface *surface; + const struct wlr_surface_synced_impl *impl; + struct wl_list link; // wlr_surface.synced + size_t index; +}; + +/** + * Initialize a sync'ed object. + * + * pending and current must be pointers to the sync'ed object's state. This + * function will initialize them. + */ +bool wlr_surface_synced_init(struct wlr_surface_synced *synced, + struct wlr_surface *surface, const struct wlr_surface_synced_impl *impl, + void *pending, void *current); +/** + * Finish a sync'ed object. + * + * This must be called before the struct wlr_surface is destroyed. + */ +void wlr_surface_synced_finish(struct wlr_surface_synced *synced); +/** + * Obtain a sync'ed object state. + */ +void *wlr_surface_synced_get_state(struct wlr_surface_synced *synced, + const struct wlr_surface_state *state); + +/** + * Get a Pixman region from a wl_region resource. + */ +const pixman_region32_t *wlr_region_from_resource(struct wl_resource *resource); + /** * Create the wl_compositor global, which can be used by clients to create * surfaces and regions. @@ -424,4 +525,16 @@ void wlr_surface_set_preferred_buffer_transform(struct wlr_surface *surface, struct wlr_compositor *wlr_compositor_create(struct wl_display *display, uint32_t version, struct wlr_renderer *renderer); +/** + * Set the renderer used for creating struct wlr_texture objects from client + * buffers on surface commit. + * + * The renderer may be NULL, in which case no textures are created. + * + * Calling this function does not update existing textures, it only affects + * future surface commits. + */ +void wlr_compositor_set_renderer(struct wlr_compositor *compositor, + struct wlr_renderer *renderer); + #endif diff --git a/include/wlr/types/wlr_cursor.h b/include/wlr/types/wlr_cursor.h index 004be0f..57d6e50 100644 --- a/include/wlr/types/wlr_cursor.h +++ b/include/wlr/types/wlr_cursor.h @@ -171,7 +171,7 @@ void wlr_cursor_set_surface(struct wlr_cursor *cur, struct wlr_surface *surface, * * - WLR_INPUT_DEVICE_POINTER * - WLR_INPUT_DEVICE_TOUCH - * - WLR_INPUT_DEVICE_TABLET_TOOL + * - WLR_INPUT_DEVICE_TABLET */ void wlr_cursor_attach_input_device(struct wlr_cursor *cur, struct wlr_input_device *dev); diff --git a/include/wlr/types/wlr_cursor_shape_v1.h b/include/wlr/types/wlr_cursor_shape_v1.h index 2fef1e0..55818c8 100644 --- a/include/wlr/types/wlr_cursor_shape_v1.h +++ b/include/wlr/types/wlr_cursor_shape_v1.h @@ -41,6 +41,8 @@ enum wlr_cursor_shape_manager_v1_device_type { struct wlr_cursor_shape_manager_v1_request_set_shape_event { struct wlr_seat_client *seat_client; enum wlr_cursor_shape_manager_v1_device_type device_type; + // NULL if device_type is not TABLET_TOOL + struct wlr_tablet_v2_tablet_tool *tablet_tool; uint32_t serial; enum wp_cursor_shape_device_v1_shape shape; }; diff --git a/include/wlr/types/wlr_damage_ring.h b/include/wlr/types/wlr_damage_ring.h index d503a50..203d919 100644 --- a/include/wlr/types/wlr_damage_ring.h +++ b/include/wlr/types/wlr_damage_ring.h @@ -13,12 +13,22 @@ #include #include #include +#include /* For triple buffering, a history of two frames is required. */ #define WLR_DAMAGE_RING_PREVIOUS_LEN 2 struct wlr_box; +struct wlr_damage_ring_buffer { + struct wlr_buffer *buffer; + struct wl_listener destroy; + pixman_region32_t damage; + + struct wlr_damage_ring *ring; + struct wl_list link; // wlr_damage_ring.buffers +}; + struct wlr_damage_ring { int32_t width, height; @@ -29,6 +39,8 @@ struct wlr_damage_ring { pixman_region32_t previous[WLR_DAMAGE_RING_PREVIOUS_LEN]; size_t previous_idx; + + struct wl_list buffers; // wlr_damage_ring_buffer.link }; void wlr_damage_ring_init(struct wlr_damage_ring *ring); @@ -81,4 +93,17 @@ void wlr_damage_ring_rotate(struct wlr_damage_ring *ring); void wlr_damage_ring_get_buffer_damage(struct wlr_damage_ring *ring, int buffer_age, pixman_region32_t *damage); +/** + * Get accumulated buffer damage and rotate the damage ring. + * + * The accumulated buffer damage is the difference between the to-be-painted + * buffer and the passed-in buffer. In other words, this is the region that + * needs to be redrawn. + * + * Users should damage the ring if an error occurs while rendering or + * submitting the new buffer to the backend. + */ +void wlr_damage_ring_rotate_buffer(struct wlr_damage_ring *ring, + struct wlr_buffer *buffer, pixman_region32_t *damage); + #endif diff --git a/include/wlr/types/wlr_drm.h b/include/wlr/types/wlr_drm.h index d28c7b8..b2a5ce5 100644 --- a/include/wlr/types/wlr_drm.h +++ b/include/wlr/types/wlr_drm.h @@ -29,6 +29,9 @@ struct wlr_drm_buffer { * * It only implements the minimum necessary for modern clients to behave * properly. In particular, flink handles are left unimplemented. + * + * Deprecated: this protocol is legacy and superseded by linux-dmabuf. The + * implementation will be dropped in a future wlroots version. */ struct wlr_drm { struct wl_global *global; diff --git a/include/wlr/types/wlr_ext_foreign_toplevel_list_v1.h b/include/wlr/types/wlr_ext_foreign_toplevel_list_v1.h new file mode 100644 index 0000000..a5ba9d3 --- /dev/null +++ b/include/wlr/types/wlr_ext_foreign_toplevel_list_v1.h @@ -0,0 +1,67 @@ +/* + * 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_FOREIGN_TOPLEVEL_LIST_V1_H +#define WLR_TYPES_WLR_FOREIGN_TOPLEVEL_LIST_V1_H + +#include + +struct wlr_ext_foreign_toplevel_list_v1 { + struct wl_global *global; + struct wl_list resources; // wl_resource_get_link() + struct wl_list toplevels; // ext_foreign_toplevel_handle_v1.link + + struct wl_listener display_destroy; + + struct { + struct wl_signal destroy; + } events; + + void *data; +}; + +struct wlr_ext_foreign_toplevel_handle_v1 { + struct wlr_ext_foreign_toplevel_list_v1 *list; + struct wl_list resources; // wl_resource_get_link() + struct wl_list link; // wlr_ext_foreign_toplevel_list_v1.toplevels + + char *title; + char *app_id; + char *identifier; + + struct { + struct wl_signal destroy; + } events; + + void *data; +}; + +struct wlr_ext_foreign_toplevel_handle_v1_state { + const char *title; + const char *app_id; +}; + +struct wlr_ext_foreign_toplevel_list_v1 *wlr_ext_foreign_toplevel_list_v1_create( + struct wl_display *display, uint32_t version); + +struct wlr_ext_foreign_toplevel_handle_v1 *wlr_ext_foreign_toplevel_handle_v1_create( + struct wlr_ext_foreign_toplevel_list_v1 *list, + const struct wlr_ext_foreign_toplevel_handle_v1_state *state); + +/** + * Destroy the given toplevel handle, sending the closed event to any + * client. + */ +void wlr_ext_foreign_toplevel_handle_v1_destroy( + struct wlr_ext_foreign_toplevel_handle_v1 *toplevel); + +void wlr_ext_foreign_toplevel_handle_v1_update_state( + struct wlr_ext_foreign_toplevel_handle_v1 *toplevel, + const struct wlr_ext_foreign_toplevel_handle_v1_state *state); + +#endif diff --git a/include/wlr/types/wlr_fullscreen_shell_v1.h b/include/wlr/types/wlr_fullscreen_shell_v1.h index 4e5b2cf..54e4c05 100644 --- a/include/wlr/types/wlr_fullscreen_shell_v1.h +++ b/include/wlr/types/wlr_fullscreen_shell_v1.h @@ -1,7 +1,8 @@ /* - * This an unstable interface of wlroots. No guarantees are made regarding the - * future consistency of this API. + * This is a deprecated interface of wlroots. It will be removed in a future + * version. */ + #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif diff --git a/include/wlr/types/wlr_idle_notify_v1.h b/include/wlr/types/wlr_idle_notify_v1.h index 7bc11cb..8dea080 100644 --- a/include/wlr/types/wlr_idle_notify_v1.h +++ b/include/wlr/types/wlr_idle_notify_v1.h @@ -16,7 +16,17 @@ struct wlr_seat; /** * An idle notifier, implementing the ext-idle-notify-v1 protocol. */ -struct wlr_idle_notifier_v1; +struct wlr_idle_notifier_v1 { + struct wl_global *global; + + // private state + + bool inhibited; + struct wl_list notifications; // wlr_idle_notification_v1.link + + struct wl_listener display_destroy; +}; + /** * Create the ext_idle_notifier_v1 global. diff --git a/include/wlr/types/wlr_input_device.h b/include/wlr/types/wlr_input_device.h index 0bbf517..7f78cf2 100644 --- a/include/wlr/types/wlr_input_device.h +++ b/include/wlr/types/wlr_input_device.h @@ -16,19 +16,30 @@ enum wlr_button_state { WLR_BUTTON_PRESSED, }; +/** + * Type of an input device. + */ enum wlr_input_device_type { - WLR_INPUT_DEVICE_KEYBOARD, - WLR_INPUT_DEVICE_POINTER, - WLR_INPUT_DEVICE_TOUCH, - WLR_INPUT_DEVICE_TABLET_TOOL, - WLR_INPUT_DEVICE_TABLET_PAD, - WLR_INPUT_DEVICE_SWITCH, + WLR_INPUT_DEVICE_KEYBOARD, // struct wlr_keyboard + WLR_INPUT_DEVICE_POINTER, // struct wlr_pointer + WLR_INPUT_DEVICE_TOUCH, // struct wlr_touch + WLR_INPUT_DEVICE_TABLET, // struct wlr_tablet + WLR_INPUT_DEVICE_TABLET_PAD, // struct wlr_tablet_pad + WLR_INPUT_DEVICE_SWITCH, // struct wlr_switch }; +/** + * An input device. + * + * Depending on its type, the input device can be converted to a more specific + * type. See the various wlr_*_from_input_device() functions. + * + * Input devices are typically advertised by the new_input event in + * struct wlr_backend. + */ struct wlr_input_device { enum wlr_input_device_type type; - unsigned int vendor, product; - char *name; + char *name; // may be NULL struct { struct wl_signal destroy; diff --git a/include/wlr/types/wlr_input_inhibitor.h b/include/wlr/types/wlr_input_inhibitor.h deleted file mode 100644 index 49bc3ac..0000000 --- a/include/wlr/types/wlr_input_inhibitor.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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_INPUT_INHIBITOR_H -#define WLR_TYPES_INPUT_INHIBITOR_H -#include - -/* - * NOTE: Following the protocol deprecation, wlr/types/wlr_input_inhibitor.h is - * deprecated and will be removed in the next release. - */ - -struct wlr_input_inhibit_manager { - struct wl_global *global; - struct wl_client *active_client; - struct wl_resource *active_inhibitor; - - struct wl_listener display_destroy; - - struct { - struct wl_signal activate; // struct wlr_input_inhibit_manager - struct wl_signal deactivate; // struct wlr_input_inhibit_manager - struct wl_signal destroy; - } events; - - void *data; -}; - -struct wlr_input_inhibit_manager *wlr_input_inhibit_manager_create( - struct wl_display *display); - -#endif diff --git a/include/wlr/types/wlr_keyboard.h b/include/wlr/types/wlr_keyboard.h index abc4edf..9046493 100644 --- a/include/wlr/types/wlr_keyboard.h +++ b/include/wlr/types/wlr_keyboard.h @@ -45,7 +45,7 @@ struct wlr_keyboard_modifiers { xkb_mod_mask_t depressed; xkb_mod_mask_t latched; xkb_mod_mask_t locked; - xkb_mod_mask_t group; + xkb_layout_index_t group; }; struct wlr_keyboard { diff --git a/include/wlr/types/wlr_layer_shell_v1.h b/include/wlr/types/wlr_layer_shell_v1.h index df2107c..eaf6af4 100644 --- a/include/wlr/types/wlr_layer_shell_v1.h +++ b/include/wlr/types/wlr_layer_shell_v1.h @@ -83,7 +83,7 @@ struct wlr_layer_surface_v1 { char *namespace; - bool added, configured; + bool configured; struct wl_list configure_list; struct wlr_layer_surface_v1_state current, pending; @@ -110,6 +110,10 @@ struct wlr_layer_surface_v1 { } events; void *data; + + // private state + + struct wlr_surface_synced synced; }; struct wlr_layer_shell_v1 *wlr_layer_shell_v1_create(struct wl_display *display, diff --git a/include/wlr/types/wlr_linux_dmabuf_v1.h b/include/wlr/types/wlr_linux_dmabuf_v1.h index e39c099..cf967f9 100644 --- a/include/wlr/types/wlr_linux_dmabuf_v1.h +++ b/include/wlr/types/wlr_linux_dmabuf_v1.h @@ -6,8 +6,8 @@ #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif -#ifndef WLR_TYPES_WLR_LINUX_DMABUF_H -#define WLR_TYPES_WLR_LINUX_DMABUF_H +#ifndef WLR_TYPES_WLR_LINUX_DMABUF_V1_H +#define WLR_TYPES_WLR_LINUX_DMABUF_V1_H #include #include @@ -64,10 +64,13 @@ struct wlr_linux_dmabuf_v1 { int main_device_fd; // to sanity check FDs sent by clients, -1 if unavailable struct wl_listener display_destroy; + + bool (*check_dmabuf_callback)(struct wlr_dmabuf_attributes *attribs, void *data); + void *check_dmabuf_callback_data; }; /** - * Create the linux-dmabuf-unstable-v1 global. + * Create the linux-dmabuf-v1 global. * * Compositors using struct wlr_renderer should use * wlr_linux_dmabuf_v1_create_with_renderer() instead. @@ -76,13 +79,22 @@ struct wlr_linux_dmabuf_v1 *wlr_linux_dmabuf_v1_create(struct wl_display *displa uint32_t version, const struct wlr_linux_dmabuf_feedback_v1 *default_feedback); /** - * Create the linux-dmabuf-unstable-v1 global. + * Create the linux-dmabuf-v1 global. * * The default DMA-BUF feedback is initialized from the struct wlr_renderer. */ struct wlr_linux_dmabuf_v1 *wlr_linux_dmabuf_v1_create_with_renderer(struct wl_display *display, uint32_t version, struct wlr_renderer *renderer); +/** + * Set the dmabuf import check callback + * + * This can be used by consumers who want to implement specific dmabuf checks. Useful for + * users such as gamescope who do not use the rendering logic of wlroots. + */ +void wlr_linux_dmabuf_v1_set_check_dmabuf_callback(struct wlr_linux_dmabuf_v1 *linux_dmabuf, + bool (*callback)(struct wlr_dmabuf_attributes *attribs, void *data), void *data); + /** * Set a surface's DMA-BUF feedback. * diff --git a/include/wlr/types/wlr_linux_drm_syncobj_v1.h b/include/wlr/types/wlr_linux_drm_syncobj_v1.h new file mode 100644 index 0000000..c6e0617 --- /dev/null +++ b/include/wlr/types/wlr_linux_drm_syncobj_v1.h @@ -0,0 +1,46 @@ +/* + * 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_LINUX_DRM_SYNCOBJ_V1_H +#define WLR_TYPES_WLR_LINUX_DRM_SYNCOBJ_V1_H + +#include +#include + +struct wlr_linux_drm_syncobj_surface_v1_state { + struct wlr_drm_syncobj_timeline *acquire_timeline; + uint64_t acquire_point; + + struct wlr_drm_syncobj_timeline *release_timeline; + uint64_t release_point; +}; + +struct wlr_linux_drm_syncobj_manager_v1 { + struct wl_global *global; + + // private state + + int drm_fd; + + struct wl_listener display_destroy; +}; + +/** + * Advertise explicit synchronization support to clients. + * + * The compositor must be prepared to handle fences coming from clients and to + * send release fences correctly. In particular, both the renderer and the + * backend need to support explicit synchronization. + */ +struct wlr_linux_drm_syncobj_manager_v1 *wlr_linux_drm_syncobj_manager_v1_create( + struct wl_display *display, uint32_t version, int drm_fd); + +struct wlr_linux_drm_syncobj_surface_v1_state *wlr_linux_drm_syncobj_v1_get_surface_state( + struct wlr_surface *surface); + +#endif diff --git a/include/wlr/types/wlr_matrix.h b/include/wlr/types/wlr_matrix.h index 043cf6d..674cb53 100644 --- a/include/wlr/types/wlr_matrix.h +++ b/include/wlr/types/wlr_matrix.h @@ -1,16 +1,6 @@ /* - * This is a stable interface of wlroots. Future changes will be limited to: - * - * - New functions - * - New struct members - * - New enum members - * - * Note that wlroots does not make an ABI compatibility promise - in the future, - * the layout and size of structs used by wlroots may change, requiring code - * depending on this header to be recompiled (but not edited). - * - * Breaking changes are announced in the release notes and follow a 1-year - * deprecation schedule. + * This is a deprecated interface of wlroots. It will be removed in a future + * version. */ #ifndef WLR_TYPES_WLR_MATRIX_H diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 9de8dff..ff675bd 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -45,6 +45,7 @@ struct wlr_output_cursor { int32_t hotspot_x, hotspot_y; struct wlr_texture *texture; bool own_texture; + struct wl_listener renderer_destroy; struct wl_list link; }; @@ -120,14 +121,13 @@ struct wlr_render_pass; * The `frame` event will be emitted when it is a good time for the compositor * to submit a new frame. * - * To render a new frame, compositors should call wlr_output_attach_render(), - * render and call wlr_output_commit(). No rendering should happen outside a - * `frame` event handler or before wlr_output_attach_render(). + * To render a new frame, compositors should call wlr_output_begin_render_pass(), + * perform rendering on that render pass and finally call wlr_output_commit(). */ struct wlr_output { const struct wlr_output_impl *impl; struct wlr_backend *backend; - struct wl_display *display; + struct wl_event_loop *event_loop; struct wl_global *global; struct wl_list resources; @@ -150,16 +150,18 @@ struct wlr_output { enum wlr_output_adaptive_sync_status adaptive_sync_status; uint32_t render_format; + // Indicates whether making changes to adaptive sync status is supported. + // If false, changes to adaptive sync status is guaranteed to fail. If + // true, changes may either succeed or fail. + bool adaptive_sync_supported; + bool needs_frame; // damage for cursors and fullscreen surface, in output-local coordinates bool frame_pending; - float transform_matrix[9]; // true for example with VR headsets bool non_desktop; - struct wlr_output_state pending; - // Commit sequence number. Incremented on each commit, may overflow. uint32_t commit_seq; @@ -202,7 +204,6 @@ struct wlr_output { struct wlr_allocator *allocator; struct wlr_renderer *renderer; struct wlr_swapchain *swapchain; - struct wlr_buffer *back_buffer; struct wl_listener display_destroy; @@ -271,22 +272,14 @@ struct wlr_output_event_request_state { struct wlr_surface; -/** - * Enables or disables the output. A disabled output is turned off and doesn't - * emit `frame` events. - * - * Whether an output is enabled is double-buffered state, see - * wlr_output_commit(). - */ -void wlr_output_enable(struct wlr_output *output, bool enable); -void wlr_output_create_global(struct wlr_output *output); +void wlr_output_create_global(struct wlr_output *output, struct wl_display *display); void wlr_output_destroy_global(struct wlr_output *output); /** * Initialize the output's rendering subsystem with the provided allocator and * renderer. After initialization, this function may invoked again to reinitialize * the allocator and renderer to different values. * - * Call this function prior to any call to wlr_output_attach_render(), + * Call this function prior to any call to wlr_output_begin_render_pass(), * wlr_output_commit() or wlr_output_cursor_create(). * * The buffer capabilities of the provided must match the capabilities of the @@ -299,72 +292,6 @@ bool wlr_output_init_render(struct wlr_output *output, * modes, returns NULL. */ struct wlr_output_mode *wlr_output_preferred_mode(struct wlr_output *output); -/** - * Sets the output mode. The output needs to be enabled. - * - * Mode is double-buffered state, see wlr_output_commit(). - */ -void wlr_output_set_mode(struct wlr_output *output, - struct wlr_output_mode *mode); -/** - * Sets a custom mode on the output. - * - * When the output advertises fixed modes, custom modes are not guaranteed to - * work correctly, they may result in visual artifacts. If a suitable fixed mode - * is available, compositors should prefer it and use wlr_output_set_mode() - * instead of custom modes. - * - * Setting `refresh` to zero lets the backend pick a preferred value. The - * output needs to be enabled. - * - * Custom mode is double-buffered state, see wlr_output_commit(). - */ -void wlr_output_set_custom_mode(struct wlr_output *output, int32_t width, - int32_t height, int32_t refresh); -/** - * Sets a transform for the output. - * - * Transform is double-buffered state, see wlr_output_commit(). - */ -void wlr_output_set_transform(struct wlr_output *output, - enum wl_output_transform transform); -/** - * Enables or disables adaptive sync (ie. variable refresh rate) on this - * output. On some backends, this is just a hint and may be ignored. - * Compositors can inspect `wlr_output.adaptive_sync_status` to query the - * effective status. Backends that don't support adaptive sync will reject - * the output commit. - * - * When enabled, compositors can submit frames a little bit later than the - * deadline without dropping a frame. - * - * Adaptive sync is double-buffered state, see wlr_output_commit(). - */ -void wlr_output_enable_adaptive_sync(struct wlr_output *output, bool enabled); -/** - * Set the output buffer render format. Default value: DRM_FORMAT_XRGB8888 - * - * While high bit depth render formats are necessary for a monitor to receive - * useful high bit data, they do not guarantee it; a DRM_FORMAT_XBGR2101010 - * buffer will only lead to sending 10-bpc image data to the monitor if - * hardware and software permit this. - * - * This only affects the format of the output buffer used when rendering, - * as with wlr_output_attach_render(). It has no impact on the cursor buffer - * format, or on the formats supported for direct scan-out (see also - * wlr_output_attach_buffer()). - * - * This format is double-buffered state, see wlr_output_commit(). - */ -void wlr_output_set_render_format(struct wlr_output *output, uint32_t format); -/** - * Sets a scale for the output. - * - * Scale is double-buffered state, see wlr_output_commit(). - */ -void wlr_output_set_scale(struct wlr_output *output, float scale); -void wlr_output_set_subpixel(struct wlr_output *output, - enum wl_output_subpixel subpixel); /** * Set the output name. * @@ -394,77 +321,6 @@ void wlr_output_transformed_resolution(struct wlr_output *output, */ void wlr_output_effective_resolution(struct wlr_output *output, int *width, int *height); -/** - * Attach the renderer's buffer to the output. Compositors must call this - * function before rendering. After they are done rendering, they should call - * wlr_output_commit() to submit the new frame. The output needs to be - * enabled. - * - * If non-NULL, `buffer_age` is set to the drawing buffer age in number of - * frames or -1 if unknown. This is useful for damage tracking. - * - * If the compositor decides not to render after calling this function, it - * must call wlr_output_rollback(). - */ -bool wlr_output_attach_render(struct wlr_output *output, int *buffer_age); -/** - * Attach a buffer to the output. Compositors should call wlr_output_commit() - * to submit the new frame. The output needs to be enabled. - * - * Not all backends support direct scan-out on all buffers. Compositors can - * check whether a buffer is supported by calling wlr_output_test(). - */ -void wlr_output_attach_buffer(struct wlr_output *output, - struct wlr_buffer *buffer); -/** - * Get the preferred format for reading pixels. - * This function might change the current rendering context. - */ -uint32_t wlr_output_preferred_read_format(struct wlr_output *output); -/** - * Set the damage region for the frame to be submitted. This is the region of - * the screen that has changed since the last frame. - * - * Compositors implementing damage tracking should call this function with the - * damaged region in output-buffer-local coordinates. - * - * This region is not to be confused with the renderer's buffer damage, ie. the - * region compositors need to repaint. Compositors usually need to repaint more - * than what changed since last frame since multiple render buffers are used. - */ -void wlr_output_set_damage(struct wlr_output *output, - const pixman_region32_t *damage); -/** - * Set the output layers state. - * - * See struct wlr_output_layer for more details on output layers. - * - * This state is double-buffered, see wlr_output_commit(). The layers array - * must remain valid until the wlr_output_test() or wlr_output_commit() call. - */ -void wlr_output_set_layers(struct wlr_output *output, - struct wlr_output_layer_state *layers, size_t layers_len); -/** - * Test whether the pending output state would be accepted by the backend. If - * this function returns true, wlr_output_commit() can only fail due to a - * runtime error. - * - * This function doesn't mutate the pending state. - */ -bool wlr_output_test(struct wlr_output *output); -/** - * Commit the pending output state. If wlr_output_attach_render() has been - * called, the pending frame will be submitted for display and a `frame` event - * will be scheduled. - * - * On failure, the pending changes are rolled back. - */ -bool wlr_output_commit(struct wlr_output *output); -/** - * Discard the pending output state. - */ -void wlr_output_rollback(struct wlr_output *output); - /** * Test whether this output state would be accepted by the backend. If this * function returns true, wlr_output_commit_state() will only fail due to a @@ -492,17 +348,6 @@ void wlr_output_schedule_frame(struct wlr_output *output); * Returns the maximum length of each gamma ramp, or 0 if unsupported. */ size_t wlr_output_get_gamma_size(struct wlr_output *output); -/** - * Sets the gamma table for this output. `r`, `g` and `b` are gamma ramps for - * red, green and blue. `size` is the length of the ramps and must not exceed - * the value returned by wlr_output_get_gamma_size(). - * - * Providing zero-sized ramps resets the gamma table. - * - * The gamma table is double-buffered state, see wlr_output_commit(). - */ -void wlr_output_set_gamma(struct wlr_output *output, size_t size, - const uint16_t *r, const uint16_t *g, const uint16_t *b); /** * Returns the wlr_output matching the provided wl_output resource. If the * resource isn't a wl_output, it aborts. If the resource is inert (because the @@ -724,24 +569,21 @@ bool wlr_output_configure_primary_swapchain(struct wlr_output *output, /** * Begin a render pass on this output. * - * Same as wlr_output_attach_render(), but returns a struct wlr_render_pass. + * Compositors can call this function to begin rendering. After the render pass + * has been submitted, they should call wlr_output_commit_state() to submit the + * new frame. + * + * On error, NULL is returned. Creating a render pass on a disabled output is + * an error. + * + * The state describes the output changes the rendered frame will be + * committed with. A NULL state indicates no change. + * + * If non-NULL, buffer_age is set to the drawing buffer age in number of + * frames or -1 if unknown. This is useful for damage tracking. */ struct wlr_render_pass *wlr_output_begin_render_pass(struct wlr_output *output, - struct wlr_output_state *state, int *buffer_age, struct wlr_render_timer *timer); - - -/** - * Returns the transform that, when composed with `tr`, gives - * `WL_OUTPUT_TRANSFORM_NORMAL`. - */ -enum wl_output_transform wlr_output_transform_invert( - enum wl_output_transform tr); - -/** - * Returns a transform that, when applied, has the same effect as applying - * sequentially `tr_a` and `tr_b`. - */ -enum wl_output_transform wlr_output_transform_compose( - enum wl_output_transform tr_a, enum wl_output_transform tr_b); + struct wlr_output_state *state, int *buffer_age, + struct wlr_buffer_pass_options *render_options); #endif diff --git a/include/wlr/types/wlr_output_layer.h b/include/wlr/types/wlr_output_layer.h index f4aef05..af843d0 100644 --- a/include/wlr/types/wlr_output_layer.h +++ b/include/wlr/types/wlr_output_layer.h @@ -18,8 +18,8 @@ * An output layer. * * Output layers are displayed between the output primary buffer (see - * wlr_output_attach_buffer() and wlr_output_attach_render()) and the cursor - * buffer. They can offload some rendering work to the backend. + * wlr_output_attach_buffer()) and the cursor buffer. They can offload some + * rendering work to the backend. * * To configure output layers, callers should call wlr_output_layer_create() to * create layers, attach struct wlr_output_layer_state onto diff --git a/include/wlr/types/wlr_output_layout.h b/include/wlr/types/wlr_output_layout.h index 26bb79b..40c7fb9 100644 --- a/include/wlr/types/wlr_output_layout.h +++ b/include/wlr/types/wlr_output_layout.h @@ -26,6 +26,7 @@ struct wlr_box; */ struct wlr_output_layout { struct wl_list outputs; + struct wl_display *display; struct { struct wl_signal add; // struct wlr_output_layout_output @@ -34,6 +35,10 @@ struct wlr_output_layout { } events; void *data; + + // private state + + struct wl_listener display_destroy; }; struct wlr_output_layout_output { @@ -57,7 +62,7 @@ struct wlr_output_layout_output { struct wl_listener commit; }; -struct wlr_output_layout *wlr_output_layout_create(void); +struct wlr_output_layout *wlr_output_layout_create(struct wl_display *display); void wlr_output_layout_destroy(struct wlr_output_layout *layout); diff --git a/include/wlr/types/wlr_output_management_v1.h b/include/wlr/types/wlr_output_management_v1.h index f1cd5ec..57b880d 100644 --- a/include/wlr/types/wlr_output_management_v1.h +++ b/include/wlr/types/wlr_output_management_v1.h @@ -154,4 +154,16 @@ void wlr_output_head_v1_state_apply( const struct wlr_output_head_v1_state *head_state, struct wlr_output_state *output_state); +/** + * Build an array of struct wlr_output_state reflecting the new configuration. + * + * The states_len pointer will be populated with the number of elements in the + * array. The caller is responsible for freeing the array. + * + * The returned array can be passed to wlr_backend_test() and + * wlr_backend_commit(). + */ +struct wlr_backend_output_state *wlr_output_configuration_v1_build_state( + const struct wlr_output_configuration_v1 *config, size_t *states_len); + #endif diff --git a/include/wlr/types/wlr_output_swapchain_manager.h b/include/wlr/types/wlr_output_swapchain_manager.h new file mode 100644 index 0000000..24a5578 --- /dev/null +++ b/include/wlr/types/wlr_output_swapchain_manager.h @@ -0,0 +1,90 @@ +/* + * 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_OUTPUT_SWAPCHAIN_MANAGER_H +#define WLR_TYPES_WLR_OUTPUT_SWAPCHAIN_MANAGER_H + +#include + +struct wlr_backend; +struct wlr_backend_output_state; + +/** + * Helper to allocate swapchains for mode-setting. + * + * Compositors are expected to call wlr_output_swapchain_manager_init(), then + * pass the new desired output states to wlr_output_swapchain_manager_prepare(). + * Compositors may retry that step with different desired output states until + * they find a working configuration. Then, compositors should use + * wlr_output_swapchain_manager_get_swapchain() to get pending swapchains for + * outputs, render onto a new buffer acquired from the swapchain, and call + * wlr_backend_commit(). If that succeeds, wlr_output_swapchain_manager_apply() + * should be called. After compositors are done with the manager, be it after a + * success or failure, they should call wlr_output_swapchain_manager_finish(). + */ +struct wlr_output_swapchain_manager { + struct wlr_backend *backend; + + // private state + + struct wl_array outputs; // struct wlr_output_swapchain_manager_output +}; + +/** + * Initialize the manager. + * + * Compositors should call wlr_output_swapchain_manager_finish() to cleanup the + * manager. + */ +void wlr_output_swapchain_manager_init(struct wlr_output_swapchain_manager *manager, + struct wlr_backend *backend); + +/** + * Prepare a commit for a mode-setting backend commit. + * + * This function allocates (and potentially re-allocates) swapchains suitable + * for the new output configuration. On success, compositors should call + * wlr_output_swapchain_manager_get_swapchain() to get the pending swapchain, + * repaint with a buffer acquired from the swapchain, call wlr_backend_commit() + * and then wlr_output_swapchain_manager_apply(). + * + * Compositors should include all enabled outputs to maximize the chance to + * find a working configuration, even if an output state is unchanged by the + * compositor. This function might re-create swapchains for already-enabled + * outputs. + */ +bool wlr_output_swapchain_manager_prepare(struct wlr_output_swapchain_manager *manager, + const struct wlr_backend_output_state *states, size_t states_len); + +/** + * Get the pending swapchain for an output. + * + * This can only be called after a successful + * wlr_output_swapchain_manager_prepare(), if the output was passed in. + * + * If the output is disabled, NULL is returned. + */ +struct wlr_swapchain *wlr_output_swapchain_manager_get_swapchain( + struct wlr_output_swapchain_manager *manager, struct wlr_output *output); + +/** + * Apply swapchains allocated for the last successful call to + * wlr_output_swapchain_manager_prepare(). + * + * This function swaps output swapchains with new swapchains suitable for the + * new output configuration. It should be called after a successful + * wlr_backend_commit(). + */ +void wlr_output_swapchain_manager_apply(struct wlr_output_swapchain_manager *manager); + +/** + * Cleanup resources allocated by the manager. + */ +void wlr_output_swapchain_manager_finish(struct wlr_output_swapchain_manager *manager); + +#endif diff --git a/include/wlr/types/wlr_pointer.h b/include/wlr/types/wlr_pointer.h index 16aed1d..2fa8513 100644 --- a/include/wlr/types/wlr_pointer.h +++ b/include/wlr/types/wlr_pointer.h @@ -63,19 +63,7 @@ struct wlr_pointer_button_event { struct wlr_pointer *pointer; uint32_t time_msec; uint32_t button; - enum wlr_button_state state; -}; - -enum wlr_axis_source { - WLR_AXIS_SOURCE_WHEEL, - WLR_AXIS_SOURCE_FINGER, - WLR_AXIS_SOURCE_CONTINUOUS, - WLR_AXIS_SOURCE_WHEEL_TILT, -}; - -enum wlr_axis_orientation { - WLR_AXIS_ORIENTATION_VERTICAL, - WLR_AXIS_ORIENTATION_HORIZONTAL, + enum wl_pointer_button_state state; }; #define WLR_POINTER_AXIS_DISCRETE_STEP 120 @@ -83,8 +71,9 @@ enum wlr_axis_orientation { struct wlr_pointer_axis_event { struct wlr_pointer *pointer; uint32_t time_msec; - enum wlr_axis_source source; - enum wlr_axis_orientation orientation; + enum wl_pointer_axis_source source; + enum wl_pointer_axis orientation; + enum wl_pointer_axis_relative_direction relative_direction; double delta; int32_t delta_discrete; }; diff --git a/include/wlr/types/wlr_pointer_constraints_v1.h b/include/wlr/types/wlr_pointer_constraints_v1.h index 21280ff..2b4722f 100644 --- a/include/wlr/types/wlr_pointer_constraints_v1.h +++ b/include/wlr/types/wlr_pointer_constraints_v1.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include "pointer-constraints-unstable-v1-protocol.h" @@ -33,6 +34,7 @@ struct wlr_pointer_constraint_v1_state { // only valid for locked_pointer struct { + bool enabled; double x, y; } cursor_hint; }; @@ -49,10 +51,6 @@ struct wlr_pointer_constraint_v1 { struct wlr_pointer_constraint_v1_state current, pending; - struct wl_listener surface_commit; - struct wl_listener surface_destroy; - struct wl_listener seat_destroy; - struct wl_list link; // wlr_pointer_constraints_v1.constraints struct { @@ -65,6 +63,14 @@ struct wlr_pointer_constraint_v1 { } events; void *data; + + // private state + + struct wl_listener surface_commit; + struct wl_listener surface_destroy; + struct wl_listener seat_destroy; + + struct wlr_surface_synced synced; }; struct wlr_pointer_constraints_v1 { diff --git a/include/wlr/types/wlr_presentation_time.h b/include/wlr/types/wlr_presentation_time.h index 9543cff..b772295 100644 --- a/include/wlr/types/wlr_presentation_time.h +++ b/include/wlr/types/wlr_presentation_time.h @@ -71,7 +71,7 @@ struct wlr_presentation *wlr_presentation_create(struct wl_display *display, * this surface. */ struct wlr_presentation_feedback *wlr_presentation_surface_sampled( - struct wlr_presentation *presentation, struct wlr_surface *surface); + struct wlr_surface *surface); void wlr_presentation_feedback_send_presented( struct wlr_presentation_feedback *feedback, const struct wlr_presentation_event *event); @@ -92,8 +92,7 @@ void wlr_presentation_event_from_output(struct wlr_presentation_event *event, * before a wlr_output_commit() call to indicate that the surface's current * contents have been copied to a buffer which will be displayed on the output. */ -void wlr_presentation_surface_textured_on_output( - struct wlr_presentation *presentation, struct wlr_surface *surface, +void wlr_presentation_surface_textured_on_output(struct wlr_surface *surface, struct wlr_output *output); /** * Mark the current surface's buffer as scanned out on the given output. @@ -101,8 +100,7 @@ void wlr_presentation_surface_textured_on_output( * Same as wlr_presentation_surface_textured_on_output(), but indicates direct * scan-out. */ -void wlr_presentation_surface_scanned_out_on_output( - struct wlr_presentation *presentation, struct wlr_surface *surface, +void wlr_presentation_surface_scanned_out_on_output(struct wlr_surface *surface, struct wlr_output *output); #endif diff --git a/include/wlr/types/wlr_region.h b/include/wlr/types/wlr_region.h index 62ac85c..483ac96 100644 --- a/include/wlr/types/wlr_region.h +++ b/include/wlr/types/wlr_region.h @@ -1,16 +1,6 @@ /* - * This is a stable interface of wlroots. Future changes will be limited to: - * - * - New functions - * - New struct members - * - New enum members - * - * Note that wlroots does not make an ABI compatibility promise - in the future, - * the layout and size of structs used by wlroots may change, requiring code - * depending on this header to be recompiled (but not edited). - * - * Breaking changes are announced in the release notes and follow a 1-year - * deprecation schedule. + * This is a deprecated interface of wlroots. It will be removed in a future + * version. wlr/types/wlr_compositor.h should be used instead. */ #ifndef WLR_TYPES_WLR_REGION_H diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index dc8d6fe..a8fc215 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -99,17 +99,16 @@ struct wlr_scene { struct wl_list outputs; // wlr_scene_output.link // May be NULL - struct wlr_presentation *presentation; struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1; // private state - struct wl_listener presentation_destroy; struct wl_listener linux_dmabuf_v1_destroy; enum wlr_scene_debug_damage_option debug_damage_option; bool direct_scanout; bool calculate_visibility; + bool highlight_transparent_region; }; /** A scene-graph node displaying a single surface. */ @@ -187,6 +186,13 @@ struct wlr_scene_buffer { uint64_t active_outputs; struct wlr_texture *texture; struct wlr_linux_dmabuf_feedback_v1_init_options prev_feedback_options; + + bool own_buffer; + int buffer_width, buffer_height; + bool buffer_is_opaque; + + struct wl_listener buffer_release; + struct wl_listener renderer_destroy; }; /** A viewport for an output in the scene-graph */ @@ -206,6 +212,8 @@ struct wlr_scene_output { // private state + pixman_region32_t pending_commit_damage; + uint8_t index; bool prev_scanout; @@ -301,15 +309,6 @@ struct wlr_scene_node *wlr_scene_node_at(struct wlr_scene_node *node, */ struct wlr_scene *wlr_scene_create(void); -/** - * Handle presentation feedback for all surfaces in the scene, assuming that - * scene outputs and the scene rendering functions are used. - * - * Asserts that a struct wlr_presentation hasn't already been set for the scene. - */ -void wlr_scene_set_presentation(struct wlr_scene *scene, - struct wlr_presentation *presentation); - /** * Handles linux_dmabuf_v1 feedback for all surfaces in the scene. * @@ -471,6 +470,14 @@ void wlr_scene_output_set_position(struct wlr_scene_output *scene_output, struct wlr_scene_output_state_options { struct wlr_scene_timer *timer; + struct wlr_color_transform *color_transform; + + /** + * Allows use of a custom swapchain. This can be useful when trying out an + * output configuration. The swapchain dimensions must match the respective + * wlr_output_state or output size if not specified. + */ + struct wlr_swapchain *swapchain; }; /** @@ -551,7 +558,7 @@ struct wlr_scene_tree *wlr_scene_subsurface_tree_create( * A NULL or empty clip will disable clipping */ void wlr_scene_subsurface_tree_set_clip(struct wlr_scene_node *node, - struct wlr_box *clip); + const struct wlr_box *clip); /** * Add a node displaying an xdg_surface and all of its sub-surfaces to the diff --git a/include/wlr/types/wlr_seat.h b/include/wlr/types/wlr_seat.h index 70cc078..06d8224 100644 --- a/include/wlr/types/wlr_seat.h +++ b/include/wlr/types/wlr_seat.h @@ -98,10 +98,11 @@ struct wlr_pointer_grab_interface { void (*motion)(struct wlr_seat_pointer_grab *grab, uint32_t time_msec, double sx, double sy); uint32_t (*button)(struct wlr_seat_pointer_grab *grab, uint32_t time_msec, - uint32_t button, enum wlr_button_state state); + uint32_t button, enum wl_pointer_button_state state); void (*axis)(struct wlr_seat_pointer_grab *grab, uint32_t time_msec, - enum wlr_axis_orientation orientation, double value, - int32_t value_discrete, enum wlr_axis_source source); + enum wl_pointer_axis orientation, double value, + int32_t value_discrete, enum wl_pointer_axis_source source, + enum wl_pointer_axis_relative_direction relative_direction); void (*frame)(struct wlr_seat_pointer_grab *grab); void (*cancel)(struct wlr_seat_pointer_grab *grab); }; @@ -125,7 +126,7 @@ struct wlr_seat_touch_grab; struct wlr_touch_grab_interface { uint32_t (*down)(struct wlr_seat_touch_grab *grab, uint32_t time_msec, struct wlr_touch_point *point); - void (*up)(struct wlr_seat_touch_grab *grab, uint32_t time_msec, + uint32_t (*up)(struct wlr_seat_touch_grab *grab, uint32_t time_msec, struct wlr_touch_point *point); void (*motion)(struct wlr_seat_touch_grab *grab, uint32_t time_msec, struct wlr_touch_point *point); @@ -136,7 +137,7 @@ struct wlr_touch_grab_interface { void (*cancel)(struct wlr_seat_touch_grab *grab); // Send wl_touch.cancel void (*wl_cancel)(struct wlr_seat_touch_grab *grab, - struct wlr_surface *surface); + struct wlr_seat_client *seat_client); }; /** @@ -181,7 +182,7 @@ struct wlr_seat_pointer_state { struct wlr_seat_pointer_grab *default_grab; bool sent_axis_source; - enum wlr_axis_source cached_axis_source; + enum wl_pointer_axis_source cached_axis_source; uint32_t buttons[WLR_POINTER_BUTTONS_CAP]; size_t button_count; @@ -398,7 +399,7 @@ void wlr_seat_pointer_send_motion(struct wlr_seat *wlr_seat, uint32_t time_msec, * instead. */ uint32_t wlr_seat_pointer_send_button(struct wlr_seat *wlr_seat, - uint32_t time_msec, uint32_t button, enum wlr_button_state state); + uint32_t time_msec, uint32_t button, enum wl_pointer_button_state state); /** * Send an axis event to the surface with pointer focus. This function does not @@ -406,8 +407,9 @@ uint32_t wlr_seat_pointer_send_button(struct wlr_seat *wlr_seat, * instead. */ void wlr_seat_pointer_send_axis(struct wlr_seat *wlr_seat, uint32_t time_msec, - enum wlr_axis_orientation orientation, double value, - int32_t value_discrete, enum wlr_axis_source source); + enum wl_pointer_axis orientation, double value, + int32_t value_discrete, enum wl_pointer_axis_source source, + enum wl_pointer_axis_relative_direction relative_direction); /** * Send a frame event to the surface with pointer focus. This function does not @@ -451,14 +453,15 @@ void wlr_seat_pointer_notify_motion(struct wlr_seat *wlr_seat, * pointer. */ uint32_t wlr_seat_pointer_notify_button(struct wlr_seat *wlr_seat, - uint32_t time_msec, uint32_t button, enum wlr_button_state state); + uint32_t time_msec, uint32_t button, enum wl_pointer_button_state state); /** * Notify the seat of an axis event. Defers to any grab of the pointer. */ void wlr_seat_pointer_notify_axis(struct wlr_seat *wlr_seat, uint32_t time_msec, - enum wlr_axis_orientation orientation, double value, - int32_t value_discrete, enum wlr_axis_source source); + enum wl_pointer_axis orientation, double value, + int32_t value_discrete, enum wl_pointer_axis_source source, + enum wl_pointer_axis_relative_direction relative_direction); /** * Notify the seat of a frame event. Frame events are sent to end a group of @@ -615,7 +618,7 @@ uint32_t wlr_seat_touch_send_down(struct wlr_seat *seat, * event. This will remove the touch point. This function does not respect touch * grabs: you probably want wlr_seat_touch_notify_up() instead. */ -void wlr_seat_touch_send_up(struct wlr_seat *seat, uint32_t time_msec, +uint32_t wlr_seat_touch_send_up(struct wlr_seat *seat, uint32_t time_msec, int32_t touch_id); /** @@ -629,11 +632,12 @@ void wlr_seat_touch_send_motion(struct wlr_seat *seat, uint32_t time_msec, /** * Notify the seat that this is a global gesture and the client should cancel - * processing it. The event will go to the client for the surface given. + * processing it. The event will go to the client given. * This function does not respect touch grabs: you probably want * wlr_seat_touch_notify_cancel() instead. */ -void wlr_seat_touch_send_cancel(struct wlr_seat *seat, struct wlr_surface *surface); +void wlr_seat_touch_send_cancel(struct wlr_seat *seat, + struct wlr_seat_client *seat_client); void wlr_seat_touch_send_frame(struct wlr_seat *seat); @@ -649,7 +653,7 @@ uint32_t wlr_seat_touch_notify_down(struct wlr_seat *seat, * Notify the seat that the touch point given by `touch_id` is up. Defers to any * grab of the touch device. */ -void wlr_seat_touch_notify_up(struct wlr_seat *seat, uint32_t time_msec, +uint32_t wlr_seat_touch_notify_up(struct wlr_seat *seat, uint32_t time_msec, int32_t touch_id); /** @@ -666,7 +670,7 @@ void wlr_seat_touch_notify_motion(struct wlr_seat *seat, uint32_t time_msec, * cancel processing it. Defers to any grab of the touch device. */ void wlr_seat_touch_notify_cancel(struct wlr_seat *seat, - struct wlr_surface *surface); + struct wlr_seat_client *seat_client); void wlr_seat_touch_notify_frame(struct wlr_seat *seat); diff --git a/include/wlr/types/wlr_security_context_v1.h b/include/wlr/types/wlr_security_context_v1.h index 028e9bd..f932a2b 100644 --- a/include/wlr/types/wlr_security_context_v1.h +++ b/include/wlr/types/wlr_security_context_v1.h @@ -50,6 +50,6 @@ struct wlr_security_context_v1_commit_event { struct wlr_security_context_manager_v1 *wlr_security_context_manager_v1_create( struct wl_display *display); const struct wlr_security_context_v1_state *wlr_security_context_manager_v1_lookup_client( - struct wlr_security_context_manager_v1 *manager, struct wl_client *client); + struct wlr_security_context_manager_v1 *manager, const struct wl_client *client); #endif diff --git a/include/wlr/types/wlr_session_lock_v1.h b/include/wlr/types/wlr_session_lock_v1.h index 981e224..058bf0d 100644 --- a/include/wlr/types/wlr_session_lock_v1.h +++ b/include/wlr/types/wlr_session_lock_v1.h @@ -12,6 +12,7 @@ #include #include #include +#include struct wlr_session_lock_manager_v1 { struct wl_global *global; @@ -80,6 +81,8 @@ struct wlr_session_lock_surface_v1 { // private state + struct wlr_surface_synced synced; + struct wl_listener output_destroy; }; diff --git a/include/wlr/types/wlr_shm.h b/include/wlr/types/wlr_shm.h index 396fc83..13a5ca5 100644 --- a/include/wlr/types/wlr_shm.h +++ b/include/wlr/types/wlr_shm.h @@ -22,7 +22,16 @@ struct wlr_renderer; * Currently, accessing two buffers concurrently via * wlr_buffer_begin_data_ptr_access() will return an error. */ -struct wlr_shm; +struct wlr_shm { + struct wl_global *global; + + // private state + + uint32_t *formats; + size_t formats_len; + + struct wl_listener display_destroy; +}; /** * Create the wl_shm global. diff --git a/include/wlr/types/wlr_single_pixel_buffer_v1.h b/include/wlr/types/wlr_single_pixel_buffer_v1.h index 747d2a6..3836203 100644 --- a/include/wlr/types/wlr_single_pixel_buffer_v1.h +++ b/include/wlr/types/wlr_single_pixel_buffer_v1.h @@ -11,7 +11,14 @@ #include -struct wlr_single_pixel_buffer_manager_v1; +struct wlr_single_pixel_buffer_manager_v1 { + struct wl_global *global; + + // private state + + struct wl_listener display_destroy; +}; + struct wlr_single_pixel_buffer_manager_v1 *wlr_single_pixel_buffer_manager_v1_create( struct wl_display *display); diff --git a/include/wlr/types/wlr_subcompositor.h b/include/wlr/types/wlr_subcompositor.h index d20fef9..bd3899b 100644 --- a/include/wlr/types/wlr_subcompositor.h +++ b/include/wlr/types/wlr_subcompositor.h @@ -12,8 +12,7 @@ #include #include #include - -struct wlr_surface; +#include /** * The sub-surface state describing the sub-surface's relationship with its @@ -23,6 +22,10 @@ struct wlr_surface; struct wlr_subsurface_parent_state { int32_t x, y; struct wl_list link; + + // private state + + struct wlr_surface_synced *synced; }; struct wlr_subsurface { @@ -36,7 +39,6 @@ struct wlr_subsurface { bool has_cache; bool synchronized; - bool reordered; bool added; struct wl_listener surface_client_commit; @@ -47,6 +49,14 @@ struct wlr_subsurface { } events; void *data; + + // private state + + struct wlr_surface_synced parent_synced; + + struct { + int32_t x, y; + } previous; }; struct wlr_subcompositor { diff --git a/include/wlr/types/wlr_switch.h b/include/wlr/types/wlr_switch.h index b560f0b..641df19 100644 --- a/include/wlr/types/wlr_switch.h +++ b/include/wlr/types/wlr_switch.h @@ -15,13 +15,19 @@ struct wlr_switch_impl; +/** + * A switch input device. Typically a switch input device can indicate whether + * a laptop lid is opened or closed, or whether tablet mode is enabled. + * + * See https://wayland.freedesktop.org/libinput/doc/latest/switches.html + */ struct wlr_switch { struct wlr_input_device base; const struct wlr_switch_impl *impl; struct { - struct wl_signal toggle; + struct wl_signal toggle; // struct wlr_switch_toggle_event } events; void *data; diff --git a/include/wlr/types/wlr_tablet_tool.h b/include/wlr/types/wlr_tablet_tool.h index 41d637f..97abd56 100644 --- a/include/wlr/types/wlr_tablet_tool.h +++ b/include/wlr/types/wlr_tablet_tool.h @@ -15,7 +15,7 @@ /* * Copy+Paste from libinput, but this should neither use libinput, nor - * tablet-unstable-v2 headers, so we can't include them + * tablet-v2 headers, so we can't include them */ enum wlr_tablet_tool_type { /** A generic pen */ @@ -34,7 +34,6 @@ enum wlr_tablet_tool_type { WLR_TABLET_TOOL_TYPE_LENS, /** A rotary device with positional and rotation data */ WLR_TABLET_TOOL_TYPE_TOTEM, - }; struct wlr_tablet_tool { @@ -64,13 +63,14 @@ struct wlr_tablet { const struct wlr_tablet_impl *impl; + uint16_t usb_vendor_id, usb_product_id; // zero if unset double width_mm, height_mm; struct { - struct wl_signal axis; - struct wl_signal proximity; - struct wl_signal tip; - struct wl_signal button; + struct wl_signal axis; // struct wlr_tablet_tool_axis_event + struct wl_signal proximity; // struct wlr_tablet_tool_proximity_event + struct wl_signal tip; // struct wlr_tablet_tool_tip_event + struct wl_signal button; // struct wlr_tablet_tool_button_event } events; struct wl_array paths; // char * diff --git a/include/wlr/types/wlr_tablet_v2.h b/include/wlr/types/wlr_tablet_v2.h index 5482824..6da893b 100644 --- a/include/wlr/types/wlr_tablet_v2.h +++ b/include/wlr/types/wlr_tablet_v2.h @@ -12,7 +12,7 @@ #include #include -#include "tablet-unstable-v2-protocol.h" +#include "tablet-v2-protocol.h" /* This can probably be even lower,the tools don't have a lot of buttons */ #define WLR_TABLET_V2_TOOL_BUTTONS_CAP 16 @@ -59,7 +59,7 @@ struct wlr_tablet_v2_tablet { struct wlr_input_device *wlr_device; struct wl_list clients; // wlr_tablet_client_v2.tablet_link - struct wl_listener tool_destroy; + struct wl_listener tablet_destroy; struct wlr_tablet_client_v2 *current_client; }; diff --git a/include/wlr/types/wlr_tearing_control_v1.h b/include/wlr/types/wlr_tearing_control_v1.h index 31cc5be..76c5c60 100644 --- a/include/wlr/types/wlr_tearing_control_v1.h +++ b/include/wlr/types/wlr_tearing_control_v1.h @@ -17,11 +17,12 @@ #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; + enum wp_tearing_control_v1_presentation_hint current, pending; + struct { struct wl_signal set_hint; struct wl_signal destroy; @@ -29,7 +30,13 @@ struct wlr_tearing_control_v1 { struct wlr_surface *surface; + // private state + + enum wp_tearing_control_v1_presentation_hint previous; struct wlr_addon addon; + struct wlr_surface_synced synced; + + struct wl_listener surface_commit; }; struct wlr_tearing_control_manager_v1 { diff --git a/include/wlr/types/wlr_transient_seat_v1.h b/include/wlr/types/wlr_transient_seat_v1.h new file mode 100644 index 0000000..84d419b --- /dev/null +++ b/include/wlr/types/wlr_transient_seat_v1.h @@ -0,0 +1,63 @@ +/* + * 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_TRANSIENT_SEAT_V1_H +#define WLR_TYPES_WLR_TRANSIENT_SEAT_V1_H + +#include + +struct wlr_seat; + +struct wlr_transient_seat_v1 { + struct wl_resource *resource; + struct wlr_seat *seat; + + // private state + struct wl_listener seat_destroy; +}; + +struct wlr_transient_seat_manager_v1 { + struct wl_global *global; + struct wl_listener display_destroy; + + struct { + /** + * Upon receiving this signal, call + * wlr_transient_seat_v1_ready() to pass a newly created seat + * to the manager, or + * wlr_transient_seat_v1_deny() to deny the request to create + * a seat. + */ + struct wl_signal create_seat; // struct wlr_transient_seat_v1 + } events; +}; + +struct wlr_transient_seat_manager_v1 *wlr_transient_seat_manager_v1_create( + struct wl_display *display); + +/** + * To be called when the create_seat event is received. + * + * This signals that the seat was successfully added and is ready. + * + * When the transient seat is destroyed by the client, the wlr_seat will be + * destroyed. The wlr_seat may also be destroyed from elsewhere, in which case + * the transient seat will become inert. + */ +void wlr_transient_seat_v1_ready(struct wlr_transient_seat_v1 *seat, + struct wlr_seat *wlr_seat); + +/** + * To be called when the create_seat event is received. + * + * This signals that the compositor has denied the user's request to create a + * transient seat. + */ +void wlr_transient_seat_v1_deny(struct wlr_transient_seat_v1 *seat); + +#endif /* WLR_TYPES_WLR_TRANSIENT_SEAT_V1_H */ diff --git a/include/wlr/types/wlr_viewporter.h b/include/wlr/types/wlr_viewporter.h index 8a17936..e1e2ec0 100644 --- a/include/wlr/types/wlr_viewporter.h +++ b/include/wlr/types/wlr_viewporter.h @@ -18,9 +18,9 @@ * * - The size of the surface texture may not match the surface size anymore. * Compositors must use the surface size only. - * - Compositors must call wlr_render_subtexture_with_matrix() when rendering a - * surface texture with the source box returned by - * wlr_surface_get_buffer_source_box(). + * - When rendering a surface texture, compositors should use + * wlr_surface_get_buffer_source_box() to get the source rectangle and crop + * the texture accordingly. */ struct wlr_viewporter { struct wl_global *global; diff --git a/include/wlr/types/wlr_xdg_decoration_v1.h b/include/wlr/types/wlr_xdg_decoration_v1.h index 61ce4bc..f1a8bc5 100644 --- a/include/wlr/types/wlr_xdg_decoration_v1.h +++ b/include/wlr/types/wlr_xdg_decoration_v1.h @@ -36,7 +36,7 @@ struct wlr_xdg_toplevel_decoration_v1_state { struct wlr_xdg_toplevel_decoration_v1 { struct wl_resource *resource; - struct wlr_xdg_toplevel * toplevel; + struct wlr_xdg_toplevel *toplevel; struct wlr_xdg_decoration_manager_v1 *manager; struct wl_list link; // wlr_xdg_decoration_manager_v1.link @@ -45,8 +45,6 @@ struct wlr_xdg_toplevel_decoration_v1 { enum wlr_xdg_toplevel_decoration_v1_mode scheduled_mode; enum wlr_xdg_toplevel_decoration_v1_mode requested_mode; - bool added; - struct wl_list configure_list; // wlr_xdg_toplevel_decoration_v1_configure.link struct { @@ -54,12 +52,15 @@ struct wlr_xdg_toplevel_decoration_v1 { struct wl_signal request_mode; } events; - struct wl_listener surface_destroy; + void *data; + + // private state + + struct wl_listener toplevel_destroy; struct wl_listener surface_configure; struct wl_listener surface_ack_configure; - struct wl_listener surface_commit; - void *data; + struct wlr_surface_synced synced; }; struct wlr_xdg_decoration_manager_v1 * diff --git a/include/wlr/types/wlr_xdg_foreign_v1.h b/include/wlr/types/wlr_xdg_foreign_v1.h index 45f060d..bb26fcf 100644 --- a/include/wlr/types/wlr_xdg_foreign_v1.h +++ b/include/wlr/types/wlr_xdg_foreign_v1.h @@ -34,7 +34,7 @@ struct wlr_xdg_exported_v1 { struct wlr_xdg_foreign_exported base; struct wl_resource *resource; - struct wl_listener xdg_surface_destroy; + struct wl_listener xdg_toplevel_destroy; struct wl_list link; // wlr_xdg_foreign_v1.exporter.objects }; @@ -54,7 +54,7 @@ struct wlr_xdg_imported_child_v1 { struct wl_list link; // wlr_xdg_imported_v1.children - struct wl_listener xdg_surface_destroy; + struct wl_listener xdg_toplevel_destroy; struct wl_listener xdg_toplevel_set_parent; }; diff --git a/include/wlr/types/wlr_xdg_foreign_v2.h b/include/wlr/types/wlr_xdg_foreign_v2.h index b091b45..d82854b 100644 --- a/include/wlr/types/wlr_xdg_foreign_v2.h +++ b/include/wlr/types/wlr_xdg_foreign_v2.h @@ -34,7 +34,7 @@ struct wlr_xdg_exported_v2 { struct wlr_xdg_foreign_exported base; struct wl_resource *resource; - struct wl_listener xdg_surface_destroy; + struct wl_listener xdg_toplevel_destroy; struct wl_list link; // wlr_xdg_foreign_v2.exporter.objects }; @@ -54,7 +54,7 @@ struct wlr_xdg_imported_child_v2 { struct wl_list link; // wlr_xdg_imported_v2.children - struct wl_listener xdg_surface_destroy; + struct wl_listener xdg_toplevel_destroy; struct wl_listener xdg_toplevel_set_parent; }; diff --git a/include/wlr/types/wlr_xdg_shell.h b/include/wlr/types/wlr_xdg_shell.h index 29ced59..d54c536 100644 --- a/include/wlr/types/wlr_xdg_shell.h +++ b/include/wlr/types/wlr_xdg_shell.h @@ -26,6 +26,8 @@ struct wlr_xdg_shell { struct { struct wl_signal new_surface; // struct wlr_xdg_surface + struct wl_signal new_toplevel; // struct wlr_xdg_toplevel + struct wl_signal new_popup; // struct wlr_xdg_popup struct wl_signal destroy; } events; @@ -93,7 +95,6 @@ struct wlr_xdg_popup { struct wl_list link; struct wl_resource *resource; - bool sent_initial_configure; struct wlr_surface *parent; struct wlr_seat *seat; @@ -102,10 +103,16 @@ struct wlr_xdg_popup { struct wlr_xdg_popup_state current, pending; struct { + struct wl_signal destroy; + struct wl_signal reposition; } events; struct wl_list grab_link; // wlr_xdg_popup_grab.popups + + // private state + + struct wlr_surface_synced synced; }; // each seat gets a popup grab @@ -184,6 +191,8 @@ struct wlr_xdg_toplevel { char *app_id; struct { + struct wl_signal destroy; + // Note: as per xdg-shell protocol, the compositor has to // handle state requests by sending a configure event, // even if it didn't actually change the state. Therefore, @@ -202,6 +211,10 @@ struct wlr_xdg_toplevel { struct wl_signal set_title; struct wl_signal set_app_id; } events; + + // private state + + struct wlr_surface_synced synced; }; struct wlr_xdg_surface_configure { @@ -249,7 +262,7 @@ struct wlr_xdg_surface { struct wl_list popups; // wlr_xdg_popup.link - bool added, configured; + bool configured; struct wl_event_source *configure_idle; uint32_t scheduled_serial; struct wl_list configure_list; @@ -275,6 +288,8 @@ struct wlr_xdg_surface { // private state + struct wlr_surface_synced synced; + struct wl_listener role_resource_destroy; }; @@ -431,6 +446,11 @@ void wlr_xdg_popup_destroy(struct wlr_xdg_popup *popup); void wlr_xdg_popup_get_position(struct wlr_xdg_popup *popup, double *popup_sx, double *popup_sy); +/** + * Returns true if a positioner is complete. + */ +bool wlr_xdg_positioner_is_complete(struct wlr_xdg_positioner *positioner); + /** * Get the geometry based on positioner rules. */ diff --git a/include/wlr/util/log.h b/include/wlr/util/log.h index 587104c..799dd50 100644 --- a/include/wlr/util/log.h +++ b/include/wlr/util/log.h @@ -64,13 +64,23 @@ void _wlr_vlog(enum wlr_log_importance verbosity, const char *format, va_list ar #define _WLR_FILENAME __FILE__ #endif -#define wlr_log(verb, fmt, ...) \ - _wlr_log(verb, "[%s:%d] " fmt, _WLR_FILENAME, __LINE__, ##__VA_ARGS__) - #define wlr_vlog(verb, fmt, args) \ _wlr_vlog(verb, "[%s:%d] " fmt, _WLR_FILENAME, __LINE__, args) +#if __STDC_VERSION__ >= 202311L + +#define wlr_log(verb, fmt, ...) \ + _wlr_log(verb, "[%s:%d] " fmt, _WLR_FILENAME, __LINE__ __VA_OPT__(,) __VA_ARGS__) +#define wlr_log_errno(verb, fmt, ...) \ + wlr_log(verb, fmt ": %s" __VA_OPT__(,) __VA_ARGS__, strerror(errno)) + +#else + +#define wlr_log(verb, fmt, ...) \ + _wlr_log(verb, "[%s:%d] " fmt, _WLR_FILENAME, __LINE__, ##__VA_ARGS__) #define wlr_log_errno(verb, fmt, ...) \ wlr_log(verb, fmt ": %s", ##__VA_ARGS__, strerror(errno)) #endif + +#endif diff --git a/include/wlr/util/region.h b/include/wlr/util/region.h index 948307a..ccd926f 100644 --- a/include/wlr/util/region.h +++ b/include/wlr/util/region.h @@ -21,14 +21,26 @@ #include /** - * Scales a region, ie. multiplies all its coordinates by `scale`. + * Scale a region by the specified factor. * * The resulting coordinates are rounded up or down so that the new region is - * at least as big as the original one. + * at least as big as the original one if the scale factor is greater or equal + * to 1. + * + * Also see wlr_region_scale_xy(). */ void wlr_region_scale(pixman_region32_t *dst, const pixman_region32_t *src, float scale); +/** + * Scale a region by the specified factors. + * + * The X and Y coordinates are scaled separately by scale_x and scale_y. + * + * The resulting coordinates are rounded up or down so that the new region is + * at least as big as the original one if the scale factor is greater or equal + * to 1. + */ void wlr_region_scale_xy(pixman_region32_t *dst, const pixman_region32_t *src, float scale_x, float scale_y); @@ -52,6 +64,13 @@ void wlr_region_expand(pixman_region32_t *dst, const pixman_region32_t *src, void wlr_region_rotated_bounds(pixman_region32_t *dst, const pixman_region32_t *src, float rotation, int ox, int oy); +/** + * Confine a point inside a region. + * + * x1 and y1 are the old position, x2 and y2 are the new tentative position. + * The function returns true with confined coordinates in x2_out and y2_out if + * the old position is within the region, or false otherwise. + */ bool wlr_region_confine(const pixman_region32_t *region, double x1, double y1, double x2, double y2, double *x2_out, double *y2_out); diff --git a/include/wlr/util/transform.h b/include/wlr/util/transform.h new file mode 100644 index 0000000..6195e6d --- /dev/null +++ b/include/wlr/util/transform.h @@ -0,0 +1,33 @@ +/* + * 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_UTIL_TRANSFORM_H +#define WLR_UTIL_TRANSFORM_H + +#include + +/** + * Returns the transform that, when composed with `tr`, gives + * `WL_OUTPUT_TRANSFORM_NORMAL`. + */ +enum wl_output_transform wlr_output_transform_invert( + enum wl_output_transform tr); + +/** + * Returns a transform that, when applied, has the same effect as applying + * sequentially `tr_a` and `tr_b`. + */ +enum wl_output_transform wlr_output_transform_compose( + enum wl_output_transform tr_a, enum wl_output_transform tr_b); + +/** + * Applies a transform to coordinates. + */ +void wlr_output_transform_coords(enum wl_output_transform tr, int *x, int *y); + +#endif diff --git a/include/wlr/xwayland/xwayland.h b/include/wlr/xwayland/xwayland.h index cb9e35f..4b476a0 100644 --- a/include/wlr/xwayland/xwayland.h +++ b/include/wlr/xwayland/xwayland.h @@ -21,6 +21,21 @@ struct wlr_xwm; struct wlr_data_source; struct wlr_drag; +/** + * Xwayland integration. + * + * This includes a utility to start and monitor the Xwayland process (see + * struct wlr_xwayland_server), an implementation of the xwayland_shell_v1 + * Wayland protocol, and a X11 window manager. + * + * Compositors are expected to set DISPLAY (see display_name) and listen to the + * new_surface event. + * + * Compositors may want to only expose the xwayland_shell_v1 Wayland global to + * the Xwayland client. To do so, they can set up a global filter via + * wl_display_set_global_filter() to ensure the global stored in + * shell_v1.global is only exposed to the client stored in server.client. + */ struct wlr_xwayland { struct wlr_xwayland_server *server; bool own_server; @@ -28,6 +43,7 @@ struct wlr_xwayland { struct wlr_xwayland_shell_v1 *shell_v1; struct wlr_xwayland_cursor *cursor; + // Value the DISPLAY environment variable should be set to by the compositor const char *display_name; struct wl_display *wl_display; @@ -47,13 +63,15 @@ struct wlr_xwayland { */ int (*user_event_handler)(struct wlr_xwm *xwm, xcb_generic_event_t *event); + void *data; + + // private state + struct wl_listener server_start; struct wl_listener server_ready; struct wl_listener server_destroy; struct wl_listener seat_destroy; struct wl_listener shell_destroy; - - void *data; }; enum wlr_xwayland_surface_decorations { @@ -100,7 +118,6 @@ struct wlr_xwayland_surface { int16_t x, y; uint16_t width, height; - uint16_t saved_width, saved_height; bool override_redirect; char *title; @@ -169,6 +186,8 @@ struct wlr_xwayland_surface { struct wl_signal set_strut_partial; struct wl_signal set_override_redirect; struct wl_signal set_geometry; + /* can be used to set initial maximized/fullscreen geometry */ + struct wl_signal map_request; struct wl_signal ping_timeout; } events; @@ -296,4 +315,15 @@ enum wlr_xwayland_icccm_input_model wlr_xwayland_icccm_input_model( void wlr_xwayland_set_workareas(struct wlr_xwayland *wlr_xwayland, const struct wlr_box *workareas, size_t num_workareas); + +/** + * Get the XCB connection of the XWM. + * + * The connection is only valid after wlr_xwayland.events.ready, and becomes + * invalid on wlr_xwayland_server.events.destroy. In that case, NULL is + * returned. + */ +xcb_connection_t *wlr_xwayland_get_xwm_connection( + struct wlr_xwayland *wlr_xwayland); + #endif diff --git a/include/xwayland/xwm.h b/include/xwayland/xwm.h index da3515d..6259dd9 100644 --- a/include/xwayland/xwm.h +++ b/include/xwayland/xwm.h @@ -72,6 +72,7 @@ enum atom_name { NET_WM_WINDOW_TYPE_MENU, NET_WM_WINDOW_TYPE_NOTIFICATION, NET_WM_WINDOW_TYPE_SPLASH, + NET_WM_WINDOW_TYPE_DESKTOP, DND_SELECTION, DND_AWARE, DND_STATUS, @@ -161,4 +162,8 @@ char *xwm_get_atom_name(struct wlr_xwm *xwm, xcb_atom_t atom); bool xwm_atoms_contains(struct wlr_xwm *xwm, xcb_atom_t *atoms, size_t num_atoms, enum atom_name needle); +xcb_void_cookie_t xwm_send_event_with_size(xcb_connection_t *c, + uint8_t propagate, xcb_window_t destination, + uint32_t event_mask, const void *event, uint32_t length); + #endif diff --git a/meson.build b/meson.build index 0122500..c28bb77 100644 --- a/meson.build +++ b/meson.build @@ -1,26 +1,26 @@ project( 'wlroots', 'c', - version: '0.17.2', + version: '0.18.0', license: 'MIT', meson_version: '>=0.59.0', default_options: [ - 'c_std=c11', + 'c_std=' + (meson.version().version_compare('>=1.3.0') ? 'c23,c11' : 'c11'), 'warning_level=2', 'werror=true', ], ) -# When doing a major or minor release, *always* increase soversion. This isn't -# necessary for bugfix releases. Increasing soversion is required because -# wlroots never guarantees ABI stability -- only API stability is guaranteed -# between minor releases. -soversion = 12 +version = meson.project_version().split('-')[0] +version_major = version.split('.')[0] +version_minor = version.split('.')[1] +versioned_name = '@0@-@1@.@2@'.format(meson.project_name(), version_major, version_minor) little_endian = target_machine.endian() == 'little' big_endian = target_machine.endian() == 'big' add_project_arguments([ + '-D_POSIX_C_SOURCE=200809L', '-DWLR_USE_UNSTABLE', '-DWLR_LITTLE_ENDIAN=@0@'.format(little_endian.to_int()), '-DWLR_BIG_ENDIAN=@0@'.format(big_endian.to_int()), @@ -94,38 +94,27 @@ features = { 'vulkan-renderer': false, 'gbm-allocator': false, 'session': false, + 'color-management': false, } internal_features = { 'xcb-errors': false, 'egl': false, + 'libliftoff': false, } internal_config = configuration_data() wayland_project_options = ['tests=false', 'documentation=false'] wayland_server = dependency('wayland-server', - version: '>=1.22', + version: '>=1.23', fallback: 'wayland', default_options: wayland_project_options, ) drm = dependency('libdrm', - version: '>=2.4.114', + version: '>=2.4.122', fallback: 'libdrm', default_options: [ - 'intel=disabled', - 'radeon=disabled', - 'amdgpu=disabled', - 'nouveau=disabled', - 'vmwgfx=disabled', - 'omap=disabled', - 'exynos=disabled', - 'freedreno=disabled', - 'tegra=disabled', - 'vc4=disabled', - 'etnaviv=disabled', - 'cairo-tests=disabled', - 'man-pages=disabled', - 'valgrind=disabled', + 'auto_features=disabled', 'tests=false', ], ) @@ -173,8 +162,7 @@ wlr_inc = include_directories('include') symbols_file = 'wlroots.syms' symbols_flag = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), symbols_file) lib_wlr = library( - meson.project_name(), wlr_files, - soversion: soversion.to_string(), + versioned_name, wlr_files, dependencies: wlr_deps, include_directories: [wlr_inc], install: true, @@ -194,7 +182,7 @@ wlroots = declare_dependency( variables: wlr_vars, ) -meson.override_dependency('wlroots', wlroots) +meson.override_dependency(versioned_name, wlroots) summary(features + internal_features, bool_yn: true) @@ -206,7 +194,9 @@ endif pkgconfig = import('pkgconfig') pkgconfig.generate( lib_wlr, + name: versioned_name, description: 'Wayland compositor library', + subdirs: versioned_name, url: 'https://gitlab.freedesktop.org/wlroots/wlroots', variables: wlr_vars, ) diff --git a/meson.build.user b/meson.build.user new file mode 100644 index 0000000..903c08c --- /dev/null +++ b/meson.build.user @@ -0,0 +1,289 @@ + + + + + + EnvironmentId + {78a71fa8-e90f-414a-b38d-b4d11e352597} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + 80 + true + true + 1 + 0 + false + true + false + 0 + true + true + 0 + 8 + true + false + 1 + true + true + true + *.md, *.MD, Makefile + false + true + true + + + + ProjectExplorer.Project.PluginSettings + + + true + false + true + true + true + true + + + 0 + true + + true + true + Builtin.DefaultTidyAndClazy + 8 + true + + + + true + + + + + ProjectExplorer.Project.Target.0 + + Desktop + 桌面 + 桌面 + {e381a9b0-d3ee-4d53-8cc3-f541d06e5fb3} + 0 + 0 + 0 + + + debug + /home/rewine/new/build-wlroots-unknown-debug + + + + all + true + 构建 + MesonProjectManager.BuildStep + + 1 + 构建 + 构建 + ProjectExplorer.BuildSteps.Build + + + + + clean + true + 构建 + MesonProjectManager.BuildStep + + 1 + 清除 + 清除 + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Debug + MesonProjectManager.BuildConfiguration + + + + release + /home/rewine/new/build-wlroots-unknown-release + + + + all + true + MesonProjectManager.BuildStep + + 1 + 构建 + 构建 + ProjectExplorer.BuildSteps.Build + + + + + clean + true + MesonProjectManager.BuildStep + + 1 + 清除 + 清除 + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Release + MesonProjectManager.BuildConfiguration + + + + debugoptimized + /home/rewine/new/build-wlroots-unknown-debugoptimized + + + + all + true + MesonProjectManager.BuildStep + + 1 + 构建 + 构建 + ProjectExplorer.BuildSteps.Build + + + + + clean + true + MesonProjectManager.BuildStep + + 1 + 清除 + 清除 + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Debug With Optimizations + MesonProjectManager.BuildConfiguration + + + + minsize + /home/rewine/new/build-wlroots-unknown-minsize + + + + all + true + MesonProjectManager.BuildStep + + 1 + 构建 + 构建 + ProjectExplorer.BuildSteps.Build + + + + + clean + true + MesonProjectManager.BuildStep + + 1 + 清除 + 清除 + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Minimum Size + MesonProjectManager.BuildConfiguration + + 4 + + + 0 + 部署 + 部署 + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + true + true + 0 + true + + 2 + + false + + ProjectExplorer.CustomExecutableRunConfiguration + + false + true + true + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 22 + + + Version + 22 + + diff --git a/meson_options.txt b/meson_options.txt index 6977643..35961b1 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -7,3 +7,5 @@ option('backends', type: 'array', choices: ['auto', 'drm', 'libinput', 'x11'], v option('allocators', type: 'array', choices: ['auto', 'gbm'], value: ['auto'], description: 'Select built-in allocators') option('session', type: 'feature', value: 'auto', description: 'Enable session support') +option('color-management', type: 'feature', value: 'auto', description: 'Enable support for color management') +option('libliftoff', type: 'feature', value: 'auto', description: 'Enable support for libliftoff') diff --git a/protocol/meson.build b/protocol/meson.build index af0ae03..a447691 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -1,5 +1,5 @@ wayland_protos = dependency('wayland-protocols', - version: '>=1.32', + version: '>=1.35', fallback: 'wayland-protocols', default_options: ['tests=false'], ) @@ -13,17 +13,22 @@ wayland_scanner = find_program( protocols = { # Stable upstream protocols + 'linux-dmabuf-v1': wl_protocol_dir / 'stable/linux-dmabuf/linux-dmabuf-v1.xml', 'presentation-time': wl_protocol_dir / 'stable/presentation-time/presentation-time.xml', + 'tablet-v2': wl_protocol_dir / 'stable/tablet/tablet-v2.xml', 'viewporter': wl_protocol_dir / 'stable/viewporter/viewporter.xml', 'xdg-shell': wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml', # Staging upstream protocols + 'alpha-modifier-v1': wl_protocol_dir / 'staging/alpha-modifier/alpha-modifier-v1.xml', 'content-type-v1': wl_protocol_dir / 'staging/content-type/content-type-v1.xml', 'cursor-shape-v1': wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml', 'drm-lease-v1': wl_protocol_dir / 'staging/drm-lease/drm-lease-v1.xml', + 'ext-foreign-toplevel-list-v1': wl_protocol_dir / 'staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-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', + 'linux-drm-syncobj-v1': wl_protocol_dir / 'staging/linux-drm-syncobj/linux-drm-syncobj-v1.xml', 'security-context-v1': wl_protocol_dir / 'staging/security-context/security-context-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', @@ -34,17 +39,16 @@ protocols = { 'fullscreen-shell-unstable-v1': wl_protocol_dir / 'unstable/fullscreen-shell/fullscreen-shell-unstable-v1.xml', 'idle-inhibit-unstable-v1': wl_protocol_dir / 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml', 'keyboard-shortcuts-inhibit-unstable-v1': wl_protocol_dir / 'unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml', - 'linux-dmabuf-unstable-v1': wl_protocol_dir / 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml', 'pointer-constraints-unstable-v1': wl_protocol_dir / 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml', 'pointer-gestures-unstable-v1': wl_protocol_dir / 'unstable/pointer-gestures/pointer-gestures-unstable-v1.xml', 'primary-selection-unstable-v1': wl_protocol_dir / 'unstable/primary-selection/primary-selection-unstable-v1.xml', 'relative-pointer-unstable-v1': wl_protocol_dir / 'unstable/relative-pointer/relative-pointer-unstable-v1.xml', - 'tablet-unstable-v2': wl_protocol_dir / 'unstable/tablet/tablet-unstable-v2.xml', 'text-input-unstable-v3': wl_protocol_dir / 'unstable/text-input/text-input-unstable-v3.xml', 'xdg-decoration-unstable-v1': wl_protocol_dir / 'unstable/xdg-decoration/xdg-decoration-unstable-v1.xml', 'xdg-foreign-unstable-v1': wl_protocol_dir / 'unstable/xdg-foreign/xdg-foreign-unstable-v1.xml', 'xdg-foreign-unstable-v2': wl_protocol_dir / 'unstable/xdg-foreign/xdg-foreign-unstable-v2.xml', 'xdg-output-unstable-v1': wl_protocol_dir / 'unstable/xdg-output/xdg-output-unstable-v1.xml', + 'ext-transient-seat-v1': wl_protocol_dir / 'staging/ext-transient-seat/ext-transient-seat-v1.xml', # Other protocols 'drm': 'drm.xml', @@ -55,7 +59,6 @@ protocols = { 'wlr-export-dmabuf-unstable-v1': 'wlr-export-dmabuf-unstable-v1.xml', 'wlr-foreign-toplevel-management-unstable-v1': 'wlr-foreign-toplevel-management-unstable-v1.xml', 'wlr-gamma-control-unstable-v1': 'wlr-gamma-control-unstable-v1.xml', - 'wlr-input-inhibitor-unstable-v1': 'wlr-input-inhibitor-unstable-v1.xml', 'wlr-layer-shell-unstable-v1': 'wlr-layer-shell-unstable-v1.xml', 'wlr-output-management-unstable-v1': 'wlr-output-management-unstable-v1.xml', 'wlr-output-power-management-unstable-v1': 'wlr-output-power-management-unstable-v1.xml', diff --git a/protocol/wlr-input-inhibitor-unstable-v1.xml b/protocol/wlr-input-inhibitor-unstable-v1.xml deleted file mode 100644 index b62d1bb..0000000 --- a/protocol/wlr-input-inhibitor-unstable-v1.xml +++ /dev/null @@ -1,67 +0,0 @@ - - - - Copyright © 2018 Drew DeVault - - Permission to use, copy, modify, distribute, and sell this - software and its documentation for any purpose is hereby granted - without fee, provided that the above copyright notice appear in - all copies and that both that copyright notice and this permission - notice appear in supporting documentation, and that the name of - the copyright holders not be used in advertising or publicity - pertaining to distribution of the software without specific, - written prior permission. The copyright holders make no - representations about the suitability of this software for any - purpose. It is provided "as is" without express or implied - warranty. - - THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS - SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN - AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, - ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF - THIS SOFTWARE. - - - - - Clients can use this interface to prevent input events from being sent to - any surfaces but its own, which is useful for example in lock screen - software. It is assumed that access to this interface will be locked down - to whitelisted clients by the compositor. - - - - - Activates the input inhibitor. As long as the inhibitor is active, the - compositor will not send input events to other clients. - - - - - - - - - - - - While this resource exists, input to clients other than the owner of the - inhibitor resource will not receive input events. The client that owns - this resource will receive all input events normally. The compositor will - also disable all of its own input processing (such as keyboard shortcuts) - while the inhibitor is active. - - The compositor may continue to send input events to selected clients, - such as an on-screen keyboard (via the input-method protocol). - - - - - Destroy the inhibitor and allow other clients to receive input. - - - - diff --git a/render/allocator/allocator.c b/render/allocator/allocator.c index f71902f..27b08fc 100644 --- a/render/allocator/allocator.c +++ b/render/allocator/allocator.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -93,10 +92,9 @@ static int reopen_drm_node(int drm_fd, bool allow_render_node) { } struct wlr_allocator *allocator_autocreate_with_drm_fd( - struct wlr_backend *backend, struct wlr_renderer *renderer, + uint32_t backend_caps, struct wlr_renderer *renderer, int drm_fd) { - uint32_t backend_caps = backend_get_buffer_caps(backend); - uint32_t renderer_caps = renderer_get_render_buffer_caps(renderer); + uint32_t renderer_caps = renderer->render_buffer_caps; struct wlr_allocator *alloc = NULL; @@ -149,12 +147,14 @@ struct wlr_allocator *allocator_autocreate_with_drm_fd( struct wlr_allocator *wlr_allocator_autocreate(struct wlr_backend *backend, struct wlr_renderer *renderer) { + uint32_t backend_caps = backend_get_buffer_caps(backend); // Note, drm_fd may be negative if unavailable int drm_fd = wlr_backend_get_drm_fd(backend); if (drm_fd < 0) { drm_fd = wlr_renderer_get_drm_fd(renderer); } - return allocator_autocreate_with_drm_fd(backend, renderer, drm_fd); + + return allocator_autocreate_with_drm_fd(backend_caps, renderer, drm_fd); } void wlr_allocator_destroy(struct wlr_allocator *alloc) { diff --git a/render/allocator/drm_dumb.c b/render/allocator/drm_dumb.c index 6de44f4..eb4ce99 100644 --- a/render/allocator/drm_dumb.c +++ b/render/allocator/drm_dumb.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include diff --git a/render/allocator/gbm.c b/render/allocator/gbm.c index 25ddbba..baa0fb6 100644 --- a/render/allocator/gbm.c +++ b/render/allocator/gbm.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include diff --git a/render/color.c b/render/color.c new file mode 100644 index 0000000..639b004 --- /dev/null +++ b/render/color.c @@ -0,0 +1,36 @@ +#include +#include +#include +#include "render/color.h" + +struct wlr_color_transform *wlr_color_transform_init_srgb(void) { + struct wlr_color_transform *tx = calloc(1, sizeof(struct wlr_color_transform)); + if (!tx) { + return NULL; + } + tx->type = COLOR_TRANSFORM_SRGB; + tx->ref_count = 1; + wlr_addon_set_init(&tx->addons); + return tx; +} + +static void color_transform_destroy(struct wlr_color_transform *tr) { + free(tr->lut3d.lut_3d); + wlr_addon_set_finish(&tr->addons); + free(tr); +} + +void wlr_color_transform_ref(struct wlr_color_transform *tr) { + tr->ref_count += 1; +} + +void wlr_color_transform_unref(struct wlr_color_transform *tr) { + if (!tr) { + return; + } + assert(tr->ref_count > 0); + tr->ref_count -= 1; + if (tr->ref_count == 0) { + color_transform_destroy(tr); + } +} diff --git a/render/color_fallback.c b/render/color_fallback.c new file mode 100644 index 0000000..72741c3 --- /dev/null +++ b/render/color_fallback.c @@ -0,0 +1,9 @@ +#include +#include + +struct wlr_color_transform *wlr_color_transform_init_linear_to_icc( + const void *data, size_t size) { + wlr_log(WLR_ERROR, "Cannot create color transform from ICC profile: " + "LCMS2 is compile-time disabled"); + return NULL; +} diff --git a/render/color_lcms2.c b/render/color_lcms2.c new file mode 100644 index 0000000..fae4fb6 --- /dev/null +++ b/render/color_lcms2.c @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include "render/color.h" + +static const cmsCIExyY srgb_whitepoint = { 0.3127, 0.3291, 1 }; + +static const cmsCIExyYTRIPLE srgb_primaries = { + .Red = { 0.64, 0.33, 1 }, + .Green = { 0.3, 0.6, 1 }, + .Blue = { 0.15, 0.06, 1}, +}; + +static void handle_lcms_error(cmsContext ctx, cmsUInt32Number code, const char *text) { + wlr_log(WLR_ERROR, "[lcms] %s", text); +} + +struct wlr_color_transform *wlr_color_transform_init_linear_to_icc( + const void *data, size_t size) { + struct wlr_color_transform *tx = NULL; + + cmsContext ctx = cmsCreateContext(NULL, NULL); + if (ctx == NULL) { + wlr_log(WLR_ERROR, "cmsCreateContext failed"); + return NULL; + } + + cmsSetLogErrorHandlerTHR(ctx, handle_lcms_error); + + cmsHPROFILE icc_profile = cmsOpenProfileFromMemTHR(ctx, data, size); + if (icc_profile == NULL) { + wlr_log(WLR_ERROR, "cmsOpenProfileFromMemTHR failed"); + goto out_ctx; + } + + if (cmsGetDeviceClass(icc_profile) != cmsSigDisplayClass) { + wlr_log(WLR_ERROR, "ICC profile must have the Display device class"); + goto out_icc_profile; + } + + cmsToneCurve *linear_tone_curve = cmsBuildGamma(ctx, 1); + if (linear_tone_curve == NULL) { + wlr_log(WLR_ERROR, "cmsBuildGamma failed"); + goto out_icc_profile; + } + + cmsToneCurve *linear_tf[] = { + linear_tone_curve, + linear_tone_curve, + linear_tone_curve, + }; + cmsHPROFILE srgb_profile = cmsCreateRGBProfileTHR(ctx, &srgb_whitepoint, + &srgb_primaries, linear_tf); + if (srgb_profile == NULL) { + wlr_log(WLR_ERROR, "cmsCreateRGBProfileTHR failed"); + goto out_linear_tone_curve; + } + + cmsHTRANSFORM lcms_tr = cmsCreateTransformTHR(ctx, + srgb_profile, TYPE_RGB_FLT, icc_profile, TYPE_RGB_FLT, + INTENT_RELATIVE_COLORIMETRIC, 0); + if (lcms_tr == NULL) { + wlr_log(WLR_ERROR, "cmsCreateTransformTHR failed"); + goto out_srgb_profile; + } + + size_t dim_len = 33; + float *lut_3d = calloc(3 * dim_len * dim_len * dim_len, sizeof(float)); + if (lut_3d == NULL) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + goto out_lcms_tr; + } + + float factor = 1.0f / (dim_len - 1); + for (size_t b_index = 0; b_index < dim_len; b_index++) { + for (size_t g_index = 0; g_index < dim_len; g_index++) { + for (size_t r_index = 0; r_index < dim_len; r_index++) { + float rgb_in[3] = { + r_index * factor, + g_index * factor, + b_index * factor, + }; + float rgb_out[3]; + // TODO: use a single call to cmsDoTransform for the entire calculation? + // this does require allocating an extra temp buffer + cmsDoTransform(lcms_tr, rgb_in, rgb_out, 1); + + size_t offset = 3 * (r_index + dim_len * g_index + dim_len * dim_len * b_index); + // TODO: maybe clamp values to [0.0, 1.0] here? + lut_3d[offset] = rgb_out[0]; + lut_3d[offset + 1] = rgb_out[1]; + lut_3d[offset + 2] = rgb_out[2]; + } + } + } + + tx = calloc(1, sizeof(struct wlr_color_transform)); + if (!tx) { + goto out_lcms_tr; + } + tx->type = COLOR_TRANSFORM_LUT_3D; + tx->lut3d.dim_len = dim_len; + tx->lut3d.lut_3d = lut_3d; + tx->ref_count = 1; + wlr_addon_set_init(&tx->addons); + +out_lcms_tr: + cmsDeleteTransform(lcms_tr); +out_linear_tone_curve: + cmsFreeToneCurve(linear_tone_curve); +out_srgb_profile: + cmsCloseProfile(srgb_profile); +out_icc_profile: + cmsCloseProfile(icc_profile); +out_ctx: + cmsDeleteContext(ctx); + return tx; +} diff --git a/render/dmabuf.c b/render/dmabuf.c index b0b8ef4..7096dbc 100644 --- a/render/dmabuf.c +++ b/render/dmabuf.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include diff --git a/render/drm_format_set.c b/render/drm_format_set.c index 1b837fa..dc57ad9 100644 --- a/render/drm_format_set.c +++ b/render/drm_format_set.c @@ -74,6 +74,7 @@ bool wlr_drm_format_set_add(struct wlr_drm_format_set *set, uint32_t format, struct wlr_drm_format *fmts = realloc(set->formats, sizeof(*fmts) * capacity); if (!fmts) { wlr_log_errno(WLR_ERROR, "Allocation failed"); + wlr_drm_format_finish(&fmt); return false; } @@ -270,10 +271,9 @@ bool wlr_drm_format_set_union(struct wlr_drm_format_set *dst, } // Add both a and b sets into out - if (!drm_format_set_extend(&out, a)) { - return false; - } - if (!drm_format_set_extend(&out, b)) { + if (!drm_format_set_extend(&out, a) || + !drm_format_set_extend(&out, b)) { + wlr_drm_format_set_finish(&out); return false; } diff --git a/render/drm_syncobj.c b/render/drm_syncobj.c new file mode 100644 index 0000000..af3e79f --- /dev/null +++ b/render/drm_syncobj.c @@ -0,0 +1,212 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" + +#if HAVE_EVENTFD +#include +#endif + +struct wlr_drm_syncobj_timeline *wlr_drm_syncobj_timeline_create(int drm_fd) { + struct wlr_drm_syncobj_timeline *timeline = calloc(1, sizeof(*timeline)); + if (timeline == NULL) { + return NULL; + } + timeline->drm_fd = drm_fd; + timeline->n_refs = 1; + + if (drmSyncobjCreate(drm_fd, 0, &timeline->handle) != 0) { + wlr_log_errno(WLR_ERROR, "drmSyncobjCreate failed"); + free(timeline); + return NULL; + } + + return timeline; +} + +struct wlr_drm_syncobj_timeline *wlr_drm_syncobj_timeline_import(int drm_fd, + int drm_syncobj_fd) { + struct wlr_drm_syncobj_timeline *timeline = calloc(1, sizeof(*timeline)); + if (timeline == NULL) { + return NULL; + } + timeline->drm_fd = drm_fd; + timeline->n_refs = 1; + + if (drmSyncobjFDToHandle(drm_fd, drm_syncobj_fd, &timeline->handle) != 0) { + wlr_log_errno(WLR_ERROR, "drmSyncobjFDToHandle failed"); + free(timeline); + return NULL; + } + + return timeline; +} + +struct wlr_drm_syncobj_timeline *wlr_drm_syncobj_timeline_ref(struct wlr_drm_syncobj_timeline *timeline) { + timeline->n_refs++; + return timeline; +} + +void wlr_drm_syncobj_timeline_unref(struct wlr_drm_syncobj_timeline *timeline) { + if (timeline == NULL) { + return; + } + + assert(timeline->n_refs > 0); + timeline->n_refs--; + if (timeline->n_refs > 0) { + return; + } + + drmSyncobjDestroy(timeline->drm_fd, timeline->handle); + free(timeline); +} + +int wlr_drm_syncobj_timeline_export_sync_file(struct wlr_drm_syncobj_timeline *timeline, + uint64_t src_point) { + int sync_file_fd = -1; + + uint32_t syncobj_handle; + if (drmSyncobjCreate(timeline->drm_fd, 0, &syncobj_handle) != 0) { + wlr_log_errno(WLR_ERROR, "drmSyncobjCreate failed"); + return -1; + } + + if (drmSyncobjTransfer(timeline->drm_fd, syncobj_handle, 0, + timeline->handle, src_point, 0) != 0) { + wlr_log_errno(WLR_ERROR, "drmSyncobjTransfer failed"); + goto out; + } + + if (drmSyncobjExportSyncFile(timeline->drm_fd, + syncobj_handle, &sync_file_fd) != 0) { + wlr_log_errno(WLR_ERROR, "drmSyncobjExportSyncFile failed"); + goto out; + } + +out: + drmSyncobjDestroy(timeline->drm_fd, syncobj_handle); + return sync_file_fd; +} + +bool wlr_drm_syncobj_timeline_import_sync_file(struct wlr_drm_syncobj_timeline *timeline, + uint64_t dst_point, int sync_file_fd) { + bool ok = false; + + uint32_t syncobj_handle; + if (drmSyncobjCreate(timeline->drm_fd, 0, &syncobj_handle) != 0) { + wlr_log_errno(WLR_ERROR, "drmSyncobjCreate failed"); + return -1; + } + + if (drmSyncobjImportSyncFile(timeline->drm_fd, syncobj_handle, + sync_file_fd) != 0) { + wlr_log_errno(WLR_ERROR, "drmSyncobjImportSyncFile failed"); + goto out; + } + + if (drmSyncobjTransfer(timeline->drm_fd, timeline->handle, dst_point, + syncobj_handle, 0, 0) != 0) { + wlr_log_errno(WLR_ERROR, "drmSyncobjTransfer failed"); + goto out; + } + + ok = true; + +out: + drmSyncobjDestroy(timeline->drm_fd, syncobj_handle); + return ok; +} + +bool wlr_drm_syncobj_timeline_check(struct wlr_drm_syncobj_timeline *timeline, + uint64_t point, uint32_t flags, bool *result) { + int etime; +#if defined(__FreeBSD__) + etime = ETIMEDOUT; +#else + etime = ETIME; +#endif + + uint32_t signaled_point; + int ret = drmSyncobjTimelineWait(timeline->drm_fd, &timeline->handle, &point, 1, 0, flags, &signaled_point); + if (ret != 0 && ret != -etime) { + wlr_log_errno(WLR_ERROR, "drmSyncobjWait() failed"); + return false; + } + + *result = ret == 0; + return true; +} + +static int handle_eventfd_ready(int ev_fd, uint32_t mask, void *data) { + struct wlr_drm_syncobj_timeline_waiter *waiter = data; + + if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) { + wlr_log(WLR_ERROR, "Failed to wait for render timeline: eventfd error"); + } + + if (mask & WL_EVENT_READABLE) { + uint64_t ev_fd_value; + if (read(ev_fd, &ev_fd_value, sizeof(ev_fd_value)) <= 0) { + wlr_log(WLR_ERROR, "Failed to wait for render timeline: read() failed"); + } + } + + wl_signal_emit_mutable(&waiter->events.ready, NULL); + return 0; +} + +bool wlr_drm_syncobj_timeline_waiter_init(struct wlr_drm_syncobj_timeline_waiter *waiter, + struct wlr_drm_syncobj_timeline *timeline, uint64_t point, uint32_t flags, + struct wl_event_loop *loop) { + int ev_fd; +#if HAVE_EVENTFD + ev_fd = eventfd(0, EFD_CLOEXEC); + if (ev_fd < 0) { + wlr_log_errno(WLR_ERROR, "eventfd() failed"); + } +#else + ev_fd = -1; + wlr_log(WLR_ERROR, "eventfd() is unavailable"); +#endif + if (ev_fd < 0) { + return NULL; + } + + struct drm_syncobj_eventfd syncobj_eventfd = { + .handle = timeline->handle, + .flags = flags, + .point = point, + .fd = ev_fd, + }; + if (drmIoctl(timeline->drm_fd, DRM_IOCTL_SYNCOBJ_EVENTFD, &syncobj_eventfd) != 0) { + wlr_log_errno(WLR_ERROR, "DRM_IOCTL_SYNCOBJ_EVENTFD failed"); + close(ev_fd); + return NULL; + } + + struct wl_event_source *source = wl_event_loop_add_fd(loop, ev_fd, WL_EVENT_READABLE, handle_eventfd_ready, waiter); + if (source == NULL) { + wlr_log(WLR_ERROR, "Failed to add FD to event loop"); + close(ev_fd); + return NULL; + } + + *waiter = (struct wlr_drm_syncobj_timeline_waiter){ + .ev_fd = ev_fd, + .event_source = source, + }; + wl_signal_init(&waiter->events.ready); + return true; +} + +void wlr_drm_syncobj_timeline_waiter_finish(struct wlr_drm_syncobj_timeline_waiter *waiter) { + wl_list_remove(&waiter->events.ready.listener_list); + wl_event_source_remove(waiter->event_source); + close(waiter->ev_fd); +} diff --git a/render/egl.c b/render/egl.c index 162634b..19868ca 100644 --- a/render/egl.c +++ b/render/egl.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -650,7 +649,14 @@ bool wlr_egl_destroy_image(struct wlr_egl *egl, EGLImage image) { return egl->procs.eglDestroyImageKHR(egl->display, image); } -bool wlr_egl_make_current(struct wlr_egl *egl) { +bool wlr_egl_make_current(struct wlr_egl *egl, + struct wlr_egl_context *save_context) { + if (save_context != NULL) { + save_context->display = eglGetCurrentDisplay(); + save_context->context = eglGetCurrentContext(); + save_context->draw_surface = eglGetCurrentSurface(EGL_DRAW); + save_context->read_surface = eglGetCurrentSurface(EGL_READ); + } if (!eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE, egl->context)) { wlr_log(WLR_ERROR, "eglMakeCurrent failed"); @@ -668,17 +674,6 @@ bool wlr_egl_unset_current(struct wlr_egl *egl) { return true; } -bool wlr_egl_is_current(struct wlr_egl *egl) { - return eglGetCurrentContext() == egl->context; -} - -void wlr_egl_save_context(struct wlr_egl_context *context) { - context->display = eglGetCurrentDisplay(); - context->context = eglGetCurrentContext(); - context->draw_surface = eglGetCurrentSurface(EGL_DRAW); - context->read_surface = eglGetCurrentSurface(EGL_READ); -} - bool wlr_egl_restore_context(struct wlr_egl_context *context) { // If the saved context is a null-context, we must use the current // display instead of the saved display because eglMakeCurrent() can't diff --git a/render/gles2/pass.c b/render/gles2/pass.c index 768e81d..9177b0a 100644 --- a/render/gles2/pass.c +++ b/render/gles2/pass.c @@ -1,9 +1,9 @@ -#define _POSIX_C_SOURCE 199309L #include #include #include #include #include +#include #include "render/gles2.h" #include "types/wlr_matrix.h" @@ -40,6 +40,7 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { glBindFramebuffer(GL_FRAMEBUFFER, 0); pop_gles2_debug(renderer); + wlr_egl_restore_context(&pass->prev_ctx); wlr_buffer_unlock(pass->buffer->buffer); free(pass); @@ -246,7 +247,7 @@ static const char *reset_status_str(GLenum status) { } struct wlr_gles2_render_pass *begin_gles2_buffer_pass(struct wlr_gles2_buffer *buffer, - struct wlr_gles2_render_timer *timer) { + struct wlr_egl_context *prev_ctx, struct wlr_gles2_render_timer *timer) { struct wlr_gles2_renderer *renderer = buffer->renderer; struct wlr_buffer *wlr_buffer = buffer->buffer; @@ -259,6 +260,11 @@ struct wlr_gles2_render_pass *begin_gles2_buffer_pass(struct wlr_gles2_buffer *b } } + GLint fbo = gles2_buffer_get_fbo(buffer); + if (!fbo) { + return NULL; + } + struct wlr_gles2_render_pass *pass = calloc(1, sizeof(*pass)); if (pass == NULL) { return NULL; @@ -268,12 +274,13 @@ struct wlr_gles2_render_pass *begin_gles2_buffer_pass(struct wlr_gles2_buffer *b wlr_buffer_lock(wlr_buffer); pass->buffer = buffer; pass->timer = timer; + pass->prev_ctx = *prev_ctx; matrix_projection(pass->projection_matrix, wlr_buffer->width, wlr_buffer->height, WL_OUTPUT_TRANSFORM_FLIPPED_180); push_gles2_debug(renderer); - glBindFramebuffer(GL_FRAMEBUFFER, buffer->fbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); glViewport(0, 0, wlr_buffer->width, wlr_buffer->height); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); diff --git a/render/gles2/pixel_format.c b/render/gles2/pixel_format.c index acaac5c..e74cb29 100644 --- a/render/gles2/pixel_format.c +++ b/render/gles2/pixel_format.c @@ -2,6 +2,7 @@ #include #include #include "render/gles2.h" +#include "render/pixel_format.h" /* * The DRM formats are little endian while the GL formats are big endian, @@ -12,100 +13,84 @@ static const struct wlr_gles2_pixel_format formats[] = { .drm_format = DRM_FORMAT_ARGB8888, .gl_format = GL_BGRA_EXT, .gl_type = GL_UNSIGNED_BYTE, - .has_alpha = true, }, { .drm_format = DRM_FORMAT_XRGB8888, .gl_format = GL_BGRA_EXT, .gl_type = GL_UNSIGNED_BYTE, - .has_alpha = false, }, { .drm_format = DRM_FORMAT_XBGR8888, .gl_format = GL_RGBA, .gl_type = GL_UNSIGNED_BYTE, - .has_alpha = false, }, { .drm_format = DRM_FORMAT_ABGR8888, .gl_format = GL_RGBA, .gl_type = GL_UNSIGNED_BYTE, - .has_alpha = true, }, { .drm_format = DRM_FORMAT_BGR888, .gl_format = GL_RGB, .gl_type = GL_UNSIGNED_BYTE, - .has_alpha = false, }, #if WLR_LITTLE_ENDIAN { .drm_format = DRM_FORMAT_RGBX4444, .gl_format = GL_RGBA, .gl_type = GL_UNSIGNED_SHORT_4_4_4_4, - .has_alpha = false, }, { .drm_format = DRM_FORMAT_RGBA4444, .gl_format = GL_RGBA, .gl_type = GL_UNSIGNED_SHORT_4_4_4_4, - .has_alpha = true, }, { .drm_format = DRM_FORMAT_RGBX5551, .gl_format = GL_RGBA, .gl_type = GL_UNSIGNED_SHORT_5_5_5_1, - .has_alpha = false, }, { .drm_format = DRM_FORMAT_RGBA5551, .gl_format = GL_RGBA, .gl_type = GL_UNSIGNED_SHORT_5_5_5_1, - .has_alpha = true, }, { .drm_format = DRM_FORMAT_RGB565, .gl_format = GL_RGB, .gl_type = GL_UNSIGNED_SHORT_5_6_5, - .has_alpha = false, }, { .drm_format = DRM_FORMAT_XBGR2101010, .gl_format = GL_RGBA, .gl_type = GL_UNSIGNED_INT_2_10_10_10_REV_EXT, - .has_alpha = false, }, { .drm_format = DRM_FORMAT_ABGR2101010, .gl_format = GL_RGBA, .gl_type = GL_UNSIGNED_INT_2_10_10_10_REV_EXT, - .has_alpha = true, }, { .drm_format = DRM_FORMAT_XBGR16161616F, .gl_format = GL_RGBA, .gl_type = GL_HALF_FLOAT_OES, - .has_alpha = false, }, { .drm_format = DRM_FORMAT_ABGR16161616F, .gl_format = GL_RGBA, .gl_type = GL_HALF_FLOAT_OES, - .has_alpha = true, }, { .drm_format = DRM_FORMAT_XBGR16161616, .gl_internalformat = GL_RGBA16_EXT, .gl_format = GL_RGBA, .gl_type = GL_UNSIGNED_SHORT, - .has_alpha = false, }, { .drm_format = DRM_FORMAT_ABGR16161616, .gl_internalformat = GL_RGBA16_EXT, .gl_format = GL_RGBA, .gl_type = GL_UNSIGNED_SHORT, - .has_alpha = true, }, #endif }; @@ -151,25 +136,27 @@ const struct wlr_gles2_pixel_format *get_gles2_format_from_drm(uint32_t fmt) { const struct wlr_gles2_pixel_format *get_gles2_format_from_gl( GLint gl_format, GLint gl_type, bool alpha) { for (size_t i = 0; i < sizeof(formats) / sizeof(*formats); ++i) { - if (formats[i].gl_format == gl_format && - formats[i].gl_type == gl_type && - formats[i].has_alpha == alpha) { - return &formats[i]; + if (formats[i].gl_format != gl_format || + formats[i].gl_type != gl_type) { + continue; + } + + if (pixel_format_has_alpha(formats[i].drm_format) != alpha) { + continue; } + + return &formats[i]; } return NULL; } -const uint32_t *get_gles2_shm_formats(const struct wlr_gles2_renderer *renderer, - size_t *len) { - static uint32_t shm_formats[sizeof(formats) / sizeof(formats[0])]; - size_t j = 0; +void get_gles2_shm_formats(const struct wlr_gles2_renderer *renderer, + struct wlr_drm_format_set *out) { for (size_t i = 0; i < sizeof(formats) / sizeof(formats[0]); i++) { if (!is_gles2_pixel_format_supported(renderer, &formats[i])) { continue; } - shm_formats[j++] = formats[i].drm_format; + wlr_drm_format_set_add(out, formats[i].drm_format, DRM_FORMAT_MOD_INVALID); + wlr_drm_format_set_add(out, formats[i].drm_format, DRM_FORMAT_MOD_LINEAR); } - *len = j; - return shm_formats; } diff --git a/render/gles2/renderer.c b/render/gles2/renderer.c index 4899cfc..4694b2a 100644 --- a/render/gles2/renderer.c +++ b/render/gles2/renderer.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 199309L #include #include #include @@ -28,13 +27,6 @@ #include "tex_rgbx_frag_src.h" #include "tex_external_frag_src.h" -static const GLfloat verts[] = { - 1, 0, // top right - 0, 0, // top left - 1, 1, // bottom right - 0, 1, // bottom left -}; - static const struct wlr_renderer_impl renderer_impl; static const struct wlr_render_timer_impl render_timer_impl; @@ -49,14 +41,6 @@ struct wlr_gles2_renderer *gles2_get_renderer( return renderer; } -static struct wlr_gles2_renderer *gles2_get_renderer_in_context( - struct wlr_renderer *wlr_renderer) { - struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); - assert(wlr_egl_is_current(renderer->egl)); - assert(renderer->current_buffer != NULL); - return renderer; -} - bool wlr_render_timer_is_gles2(struct wlr_render_timer *timer) { return timer->impl == &render_timer_impl; } @@ -72,13 +56,13 @@ static void destroy_buffer(struct wlr_gles2_buffer *buffer) { wlr_addon_finish(&buffer->addon); struct wlr_egl_context prev_ctx; - wlr_egl_save_context(&prev_ctx); - wlr_egl_make_current(buffer->renderer->egl); + wlr_egl_make_current(buffer->renderer->egl, &prev_ctx); push_gles2_debug(buffer->renderer); glDeleteFramebuffers(1, &buffer->fbo); glDeleteRenderbuffers(1, &buffer->rbo); + glDeleteTextures(1, &buffer->tex); pop_gles2_debug(buffer->renderer); @@ -100,7 +84,45 @@ static const struct wlr_addon_interface buffer_addon_impl = { .destroy = handle_buffer_destroy, }; -static struct wlr_gles2_buffer *get_or_create_buffer(struct wlr_gles2_renderer *renderer, +GLuint gles2_buffer_get_fbo(struct wlr_gles2_buffer *buffer) { + if (buffer->external_only) { + wlr_log(WLR_ERROR, "DMA-BUF format is external-only"); + return 0; + } + + if (buffer->fbo) { + return buffer->fbo; + } + + push_gles2_debug(buffer->renderer); + + if (!buffer->rbo) { + glGenRenderbuffers(1, &buffer->rbo); + glBindRenderbuffer(GL_RENDERBUFFER, buffer->rbo); + buffer->renderer->procs.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, + buffer->image); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + } + + glGenFramebuffers(1, &buffer->fbo); + glBindFramebuffer(GL_FRAMEBUFFER, buffer->fbo); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_RENDERBUFFER, buffer->rbo); + GLenum fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + if (fb_status != GL_FRAMEBUFFER_COMPLETE) { + wlr_log(WLR_ERROR, "Failed to create FBO"); + glDeleteFramebuffers(1, &buffer->fbo); + buffer->fbo = 0; + } + + pop_gles2_debug(buffer->renderer); + + return buffer->fbo; +} + +struct wlr_gles2_buffer *gles2_buffer_get_or_create(struct wlr_gles2_renderer *renderer, struct wlr_buffer *wlr_buffer) { struct wlr_addon *addon = wlr_addon_find(&wlr_buffer->addons, renderer, &buffer_addon_impl); @@ -122,35 +144,12 @@ static struct wlr_gles2_buffer *get_or_create_buffer(struct wlr_gles2_renderer * goto error_buffer; } - bool external_only; buffer->image = wlr_egl_create_image_from_dmabuf(renderer->egl, - &dmabuf, &external_only); + &dmabuf, &buffer->external_only); if (buffer->image == EGL_NO_IMAGE_KHR) { goto error_buffer; } - push_gles2_debug(renderer); - - glGenRenderbuffers(1, &buffer->rbo); - glBindRenderbuffer(GL_RENDERBUFFER, buffer->rbo); - renderer->procs.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, - buffer->image); - glBindRenderbuffer(GL_RENDERBUFFER, 0); - - glGenFramebuffers(1, &buffer->fbo); - glBindFramebuffer(GL_FRAMEBUFFER, buffer->fbo); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_RENDERBUFFER, buffer->rbo); - GLenum fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - - pop_gles2_debug(renderer); - - if (fb_status != GL_FRAMEBUFFER_COMPLETE) { - wlr_log(WLR_ERROR, "Failed to create FBO"); - goto error_image; - } - wlr_addon_init(&buffer->addon, &wlr_buffer->addons, renderer, &buffer_addon_impl); @@ -161,246 +160,21 @@ static struct wlr_gles2_buffer *get_or_create_buffer(struct wlr_gles2_renderer * return buffer; -error_image: - wlr_egl_destroy_image(renderer->egl, buffer->image); error_buffer: free(buffer); return NULL; } -static bool gles2_bind_buffer(struct wlr_renderer *wlr_renderer, - struct wlr_buffer *wlr_buffer) { +static const struct wlr_drm_format_set *gles2_get_texture_formats( + struct wlr_renderer *wlr_renderer, uint32_t buffer_caps) { struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); - - if (renderer->current_buffer != NULL) { - assert(wlr_egl_is_current(renderer->egl)); - - push_gles2_debug(renderer); - glFlush(); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - pop_gles2_debug(renderer); - - wlr_buffer_unlock(renderer->current_buffer->buffer); - renderer->current_buffer = NULL; - } - - if (wlr_buffer == NULL) { - wlr_egl_unset_current(renderer->egl); - return true; - } - - wlr_egl_make_current(renderer->egl); - - struct wlr_gles2_buffer *buffer = get_or_create_buffer(renderer, wlr_buffer); - if (buffer == NULL) { - return false; - } - - wlr_buffer_lock(wlr_buffer); - renderer->current_buffer = buffer; - - push_gles2_debug(renderer); - glBindFramebuffer(GL_FRAMEBUFFER, renderer->current_buffer->fbo); - pop_gles2_debug(renderer); - - return true; -} - -static const char *reset_status_str(GLenum status) { - switch (status) { - case GL_GUILTY_CONTEXT_RESET_KHR: - return "guilty"; - case GL_INNOCENT_CONTEXT_RESET_KHR: - return "innocent"; - case GL_UNKNOWN_CONTEXT_RESET_KHR: - return "unknown"; - default: - return ""; - } -} - -static bool gles2_begin(struct wlr_renderer *wlr_renderer, uint32_t width, - uint32_t height) { - struct wlr_gles2_renderer *renderer = - gles2_get_renderer_in_context(wlr_renderer); - - push_gles2_debug(renderer); - - if (renderer->procs.glGetGraphicsResetStatusKHR) { - GLenum status = renderer->procs.glGetGraphicsResetStatusKHR(); - if (status != GL_NO_ERROR) { - wlr_log(WLR_ERROR, "GPU reset (%s)", reset_status_str(status)); - wl_signal_emit_mutable(&wlr_renderer->events.lost, NULL); - return false; - } - } - - glViewport(0, 0, width, height); - renderer->viewport_width = width; - renderer->viewport_height = height; - - // refresh projection matrix - matrix_projection(renderer->projection, width, height, - WL_OUTPUT_TRANSFORM_FLIPPED_180); - - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - - // XXX: maybe we should save output projection and remove some of the need - // for users to sling matricies themselves - - pop_gles2_debug(renderer); - - return true; -} - -static void gles2_end(struct wlr_renderer *wlr_renderer) { - gles2_get_renderer_in_context(wlr_renderer); - // no-op -} - -static void gles2_clear(struct wlr_renderer *wlr_renderer, - const float color[static 4]) { - struct wlr_gles2_renderer *renderer = - gles2_get_renderer_in_context(wlr_renderer); - - push_gles2_debug(renderer); - glClearColor(color[0], color[1], color[2], color[3]); - glClear(GL_COLOR_BUFFER_BIT); - pop_gles2_debug(renderer); -} - -static void gles2_scissor(struct wlr_renderer *wlr_renderer, - struct wlr_box *box) { - struct wlr_gles2_renderer *renderer = - gles2_get_renderer_in_context(wlr_renderer); - - push_gles2_debug(renderer); - if (box != NULL) { - glScissor(box->x, box->y, box->width, box->height); - glEnable(GL_SCISSOR_TEST); - } else { - glDisable(GL_SCISSOR_TEST); - } - pop_gles2_debug(renderer); -} - -static bool gles2_render_subtexture_with_matrix( - struct wlr_renderer *wlr_renderer, struct wlr_texture *wlr_texture, - const struct wlr_fbox *box, const float matrix[static 9], - float alpha) { - struct wlr_gles2_renderer *renderer = - gles2_get_renderer_in_context(wlr_renderer); - struct wlr_gles2_texture *texture = - gles2_get_texture(wlr_texture); - assert(texture->renderer == renderer); - - struct wlr_gles2_tex_shader *shader = NULL; - - switch (texture->target) { - case GL_TEXTURE_2D: - if (texture->has_alpha) { - shader = &renderer->shaders.tex_rgba; - } else { - shader = &renderer->shaders.tex_rgbx; - } - break; - case GL_TEXTURE_EXTERNAL_OES: - // EGL_EXT_image_dma_buf_import_modifiers requires - // GL_OES_EGL_image_external - assert(renderer->exts.OES_egl_image_external); - shader = &renderer->shaders.tex_ext; - break; - default: - abort(); - } - - float gl_matrix[9]; - wlr_matrix_multiply(gl_matrix, renderer->projection, matrix); - - push_gles2_debug(renderer); - - if (!texture->has_alpha && alpha == 1.0) { - glDisable(GL_BLEND); + if (buffer_caps & WLR_BUFFER_CAP_DMABUF) { + return wlr_egl_get_dmabuf_texture_formats(renderer->egl); + } else if (buffer_caps & WLR_BUFFER_CAP_DATA_PTR) { + return &renderer->shm_texture_formats; } else { - glEnable(GL_BLEND); + return NULL; } - - glActiveTexture(GL_TEXTURE0); - glBindTexture(texture->target, texture->tex); - - glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - - glUseProgram(shader->program); - - glUniformMatrix3fv(shader->proj, 1, GL_FALSE, gl_matrix); - glUniform1i(shader->tex, 0); - glUniform1f(shader->alpha, alpha); - - float tex_matrix[9]; - wlr_matrix_identity(tex_matrix); - wlr_matrix_translate(tex_matrix, box->x / texture->wlr_texture.width, - box->y / texture->wlr_texture.height); - wlr_matrix_scale(tex_matrix, box->width / texture->wlr_texture.width, - box->height / texture->wlr_texture.height); - glUniformMatrix3fv(shader->tex_proj, 1, GL_FALSE, tex_matrix); - - glVertexAttribPointer(shader->pos_attrib, 2, GL_FLOAT, GL_FALSE, 0, verts); - - glEnableVertexAttribArray(shader->pos_attrib); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - glDisableVertexAttribArray(shader->pos_attrib); - - glBindTexture(texture->target, 0); - - pop_gles2_debug(renderer); - return true; -} - -static void gles2_render_quad_with_matrix(struct wlr_renderer *wlr_renderer, - const float color[static 4], const float matrix[static 9]) { - struct wlr_gles2_renderer *renderer = - gles2_get_renderer_in_context(wlr_renderer); - - float gl_matrix[9]; - wlr_matrix_multiply(gl_matrix, renderer->projection, matrix); - - push_gles2_debug(renderer); - - if (color[3] == 1.0) { - glDisable(GL_BLEND); - } else { - glEnable(GL_BLEND); - } - - glUseProgram(renderer->shaders.quad.program); - - glUniformMatrix3fv(renderer->shaders.quad.proj, 1, GL_FALSE, gl_matrix); - glUniform4f(renderer->shaders.quad.color, color[0], color[1], color[2], color[3]); - - glVertexAttribPointer(renderer->shaders.quad.pos_attrib, 2, GL_FLOAT, GL_FALSE, - 0, verts); - - glEnableVertexAttribArray(renderer->shaders.quad.pos_attrib); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - glDisableVertexAttribArray(renderer->shaders.quad.pos_attrib); - - pop_gles2_debug(renderer); -} - -static const uint32_t *gles2_get_shm_texture_formats( - struct wlr_renderer *wlr_renderer, size_t *len) { - struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); - return get_gles2_shm_formats(renderer, len); -} - -static const struct wlr_drm_format_set *gles2_get_dmabuf_texture_formats( - struct wlr_renderer *wlr_renderer) { - struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); - return wlr_egl_get_dmabuf_texture_formats(renderer->egl); } static const struct wlr_drm_format_set *gles2_get_render_formats( @@ -409,90 +183,6 @@ static const struct wlr_drm_format_set *gles2_get_render_formats( return wlr_egl_get_dmabuf_render_formats(renderer->egl); } -static uint32_t gles2_preferred_read_format( - struct wlr_renderer *wlr_renderer) { - struct wlr_gles2_renderer *renderer = - gles2_get_renderer_in_context(wlr_renderer); - - push_gles2_debug(renderer); - - GLint gl_format = -1, gl_type = -1, alpha_size = -1; - glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &gl_format); - glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &gl_type); - glGetIntegerv(GL_ALPHA_BITS, &alpha_size); - - pop_gles2_debug(renderer); - - const struct wlr_gles2_pixel_format *fmt = - get_gles2_format_from_gl(gl_format, gl_type, alpha_size > 0); - if (fmt != NULL) { - return fmt->drm_format; - } - - if (renderer->exts.EXT_read_format_bgra) { - return DRM_FORMAT_XRGB8888; - } - return DRM_FORMAT_XBGR8888; -} - -static bool gles2_read_pixels(struct wlr_renderer *wlr_renderer, - uint32_t drm_format, uint32_t stride, - uint32_t width, uint32_t height, uint32_t src_x, uint32_t src_y, - uint32_t dst_x, uint32_t dst_y, void *data) { - struct wlr_gles2_renderer *renderer = - gles2_get_renderer_in_context(wlr_renderer); - - const struct wlr_gles2_pixel_format *fmt = - get_gles2_format_from_drm(drm_format); - if (fmt == NULL || !is_gles2_pixel_format_supported(renderer, fmt)) { - wlr_log(WLR_ERROR, "Cannot read pixels: unsupported pixel format 0x%"PRIX32, drm_format); - return false; - } - - if (fmt->gl_format == GL_BGRA_EXT && !renderer->exts.EXT_read_format_bgra) { - wlr_log(WLR_ERROR, - "Cannot read pixels: missing GL_EXT_read_format_bgra extension"); - return false; - } - - const struct wlr_pixel_format_info *drm_fmt = - drm_get_pixel_format_info(fmt->drm_format); - assert(drm_fmt); - if (pixel_format_info_pixels_per_block(drm_fmt) != 1) { - wlr_log(WLR_ERROR, "Cannot read pixels: block formats are not supported"); - return false; - } - - push_gles2_debug(renderer); - - // Make sure any pending drawing is finished before we try to read it - glFinish(); - - glGetError(); // Clear the error flag - - unsigned char *p = (unsigned char *)data + dst_y * stride; - glPixelStorei(GL_PACK_ALIGNMENT, 1); - uint32_t pack_stride = pixel_format_info_min_stride(drm_fmt, width); - if (pack_stride == stride && dst_x == 0) { - // Under these particular conditions, we can read the pixels with only - // one glReadPixels call - - glReadPixels(src_x, src_y, width, height, fmt->gl_format, fmt->gl_type, p); - } else { - // Unfortunately GLES2 doesn't support GL_PACK_ROW_LENGTH, so we have to read - // the lines out row by row - for (size_t i = 0; i < height; ++i) { - uint32_t y = src_y + i; - glReadPixels(src_x, y, width, 1, fmt->gl_format, - fmt->gl_type, p + i * stride + dst_x * drm_fmt->bytes_per_block); - } - } - - pop_gles2_debug(renderer); - - return glGetError() == GL_NO_ERROR; -} - static int gles2_get_drm_fd(struct wlr_renderer *wlr_renderer) { struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); @@ -504,10 +194,6 @@ static int gles2_get_drm_fd(struct wlr_renderer *wlr_renderer) { return renderer->drm_fd; } -static uint32_t gles2_get_render_buffer_caps(struct wlr_renderer *wlr_renderer) { - return WLR_BUFFER_CAP_DMABUF; -} - struct wlr_egl *wlr_gles2_renderer_get_egl(struct wlr_renderer *wlr_renderer) { struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); @@ -517,18 +203,18 @@ struct wlr_egl *wlr_gles2_renderer_get_egl(struct wlr_renderer *wlr_renderer) { static void gles2_destroy(struct wlr_renderer *wlr_renderer) { struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); - wlr_egl_make_current(renderer->egl); - - struct wlr_gles2_buffer *buffer, *buffer_tmp; - wl_list_for_each_safe(buffer, buffer_tmp, &renderer->buffers, link) { - destroy_buffer(buffer); - } + wlr_egl_make_current(renderer->egl, NULL); struct wlr_gles2_texture *tex, *tex_tmp; wl_list_for_each_safe(tex, tex_tmp, &renderer->textures, link) { gles2_texture_destroy(tex); } + struct wlr_gles2_buffer *buffer, *buffer_tmp; + wl_list_for_each_safe(buffer, buffer_tmp, &renderer->buffers, link) { + destroy_buffer(buffer); + } + push_gles2_debug(renderer); glDeleteProgram(renderer->shaders.quad.program); glDeleteProgram(renderer->shaders.tex_rgba.program); @@ -544,6 +230,8 @@ static void gles2_destroy(struct wlr_renderer *wlr_renderer) { wlr_egl_unset_current(renderer->egl); wlr_egl_destroy(renderer->egl); + wlr_drm_format_set_finish(&renderer->shm_texture_formats); + if (renderer->drm_fd >= 0) { close(renderer->drm_fd); } @@ -554,7 +242,9 @@ static void gles2_destroy(struct wlr_renderer *wlr_renderer) { static struct wlr_render_pass *gles2_begin_buffer_pass(struct wlr_renderer *wlr_renderer, struct wlr_buffer *wlr_buffer, const struct wlr_buffer_pass_options *options) { struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); - if (!wlr_egl_make_current(renderer->egl)) { + + struct wlr_egl_context prev_ctx = {0}; + if (!wlr_egl_make_current(renderer->egl, &prev_ctx)) { return NULL; } @@ -564,18 +254,37 @@ static struct wlr_render_pass *gles2_begin_buffer_pass(struct wlr_renderer *wlr_ clock_gettime(CLOCK_MONOTONIC, &timer->cpu_start); } - struct wlr_gles2_buffer *buffer = get_or_create_buffer(renderer, wlr_buffer); + struct wlr_gles2_buffer *buffer = gles2_buffer_get_or_create(renderer, wlr_buffer); if (!buffer) { return NULL; } - struct wlr_gles2_render_pass *pass = begin_gles2_buffer_pass(buffer, timer); + struct wlr_gles2_render_pass *pass = begin_gles2_buffer_pass(buffer, &prev_ctx, timer); if (!pass) { return NULL; } return &pass->base; } +GLuint wlr_gles2_renderer_get_buffer_fbo(struct wlr_renderer *wlr_renderer, + struct wlr_buffer *wlr_buffer) { + struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); + GLuint fbo = 0; + + struct wlr_egl_context prev_ctx = {0}; + if (!wlr_egl_make_current(renderer->egl, &prev_ctx)) { + return 0; + } + + struct wlr_gles2_buffer *buffer = gles2_buffer_get_or_create(renderer, wlr_buffer); + if (buffer) { + fbo = gles2_buffer_get_fbo(buffer); + } + + wlr_egl_restore_context(&prev_ctx); + return fbo; +} + static struct wlr_render_timer *gles2_render_timer_create(struct wlr_renderer *wlr_renderer) { struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); if (!renderer->exts.EXT_disjoint_timer_query) { @@ -591,8 +300,7 @@ static struct wlr_render_timer *gles2_render_timer_create(struct wlr_renderer *w timer->renderer = renderer; struct wlr_egl_context prev_ctx; - wlr_egl_save_context(&prev_ctx); - wlr_egl_make_current(renderer->egl); + wlr_egl_make_current(renderer->egl, &prev_ctx); renderer->procs.glGenQueriesEXT(1, &timer->id); wlr_egl_restore_context(&prev_ctx); @@ -604,8 +312,7 @@ static int gles2_get_render_time(struct wlr_render_timer *wlr_timer) { struct wlr_gles2_renderer *renderer = timer->renderer; struct wlr_egl_context prev_ctx; - wlr_egl_save_context(&prev_ctx); - wlr_egl_make_current(renderer->egl); + wlr_egl_make_current(renderer->egl, &prev_ctx); GLint64 disjoint; renderer->procs.glGetInteger64vEXT(GL_GPU_DISJOINT_EXT, &disjoint); @@ -639,8 +346,7 @@ static void gles2_render_timer_destroy(struct wlr_render_timer *wlr_timer) { struct wlr_gles2_renderer *renderer = timer->renderer; struct wlr_egl_context prev_ctx; - wlr_egl_save_context(&prev_ctx); - wlr_egl_make_current(renderer->egl); + wlr_egl_make_current(renderer->egl, &prev_ctx); renderer->procs.glDeleteQueriesEXT(1, &timer->id); wlr_egl_restore_context(&prev_ctx); free(timer); @@ -648,20 +354,9 @@ static void gles2_render_timer_destroy(struct wlr_render_timer *wlr_timer) { static const struct wlr_renderer_impl renderer_impl = { .destroy = gles2_destroy, - .bind_buffer = gles2_bind_buffer, - .begin = gles2_begin, - .end = gles2_end, - .clear = gles2_clear, - .scissor = gles2_scissor, - .render_subtexture_with_matrix = gles2_render_subtexture_with_matrix, - .render_quad_with_matrix = gles2_render_quad_with_matrix, - .get_shm_texture_formats = gles2_get_shm_texture_formats, - .get_dmabuf_texture_formats = gles2_get_dmabuf_texture_formats, + .get_texture_formats = gles2_get_texture_formats, .get_render_formats = gles2_get_render_formats, - .preferred_read_format = gles2_preferred_read_format, - .read_pixels = gles2_read_pixels, .get_drm_fd = gles2_get_drm_fd, - .get_render_buffer_caps = gles2_get_render_buffer_caps, .texture_from_buffer = gles2_texture_from_buffer, .begin_buffer_pass = gles2_begin_buffer_pass, .render_timer_create = gles2_render_timer_create, @@ -816,7 +511,7 @@ struct wlr_renderer *wlr_gles2_renderer_create_with_drm_fd(int drm_fd) { } struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) { - if (!wlr_egl_make_current(egl)) { + if (!wlr_egl_make_current(egl, NULL)) { return NULL; } @@ -830,7 +525,8 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) { if (renderer == NULL) { return NULL; } - wlr_renderer_init(&renderer->wlr_renderer, &renderer_impl); + wlr_renderer_init(&renderer->wlr_renderer, &renderer_impl, WLR_BUFFER_CAP_DMABUF); + renderer->wlr_renderer.features.output_color_transform = false; wl_list_init(&renderer->buffers); wl_list_init(&renderer->textures); @@ -915,7 +611,11 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) { load_gl_proc(&renderer->procs.glQueryCounterEXT, "glQueryCounterEXT"); load_gl_proc(&renderer->procs.glGetQueryObjectivEXT, "glGetQueryObjectivEXT"); load_gl_proc(&renderer->procs.glGetQueryObjectui64vEXT, "glGetQueryObjectui64vEXT"); - load_gl_proc(&renderer->procs.glGetInteger64vEXT, "glGetInteger64vEXT"); + if (eglGetProcAddress("glGetInteger64vEXT")) { + load_gl_proc(&renderer->procs.glGetInteger64vEXT, "glGetInteger64vEXT"); + } else { + load_gl_proc(&renderer->procs.glGetInteger64vEXT, "glGetInteger64v"); + } } if (renderer->exts.KHR_debug) { @@ -981,6 +681,8 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) { wlr_egl_unset_current(renderer->egl); + get_gles2_shm_formats(renderer, &renderer->shm_texture_formats); + return &renderer->wlr_renderer; error: @@ -1007,9 +709,3 @@ bool wlr_gles2_renderer_check_ext(struct wlr_renderer *wlr_renderer, struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); return check_gl_ext(renderer->exts_str, ext); } - -GLuint wlr_gles2_renderer_get_current_fbo(struct wlr_renderer *wlr_renderer) { - struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); - assert(renderer->current_buffer); - return renderer->current_buffer->fbo; -} diff --git a/render/gles2/texture.c b/render/gles2/texture.c index b444745..f9ad4cc 100644 --- a/render/gles2/texture.c +++ b/render/gles2/texture.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include "render/egl.h" #include "render/gles2.h" @@ -33,7 +32,7 @@ static bool gles2_texture_update_from_buffer(struct wlr_texture *wlr_texture, struct wlr_buffer *buffer, const pixman_region32_t *damage) { struct wlr_gles2_texture *texture = gles2_get_texture(wlr_texture); - if (texture->target != GL_TEXTURE_2D || texture->image != EGL_NO_IMAGE_KHR) { + if (texture->drm_format == DRM_FORMAT_INVALID) { return false; } @@ -69,8 +68,7 @@ static bool gles2_texture_update_from_buffer(struct wlr_texture *wlr_texture, } struct wlr_egl_context prev_ctx; - wlr_egl_save_context(&prev_ctx); - wlr_egl_make_current(texture->renderer->egl); + wlr_egl_make_current(texture->renderer->egl, &prev_ctx); push_gles2_debug(texture->renderer); @@ -107,69 +105,179 @@ static bool gles2_texture_update_from_buffer(struct wlr_texture *wlr_texture, return true; } -static bool gles2_texture_invalidate(struct wlr_gles2_texture *texture) { - if (texture->image == EGL_NO_IMAGE_KHR) { - return false; - } - if (texture->target == GL_TEXTURE_EXTERNAL_OES) { - // External changes are immediately made visible by the GL implementation - return true; +void gles2_texture_destroy(struct wlr_gles2_texture *texture) { + wl_list_remove(&texture->link); + if (texture->buffer != NULL) { + wlr_buffer_unlock(texture->buffer->buffer); + } else { + struct wlr_egl_context prev_ctx; + wlr_egl_make_current(texture->renderer->egl, &prev_ctx); + + push_gles2_debug(texture->renderer); + + glDeleteTextures(1, &texture->tex); + glDeleteFramebuffers(1, &texture->fbo); + + pop_gles2_debug(texture->renderer); + + wlr_egl_restore_context(&prev_ctx); } - struct wlr_egl_context prev_ctx; - wlr_egl_save_context(&prev_ctx); - wlr_egl_make_current(texture->renderer->egl); + free(texture); +} - push_gles2_debug(texture->renderer); +static void handle_gles2_texture_destroy(struct wlr_texture *wlr_texture) { + gles2_texture_destroy(gles2_get_texture(wlr_texture)); +} - glBindTexture(texture->target, texture->tex); - texture->renderer->procs.glEGLImageTargetTexture2DOES(texture->target, - texture->image); - glBindTexture(texture->target, 0); +static bool gles2_texture_bind(struct wlr_gles2_texture *texture) { + if (texture->fbo) { + glBindFramebuffer(GL_FRAMEBUFFER, texture->fbo); + } else if (texture->buffer) { + if (texture->buffer->external_only) { + return false; + } - pop_gles2_debug(texture->renderer); + GLuint fbo = gles2_buffer_get_fbo(texture->buffer); + if (!fbo) { + return false; + } - wlr_egl_restore_context(&prev_ctx); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + } else { + glGenFramebuffers(1, &texture->fbo); + glBindFramebuffer(GL_FRAMEBUFFER, texture->fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + texture->target, texture->tex, 0); + + GLenum fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (fb_status != GL_FRAMEBUFFER_COMPLETE) { + wlr_log(WLR_ERROR, "Failed to create FBO"); + glDeleteFramebuffers(1, &texture->fbo); + texture->fbo = 0; + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + return false; + } + } return true; } -void gles2_texture_destroy(struct wlr_gles2_texture *texture) { - wl_list_remove(&texture->link); - if (texture->buffer != NULL) { - wlr_addon_finish(&texture->buffer_addon); +static bool gles2_texture_read_pixels(struct wlr_texture *wlr_texture, + const struct wlr_texture_read_pixels_options *options) { + struct wlr_gles2_texture *texture = gles2_get_texture(wlr_texture); + + struct wlr_box src; + wlr_texture_read_pixels_options_get_src_box(options, wlr_texture, &src); + + const struct wlr_gles2_pixel_format *fmt = + get_gles2_format_from_drm(options->format); + if (fmt == NULL || !is_gles2_pixel_format_supported(texture->renderer, fmt)) { + wlr_log(WLR_ERROR, "Cannot read pixels: unsupported pixel format 0x%"PRIX32, options->format); + return false; } - struct wlr_egl_context prev_ctx; - wlr_egl_save_context(&prev_ctx); - wlr_egl_make_current(texture->renderer->egl); + if (fmt->gl_format == GL_BGRA_EXT && !texture->renderer->exts.EXT_read_format_bgra) { + wlr_log(WLR_ERROR, + "Cannot read pixels: missing GL_EXT_read_format_bgra extension"); + return false; + } + + const struct wlr_pixel_format_info *drm_fmt = + drm_get_pixel_format_info(fmt->drm_format); + assert(drm_fmt); + if (pixel_format_info_pixels_per_block(drm_fmt) != 1) { + wlr_log(WLR_ERROR, "Cannot read pixels: block formats are not supported"); + return false; + } push_gles2_debug(texture->renderer); + struct wlr_egl_context prev_ctx; + if (!wlr_egl_make_current(texture->renderer->egl, &prev_ctx)) { + return false; + } - glDeleteTextures(1, &texture->tex); - wlr_egl_destroy_image(texture->renderer->egl, texture->image); + if (!gles2_texture_bind(texture)) { + return false; + } - pop_gles2_debug(texture->renderer); + // Make sure any pending drawing is finished before we try to read it + glFinish(); + + glGetError(); // Clear the error flag + + unsigned char *p = wlr_texture_read_pixel_options_get_data(options); + + glPixelStorei(GL_PACK_ALIGNMENT, 1); + uint32_t pack_stride = pixel_format_info_min_stride(drm_fmt, src.width); + if (pack_stride == options->stride && options->dst_x == 0) { + // Under these particular conditions, we can read the pixels with only + // one glReadPixels call + + glReadPixels(src.x, src.y, src.width, src.height, fmt->gl_format, fmt->gl_type, p); + } else { + // Unfortunately GLES2 doesn't support GL_PACK_ROW_LENGTH, so we have to read + // the lines out row by row + for (int32_t i = 0; i < src.height; ++i) { + uint32_t y = src.y + i; + glReadPixels(src.x, y, src.width, 1, fmt->gl_format, + fmt->gl_type, p + i * options->stride); + } + } wlr_egl_restore_context(&prev_ctx); + pop_gles2_debug(texture->renderer); - free(texture); + return glGetError() == GL_NO_ERROR; } -static void gles2_texture_unref(struct wlr_texture *wlr_texture) { +static uint32_t gles2_texture_preferred_read_format(struct wlr_texture *wlr_texture) { struct wlr_gles2_texture *texture = gles2_get_texture(wlr_texture); - if (texture->buffer != NULL) { - // Keep the texture around, in case the buffer is re-used later. We're - // still listening to the buffer's destroy event. - wlr_buffer_unlock(texture->buffer); - } else { - gles2_texture_destroy(texture); + + push_gles2_debug(texture->renderer); + + uint32_t fmt = DRM_FORMAT_INVALID; + + struct wlr_egl_context prev_ctx; + if (!wlr_egl_make_current(texture->renderer->egl, &prev_ctx)) { + return fmt; } + + if (!gles2_texture_bind(texture)) { + goto out; + } + + GLint gl_format = -1, gl_type = -1, alpha_size = -1; + glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &gl_format); + glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &gl_type); + glGetIntegerv(GL_ALPHA_BITS, &alpha_size); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + pop_gles2_debug(texture->renderer); + + const struct wlr_gles2_pixel_format *pix_fmt = + get_gles2_format_from_gl(gl_format, gl_type, alpha_size > 0); + if (pix_fmt != NULL) { + fmt = pix_fmt->drm_format; + goto out; + } + + if (texture->renderer->exts.EXT_read_format_bgra) { + fmt = DRM_FORMAT_XRGB8888; + goto out; + } + +out: + wlr_egl_restore_context(&prev_ctx); + return fmt; } static const struct wlr_texture_impl texture_impl = { .update_from_buffer = gles2_texture_update_from_buffer, - .destroy = gles2_texture_unref, + .read_pixels = gles2_texture_read_pixels, + .preferred_read_format = gles2_texture_preferred_read_format, + .destroy = handle_gles2_texture_destroy, }; static struct wlr_gles2_texture *gles2_texture_create( @@ -217,7 +325,7 @@ static struct wlr_texture *gles2_texture_from_pixels( return NULL; } texture->target = GL_TEXTURE_2D; - texture->has_alpha = fmt->has_alpha; + texture->has_alpha = pixel_format_has_alpha(fmt->drm_format); texture->drm_format = fmt->drm_format; GLint internal_format = fmt->gl_internalformat; @@ -226,8 +334,7 @@ static struct wlr_texture *gles2_texture_from_pixels( } struct wlr_egl_context prev_ctx; - wlr_egl_save_context(&prev_ctx); - wlr_egl_make_current(renderer->egl); + wlr_egl_make_current(renderer->egl, &prev_ctx); push_gles2_debug(renderer); @@ -251,101 +358,54 @@ static struct wlr_texture *gles2_texture_from_pixels( } static struct wlr_texture *gles2_texture_from_dmabuf( - struct wlr_renderer *wlr_renderer, + struct wlr_gles2_renderer *renderer, struct wlr_buffer *wlr_buffer, struct wlr_dmabuf_attributes *attribs) { - struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); - if (!renderer->procs.glEGLImageTargetTexture2DOES) { return NULL; } + struct wlr_gles2_buffer *buffer = gles2_buffer_get_or_create(renderer, wlr_buffer); + if (!buffer) { + return NULL; + } + struct wlr_gles2_texture *texture = gles2_texture_create(renderer, attribs->width, attribs->height); if (texture == NULL) { return NULL; } - texture->drm_format = DRM_FORMAT_INVALID; // texture can't be written anyways - const struct wlr_pixel_format_info *drm_fmt = - drm_get_pixel_format_info(attribs->format); - if (drm_fmt != NULL) { - texture->has_alpha = drm_fmt->has_alpha; - } else { - // We don't know, assume the texture has an alpha channel - texture->has_alpha = true; - } + texture->target = buffer->external_only ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D; + texture->buffer = buffer; + texture->drm_format = DRM_FORMAT_INVALID; // texture can't be written anyways + texture->has_alpha = pixel_format_has_alpha(attribs->format); struct wlr_egl_context prev_ctx; - wlr_egl_save_context(&prev_ctx); - wlr_egl_make_current(renderer->egl); - - bool external_only; - texture->image = - wlr_egl_create_image_from_dmabuf(renderer->egl, attribs, &external_only); - if (texture->image == EGL_NO_IMAGE_KHR) { - wlr_log(WLR_ERROR, "Failed to create EGL image from DMA-BUF"); - wlr_egl_restore_context(&prev_ctx); - wl_list_remove(&texture->link); - free(texture); - return NULL; - } - - texture->target = external_only ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D; - - push_gles2_debug(renderer); - - glGenTextures(1, &texture->tex); - glBindTexture(texture->target, texture->tex); - glTexParameteri(texture->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(texture->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - renderer->procs.glEGLImageTargetTexture2DOES(texture->target, texture->image); - glBindTexture(texture->target, 0); - - pop_gles2_debug(renderer); - - wlr_egl_restore_context(&prev_ctx); - - return &texture->wlr_texture; -} - -static void texture_handle_buffer_destroy(struct wlr_addon *addon) { - struct wlr_gles2_texture *texture = - wl_container_of(addon, texture, buffer_addon); - gles2_texture_destroy(texture); -} - -static const struct wlr_addon_interface texture_addon_impl = { - .name = "wlr_gles2_texture", - .destroy = texture_handle_buffer_destroy, -}; + wlr_egl_make_current(renderer->egl, &prev_ctx); + push_gles2_debug(texture->renderer); -static struct wlr_texture *gles2_texture_from_dmabuf_buffer( - struct wlr_gles2_renderer *renderer, struct wlr_buffer *buffer, - struct wlr_dmabuf_attributes *dmabuf) { - struct wlr_addon *addon = - wlr_addon_find(&buffer->addons, renderer, &texture_addon_impl); - if (addon != NULL) { - struct wlr_gles2_texture *texture = - wl_container_of(addon, texture, buffer_addon); - if (!gles2_texture_invalidate(texture)) { - wlr_log(WLR_ERROR, "Failed to invalidate texture"); - return false; - } - wlr_buffer_lock(texture->buffer); - return &texture->wlr_texture; + bool invalid; + if (!buffer->tex) { + glGenTextures(1, &buffer->tex); + invalid = true; + } else { + // External changes are immediately made visible by the GL implementation + invalid = !buffer->external_only; } - struct wlr_texture *wlr_texture = - gles2_texture_from_dmabuf(&renderer->wlr_renderer, dmabuf); - if (wlr_texture == NULL) { - return false; + if (invalid) { + glBindTexture(texture->target, buffer->tex); + glTexParameteri(texture->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(texture->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + renderer->procs.glEGLImageTargetTexture2DOES(texture->target, buffer->image); + glBindTexture(texture->target, 0); } - struct wlr_gles2_texture *texture = gles2_get_texture(wlr_texture); - texture->buffer = wlr_buffer_lock(buffer); - wlr_addon_init(&texture->buffer_addon, &buffer->addons, - renderer, &texture_addon_impl); + pop_gles2_debug(texture->renderer); + wlr_egl_restore_context(&prev_ctx); + texture->tex = buffer->tex; + wlr_buffer_lock(texture->buffer->buffer); return &texture->wlr_texture; } @@ -358,7 +418,7 @@ struct wlr_texture *gles2_texture_from_buffer(struct wlr_renderer *wlr_renderer, size_t stride; struct wlr_dmabuf_attributes dmabuf; if (wlr_buffer_get_dmabuf(buffer, &dmabuf)) { - return gles2_texture_from_dmabuf_buffer(renderer, buffer, &dmabuf); + return gles2_texture_from_dmabuf(renderer, buffer, &dmabuf); } else if (wlr_buffer_begin_data_ptr_access(buffer, WLR_BUFFER_DATA_PTR_ACCESS_READ, &data, &format, &stride)) { struct wlr_texture *tex = gles2_texture_from_pixels(wlr_renderer, diff --git a/render/meson.build b/render/meson.build index f09905c..7c12540 100644 --- a/render/meson.build +++ b/render/meson.build @@ -6,8 +6,10 @@ elif 'auto' in renderers and get_option('auto_features').disabled() endif wlr_files += files( + 'color.c', 'dmabuf.c', 'drm_format_set.c', + 'drm_syncobj.c', 'pass.c', 'pixel_format.c', 'swapchain.c', @@ -21,8 +23,24 @@ else wlr_files += files('dmabuf_fallback.c') endif +internal_config.set10('HAVE_EVENTFD', cc.has_header('sys/eventfd.h')) + if 'gles2' in renderers or 'auto' in renderers egl = dependency('egl', required: 'gles2' in renderers) + if egl.found() + eglext_version = cc.get_define( + 'EGL_EGLEXT_VERSION', + dependencies: egl, + prefix: '#include ', + ).to_int() + if eglext_version < 20210604 + egl = dependency( + '', + required: 'gles2' in renderers, + not_found_message: 'EGL headers too old', + ) + endif + endif gbm = dependency('gbm', required: 'gles2' in renderers) if egl.found() and gbm.found() wlr_deps += [egl, gbm] @@ -39,3 +57,12 @@ endif subdir('pixman') subdir('allocator') + +lcms2 = dependency('lcms2', required: get_option('color-management')) +if lcms2.found() + wlr_deps += lcms2 + wlr_files += files('color_lcms2.c') + features += { 'color-management': true } +else + wlr_files += files('color_fallback.c') +endif diff --git a/render/pass.c b/render/pass.c index 243f780..9022282 100644 --- a/render/pass.c +++ b/render/pass.c @@ -1,16 +1,6 @@ #include -#include #include #include -#include -#include -#include "render/pass.h" - -struct wlr_render_pass_legacy { - struct wlr_render_pass base; - struct wlr_renderer *renderer; - int width, height; -}; void wlr_render_pass_init(struct wlr_render_pass *render_pass, const struct wlr_render_pass_impl *impl) { @@ -84,141 +74,3 @@ void wlr_render_rect_options_get_box(const struct wlr_render_rect_options *optio *box = options->box; } - -static const struct wlr_render_pass_impl legacy_impl; - -static struct wlr_render_pass_legacy *legacy_pass_from_pass(struct wlr_render_pass *pass) { - assert(pass->impl == &legacy_impl); - struct wlr_render_pass_legacy *legacy = wl_container_of(pass, legacy, base); - return legacy; -} - -static bool legacy_submit(struct wlr_render_pass *wlr_pass) { - struct wlr_render_pass_legacy *pass = legacy_pass_from_pass(wlr_pass); - wlr_renderer_end(pass->renderer); - free(pass); - return true; -} - -static void get_clip_region(struct wlr_render_pass_legacy *pass, - const pixman_region32_t *in, pixman_region32_t *out) { - if (in != NULL) { - pixman_region32_init(out); - pixman_region32_copy(out, in); - } else { - pixman_region32_init_rect(out, 0, 0, pass->width, pass->height); - } -} - -static void scissor(struct wlr_renderer *renderer, const pixman_box32_t *rect) { - struct wlr_box box = { - .x = rect->x1, - .y = rect->y1, - .width = rect->x2 - rect->x1, - .height = rect->y2 - rect->y1, - }; - wlr_renderer_scissor(renderer, &box); -} - -static void legacy_add_texture(struct wlr_render_pass *wlr_pass, - const struct wlr_render_texture_options *options) { - struct wlr_render_pass_legacy *pass = legacy_pass_from_pass(wlr_pass); - struct wlr_texture *texture = options->texture; - - struct wlr_fbox src_box; - wlr_render_texture_options_get_src_box(options, &src_box); - struct wlr_box dst_box; - wlr_render_texture_options_get_dst_box(options, &dst_box); - float alpha = wlr_render_texture_options_get_alpha(options); - - float proj[9], matrix[9]; - wlr_matrix_identity(proj); - wlr_matrix_project_box(matrix, &dst_box, options->transform, 0.0, proj); - - pixman_region32_t clip; - get_clip_region(pass, options->clip, &clip); - - float black[4] = {0}; - int rects_len = 0; - const pixman_box32_t *rects = pixman_region32_rectangles(&clip, &rects_len); - for (int i = 0; i < rects_len; i++) { - scissor(pass->renderer, &rects[i]); - - if (options->blend_mode == WLR_RENDER_BLEND_MODE_NONE) { - wlr_renderer_clear(pass->renderer, black); - } - - wlr_render_subtexture_with_matrix(pass->renderer, texture, &src_box, matrix, alpha); - } - - wlr_renderer_scissor(pass->renderer, NULL); - pixman_region32_fini(&clip); -} - -static void legacy_add_rect(struct wlr_render_pass *wlr_pass, - const struct wlr_render_rect_options *options) { - struct wlr_render_pass_legacy *pass = legacy_pass_from_pass(wlr_pass); - - float proj[9], matrix[9]; - wlr_matrix_identity(proj); - wlr_matrix_project_box(matrix, &options->box, WL_OUTPUT_TRANSFORM_NORMAL, 0.0, proj); - - pixman_region32_t clip; - get_clip_region(pass, options->clip, &clip); - pixman_region32_intersect_rect(&clip, &clip, - options->box.x, options->box.y, options->box.width, options->box.height); - - float color[4] = { - options->color.r, - options->color.g, - options->color.b, - options->color.a, - }; - - int rects_len = 0; - const pixman_box32_t *rects = pixman_region32_rectangles(&clip, &rects_len); - for (int i = 0; i < rects_len; i++) { - scissor(pass->renderer, &rects[i]); - switch (options->blend_mode) { - case WLR_RENDER_BLEND_MODE_PREMULTIPLIED: - wlr_render_quad_with_matrix(pass->renderer, color, matrix); - break; - case WLR_RENDER_BLEND_MODE_NONE: - wlr_renderer_clear(pass->renderer, color); - break; - } - } - - wlr_renderer_scissor(pass->renderer, NULL); - pixman_region32_fini(&clip); -} - -static const struct wlr_render_pass_impl legacy_impl = { - .submit = legacy_submit, - .add_texture = legacy_add_texture, - .add_rect = legacy_add_rect, -}; - -struct wlr_render_pass *begin_legacy_buffer_render_pass(struct wlr_renderer *renderer, - struct wlr_buffer *buffer) { - if (renderer->rendering) { - return NULL; - } - - struct wlr_render_pass_legacy *pass = calloc(1, sizeof(*pass)); - if (pass == NULL) { - return NULL; - } - - wlr_render_pass_init(&pass->base, &legacy_impl); - pass->renderer = renderer; - pass->width = buffer->width; - pass->height = buffer->height; - - if (!wlr_renderer_begin_with_buffer(renderer, buffer)) { - free(pass); - return NULL; - } - - return &pass->base; -} diff --git a/render/pixel_format.c b/render/pixel_format.c index b81f561..7befbf0 100644 --- a/render/pixel_format.c +++ b/render/pixel_format.c @@ -12,7 +12,6 @@ static const struct wlr_pixel_format_info pixel_format_info[] = { .drm_format = DRM_FORMAT_ARGB8888, .opaque_substitute = DRM_FORMAT_XRGB8888, .bytes_per_block = 4, - .has_alpha = true, }, { .drm_format = DRM_FORMAT_XBGR8888, @@ -22,7 +21,6 @@ static const struct wlr_pixel_format_info pixel_format_info[] = { .drm_format = DRM_FORMAT_ABGR8888, .opaque_substitute = DRM_FORMAT_XBGR8888, .bytes_per_block = 4, - .has_alpha = true, }, { .drm_format = DRM_FORMAT_RGBX8888, @@ -32,7 +30,6 @@ static const struct wlr_pixel_format_info pixel_format_info[] = { .drm_format = DRM_FORMAT_RGBA8888, .opaque_substitute = DRM_FORMAT_RGBX8888, .bytes_per_block = 4, - .has_alpha = true, }, { .drm_format = DRM_FORMAT_BGRX8888, @@ -42,7 +39,6 @@ static const struct wlr_pixel_format_info pixel_format_info[] = { .drm_format = DRM_FORMAT_BGRA8888, .opaque_substitute = DRM_FORMAT_BGRX8888, .bytes_per_block = 4, - .has_alpha = true, }, { .drm_format = DRM_FORMAT_R8, @@ -68,7 +64,6 @@ static const struct wlr_pixel_format_info pixel_format_info[] = { .drm_format = DRM_FORMAT_RGBA4444, .opaque_substitute = DRM_FORMAT_RGBX4444, .bytes_per_block = 2, - .has_alpha = true, }, { .drm_format = DRM_FORMAT_BGRX4444, @@ -78,7 +73,6 @@ static const struct wlr_pixel_format_info pixel_format_info[] = { .drm_format = DRM_FORMAT_BGRA4444, .opaque_substitute = DRM_FORMAT_BGRX4444, .bytes_per_block = 2, - .has_alpha = true, }, { .drm_format = DRM_FORMAT_RGBX5551, @@ -88,7 +82,6 @@ static const struct wlr_pixel_format_info pixel_format_info[] = { .drm_format = DRM_FORMAT_RGBA5551, .opaque_substitute = DRM_FORMAT_RGBX5551, .bytes_per_block = 2, - .has_alpha = true, }, { .drm_format = DRM_FORMAT_BGRX5551, @@ -98,7 +91,6 @@ static const struct wlr_pixel_format_info pixel_format_info[] = { .drm_format = DRM_FORMAT_BGRA5551, .opaque_substitute = DRM_FORMAT_BGRX5551, .bytes_per_block = 2, - .has_alpha = true, }, { .drm_format = DRM_FORMAT_XRGB1555, @@ -108,7 +100,6 @@ static const struct wlr_pixel_format_info pixel_format_info[] = { .drm_format = DRM_FORMAT_ARGB1555, .opaque_substitute = DRM_FORMAT_XRGB1555, .bytes_per_block = 2, - .has_alpha = true, }, { .drm_format = DRM_FORMAT_RGB565, @@ -126,7 +117,6 @@ static const struct wlr_pixel_format_info pixel_format_info[] = { .drm_format = DRM_FORMAT_ARGB2101010, .opaque_substitute = DRM_FORMAT_XRGB2101010, .bytes_per_block = 4, - .has_alpha = true, }, { .drm_format = DRM_FORMAT_XBGR2101010, @@ -136,7 +126,6 @@ static const struct wlr_pixel_format_info pixel_format_info[] = { .drm_format = DRM_FORMAT_ABGR2101010, .opaque_substitute = DRM_FORMAT_XBGR2101010, .bytes_per_block = 4, - .has_alpha = true, }, { .drm_format = DRM_FORMAT_XBGR16161616F, @@ -146,7 +135,6 @@ static const struct wlr_pixel_format_info pixel_format_info[] = { .drm_format = DRM_FORMAT_ABGR16161616F, .opaque_substitute = DRM_FORMAT_XBGR16161616F, .bytes_per_block = 8, - .has_alpha = true, }, { .drm_format = DRM_FORMAT_XBGR16161616, @@ -156,7 +144,6 @@ static const struct wlr_pixel_format_info pixel_format_info[] = { .drm_format = DRM_FORMAT_ABGR16161616, .opaque_substitute = DRM_FORMAT_XBGR16161616, .bytes_per_block = 8, - .has_alpha = true, }, { .drm_format = DRM_FORMAT_YVYU, @@ -172,9 +159,38 @@ static const struct wlr_pixel_format_info pixel_format_info[] = { }, }; +static const uint32_t opaque_pixel_formats[] = { + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_RGBX8888, + DRM_FORMAT_BGRX8888, + DRM_FORMAT_R8, + DRM_FORMAT_GR88, + DRM_FORMAT_RGB888, + DRM_FORMAT_BGR888, + DRM_FORMAT_RGBX4444, + DRM_FORMAT_BGRX4444, + DRM_FORMAT_RGBX5551, + DRM_FORMAT_BGRX5551, + DRM_FORMAT_XRGB1555, + DRM_FORMAT_RGB565, + DRM_FORMAT_BGR565, + DRM_FORMAT_XRGB2101010, + DRM_FORMAT_XBGR2101010, + DRM_FORMAT_XBGR16161616F, + DRM_FORMAT_XBGR16161616, + DRM_FORMAT_YVYU, + DRM_FORMAT_VYUY, + DRM_FORMAT_NV12, + DRM_FORMAT_P010, +}; + static const size_t pixel_format_info_size = sizeof(pixel_format_info) / sizeof(pixel_format_info[0]); +static const size_t opaque_pixel_formats_size = + sizeof(opaque_pixel_formats) / sizeof(opaque_pixel_formats[0]); + const struct wlr_pixel_format_info *drm_get_pixel_format_info(uint32_t fmt) { for (size_t i = 0; i < pixel_format_info_size; ++i) { if (pixel_format_info[i].drm_format == fmt) { @@ -250,3 +266,12 @@ bool pixel_format_info_check_stride(const struct wlr_pixel_format_info *fmt, return true; } + +bool pixel_format_has_alpha(uint32_t fmt) { + for (size_t i = 0; i < opaque_pixel_formats_size; i++) { + if (fmt == opaque_pixel_formats[i]) { + return false; + } + } + return true; +} diff --git a/render/pixman/pass.c b/render/pixman/pass.c index 5fe73b0..30883c8 100644 --- a/render/pixman/pass.c +++ b/render/pixman/pass.c @@ -47,6 +47,9 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, return; } + pixman_op_t op = get_pixman_blending(options->blend_mode); + pixman_image_set_clip_region32(buffer->image, (pixman_region32_t *)options->clip); + struct wlr_fbox src_fbox; wlr_render_texture_options_get_src_box(options, &src_fbox); struct wlr_box src_box = { @@ -67,14 +70,14 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, }); } - struct wlr_box orig_box; - wlr_box_transform(&orig_box, &dst_box, options->transform, + // Rotate the source size into destination coordinates + struct wlr_box src_box_transformed; + wlr_box_transform(&src_box_transformed, &src_box, options->transform, buffer->buffer->width, buffer->buffer->height); - int32_t dest_x, dest_y, width, height; if (options->transform != WL_OUTPUT_TRANSFORM_NORMAL || - orig_box.width != src_box.width || - orig_box.height != src_box.height) { + src_box_transformed.width != dst_box.width || + src_box_transformed.height != dst_box.height) { // Cosinus/sinus values are extact integers for enum wl_output_transform entries int tr_cos = 1, tr_sin = 0, tr_x = 0, tr_y = 0; switch (options->transform) { @@ -85,70 +88,107 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, case WL_OUTPUT_TRANSFORM_FLIPPED_90: tr_cos = 0; tr_sin = 1; - tr_x = buffer->buffer->height; + tr_y = src_box.width; break; case WL_OUTPUT_TRANSFORM_180: case WL_OUTPUT_TRANSFORM_FLIPPED_180: tr_cos = -1; tr_sin = 0; - tr_x = buffer->buffer->width; - tr_y = buffer->buffer->height; + tr_x = src_box.width; + tr_y = src_box.height; break; case WL_OUTPUT_TRANSFORM_270: case WL_OUTPUT_TRANSFORM_FLIPPED_270: tr_cos = 0; tr_sin = -1; - tr_y = buffer->buffer->width; + tr_x = src_box.height; break; } + // Pixman transforms are generally the opposite of what you expect because they + // apply to the coordinate system rather than the image. The comments here + // refer to what happens to the image, so all the code between + // pixman_transform_init_identity() and pixman_image_set_transform() is probably + // best read backwards. Also this means translations are in the opposite + // direction, imagine them as moving the origin around rather than moving the + // image. + // + // Beware that this doesn't work quite the same as wp_viewporter: We apply crop + // before transform and scale, whereas it defines crop in post-transform-scale + // coordinates. But this only applies to internal wlroots code - the viewporter + // extension code makes sure that to clients everything works as it should. + struct pixman_transform transform; pixman_transform_init_identity(&transform); + + // Apply scaling to get to the dst_box size. Because the scaling is applied last + // it depends on the whether the rotation swapped width and height, which is why + // we use src_box_transformed instead of src_box. + pixman_transform_scale(&transform, NULL, + pixman_double_to_fixed(src_box_transformed.width / (double)dst_box.width), + pixman_double_to_fixed(src_box_transformed.height / (double)dst_box.height)); + + // pixman rotates about the origin which again leaves everything outside of the + // viewport. Translate the result so that its new top-left corner is back at the + // origin. + pixman_transform_translate(&transform, NULL, + -pixman_int_to_fixed(tr_x), -pixman_int_to_fixed(tr_y)); + + // Apply the rotation pixman_transform_rotate(&transform, NULL, pixman_int_to_fixed(tr_cos), pixman_int_to_fixed(tr_sin)); + + // Apply flip before rotation if (options->transform >= WL_OUTPUT_TRANSFORM_FLIPPED) { + // The flip leaves everything left of the Y axis which is outside the + // viewport. So translate everything back into the viewport. + pixman_transform_translate(&transform, NULL, + -pixman_int_to_fixed(src_box.width), pixman_int_to_fixed(0)); + // Flip by applying a scale of -1 to the X axis pixman_transform_scale(&transform, NULL, pixman_int_to_fixed(-1), pixman_int_to_fixed(1)); } + + // Apply the translation for source crop so the origin is now at the top-left of + // the region we're actually using. Do this last so all the other transforms + // apply on top of this. pixman_transform_translate(&transform, NULL, - pixman_int_to_fixed(tr_x), pixman_int_to_fixed(tr_y)); - pixman_transform_translate(&transform, NULL, - -pixman_int_to_fixed(orig_box.x), -pixman_int_to_fixed(orig_box.y)); - pixman_transform_scale(&transform, NULL, - pixman_double_to_fixed(src_box.width / (double)orig_box.width), - pixman_double_to_fixed(src_box.height / (double)orig_box.height)); + pixman_int_to_fixed(src_box.x), pixman_int_to_fixed(src_box.y)); + pixman_image_set_transform(texture->image, &transform); - dest_x = dest_y = 0; - width = buffer->buffer->width; - height = buffer->buffer->height; + switch (options->filter_mode) { + case WLR_SCALE_FILTER_BILINEAR: + pixman_image_set_filter(texture->image, PIXMAN_FILTER_BILINEAR, NULL, 0); + break; + case WLR_SCALE_FILTER_NEAREST: + pixman_image_set_filter(texture->image, PIXMAN_FILTER_NEAREST, NULL, 0); + break; + } + + // Now composite the result onto the pass buffer. We specify a source origin of 0,0 + // because the x,y part of source crop is already done using the transform. The + // width,height part of source crop is done here by the width and height we pass: + // because of the scaling, cropping at the end by dst_box.{width,height} is + // equivalent to if we cropped at the start by src_box.{width,height}. + pixman_image_composite32(op, texture->image, mask, buffer->image, + 0, 0, // source x,y + 0, 0, // mask x,y + dst_box.x, dst_box.y, // dest x,y + dst_box.width, dst_box.height // composite width,height + ); + + pixman_image_set_transform(texture->image, NULL); } else { + // No transforms or crop needed, just a straight blit from the source pixman_image_set_transform(texture->image, NULL); - dest_x = dst_box.x; - dest_y = dst_box.y; - width = src_box.width; - height = src_box.height; + pixman_image_composite32(op, texture->image, mask, buffer->image, + src_box.x, src_box.y, 0, 0, dst_box.x, dst_box.y, + src_box.width, src_box.height); } - switch (options->filter_mode) { - case WLR_SCALE_FILTER_BILINEAR: - pixman_image_set_filter(texture->image, PIXMAN_FILTER_BILINEAR, NULL, 0); - break; - case WLR_SCALE_FILTER_NEAREST: - pixman_image_set_filter(texture->image, PIXMAN_FILTER_NEAREST, NULL, 0); - break; - } - - pixman_op_t op = get_pixman_blending(options->blend_mode); - - pixman_image_set_clip_region32(buffer->image, (pixman_region32_t *)options->clip); - pixman_image_composite32(op, texture->image, mask, - buffer->image, src_box.x, src_box.y, 0, 0, dest_x, dest_y, - width, height); pixman_image_set_clip_region32(buffer->image, NULL); - pixman_image_set_transform(texture->image, NULL); - if (texture->buffer != NULL) { wlr_buffer_end_data_ptr_access(texture->buffer); } diff --git a/render/pixman/renderer.c b/render/pixman/renderer.c index e30390f..a5b83e3 100644 --- a/render/pixman/renderer.c +++ b/render/pixman/renderer.c @@ -2,9 +2,8 @@ #include #include #include -#include +#include #include -#include #include #include @@ -90,7 +89,42 @@ static void texture_destroy(struct wlr_texture *wlr_texture) { free(texture); } +static bool texture_read_pixels(struct wlr_texture *wlr_texture, + const struct wlr_texture_read_pixels_options *options) { + struct wlr_pixman_texture *texture = get_texture(wlr_texture); + + struct wlr_box src; + wlr_texture_read_pixels_options_get_src_box(options, wlr_texture, &src); + + pixman_format_code_t fmt = get_pixman_format_from_drm(options->format); + if (fmt == 0) { + wlr_log(WLR_ERROR, "Cannot read pixels: unsupported pixel format"); + return false; + } + + void *p = wlr_texture_read_pixel_options_get_data(options); + + pixman_image_t *dst = pixman_image_create_bits_no_clear(fmt, + src.width, src.height, p, options->stride); + + pixman_image_composite32(PIXMAN_OP_SRC, texture->image, NULL, dst, + src.x, src.y, 0, 0, 0, 0, src.width, src.height); + + pixman_image_unref(dst); + + return true; +} + +static uint32_t pixman_texture_preferred_read_format(struct wlr_texture *wlr_texture) { + struct wlr_pixman_texture *texture = get_texture(wlr_texture); + + pixman_format_code_t pixman_format = pixman_image_get_format(texture->image); + return get_drm_format_from_pixman(pixman_format); +} + static const struct wlr_texture_impl texture_impl = { + .read_pixels = texture_read_pixels, + .preferred_read_format = pixman_texture_preferred_read_format, .destroy = texture_destroy, }; @@ -159,177 +193,14 @@ static struct wlr_pixman_buffer *create_buffer( return NULL; } -static bool pixman_begin(struct wlr_renderer *wlr_renderer, uint32_t width, - uint32_t height) { - struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); - renderer->width = width; - renderer->height = height; - - struct wlr_pixman_buffer *buffer = renderer->current_buffer; - assert(buffer != NULL); - - return begin_pixman_data_ptr_access(buffer->buffer, &buffer->image, - WLR_BUFFER_DATA_PTR_ACCESS_READ | WLR_BUFFER_DATA_PTR_ACCESS_WRITE); -} - -static void pixman_end(struct wlr_renderer *wlr_renderer) { - struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); - - assert(renderer->current_buffer != NULL); - - wlr_buffer_end_data_ptr_access(renderer->current_buffer->buffer); -} - -static void pixman_clear(struct wlr_renderer *wlr_renderer, - const float color[static 4]) { - struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); - struct wlr_pixman_buffer *buffer = renderer->current_buffer; - - const struct pixman_color colour = { - .red = color[0] * 0xFFFF, - .green = color[1] * 0xFFFF, - .blue = color[2] * 0xFFFF, - .alpha = color[3] * 0xFFFF, - }; - - pixman_image_t *fill = pixman_image_create_solid_fill(&colour); - - pixman_image_composite32(PIXMAN_OP_SRC, fill, NULL, buffer->image, 0, 0, 0, - 0, 0, 0, renderer->width, renderer->height); - - pixman_image_unref(fill); -} - -static void pixman_scissor(struct wlr_renderer *wlr_renderer, - struct wlr_box *box) { +static const struct wlr_drm_format_set *pixman_get_texture_formats( + struct wlr_renderer *wlr_renderer, uint32_t buffer_caps) { struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); - struct wlr_pixman_buffer *buffer = renderer->current_buffer; - - if (box != NULL) { - struct pixman_region32 region = {0}; - pixman_region32_init_rect(®ion, box->x, box->y, box->width, - box->height); - pixman_image_set_clip_region32(buffer->image, ®ion); - pixman_region32_fini(®ion); - } else { - pixman_image_set_clip_region32(buffer->image, NULL); - } -} - -static void matrix_to_pixman_transform(struct pixman_transform *transform, - const float mat[static 9]) { - struct pixman_f_transform ftr; - ftr.m[0][0] = mat[0]; - ftr.m[0][1] = mat[1]; - ftr.m[0][2] = mat[2]; - ftr.m[1][0] = mat[3]; - ftr.m[1][1] = mat[4]; - ftr.m[1][2] = mat[5]; - ftr.m[2][0] = mat[6]; - ftr.m[2][1] = mat[7]; - ftr.m[2][2] = mat[8]; - - pixman_transform_from_pixman_f_transform(transform, &ftr); -} - -static bool pixman_render_subtexture_with_matrix( - struct wlr_renderer *wlr_renderer, struct wlr_texture *wlr_texture, - const struct wlr_fbox *fbox, const float matrix[static 9], - float alpha) { - struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); - struct wlr_pixman_texture *texture = get_texture(wlr_texture); - struct wlr_pixman_buffer *buffer = renderer->current_buffer; - - if (texture->buffer != NULL && !begin_pixman_data_ptr_access(texture->buffer, - &texture->image, WLR_BUFFER_DATA_PTR_ACCESS_READ)) { - return false; - } - - pixman_image_t *mask = NULL; - if (alpha != 1.0) { - struct pixman_color mask_colour = {0}; - mask_colour.alpha = 0xFFFF * alpha; - mask = pixman_image_create_solid_fill(&mask_colour); - } - - float m[9]; - memcpy(m, matrix, sizeof(m)); - wlr_matrix_scale(m, 1.0 / fbox->width, 1.0 / fbox->height); - - struct pixman_transform transform = {0}; - matrix_to_pixman_transform(&transform, m); - pixman_transform_invert(&transform, &transform); - - pixman_image_set_transform(texture->image, &transform); - - // TODO clip properly with src_x and src_y - pixman_image_composite32(PIXMAN_OP_OVER, texture->image, mask, - buffer->image, 0, 0, 0, 0, 0, 0, renderer->width, - renderer->height); - - if (texture->buffer != NULL) { - wlr_buffer_end_data_ptr_access(texture->buffer); - } - - if (mask != NULL) { - pixman_image_unref(mask); - } - - return true; -} - -static void pixman_render_quad_with_matrix(struct wlr_renderer *wlr_renderer, - const float color[static 4], const float matrix[static 9]) { - struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); - struct wlr_pixman_buffer *buffer = renderer->current_buffer; - - struct pixman_color colour = { - .red = color[0] * 0xFFFF, - .green = color[1] * 0xFFFF, - .blue = color[2] * 0xFFFF, - .alpha = color[3] * 0xFFFF, - }; - - pixman_image_t *fill = pixman_image_create_solid_fill(&colour); - - float m[9]; - memcpy(m, matrix, sizeof(m)); - - // TODO get the width/height from the caller instead of extracting them - float width, height; - if (matrix[1] == 0.0 && matrix[3] == 0.0) { - width = fabs(matrix[0]); - height = fabs(matrix[4]); + if (buffer_caps & WLR_BUFFER_CAP_DATA_PTR) { + return &renderer->drm_formats; } else { - width = sqrt(matrix[0] * matrix[0] + matrix[1] * matrix[1]); - height = sqrt(matrix[3] * matrix[3] + matrix[4] * matrix[4]); + return NULL; } - - wlr_matrix_scale(m, 1.0 / width, 1.0 / height); - - pixman_image_t *image = pixman_image_create_bits(PIXMAN_a8r8g8b8, width, - height, NULL, 0); - - // TODO find a way to fill the image without allocating 2 images - pixman_image_composite32(PIXMAN_OP_SRC, fill, NULL, image, - 0, 0, 0, 0, 0, 0, width, height); - pixman_image_unref(fill); - - struct pixman_transform transform = {0}; - matrix_to_pixman_transform(&transform, m); - pixman_transform_invert(&transform, &transform); - - pixman_image_set_transform(image, &transform); - - pixman_image_composite32(PIXMAN_OP_OVER, image, NULL, buffer->image, - 0, 0, 0, 0, 0, 0, renderer->width, renderer->height); - - pixman_image_unref(image); -} - -static const uint32_t *pixman_get_shm_texture_formats( - struct wlr_renderer *wlr_renderer, size_t *len) { - return get_pixman_drm_formats(len); } static const struct wlr_drm_format_set *pixman_get_render_formats( @@ -403,33 +274,6 @@ static struct wlr_texture *pixman_texture_from_buffer( return &texture->wlr_texture; } -static bool pixman_bind_buffer(struct wlr_renderer *wlr_renderer, - struct wlr_buffer *wlr_buffer) { - struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); - - if (renderer->current_buffer != NULL) { - wlr_buffer_unlock(renderer->current_buffer->buffer); - renderer->current_buffer = NULL; - } - - if (wlr_buffer == NULL) { - return true; - } - - struct wlr_pixman_buffer *buffer = get_buffer(renderer, wlr_buffer); - if (buffer == NULL) { - buffer = create_buffer(renderer, wlr_buffer); - } - if (buffer == NULL) { - return false; - } - - wlr_buffer_lock(wlr_buffer); - renderer->current_buffer = buffer; - - return true; -} - static void pixman_destroy(struct wlr_renderer *wlr_renderer) { struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); @@ -448,49 +292,6 @@ static void pixman_destroy(struct wlr_renderer *wlr_renderer) { free(renderer); } -static uint32_t pixman_preferred_read_format( - struct wlr_renderer *wlr_renderer) { - struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); - struct wlr_pixman_buffer *buffer = renderer->current_buffer; - - pixman_format_code_t pixman_format = pixman_image_get_format( - buffer->image); - - return get_drm_format_from_pixman(pixman_format); -} - -static bool pixman_read_pixels(struct wlr_renderer *wlr_renderer, - uint32_t drm_format, uint32_t stride, - uint32_t width, uint32_t height, uint32_t src_x, uint32_t src_y, - uint32_t dst_x, uint32_t dst_y, void *data) { - struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); - struct wlr_pixman_buffer *buffer = renderer->current_buffer; - - pixman_format_code_t fmt = get_pixman_format_from_drm(drm_format); - if (fmt == 0) { - wlr_log(WLR_ERROR, "Cannot read pixels: unsupported pixel format"); - return false; - } - - const struct wlr_pixel_format_info *drm_fmt = - drm_get_pixel_format_info(drm_format); - assert(drm_fmt); - - pixman_image_t *dst = pixman_image_create_bits_no_clear(fmt, width, height, - data, stride); - - pixman_image_composite32(PIXMAN_OP_SRC, buffer->image, NULL, dst, - src_x, src_y, 0, 0, dst_x, dst_y, width, height); - - pixman_image_unref(dst); - - return true; -} - -static uint32_t pixman_get_render_buffer_caps(struct wlr_renderer *renderer) { - return WLR_BUFFER_CAP_DATA_PTR; -} - static struct wlr_render_pass *pixman_begin_buffer_pass(struct wlr_renderer *wlr_renderer, struct wlr_buffer *wlr_buffer, const struct wlr_buffer_pass_options *options) { struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); @@ -511,20 +312,10 @@ static struct wlr_render_pass *pixman_begin_buffer_pass(struct wlr_renderer *wlr } static const struct wlr_renderer_impl renderer_impl = { - .begin = pixman_begin, - .end = pixman_end, - .clear = pixman_clear, - .scissor = pixman_scissor, - .render_subtexture_with_matrix = pixman_render_subtexture_with_matrix, - .render_quad_with_matrix = pixman_render_quad_with_matrix, - .get_shm_texture_formats = pixman_get_shm_texture_formats, + .get_texture_formats = pixman_get_texture_formats, .get_render_formats = pixman_get_render_formats, .texture_from_buffer = pixman_texture_from_buffer, - .bind_buffer = pixman_bind_buffer, .destroy = pixman_destroy, - .preferred_read_format = pixman_preferred_read_format, - .read_pixels = pixman_read_pixels, - .get_render_buffer_caps = pixman_get_render_buffer_caps, .begin_buffer_pass = pixman_begin_buffer_pass, }; @@ -535,7 +326,8 @@ struct wlr_renderer *wlr_pixman_renderer_create(void) { } wlr_log(WLR_INFO, "Creating pixman renderer"); - wlr_renderer_init(&renderer->wlr_renderer, &renderer_impl); + wlr_renderer_init(&renderer->wlr_renderer, &renderer_impl, WLR_BUFFER_CAP_DATA_PTR); + renderer->wlr_renderer.features.output_color_transform = false; wl_list_init(&renderer->buffers); wl_list_init(&renderer->textures); @@ -552,14 +344,20 @@ struct wlr_renderer *wlr_pixman_renderer_create(void) { return &renderer->wlr_renderer; } +pixman_image_t *wlr_pixman_renderer_get_buffer_image( + struct wlr_renderer *wlr_renderer, struct wlr_buffer *wlr_buffer) { + struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); + struct wlr_pixman_buffer *buffer = get_buffer(renderer, wlr_buffer); + if (!buffer) { + buffer = create_buffer(renderer, wlr_buffer); + } + if (!buffer) { + return NULL; + } + return buffer->image; +} + pixman_image_t *wlr_pixman_texture_get_image(struct wlr_texture *wlr_texture) { struct wlr_pixman_texture *texture = get_texture(wlr_texture); return texture->image; } - -pixman_image_t *wlr_pixman_renderer_get_current_image( - struct wlr_renderer *wlr_renderer) { - struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); - assert(renderer->current_buffer); - return renderer->current_buffer->image; -} diff --git a/render/swapchain.c b/render/swapchain.c index a2b6373..a50dae5 100644 --- a/render/swapchain.c +++ b/render/swapchain.c @@ -113,7 +113,7 @@ struct wlr_buffer *wlr_swapchain_acquire(struct wlr_swapchain *swapchain, return slot_acquire(swapchain, free_slot, age); } -static bool swapchain_has_buffer(struct wlr_swapchain *swapchain, +bool wlr_swapchain_has_buffer(struct wlr_swapchain *swapchain, struct wlr_buffer *buffer) { for (size_t i = 0; i < WLR_SWAPCHAIN_CAP; i++) { struct wlr_swapchain_slot *slot = &swapchain->slots[i]; @@ -128,7 +128,7 @@ void wlr_swapchain_set_buffer_submitted(struct wlr_swapchain *swapchain, struct wlr_buffer *buffer) { assert(buffer != NULL); - if (!swapchain_has_buffer(swapchain, buffer)) { + if (!wlr_swapchain_has_buffer(swapchain, buffer)) { return; } diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 42bd6b4..c879b71 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -2,11 +2,14 @@ #include #include #include +#include +#include "render/color.h" #include "render/vulkan.h" #include "types/wlr_matrix.h" static const struct wlr_render_pass_impl render_pass_impl; +static const struct wlr_addon_interface vk_color_transform_impl; static struct wlr_vk_render_pass *get_render_pass(struct wlr_render_pass *wlr_pass) { assert(wlr_pass->impl == &render_pass_impl); @@ -14,6 +17,16 @@ static struct wlr_vk_render_pass *get_render_pass(struct wlr_render_pass *wlr_pa return pass; } +static struct wlr_vk_color_transform *get_color_transform( + struct wlr_color_transform *c, struct wlr_vk_renderer *renderer) { + struct wlr_addon *a = wlr_addon_find(&c->addons, renderer, &vk_color_transform_impl); + if (!a) { + return NULL; + } + struct wlr_vk_color_transform *transform = wl_container_of(a, transform, addon); + return transform; +} + static void bind_pipeline(struct wlr_vk_render_pass *pass, VkPipeline pipeline) { if (pipeline == pass->bound_pipeline) { return; @@ -48,6 +61,10 @@ static float color_to_linear(float non_linear) { non_linear / 12.92; } +static float color_to_linear_premult(float non_linear, float alpha) { + return (alpha == 0) ? 0 : color_to_linear(non_linear / alpha) * alpha; +} + static void mat3_to_mat4(const float mat3[9], float mat4[4][4]) { memset(mat4, 0, sizeof(float) * 16); mat4[0][0] = mat3[0]; @@ -83,7 +100,7 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { assert(stage_cb != NULL); renderer->stage.cb = NULL; - if (render_buffer->blend_image) { + if (!pass->srgb_pathway) { // Apply output shader to map blend image to actual output image vkCmdNextSubpass(render_cb->vk, VK_SUBPASS_CONTENTS_INLINE); @@ -99,14 +116,41 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { .uv_off = { 0, 0 }, .uv_size = { 1, 1 }, }; + size_t dim = pass->color_transform ? pass->color_transform->lut3d.dim_len : 1; + struct wlr_vk_frag_output_pcr_data frag_pcr_data = { + .lut_3d_offset = 0.5f / dim, + .lut_3d_scale = (float)(dim - 1) / dim, + }; mat3_to_mat4(final_matrix, vert_pcr_data.mat4); - bind_pipeline(pass, render_buffer->render_setup->output_pipe); + if (pass->color_transform) { + bind_pipeline(pass, render_buffer->plain.render_setup->output_pipe_lut3d); + } else { + bind_pipeline(pass, render_buffer->plain.render_setup->output_pipe_srgb); + } vkCmdPushConstants(render_cb->vk, renderer->output_pipe_layout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(vert_pcr_data), &vert_pcr_data); + vkCmdPushConstants(render_cb->vk, renderer->output_pipe_layout, + VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(vert_pcr_data), + sizeof(frag_pcr_data), &frag_pcr_data); + + VkDescriptorSet lut_ds; + if (pass->color_transform && pass->color_transform->type == COLOR_TRANSFORM_LUT_3D) { + struct wlr_vk_color_transform *transform = + get_color_transform(pass->color_transform, renderer); + assert(transform); + lut_ds = transform->lut_3d.ds; + } else { + lut_ds = renderer->output_ds_lut3d_dummy; + } + VkDescriptorSet ds[] = { + render_buffer->plain.blend_descriptor_set, // set 0 + lut_ds, // set 1 + }; + size_t ds_len = sizeof(ds) / sizeof(ds[0]); vkCmdBindDescriptorSets(render_cb->vk, VK_PIPELINE_BIND_POINT_GRAPHICS, renderer->output_pipe_layout, - 0, 1, &render_buffer->blend_descriptor_set, 0, NULL); + 0, ds_len, ds, 0, NULL); const pixman_region32_t *clip = rect_union_evaluate(&pass->updated_region); int clip_rects_len; @@ -124,14 +168,18 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { // insert acquire and release barriers for dmabuf-images uint32_t barrier_count = wl_list_length(&renderer->foreign_textures) + 1; + render_wait = calloc(barrier_count * WLR_DMABUF_MAX_PLANES, sizeof(*render_wait)); + if (render_wait == NULL) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + goto error; + } + VkImageMemoryBarrier *acquire_barriers = calloc(barrier_count, sizeof(*acquire_barriers)); VkImageMemoryBarrier *release_barriers = calloc(barrier_count, sizeof(*release_barriers)); - render_wait = calloc(barrier_count * WLR_DMABUF_MAX_PLANES, sizeof(*render_wait)); - if (acquire_barriers == NULL || release_barriers == NULL || render_wait == NULL) { + if (acquire_barriers == NULL || release_barriers == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); free(acquire_barriers); free(release_barriers); - free(render_wait); goto error; } @@ -139,9 +187,7 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { size_t idx = 0; uint32_t render_wait_len = 0; wl_list_for_each_safe(texture, tmp_tex, &renderer->foreign_textures, foreign_link) { - VkImageLayout src_layout = VK_IMAGE_LAYOUT_GENERAL; if (!texture->transitioned) { - src_layout = VK_IMAGE_LAYOUT_UNDEFINED; texture->transitioned = true; } @@ -151,7 +197,7 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { .srcQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT, .dstQueueFamilyIndex = renderer->dev->queue_family, .image = texture->image, - .oldLayout = src_layout, + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, .srcAccessMask = 0, // ignored anyways .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, @@ -198,26 +244,30 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { // also add acquire/release barriers for the current render buffer VkImageLayout src_layout = VK_IMAGE_LAYOUT_GENERAL; - if (!render_buffer->transitioned) { - src_layout = VK_IMAGE_LAYOUT_PREINITIALIZED; - render_buffer->transitioned = true; - } - - if (render_buffer->blend_image) { + if (pass->srgb_pathway) { + if (!render_buffer->srgb.transitioned) { + src_layout = VK_IMAGE_LAYOUT_PREINITIALIZED; + render_buffer->srgb.transitioned = true; + } + } else { + if (!render_buffer->plain.transitioned) { + src_layout = VK_IMAGE_LAYOUT_PREINITIALIZED; + render_buffer->plain.transitioned = true; + } // The render pass changes the blend image layout from // color attachment to read only, so on each frame, before // the render pass starts, we change it back VkImageLayout blend_src_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - if (!render_buffer->blend_transitioned) { + if (!render_buffer->plain.blend_transitioned) { blend_src_layout = VK_IMAGE_LAYOUT_UNDEFINED; - render_buffer->blend_transitioned = true; + render_buffer->plain.blend_transitioned = true; } VkImageMemoryBarrier blend_acq_barrier = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = render_buffer->blend_image, + .image = render_buffer->plain.blend_image, .oldLayout = blend_src_layout, .newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, .srcAccessMask = VK_ACCESS_SHADER_READ_BIT, @@ -393,6 +443,7 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { wlr_log(WLR_ERROR, "Failed to sync render buffer"); } + wlr_color_transform_unref(pass->color_transform); wlr_buffer_unlock(render_buffer->wlr_buffer); rect_union_finish(&pass->updated_region); free(pass); @@ -415,7 +466,7 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { static void render_pass_mark_box_updated(struct wlr_vk_render_pass *pass, const struct wlr_box *box) { - if (!pass->render_buffer->blend_image) { + if (pass->srgb_pathway) { return; } @@ -439,9 +490,9 @@ static void render_pass_add_rect(struct wlr_render_pass *wlr_pass, // colors in linear space as well (and vulkan then automatically // does the conversion for out sRGB render targets). float linear_color[] = { - color_to_linear(options->color.r), - color_to_linear(options->color.g), - color_to_linear(options->color.b), + color_to_linear_premult(options->color.r, options->color.a), + color_to_linear_premult(options->color.g, options->color.a), + color_to_linear_premult(options->color.b, options->color.a), options->color.a, // no conversion for alpha }; @@ -475,8 +526,11 @@ static void render_pass_add_rect(struct wlr_render_pass *wlr_pass, wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, proj); wlr_matrix_multiply(matrix, pass->projection, matrix); + struct wlr_vk_render_format_setup *setup = pass->srgb_pathway ? + pass->render_buffer->srgb.render_setup : + pass->render_buffer->plain.render_setup; struct wlr_vk_pipeline *pipe = setup_get_or_create_pipeline( - pass->render_buffer->render_setup, + setup, &(struct wlr_vk_pipeline_key) { .source = WLR_VK_SHADER_SOURCE_SINGLE_COLOR, .layout = { .ycbcr_format = NULL }, @@ -540,7 +594,6 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, const struct wlr_render_texture_options *options) { struct wlr_vk_render_pass *pass = get_render_pass(wlr_pass); struct wlr_vk_renderer *renderer = pass->renderer; - struct wlr_vk_render_buffer *render_buffer = pass->render_buffer; VkCommandBuffer cb = pass->command_buffer->vk; struct wlr_vk_texture *texture = vulkan_get_texture(options->texture); @@ -565,9 +618,6 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, wlr_render_texture_options_get_dst_box(options, &dst_box); float alpha = wlr_render_texture_options_get_alpha(options); - pixman_region32_t clip; - get_clip_region(pass, options->clip, &clip); - float proj[9], matrix[9]; wlr_matrix_identity(proj); wlr_matrix_project_box(matrix, &dst_box, options->transform, 0, proj); @@ -585,8 +635,11 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, }; mat3_to_mat4(matrix, vert_pcr_data.mat4); + struct wlr_vk_render_format_setup *setup = pass->srgb_pathway ? + pass->render_buffer->srgb.render_setup : + pass->render_buffer->plain.render_setup; struct wlr_vk_pipeline *pipe = setup_get_or_create_pipeline( - render_buffer->render_setup, + setup, &(struct wlr_vk_pipeline_key) { .source = WLR_VK_SHADER_SOURCE_TEXTURE, .layout = { @@ -620,6 +673,9 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(vert_pcr_data), sizeof(float), &alpha); + pixman_region32_t clip; + get_clip_region(pass, options->clip, &clip); + int clip_rects_len; const pixman_box32_t *clip_rects = pixman_region32_rectangles(&clip, &clip_rects_len); for (int i = 0; i < clip_rects_len; i++) { @@ -642,6 +698,8 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, } texture->last_used_cb = pass->command_buffer; + + pixman_region32_fini(&clip); } static const struct wlr_render_pass_impl render_pass_impl = { @@ -650,14 +708,261 @@ static const struct wlr_render_pass_impl render_pass_impl = { .add_texture = render_pass_add_texture, }; + +void vk_color_transform_destroy(struct wlr_addon *addon) { + struct wlr_vk_renderer *renderer = (struct wlr_vk_renderer *)addon->owner; + struct wlr_vk_color_transform *transform = wl_container_of(addon, transform, addon); + + VkDevice dev = renderer->dev->dev; + if (transform->lut_3d.image) { + vkDestroyImage(dev, transform->lut_3d.image, NULL); + vkDestroyImageView(dev, transform->lut_3d.image_view, NULL); + vkFreeMemory(dev, transform->lut_3d.memory, NULL); + vulkan_free_ds(renderer, transform->lut_3d.ds_pool, transform->lut_3d.ds); + } + + wl_list_remove(&transform->link); + wlr_addon_finish(&transform->addon); + free(transform); +} + +static bool create_3d_lut_image(struct wlr_vk_renderer *renderer, + const struct wlr_color_transform_lut3d *lut_3d, + VkImage *image, VkImageView *image_view, + VkDeviceMemory *memory, VkDescriptorSet *ds, + struct wlr_vk_descriptor_pool **ds_pool) { + VkDevice dev = renderer->dev->dev; + VkResult res; + + *image = VK_NULL_HANDLE; + *memory = VK_NULL_HANDLE; + *image_view = VK_NULL_HANDLE; + *ds = VK_NULL_HANDLE; + *ds_pool = NULL; + + // R32G32B32 is not a required Vulkan format + // TODO: use it when available + VkFormat format = VK_FORMAT_R32G32B32A32_SFLOAT; + + VkImageCreateInfo img_info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .imageType = VK_IMAGE_TYPE_3D, + .format = format, + .mipLevels = 1, + .arrayLayers = 1, + .samples = VK_SAMPLE_COUNT_1_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .extent = (VkExtent3D) { lut_3d->dim_len, lut_3d->dim_len, lut_3d->dim_len }, + .tiling = VK_IMAGE_TILING_OPTIMAL, + .usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, + }; + res = vkCreateImage(dev, &img_info, NULL, image); + if (res != VK_SUCCESS) { + wlr_vk_error("vkCreateImage failed", res); + return NULL; + } + + VkMemoryRequirements mem_reqs = {0}; + vkGetImageMemoryRequirements(dev, *image, &mem_reqs); + + int mem_type_index = vulkan_find_mem_type(renderer->dev, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, mem_reqs.memoryTypeBits); + if (mem_type_index == -1) { + wlr_log(WLR_ERROR, "Failed to find suitable memory type"); + goto fail_image; + } + + VkMemoryAllocateInfo mem_info = { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .allocationSize = mem_reqs.size, + .memoryTypeIndex = mem_type_index, + }; + res = vkAllocateMemory(dev, &mem_info, NULL, memory); + if (res != VK_SUCCESS) { + wlr_vk_error("vkAllocateMemory failed", res); + goto fail_image; + } + + res = vkBindImageMemory(dev, *image, *memory, 0); + if (res != VK_SUCCESS) { + wlr_vk_error("vkBindMemory failed", res); + goto fail_memory; + } + + VkImageViewCreateInfo view_info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .viewType = VK_IMAGE_VIEW_TYPE_3D, + .format = format, + .components.r = VK_COMPONENT_SWIZZLE_IDENTITY, + .components.g = VK_COMPONENT_SWIZZLE_IDENTITY, + .components.b = VK_COMPONENT_SWIZZLE_IDENTITY, + .components.a = VK_COMPONENT_SWIZZLE_IDENTITY, + .subresourceRange = (VkImageSubresourceRange) { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .image = *image, + }; + res = vkCreateImageView(dev, &view_info, NULL, image_view); + if (res != VK_SUCCESS) { + wlr_vk_error("vkCreateImageView failed", res); + goto fail_image; + } + + size_t bytes_per_block = 4 * sizeof(float); + size_t size = lut_3d->dim_len * lut_3d->dim_len * lut_3d->dim_len * bytes_per_block; + struct wlr_vk_buffer_span span = vulkan_get_stage_span(renderer, + size, bytes_per_block); + if (!span.buffer || span.alloc.size != size) { + wlr_log(WLR_ERROR, "Failed to retrieve staging buffer"); + goto fail_imageview; + } + + char *map = (char*)span.buffer->cpu_mapping + span.alloc.start; + float *dst = (float*)map; + size_t dim_len = lut_3d->dim_len; + for (size_t b_index = 0; b_index < dim_len; b_index++) { + for (size_t g_index = 0; g_index < dim_len; g_index++) { + for (size_t r_index = 0; r_index < dim_len; r_index++) { + size_t sample_index = r_index + dim_len * g_index + dim_len * dim_len * b_index; + size_t src_offset = 3 * sample_index; + size_t dst_offset = 4 * sample_index; + dst[dst_offset] = lut_3d->lut_3d[src_offset]; + dst[dst_offset + 1] = lut_3d->lut_3d[src_offset + 1]; + dst[dst_offset + 2] = lut_3d->lut_3d[src_offset + 2]; + dst[dst_offset + 3] = 1.0; + } + } + } + + VkCommandBuffer cb = vulkan_record_stage_cb(renderer); + vulkan_change_layout(cb, *image, + VK_IMAGE_LAYOUT_UNDEFINED, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT); + VkBufferImageCopy copy = { + .bufferOffset = span.alloc.start, + .imageExtent.width = lut_3d->dim_len, + .imageExtent.height = lut_3d->dim_len, + .imageExtent.depth = lut_3d->dim_len, + .imageSubresource.layerCount = 1, + .imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + }; + vkCmdCopyBufferToImage(cb, span.buffer->buffer, *image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©); + vulkan_change_layout(cb, *image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_ACCESS_SHADER_READ_BIT); + + *ds_pool = vulkan_alloc_texture_ds(renderer, + renderer->output_ds_lut3d_layout, ds); + if (!*ds_pool) { + wlr_log(WLR_ERROR, "Failed to allocate descriptor"); + goto fail_imageview; + } + + VkDescriptorImageInfo ds_img_info = { + .imageView = *image_view, + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + }; + VkWriteDescriptorSet ds_write = { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .dstSet = *ds, + .pImageInfo = &ds_img_info, + }; + vkUpdateDescriptorSets(dev, 1, &ds_write, 0, NULL); + + return true; + +fail_imageview: + vkDestroyImageView(dev, *image_view, NULL); +fail_memory: + vkFreeMemory(dev, *memory, NULL); +fail_image: + vkDestroyImage(dev, *image, NULL); + return false; +} + +static struct wlr_vk_color_transform *vk_color_transform_create( + struct wlr_vk_renderer *renderer, struct wlr_color_transform *transform) { + struct wlr_vk_color_transform *vk_transform = + calloc(1, sizeof(*vk_transform)); + if (!vk_transform) { + return NULL; + } + + if (transform->type == COLOR_TRANSFORM_LUT_3D) { + if (!create_3d_lut_image(renderer, &transform->lut3d, + &vk_transform->lut_3d.image, + &vk_transform->lut_3d.image_view, + &vk_transform->lut_3d.memory, + &vk_transform->lut_3d.ds, + &vk_transform->lut_3d.ds_pool)) { + free(vk_transform); + return NULL; + } + } + + wlr_addon_init(&vk_transform->addon, &transform->addons, + renderer, &vk_color_transform_impl); + wl_list_insert(&renderer->color_transforms, &vk_transform->link); + + return vk_transform; +} + + +static const struct wlr_addon_interface vk_color_transform_impl = { + "vk_color_transform", + .destroy = vk_color_transform_destroy, +}; + struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *renderer, - struct wlr_vk_render_buffer *buffer) { + struct wlr_vk_render_buffer *buffer, const struct wlr_buffer_pass_options *options) { + bool using_srgb_pathway; + if (options != NULL && options->color_transform != NULL) { + using_srgb_pathway = false; + + if (!get_color_transform(options->color_transform, renderer)) { + /* Try to create a new color transform */ + if (!vk_color_transform_create(renderer, options->color_transform)) { + wlr_log(WLR_ERROR, "Failed to create color transform"); + return NULL; + } + } + } else { + // Use srgb pathway if it is the default/has already been set up + using_srgb_pathway = buffer->srgb.framebuffer != VK_NULL_HANDLE; + } + + if (!using_srgb_pathway && !buffer->plain.image_view) { + struct wlr_dmabuf_attributes attribs; + wlr_buffer_get_dmabuf(buffer->wlr_buffer, &attribs); + if (!vulkan_setup_plain_framebuffer(buffer, &attribs)) { + wlr_log(WLR_ERROR, "Failed to set up blend image"); + return NULL; + } + } + struct wlr_vk_render_pass *pass = calloc(1, sizeof(*pass)); if (pass == NULL) { return NULL; } + wlr_render_pass_init(&pass->base, &render_pass_impl); pass->renderer = renderer; + pass->srgb_pathway = using_srgb_pathway; + if (options != NULL && options->color_transform != NULL) { + wlr_color_transform_ref(options->color_transform); + pass->color_transform = options->color_transform; + } rect_union_init(&pass->updated_region); @@ -678,6 +983,14 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend return NULL; } + if (!renderer->dummy3d_image_transitioned) { + renderer->dummy3d_image_transitioned = true; + vulkan_change_layout(cb->vk, renderer->dummy3d_image, + VK_IMAGE_LAYOUT_UNDEFINED, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + 0, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_ACCESS_SHADER_READ_BIT); + } + int width = buffer->wlr_buffer->width; int height = buffer->wlr_buffer->height; VkRect2D rect = { .extent = { width, height } }; @@ -685,10 +998,15 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend VkRenderPassBeginInfo rp_info = { .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, .renderArea = rect, - .renderPass = buffer->render_setup->render_pass, - .framebuffer = buffer->framebuffer, .clearValueCount = 0, }; + if (pass->srgb_pathway) { + rp_info.renderPass = buffer->srgb.render_setup->render_pass; + rp_info.framebuffer = buffer->srgb.framebuffer; + } else { + rp_info.renderPass = buffer->plain.render_setup->render_pass; + rp_info.framebuffer = buffer->plain.framebuffer; + } vkCmdBeginRenderPass(cb->vk, &rp_info, VK_SUBPASS_CONTENTS_INLINE); vkCmdSetViewport(cb->vk, 0, 1, &(VkViewport){ diff --git a/render/vulkan/pixel_format.c b/render/vulkan/pixel_format.c index ced6eb2..9319d0d 100644 --- a/render/vulkan/pixel_format.c +++ b/render/vulkan/pixel_format.c @@ -14,33 +14,33 @@ static const struct wlr_vk_format formats[] = { // order. { .drm = DRM_FORMAT_R8, - .vk = VK_FORMAT_R8_SRGB, - .is_srgb = true, + .vk = VK_FORMAT_R8_UNORM, + .vk_srgb = VK_FORMAT_R8_SRGB, }, { .drm = DRM_FORMAT_GR88, - .vk = VK_FORMAT_R8G8_SRGB, - .is_srgb = true, + .vk = VK_FORMAT_R8G8_UNORM, + .vk_srgb = VK_FORMAT_R8G8_SRGB, }, { .drm = DRM_FORMAT_RGB888, - .vk = VK_FORMAT_B8G8R8_SRGB, - .is_srgb = true, + .vk = VK_FORMAT_B8G8R8_UNORM, + .vk_srgb = VK_FORMAT_B8G8R8_SRGB, }, { .drm = DRM_FORMAT_BGR888, - .vk = VK_FORMAT_R8G8B8_SRGB, - .is_srgb = true, + .vk = VK_FORMAT_R8G8B8_UNORM, + .vk_srgb = VK_FORMAT_R8G8B8_SRGB, }, { .drm = DRM_FORMAT_XRGB8888, - .vk = VK_FORMAT_B8G8R8A8_SRGB, - .is_srgb = true, + .vk = VK_FORMAT_B8G8R8A8_UNORM, + .vk_srgb = VK_FORMAT_B8G8R8A8_SRGB, }, { .drm = DRM_FORMAT_XBGR8888, - .vk = VK_FORMAT_R8G8B8A8_SRGB, - .is_srgb = true, + .vk = VK_FORMAT_R8G8B8A8_UNORM, + .vk_srgb = VK_FORMAT_R8G8B8A8_SRGB, }, // The Vulkan _SRGB formats correspond to unpremultiplied alpha, but // the Wayland protocol specifies premultiplied alpha on electrical values @@ -230,13 +230,14 @@ const struct wlr_vk_format *vulkan_get_format_from_drm(uint32_t drm_format) { } const VkImageUsageFlags vulkan_render_usage = - VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | - VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; const VkImageUsageFlags vulkan_shm_tex_usage = VK_IMAGE_USAGE_SAMPLED_BIT | - VK_IMAGE_USAGE_TRANSFER_DST_BIT; + VK_IMAGE_USAGE_TRANSFER_DST_BIT | + VK_IMAGE_USAGE_TRANSFER_SRC_BIT; const VkImageUsageFlags vulkan_dma_tex_usage = - VK_IMAGE_USAGE_SAMPLED_BIT; + VK_IMAGE_USAGE_SAMPLED_BIT | + VK_IMAGE_USAGE_TRANSFER_SRC_BIT; static const VkFormatFeatureFlags render_features = VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | @@ -257,16 +258,28 @@ static const VkFormatFeatureFlags ycbcr_tex_features = VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT | VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT; +// vk_format_variant should be set to 0=VK_FORMAT_UNDEFINED when not used static bool query_modifier_usage_support(struct wlr_vk_device *dev, VkFormat vk_format, - VkImageUsageFlags usage, const VkDrmFormatModifierPropertiesEXT *m, + VkFormat vk_format_variant, VkImageUsageFlags usage, + const VkDrmFormatModifierPropertiesEXT *m, struct wlr_vk_format_modifier_props *out, const char **errmsg) { VkResult res; *errmsg = NULL; + VkFormat view_formats[2] = { + vk_format, + vk_format_variant, + }; + VkImageFormatListCreateInfoKHR listi = { + .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR, + .pViewFormats = view_formats, + .viewFormatCount = vk_format_variant ? 2 : 1, + }; VkPhysicalDeviceImageDrmFormatModifierInfoEXT modi = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT, .drmFormatModifier = m->drmFormatModifier, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .pNext = &listi, }; VkPhysicalDeviceExternalImageFormatInfo efmti = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO, @@ -278,6 +291,7 @@ static bool query_modifier_usage_support(struct wlr_vk_device *dev, VkFormat vk_ .type = VK_IMAGE_TYPE_2D, .format = vk_format, .usage = usage, + .flags = vk_format_variant ? VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT : 0, .tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT, .pNext = &efmti, }; @@ -314,6 +328,50 @@ static bool query_modifier_usage_support(struct wlr_vk_device *dev, VkFormat vk_ return true; } +static bool query_shm_support(struct wlr_vk_device *dev, VkFormat vk_format, + VkFormat vk_format_variant, VkImageFormatProperties *out, + const char **errmsg) { + VkResult res; + *errmsg = NULL; + + VkFormat view_formats[2] = { + vk_format, + vk_format_variant, + }; + VkImageFormatListCreateInfoKHR listi = { + .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR, + .pViewFormats = view_formats, + .viewFormatCount = vk_format_variant ? 2 : 1, + .pNext = NULL, + }; + VkPhysicalDeviceImageFormatInfo2 fmti = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2, + .type = VK_IMAGE_TYPE_2D, + .format = vk_format, + .tiling = VK_IMAGE_TILING_OPTIMAL, + .usage = vulkan_shm_tex_usage, + .flags = vk_format_variant ? VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT : 0, + .pNext = &listi, + }; + VkImageFormatProperties2 ifmtp = { + .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2, + }; + + res = vkGetPhysicalDeviceImageFormatProperties2(dev->phdev, &fmti, &ifmtp); + if (res != VK_SUCCESS) { + if (res == VK_ERROR_FORMAT_NOT_SUPPORTED) { + *errmsg = "unsupported format"; + } else { + wlr_vk_error("vkGetPhysicalDeviceImageFormatProperties2", res); + *errmsg = "failed to get format properties"; + } + return false; + } + + *out = ifmtp.imageFormatProperties; + return true; +} + static bool query_modifier_support(struct wlr_vk_device *dev, struct wlr_vk_format_props *props, size_t modifier_count) { VkDrmFormatModifierPropertiesListEXT modp = { @@ -343,6 +401,8 @@ static bool query_modifier_support(struct wlr_vk_device *dev, free(modp.pDrmFormatModifierProperties); free(props->dmabuf.render_mods); free(props->dmabuf.texture_mods); + props->dmabuf.render_mods = NULL; + props->dmabuf.texture_mods = NULL; return false; } @@ -356,7 +416,18 @@ static bool query_modifier_support(struct wlr_vk_device *dev, if ((m.drmFormatModifierTilingFeatures & render_features) == render_features && !props->format.is_ycbcr) { struct wlr_vk_format_modifier_props p = {0}; - if (query_modifier_usage_support(dev, props->format.vk, vulkan_render_usage, &m, &p, &errmsg)) { + bool supported = false; + if (query_modifier_usage_support(dev, props->format.vk, + props->format.vk_srgb, vulkan_render_usage, &m, &p, &errmsg)) { + supported = true; + p.has_mutable_srgb = props->format.vk_srgb != 0; + } + if (!supported && props->format.vk_srgb) { + supported = query_modifier_usage_support(dev, props->format.vk, + 0, vulkan_render_usage, &m, &p, &errmsg); + } + + if (supported) { props->dmabuf.render_mods[props->dmabuf.render_mod_count++] = p; wlr_drm_format_set_add(&dev->dmabuf_render_formats, props->format.drm, m.drmFormatModifier); @@ -379,7 +450,18 @@ static bool query_modifier_support(struct wlr_vk_device *dev, } if ((m.drmFormatModifierTilingFeatures & features) == features) { struct wlr_vk_format_modifier_props p = {0}; - if (query_modifier_usage_support(dev, props->format.vk, vulkan_dma_tex_usage, &m, &p, &errmsg)) { + bool supported = false; + if (query_modifier_usage_support(dev, props->format.vk, + props->format.vk_srgb, vulkan_dma_tex_usage, &m, &p, &errmsg)) { + supported = true; + p.has_mutable_srgb = props->format.vk_srgb != 0; + } + if (!supported && props->format.vk_srgb) { + supported = query_modifier_usage_support(dev, props->format.vk, + 0, vulkan_dma_tex_usage, &m, &p, &errmsg); + } + + if (supported) { props->dmabuf.texture_mods[props->dmabuf.texture_mod_count++] = p; wlr_drm_format_set_add(&dev->dmabuf_texture_formats, props->format.drm, m.drmFormatModifier); @@ -408,8 +490,6 @@ static bool query_modifier_support(struct wlr_vk_device *dev, void vulkan_format_props_query(struct wlr_vk_device *dev, const struct wlr_vk_format *format) { - VkResult res; - if (format->is_ycbcr && !dev->sampler_ycbcr_conversion) { return; } @@ -436,43 +516,39 @@ void vulkan_format_props_query(struct wlr_vk_device *dev, const struct wlr_pixel_format_info *format_info = drm_get_pixel_format_info(format->drm); // shm texture properties - const char *shm_texture_status; + char shm_texture_status[256]; + const char *errmsg = "unknown error"; if ((fmtp.formatProperties.optimalTilingFeatures & shm_tex_features) == shm_tex_features && !format->is_ycbcr && format_info != NULL) { - VkPhysicalDeviceImageFormatInfo2 fmti = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2, - .type = VK_IMAGE_TYPE_2D, - .format = format->vk, - .tiling = VK_IMAGE_TILING_OPTIMAL, - .usage = vulkan_shm_tex_usage, - }; - VkImageFormatProperties2 ifmtp = { - .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2, - }; - - res = vkGetPhysicalDeviceImageFormatProperties2(dev->phdev, &fmti, &ifmtp); - if (res != VK_SUCCESS) { - if (res == VK_ERROR_FORMAT_NOT_SUPPORTED) { - shm_texture_status = "✗ texture (unsupported format)"; - } else { - wlr_vk_error("vkGetPhysicalDeviceImageFormatProperties2", res); - shm_texture_status = "✗ texture (failed to get format properties)"; - } - } else { - VkExtent3D me = ifmtp.imageFormatProperties.maxExtent; - props.shm.max_extent.width = me.width; - props.shm.max_extent.height = me.height; - props.shm.features = fmtp.formatProperties.optimalTilingFeatures; + VkImageFormatProperties ifmtp; + bool supported = false, has_mutable_srgb = false; + if (query_shm_support(dev, format->vk, format->vk_srgb, &ifmtp, &errmsg)) { + supported = true; + has_mutable_srgb = format->vk_srgb != 0; + } + if (!supported && format->vk_srgb) { + supported = query_shm_support(dev, format->vk, 0, &ifmtp, &errmsg); + } - shm_texture_status = "✓ texture"; + if (supported) { + props.shm.max_extent.width = ifmtp.maxExtent.width; + props.shm.max_extent.height = ifmtp.maxExtent.height; + props.shm.features = fmtp.formatProperties.optimalTilingFeatures; + props.shm.has_mutable_srgb = has_mutable_srgb; - dev->shm_formats[dev->shm_format_count] = format->drm; - ++dev->shm_format_count; + wlr_drm_format_set_add(&dev->shm_texture_formats, + format->drm, DRM_FORMAT_MOD_LINEAR); add_fmt_props = true; } } else { - shm_texture_status = "✗ texture (missing required features)"; + errmsg = "missing required features"; + } + + if (errmsg != NULL) { + snprintf(shm_texture_status, sizeof(shm_texture_status), "✗ texture (%s)", errmsg); + } else { + snprintf(shm_texture_status, sizeof(shm_texture_status), "✓ texture"); } wlr_log(WLR_DEBUG, " Shared memory: %s", shm_texture_status); @@ -495,7 +571,7 @@ void vulkan_format_props_finish(struct wlr_vk_format_props *props) { } const struct wlr_vk_format_modifier_props *vulkan_format_props_find_modifier( - struct wlr_vk_format_props *props, uint64_t mod, bool render) { + const struct wlr_vk_format_props *props, uint64_t mod, bool render) { uint32_t len; const struct wlr_vk_format_modifier_props *mods; if (render) { diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index d07c7c4..32effb5 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -9,9 +8,9 @@ #include #include #include +#include #include #include -#include #include #include #include @@ -60,27 +59,6 @@ static struct wlr_vk_render_format_setup *find_or_create_render_setup( struct wlr_vk_renderer *renderer, const struct wlr_vk_format *format, bool has_blending_buffer); -// https://www.w3.org/Graphics/Color/srgb -static float color_to_linear(float non_linear) { - return (non_linear > 0.04045) ? - pow((non_linear + 0.055) / 1.055, 2.4) : - non_linear / 12.92; -} - -static void mat3_to_mat4(const float mat3[9], float mat4[4][4]) { - memset(mat4, 0, sizeof(float) * 16); - mat4[0][0] = mat3[0]; - mat4[0][1] = mat3[1]; - mat4[0][3] = mat3[2]; - - mat4[1][0] = mat3[3]; - mat4[1][1] = mat3[4]; - mat4[1][3] = mat3[5]; - - mat4[2][2] = 1.f; - mat4[3][3] = 1.f; -} - static struct wlr_vk_descriptor_pool *alloc_ds( struct wlr_vk_renderer *renderer, VkDescriptorSet *ds, VkDescriptorType type, const VkDescriptorSetLayout *layout, @@ -161,7 +139,7 @@ struct wlr_vk_descriptor_pool *vulkan_alloc_texture_ds( struct wlr_vk_descriptor_pool *vulkan_alloc_blend_ds( struct wlr_vk_renderer *renderer, VkDescriptorSet *ds) { return alloc_ds(renderer, ds, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, - &renderer->output_ds_layout, &renderer->output_descriptor_pools, + &renderer->output_ds_srgb_layout, &renderer->output_descriptor_pools, &renderer->last_output_pool_size); } @@ -179,13 +157,16 @@ static void destroy_render_format_setup(struct wlr_vk_renderer *renderer, VkDevice dev = renderer->dev->dev; vkDestroyRenderPass(dev, setup->render_pass, NULL); - vkDestroyPipeline(dev, setup->output_pipe, NULL); + vkDestroyPipeline(dev, setup->output_pipe_srgb, NULL); + vkDestroyPipeline(dev, setup->output_pipe_lut3d, NULL); struct wlr_vk_pipeline *pipeline, *tmp_pipeline; wl_list_for_each_safe(pipeline, tmp_pipeline, &setup->pipelines, link) { vkDestroyPipeline(dev, pipeline->vk, NULL); free(pipeline); } + + free(setup); } static void shared_buffer_destroy(struct wlr_vk_renderer *r, @@ -200,6 +181,10 @@ static void shared_buffer_destroy(struct wlr_vk_renderer *r, } wl_array_release(&buffer->allocs); + if (buffer->cpu_mapping) { + vkUnmapMemory(r->dev->dev, buffer->memory); + buffer->cpu_mapping = NULL; + } if (buffer->buffer) { vkDestroyBuffer(r->dev->dev, buffer->buffer, NULL); } @@ -324,6 +309,12 @@ struct wlr_vk_buffer_span vulkan_get_stage_span(struct wlr_vk_renderer *r, goto error; } + res = vkMapMemory(r->dev->dev, buf->memory, 0, VK_WHOLE_SIZE, 0, &buf->cpu_mapping); + if (res != VK_SUCCESS) { + wlr_vk_error("vkMapMemory", res); + goto error; + } + struct wlr_vk_allocation *a = wl_array_add(&buf->allocs, sizeof(*a)); if (a == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); @@ -479,6 +470,11 @@ static void release_command_buffer_resources(struct wlr_vk_command_buffer *cb, wl_list_remove(&buf->link); wl_list_insert(&renderer->stage.buffers, &buf->link); } + + if (cb->color_transform) { + wlr_color_transform_unref(cb->color_transform); + cb->color_transform = NULL; + } } static struct wlr_vk_command_buffer *get_command_buffer( @@ -584,8 +580,6 @@ static void destroy_render_buffer(struct wlr_vk_render_buffer *buffer) { wl_list_remove(&buffer->link); wlr_addon_finish(&buffer->addon); - assert(buffer->renderer->current_render_buffer != buffer); - VkDevice dev = buffer->renderer->dev->dev; // TODO: asynchronously wait for the command buffers using this render @@ -595,20 +589,22 @@ static void destroy_render_buffer(struct wlr_vk_render_buffer *buffer) { wlr_vk_error("vkQueueWaitIdle", res); } - vkDestroyFramebuffer(dev, buffer->framebuffer, NULL); - vkDestroyImageView(dev, buffer->image_view, NULL); - vkDestroyImage(dev, buffer->image, NULL); + vkDestroyFramebuffer(dev, buffer->srgb.framebuffer, NULL); + vkDestroyImageView(dev, buffer->srgb.image_view, NULL); - for (size_t i = 0u; i < buffer->mem_count; ++i) { - vkFreeMemory(dev, buffer->memories[i], NULL); + vkDestroyFramebuffer(dev, buffer->plain.framebuffer, NULL); + vkDestroyImageView(dev, buffer->plain.image_view, NULL); + vkDestroyImage(dev, buffer->plain.blend_image, NULL); + vkFreeMemory(dev, buffer->plain.blend_memory, NULL); + vkDestroyImageView(dev, buffer->plain.blend_image_view, NULL); + if (buffer->plain.blend_attachment_pool) { + vulkan_free_ds(buffer->renderer, buffer->plain.blend_attachment_pool, + buffer->plain.blend_descriptor_set); } - vkDestroyImage(dev, buffer->blend_image, NULL); - vkFreeMemory(dev, buffer->blend_memory, NULL); - vkDestroyImageView(dev, buffer->blend_image_view, NULL); - if (buffer->blend_attachment_pool) { - vulkan_free_ds(buffer->renderer, buffer->blend_attachment_pool, - buffer->blend_descriptor_set); + vkDestroyImage(dev, buffer->image, NULL); + for (size_t i = 0u; i < buffer->mem_count; ++i) { + vkFreeMemory(dev, buffer->memories[i], NULL); } free(buffer); @@ -624,11 +620,46 @@ static struct wlr_addon_interface render_buffer_addon_impl = { .destroy = handle_render_buffer_destroy, }; -static bool setup_blend_image(struct wlr_vk_renderer *renderer, - struct wlr_vk_render_buffer *buffer, int32_t width, int32_t height) { +bool vulkan_setup_plain_framebuffer(struct wlr_vk_render_buffer *buffer, + const struct wlr_dmabuf_attributes *dmabuf) { + struct wlr_vk_renderer *renderer = buffer->renderer; VkResult res; VkDevice dev = renderer->dev->dev; + const struct wlr_vk_format_props *fmt = vulkan_format_props_from_drm( + renderer->dev, dmabuf->format); + assert(fmt); + + VkImageViewCreateInfo view_info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = buffer->image, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = fmt->format.vk, + .components.r = VK_COMPONENT_SWIZZLE_IDENTITY, + .components.g = VK_COMPONENT_SWIZZLE_IDENTITY, + .components.b = VK_COMPONENT_SWIZZLE_IDENTITY, + .components.a = VK_COMPONENT_SWIZZLE_IDENTITY, + .subresourceRange = (VkImageSubresourceRange) { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }; + + res = vkCreateImageView(dev, &view_info, NULL, &buffer->plain.image_view); + if (res != VK_SUCCESS) { + wlr_vk_error("vkCreateImageView failed", res); + goto error; + } + + buffer->plain.render_setup = find_or_create_render_setup( + renderer, &fmt->format, true); + if (!buffer->plain.render_setup) { + goto error; + } + // Set up an extra 16F buffer on which to do linear blending, // and afterwards to render onto the target VkImageCreateInfo img_info = { @@ -641,18 +672,18 @@ static bool setup_blend_image(struct wlr_vk_renderer *renderer, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .tiling = VK_IMAGE_TILING_OPTIMAL, .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .extent = (VkExtent3D) { width, height, 1 }, + .extent = (VkExtent3D) { dmabuf->width, dmabuf->height, 1 }, .usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT, }; - res = vkCreateImage(dev, &img_info, NULL, &buffer->blend_image); + res = vkCreateImage(dev, &img_info, NULL, &buffer->plain.blend_image); if (res != VK_SUCCESS) { wlr_vk_error("vkCreateImage failed", res); goto error; } VkMemoryRequirements mem_reqs; - vkGetImageMemoryRequirements(dev, buffer->blend_image, &mem_reqs); + vkGetImageMemoryRequirements(dev, buffer->plain.blend_image, &mem_reqs); int mem_type_index = vulkan_find_mem_type(renderer->dev, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, mem_reqs.memoryTypeBits); @@ -667,13 +698,13 @@ static bool setup_blend_image(struct wlr_vk_renderer *renderer, .memoryTypeIndex = mem_type_index, }; - res = vkAllocateMemory(dev, &mem_info, NULL, &buffer->blend_memory); + res = vkAllocateMemory(dev, &mem_info, NULL, &buffer->plain.blend_memory); if (res != VK_SUCCESS) { wlr_vk_error("vkAllocatorMemory failed", res); goto error; } - res = vkBindImageMemory(dev, buffer->blend_image, buffer->blend_memory, 0); + res = vkBindImageMemory(dev, buffer->plain.blend_image, buffer->plain.blend_memory, 0); if (res != VK_SUCCESS) { wlr_vk_error("vkBindMemory failed", res); goto error; @@ -681,7 +712,7 @@ static bool setup_blend_image(struct wlr_vk_renderer *renderer, VkImageViewCreateInfo blend_view_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - .image = buffer->blend_image, + .image = buffer->plain.blend_image, .viewType = VK_IMAGE_VIEW_TYPE_2D, .format = img_info.format, .components.r = VK_COMPONENT_SWIZZLE_IDENTITY, @@ -697,82 +728,81 @@ static bool setup_blend_image(struct wlr_vk_renderer *renderer, }, }; - res = vkCreateImageView(dev, &blend_view_info, NULL, &buffer->blend_image_view); + res = vkCreateImageView(dev, &blend_view_info, NULL, &buffer->plain.blend_image_view); if (res != VK_SUCCESS) { wlr_vk_error("vkCreateImageView failed", res); goto error; } - buffer->blend_attachment_pool = vulkan_alloc_blend_ds(renderer, - &buffer->blend_descriptor_set); - if (!buffer->blend_attachment_pool) { + buffer->plain.blend_attachment_pool = vulkan_alloc_blend_ds(renderer, + &buffer->plain.blend_descriptor_set); + if (!buffer->plain.blend_attachment_pool) { wlr_log(WLR_ERROR, "failed to allocate descriptor"); goto error; } VkDescriptorImageInfo ds_attach_info = { .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - .imageView = buffer->blend_image_view, + .imageView = buffer->plain.blend_image_view, .sampler = VK_NULL_HANDLE, }; VkWriteDescriptorSet ds_write = { .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .descriptorCount = 1, .descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, - .dstSet = buffer->blend_descriptor_set, + .dstSet = buffer->plain.blend_descriptor_set, .dstBinding = 0, .pImageInfo = &ds_attach_info, }; vkUpdateDescriptorSets(dev, 1, &ds_write, 0, NULL); + + VkImageView attachments[2] = { + buffer->plain.blend_image_view, + buffer->plain.image_view + }; + VkFramebufferCreateInfo fb_info = { + .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + .attachmentCount = 2, + .pAttachments = attachments, + .flags = 0u, + .width = dmabuf->width, + .height = dmabuf->height, + .layers = 1u, + .renderPass = buffer->plain.render_setup->render_pass, + }; + + res = vkCreateFramebuffer(dev, &fb_info, NULL, &buffer->plain.framebuffer); + if (res != VK_SUCCESS) { + wlr_vk_error("vkCreateFramebuffer", res); + goto error; + } + return true; error: - // cleaning up blend_attachment_pool, blend_descriptor_set, blend_image, - // blend_memory, and blend_image_view is the caller's responsibility, + // cleaning up everything is the caller's responsibility, // since it will need to do this anyway if framebuffer setup fails return false; } -static struct wlr_vk_render_buffer *create_render_buffer( - struct wlr_vk_renderer *renderer, struct wlr_buffer *wlr_buffer) { +static bool vulkan_setup_srgb_framebuffer(struct wlr_vk_render_buffer *buffer, + const struct wlr_dmabuf_attributes *dmabuf) { + struct wlr_vk_renderer *renderer = buffer->renderer; VkResult res; VkDevice dev = renderer->dev->dev; - struct wlr_vk_render_buffer *buffer = calloc(1, sizeof(*buffer)); - if (buffer == NULL) { - wlr_log_errno(WLR_ERROR, "Allocation failed"); - return NULL; - } - buffer->wlr_buffer = wlr_buffer; - buffer->renderer = renderer; - - struct wlr_dmabuf_attributes dmabuf = {0}; - if (!wlr_buffer_get_dmabuf(wlr_buffer, &dmabuf)) { - goto error; - } - - wlr_log(WLR_DEBUG, "vulkan create_render_buffer: %.4s, %dx%d", - (const char*) &dmabuf.format, dmabuf.width, dmabuf.height); - - buffer->image = vulkan_import_dmabuf(renderer, &dmabuf, - buffer->memories, &buffer->mem_count, true); - if (!buffer->image) { - goto error; - } - const struct wlr_vk_format_props *fmt = vulkan_format_props_from_drm( - renderer->dev, dmabuf.format); - if (fmt == NULL) { - wlr_log(WLR_ERROR, "Unsupported pixel format %"PRIx32 " (%.4s)", - dmabuf.format, (const char*) &dmabuf.format); - goto error; - } + renderer->dev, dmabuf->format); + assert(fmt); + assert(fmt->format.vk_srgb); + // Set up the srgb framebuffer by default; plain framebuffer and + // blending image will be set up later if necessary VkImageViewCreateInfo view_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .image = buffer->image, .viewType = VK_IMAGE_VIEW_TYPE_2D, - .format = fmt->format.vk, + .format = fmt->format.vk_srgb, .components.r = VK_COMPONENT_SWIZZLE_IDENTITY, .components.g = VK_COMPONENT_SWIZZLE_IDENTITY, .components.b = VK_COMPONENT_SWIZZLE_IDENTITY, @@ -786,73 +816,96 @@ static struct wlr_vk_render_buffer *create_render_buffer( }, }; - res = vkCreateImageView(dev, &view_info, NULL, &buffer->image_view); + res = vkCreateImageView(dev, &view_info, NULL, &buffer->srgb.image_view); if (res != VK_SUCCESS) { wlr_vk_error("vkCreateImageView failed", res); goto error; } - bool has_blending_buffer = !fmt->format.is_srgb; - - buffer->render_setup = find_or_create_render_setup( - renderer, &fmt->format, has_blending_buffer); - if (!buffer->render_setup) { + buffer->srgb.render_setup = find_or_create_render_setup( + renderer, &fmt->format, false); + if (!buffer->srgb.render_setup) { goto error; } - VkImageView attachments[2] = {0}; - uint32_t attachment_count = 0; - - if (has_blending_buffer) { - if (!setup_blend_image(renderer, buffer, dmabuf.width, dmabuf.height)) { - goto error; - } - attachments[attachment_count++] = buffer->blend_image_view; - } - attachments[attachment_count++] = buffer->image_view; - VkFramebufferCreateInfo fb_info = { .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, - .attachmentCount = attachment_count, - .pAttachments = attachments, + .attachmentCount = 1, + .pAttachments = &buffer->srgb.image_view, .flags = 0u, - .width = dmabuf.width, - .height = dmabuf.height, + .width = dmabuf->width, + .height = dmabuf->height, .layers = 1u, - .renderPass = buffer->render_setup->render_pass, + .renderPass = buffer->srgb.render_setup->render_pass, }; - res = vkCreateFramebuffer(dev, &fb_info, NULL, &buffer->framebuffer); + res = vkCreateFramebuffer(dev, &fb_info, NULL, &buffer->srgb.framebuffer); if (res != VK_SUCCESS) { wlr_vk_error("vkCreateFramebuffer", res); goto error; } + return true; +error: + // cleaning up everything is the caller's responsibility, + // since it will need to do this anyway if framebuffer setup fails + return false; +} - +static struct wlr_vk_render_buffer *create_render_buffer( + struct wlr_vk_renderer *renderer, struct wlr_buffer *wlr_buffer) { + struct wlr_vk_render_buffer *buffer = calloc(1, sizeof(*buffer)); + if (buffer == NULL) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + return NULL; + } + buffer->wlr_buffer = wlr_buffer; + buffer->renderer = renderer; wlr_addon_init(&buffer->addon, &wlr_buffer->addons, renderer, &render_buffer_addon_impl); wl_list_insert(&renderer->render_buffers, &buffer->link); - return buffer; + struct wlr_dmabuf_attributes dmabuf = {0}; + if (!wlr_buffer_get_dmabuf(wlr_buffer, &dmabuf)) { + goto error; + } -error: - if (buffer->blend_attachment_pool) { - vulkan_free_ds(buffer->renderer, buffer->blend_attachment_pool, - buffer->blend_descriptor_set); + wlr_log(WLR_DEBUG, "vulkan create_render_buffer: %.4s, %dx%d", + (const char*) &dmabuf.format, dmabuf.width, dmabuf.height); + + bool using_mutable_srgb = false; + buffer->image = vulkan_import_dmabuf(renderer, &dmabuf, + buffer->memories, &buffer->mem_count, true, &using_mutable_srgb); + if (!buffer->image) { + goto error; } - vkDestroyImage(dev, buffer->blend_image, NULL); - vkFreeMemory(dev, buffer->blend_memory, NULL); - vkDestroyImageView(dev, buffer->blend_image_view, NULL); - vkDestroyFramebuffer(dev, buffer->framebuffer, NULL); - vkDestroyImageView(dev, buffer->image_view, NULL); - vkDestroyImage(dev, buffer->image, NULL); - for (size_t i = 0u; i < buffer->mem_count; ++i) { - vkFreeMemory(dev, buffer->memories[i], NULL); + const struct wlr_vk_format_props *fmt = vulkan_format_props_from_drm( + renderer->dev, dmabuf.format); + if (fmt == NULL) { + wlr_log(WLR_ERROR, "Unsupported pixel format %"PRIx32 " (%.4s)", + dmabuf.format, (const char*) &dmabuf.format); + goto error; + } + + if (using_mutable_srgb) { + if (!vulkan_setup_srgb_framebuffer(buffer, &dmabuf)) { + goto error; + } + } else { + // Set up the plain framebuffer & blending image + if (!vulkan_setup_plain_framebuffer(buffer, &dmabuf)) { + goto error; + } + } + + return buffer; + +error: + if (buffer) { + destroy_render_buffer(buffer); } wlr_dmabuf_attributes_finish(&dmabuf); - free(buffer); return NULL; } @@ -868,86 +921,6 @@ static struct wlr_vk_render_buffer *get_render_buffer( return buffer; } -static bool vulkan_bind_buffer(struct wlr_renderer *wlr_renderer, - struct wlr_buffer *wlr_buffer) { - struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); - - if (renderer->current_render_buffer) { - wlr_buffer_unlock(renderer->current_render_buffer->wlr_buffer); - renderer->current_render_buffer = NULL; - } - - if (!wlr_buffer) { - return true; - } - - struct wlr_vk_render_buffer *buffer = get_render_buffer(renderer, wlr_buffer); - if (!buffer) { - buffer = create_render_buffer(renderer, wlr_buffer); - if (!buffer) { - return false; - } - } - - wlr_buffer_lock(wlr_buffer); - renderer->current_render_buffer = buffer; - return true; -} - -static bool vulkan_begin(struct wlr_renderer *wlr_renderer, - uint32_t width, uint32_t height) { - struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); - assert(renderer->current_render_buffer); - - struct wlr_vk_command_buffer *cb = vulkan_acquire_command_buffer(renderer); - if (cb == NULL) { - return false; - } - - assert(renderer->current_command_buffer == NULL); - renderer->current_command_buffer = cb; - - VkCommandBufferBeginInfo begin_info = { - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, - }; - VkResult res = vkBeginCommandBuffer(cb->vk, &begin_info); - if (res != VK_SUCCESS) { - wlr_vk_error("vkBeginCommandBuffer", res); - return false; - } - - // begin render pass - VkFramebuffer fb = renderer->current_render_buffer->framebuffer; - - VkRect2D rect = {{0, 0}, {width, height}}; - renderer->scissor = rect; - - VkRenderPassBeginInfo rp_info = { - .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, - .renderArea = rect, - .renderPass = renderer->current_render_buffer->render_setup->render_pass, - .framebuffer = fb, - .clearValueCount = 0, - }; - vkCmdBeginRenderPass(cb->vk, &rp_info, VK_SUBPASS_CONTENTS_INLINE); - - VkViewport vp = {0.f, 0.f, (float) width, (float) height, 0.f, 1.f}; - vkCmdSetViewport(cb->vk, 0, 1, &vp); - vkCmdSetScissor(cb->vk, 0, 1, &rect); - - // Refresh projection matrix. - // matrix_projection() assumes a GL coordinate system so we need - // to pass WL_OUTPUT_TRANSFORM_FLIPPED_180 to adjust it for vulkan. - matrix_projection(renderer->projection, width, height, - WL_OUTPUT_TRANSFORM_FLIPPED_180); - - renderer->render_width = width; - renderer->render_height = height; - renderer->bound_pipe = VK_NULL_HANDLE; - - return true; -} - bool vulkan_sync_foreign_texture(struct wlr_vk_texture *texture) { struct wlr_vk_renderer *renderer = texture->renderer; VkResult res; @@ -1060,516 +1033,16 @@ bool vulkan_sync_render_buffer(struct wlr_vk_renderer *renderer, return true; } -static void vulkan_end(struct wlr_renderer *wlr_renderer) { - struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); - assert(renderer->current_render_buffer); - - struct wlr_vk_command_buffer *render_cb = renderer->current_command_buffer; - assert(render_cb != NULL); - renderer->current_command_buffer = NULL; - - if (vulkan_record_stage_cb(renderer) == VK_NULL_HANDLE) { - return; - } - - struct wlr_vk_command_buffer *stage_cb = renderer->stage.cb; - assert(stage_cb != NULL); - renderer->stage.cb = NULL; - - struct wlr_vk_render_buffer *current_rb = renderer->current_render_buffer; - - if (current_rb->blend_image) { - // Apply output shader to map blend image to actual output image - vkCmdNextSubpass(render_cb->vk, VK_SUBPASS_CONTENTS_INLINE); - - VkPipeline pipe = current_rb->render_setup->output_pipe; - if (pipe != renderer->bound_pipe) { - vkCmdBindPipeline(render_cb->vk, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe); - renderer->bound_pipe = pipe; - } - - float final_matrix[9] = { - renderer->render_width, 0.f, -1.f, - 0.f, renderer->render_height, -1.f, - 0.f, 0.f, 0.f, - }; - struct wlr_vk_vert_pcr_data vert_pcr_data; - mat3_to_mat4(final_matrix, vert_pcr_data.mat4); - vert_pcr_data.uv_off[0] = 0.f; - vert_pcr_data.uv_off[1] = 0.f; - vert_pcr_data.uv_size[0] = 1.f; - vert_pcr_data.uv_size[1] = 1.f; - - vkCmdPushConstants(render_cb->vk, renderer->output_pipe_layout, - VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(vert_pcr_data), &vert_pcr_data); - vkCmdBindDescriptorSets(render_cb->vk, - VK_PIPELINE_BIND_POINT_GRAPHICS, renderer->output_pipe_layout, - 0, 1, ¤t_rb->blend_descriptor_set, 0, NULL); - - vkCmdDraw(render_cb->vk, 4, 1, 0, 0); - } - - vkCmdEndRenderPass(render_cb->vk); - - renderer->render_width = 0u; - renderer->render_height = 0u; - renderer->bound_pipe = VK_NULL_HANDLE; - - // insert acquire and release barriers for dmabuf-images - unsigned barrier_count = wl_list_length(&renderer->foreign_textures) + 1; - VkImageMemoryBarrier *acquire_barriers = calloc(barrier_count, sizeof(*acquire_barriers)); - VkImageMemoryBarrier *release_barriers = calloc(barrier_count, sizeof(*release_barriers)); - VkSemaphoreSubmitInfoKHR *render_wait = calloc(barrier_count * WLR_DMABUF_MAX_PLANES, sizeof(*render_wait)); - if (acquire_barriers == NULL || release_barriers == NULL || render_wait == NULL) { - wlr_log_errno(WLR_ERROR, "Allocation failed"); - free(acquire_barriers); - free(release_barriers); - free(render_wait); - return; - } - - struct wlr_vk_texture *texture, *tmp_tex; - unsigned idx = 0; - uint32_t render_wait_len = 0; - wl_list_for_each_safe(texture, tmp_tex, &renderer->foreign_textures, foreign_link) { - VkImageLayout src_layout = VK_IMAGE_LAYOUT_GENERAL; - if (!texture->transitioned) { - src_layout = VK_IMAGE_LAYOUT_UNDEFINED; - texture->transitioned = true; - } - - acquire_barriers[idx] = (VkImageMemoryBarrier){ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT, - .dstQueueFamilyIndex = renderer->dev->queue_family, - .image = texture->image, - .oldLayout = src_layout, - .newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - .srcAccessMask = 0, // ignored anyways - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.layerCount = 1, - .subresourceRange.levelCount = 1, - }; - - release_barriers[idx] = (VkImageMemoryBarrier){ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcQueueFamilyIndex = renderer->dev->queue_family, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT, - .image = texture->image, - .oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - .newLayout = VK_IMAGE_LAYOUT_GENERAL, - .srcAccessMask = VK_ACCESS_SHADER_READ_BIT, - .dstAccessMask = 0, // ignored anyways - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.layerCount = 1, - .subresourceRange.levelCount = 1, - }; - - ++idx; - - if (!vulkan_sync_foreign_texture(texture)) { - wlr_log(WLR_ERROR, "Failed to wait for foreign texture DMA-BUF fence"); - } else { - for (size_t i = 0; i < WLR_DMABUF_MAX_PLANES; i++) { - if (texture->foreign_semaphores[i] != VK_NULL_HANDLE) { - assert(render_wait_len < barrier_count * WLR_DMABUF_MAX_PLANES); - render_wait[render_wait_len++] = (VkSemaphoreSubmitInfoKHR){ - .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO_KHR, - .semaphore = texture->foreign_semaphores[i], - .stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT_KHR, - }; - } - } - } - - wl_list_remove(&texture->foreign_link); - texture->owned = false; - } - - // also add acquire/release barriers for the current render buffer - VkImageLayout src_layout = VK_IMAGE_LAYOUT_GENERAL; - if (!current_rb->transitioned) { - src_layout = VK_IMAGE_LAYOUT_PREINITIALIZED; - current_rb->transitioned = true; - } - - // acquire render buffer before rendering - acquire_barriers[idx] = (VkImageMemoryBarrier){ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT, - .dstQueueFamilyIndex = renderer->dev->queue_family, - .image = renderer->current_render_buffer->image, - .oldLayout = src_layout, - .newLayout = VK_IMAGE_LAYOUT_GENERAL, - .srcAccessMask = 0, // ignored anyways - .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.layerCount = 1, - .subresourceRange.levelCount = 1, - }; - - // release render buffer after rendering - release_barriers[idx] = (VkImageMemoryBarrier){ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcQueueFamilyIndex = renderer->dev->queue_family, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT, - .image = renderer->current_render_buffer->image, - .oldLayout = VK_IMAGE_LAYOUT_GENERAL, - .newLayout = VK_IMAGE_LAYOUT_GENERAL, - .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - .dstAccessMask = 0, // ignored anyways - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.layerCount = 1, - .subresourceRange.levelCount = 1, - }; - - ++idx; - - if (current_rb->blend_image) { - // The render pass changes the blend image layout from - // color attachment to read only, so on each frame, before - // the render pass starts, we change it back - VkImageLayout blend_src_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - if (!current_rb->blend_transitioned) { - blend_src_layout = VK_IMAGE_LAYOUT_UNDEFINED; - current_rb->blend_transitioned = true; - } - - VkImageMemoryBarrier blend_acq_barrier = { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = current_rb->blend_image, - .oldLayout = blend_src_layout, - .newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - .srcAccessMask = VK_ACCESS_SHADER_READ_BIT, - .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - .subresourceRange = { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .layerCount = 1, - .levelCount = 1, - } - }; - vkCmdPipelineBarrier(stage_cb->vk, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - 0, 0, NULL, 0, NULL, 1, &blend_acq_barrier); - } - - vkCmdPipelineBarrier(stage_cb->vk, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - 0, 0, NULL, 0, NULL, barrier_count, acquire_barriers); - - vkCmdPipelineBarrier(render_cb->vk, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, - VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, NULL, 0, NULL, - barrier_count, release_barriers); - - free(acquire_barriers); - free(release_barriers); - - // No semaphores needed here. - // We don't need a semaphore from the stage/transfer submission - // to the render submissions since they are on the same queue - // and we have a renderpass dependency for that. - uint64_t stage_timeline_point = vulkan_end_command_buffer(stage_cb, renderer); - if (stage_timeline_point == 0) { - return; - } - - VkCommandBufferSubmitInfoKHR stage_cb_info = { - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO_KHR, - .commandBuffer = stage_cb->vk, - }; - VkSemaphoreSubmitInfoKHR stage_signal = { - .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO_KHR, - .semaphore = renderer->timeline_semaphore, - .value = stage_timeline_point, - }; - VkSubmitInfo2KHR stage_submit = { - .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2_KHR, - .commandBufferInfoCount = 1, - .pCommandBufferInfos = &stage_cb_info, - .signalSemaphoreInfoCount = 1, - .pSignalSemaphoreInfos = &stage_signal, - }; - - VkSemaphoreSubmitInfoKHR stage_wait; - if (renderer->stage.last_timeline_point > 0) { - stage_wait = (VkSemaphoreSubmitInfoKHR){ - .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO_KHR, - .semaphore = renderer->timeline_semaphore, - .value = renderer->stage.last_timeline_point, - .stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT_KHR, - }; - - stage_submit.waitSemaphoreInfoCount = 1; - stage_submit.pWaitSemaphoreInfos = &stage_wait; - } - - renderer->stage.last_timeline_point = stage_timeline_point; - - uint64_t render_timeline_point = vulkan_end_command_buffer(render_cb, renderer); - if (render_timeline_point == 0) { - return; - } - - size_t render_signal_len = 1; - VkSemaphoreSubmitInfoKHR render_signal[2] = {0}; - render_signal[0] = (VkSemaphoreSubmitInfoKHR){ - .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO_KHR, - .semaphore = renderer->timeline_semaphore, - .value = render_timeline_point, - }; - if (renderer->dev->implicit_sync_interop) { - if (render_cb->binary_semaphore == VK_NULL_HANDLE) { - VkExportSemaphoreCreateInfo export_info = { - .sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO, - .handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, - }; - VkSemaphoreCreateInfo semaphore_info = { - .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, - .pNext = &export_info, - }; - VkResult res = vkCreateSemaphore(renderer->dev->dev, &semaphore_info, - NULL, &render_cb->binary_semaphore); - if (res != VK_SUCCESS) { - wlr_vk_error("vkCreateSemaphore", res); - return; - } - } - - render_signal[render_signal_len++] = (VkSemaphoreSubmitInfoKHR){ - .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO_KHR, - .semaphore = render_cb->binary_semaphore, - }; - } - - VkCommandBufferSubmitInfoKHR render_cb_info = { - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO_KHR, - .commandBuffer = render_cb->vk, - }; - VkSubmitInfo2KHR render_submit = { - .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2_KHR, - .waitSemaphoreInfoCount = render_wait_len, - .pWaitSemaphoreInfos = render_wait, - .commandBufferInfoCount = 1, - .pCommandBufferInfos = &render_cb_info, - .signalSemaphoreInfoCount = render_signal_len, - .pSignalSemaphoreInfos = render_signal, - }; - - VkSubmitInfo2KHR submit_infos[] = { stage_submit, render_submit }; - VkResult res = renderer->dev->api.vkQueueSubmit2KHR(renderer->dev->queue, 2, submit_infos, VK_NULL_HANDLE); - if (res == VK_ERROR_DEVICE_LOST) { - wlr_log(WLR_ERROR, "vkQueueSubmit failed with VK_ERROR_DEVICE_LOST"); - wl_signal_emit_mutable(&wlr_renderer->events.lost, NULL); - return; - } else if (res != VK_SUCCESS) { - wlr_vk_error("vkQueueSubmit", res); - return; - } - - free(render_wait); - - struct wlr_vk_shared_buffer *stage_buf, *stage_buf_tmp; - wl_list_for_each_safe(stage_buf, stage_buf_tmp, &renderer->stage.buffers, link) { - if (stage_buf->allocs.size == 0) { - continue; - } - wl_list_remove(&stage_buf->link); - wl_list_insert(&stage_cb->stage_buffers, &stage_buf->link); - } - - if (!vulkan_sync_render_buffer(renderer, renderer->current_render_buffer, render_cb)) { - return; - } -} - -static bool vulkan_render_subtexture_with_matrix(struct wlr_renderer *wlr_renderer, - struct wlr_texture *wlr_texture, const struct wlr_fbox *box, - const float matrix[static 9], float alpha) { - struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); - VkCommandBuffer cb = renderer->current_command_buffer->vk; - - struct wlr_vk_texture *texture = vulkan_get_texture(wlr_texture); - assert(texture->renderer == renderer); - if (texture->dmabuf_imported && !texture->owned) { - // Store this texture in the list of textures that need to be - // acquired before rendering and released after rendering. - // We don't do it here immediately since barriers inside - // a renderpass are suboptimal (would require additional renderpass - // dependency and potentially multiple barriers) and it's - // better to issue one barrier for all used textures anyways. - texture->owned = true; - assert(texture->foreign_link.prev == NULL); - assert(texture->foreign_link.next == NULL); - wl_list_insert(&renderer->foreign_textures, &texture->foreign_link); - } - - struct wlr_vk_pipeline *pipe = setup_get_or_create_pipeline( - renderer->current_render_buffer->render_setup, - &(struct wlr_vk_pipeline_key) { - .layout = { - .ycbcr_format = texture->format->is_ycbcr ? texture->format : NULL, - }, - .texture_transform = texture->transform, - }); - if (!pipe) { - return false; - } - - struct wlr_vk_texture_view *view = - vulkan_texture_get_or_create_view(texture, pipe->layout); - if (!view) { - return false; - } - - if (pipe->vk != renderer->bound_pipe) { - vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe->vk); - renderer->bound_pipe = pipe->vk; - } - - vkCmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, - pipe->layout->vk, 0, 1, &view->ds, 0, NULL); - - float final_matrix[9]; - wlr_matrix_multiply(final_matrix, renderer->projection, matrix); - - struct wlr_vk_vert_pcr_data vert_pcr_data; - mat3_to_mat4(final_matrix, vert_pcr_data.mat4); - - vert_pcr_data.uv_off[0] = box->x / wlr_texture->width; - vert_pcr_data.uv_off[1] = box->y / wlr_texture->height; - vert_pcr_data.uv_size[0] = box->width / wlr_texture->width; - vert_pcr_data.uv_size[1] = box->height / wlr_texture->height; - - vkCmdPushConstants(cb, pipe->layout->vk, - VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(vert_pcr_data), &vert_pcr_data); - vkCmdPushConstants(cb, pipe->layout->vk, - VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(vert_pcr_data), sizeof(float), - &alpha); - vkCmdDraw(cb, 4, 1, 0, 0); - texture->last_used_cb = renderer->current_command_buffer; - - return true; -} - -static void vulkan_clear(struct wlr_renderer *wlr_renderer, - const float color[static 4]) { +static const struct wlr_drm_format_set *vulkan_get_texture_formats( + struct wlr_renderer *wlr_renderer, uint32_t buffer_caps) { struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); - VkCommandBuffer cb = renderer->current_command_buffer->vk; - - if (renderer->scissor.extent.width == 0 || renderer->scissor.extent.height == 0) { - return; - } - - VkClearAttachment att = { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .colorAttachment = 0u, - // Input color values are given in srgb space, vulkan expects - // them in linear space. We explicitly import argb8 render buffers - // as srgb, vulkan will convert the input values we give here to - // srgb first. - // But in other parts of wlroots we just always assume - // srgb so that's why we have to convert here. - .clearValue.color.float32 = { - color_to_linear(color[0]), - color_to_linear(color[1]), - color_to_linear(color[2]), - color[3], // no conversion for alpha - }, - }; - - VkClearRect rect = { - .rect = renderer->scissor, - .layerCount = 1, - }; - vkCmdClearAttachments(cb, 1, &att, 1, &rect); -} - -static void vulkan_scissor(struct wlr_renderer *wlr_renderer, - struct wlr_box *box) { - struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); - VkCommandBuffer cb = renderer->current_command_buffer->vk; - - uint32_t w = renderer->render_width; - uint32_t h = renderer->render_height; - struct wlr_box dst = {0, 0, w, h}; - if (box && !wlr_box_intersection(&dst, box, &dst)) { - dst = (struct wlr_box) {0, 0, 0, 0}; // empty - } - - VkRect2D rect = (VkRect2D) {{dst.x, dst.y}, {dst.width, dst.height}}; - renderer->scissor = rect; - vkCmdSetScissor(cb, 0, 1, &rect); -} - -static const uint32_t *vulkan_get_shm_texture_formats( - struct wlr_renderer *wlr_renderer, size_t *len) { - struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); - *len = renderer->dev->shm_format_count; - return renderer->dev->shm_formats; -} - -static void vulkan_render_quad_with_matrix(struct wlr_renderer *wlr_renderer, - const float color[static 4], const float matrix[static 9]) { - struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); - VkCommandBuffer cb = renderer->current_command_buffer->vk; - - struct wlr_vk_pipeline *pipe = setup_get_or_create_pipeline( - renderer->current_render_buffer->render_setup, - &(struct wlr_vk_pipeline_key) { - .source = WLR_VK_SHADER_SOURCE_SINGLE_COLOR, - .layout = { - .ycbcr_format = NULL, - }, - }); - if (!pipe) { - return; + if (buffer_caps & WLR_BUFFER_CAP_DMABUF) { + return &renderer->dev->dmabuf_texture_formats; + } else if (buffer_caps & WLR_BUFFER_CAP_DATA_PTR) { + return &renderer->dev->shm_texture_formats; + } else { + return NULL; } - - if (pipe->vk != renderer->bound_pipe) { - vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe->vk); - renderer->bound_pipe = pipe->vk; - } - - float final_matrix[9]; - wlr_matrix_multiply(final_matrix, renderer->projection, matrix); - - struct wlr_vk_vert_pcr_data vert_pcr_data; - mat3_to_mat4(final_matrix, vert_pcr_data.mat4); - vert_pcr_data.uv_off[0] = 0.f; - vert_pcr_data.uv_off[1] = 0.f; - vert_pcr_data.uv_size[0] = 1.f; - vert_pcr_data.uv_size[1] = 1.f; - - // Input color values are given in srgb space, shader expects - // them in linear space. The shader does all computation in linear - // space and expects in inputs in linear space since it outputs - // colors in linear space as well (and vulkan then automatically - // does the conversion for out SRGB render targets). - // But in other parts of wlroots we just always assume - // srgb so that's why we have to convert here. - float linear_color[4]; - linear_color[0] = color_to_linear(color[0]); - linear_color[1] = color_to_linear(color[1]); - linear_color[2] = color_to_linear(color[2]); - linear_color[3] = color[3]; // no conversion for alpha - - vkCmdPushConstants(cb, pipe->layout->vk, - VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(vert_pcr_data), &vert_pcr_data); - vkCmdPushConstants(cb, pipe->layout->vk, - VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(vert_pcr_data), sizeof(float) * 4, - linear_color); - vkCmdDraw(cb, 4, 1, 0, 0); -} - -static const struct wlr_drm_format_set *vulkan_get_dmabuf_texture_formats( - struct wlr_renderer *wlr_renderer) { - struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); - return &renderer->dev->dmabuf_texture_formats; } static const struct wlr_drm_format_set *vulkan_get_render_formats( @@ -1578,18 +1051,6 @@ static const struct wlr_drm_format_set *vulkan_get_render_formats( return &renderer->dev->dmabuf_render_formats; } -static uint32_t vulkan_preferred_read_format( - struct wlr_renderer *wlr_renderer) { - struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); - struct wlr_dmabuf_attributes dmabuf = {0}; - if (!wlr_buffer_get_dmabuf(renderer->current_render_buffer->wlr_buffer, - &dmabuf)) { - wlr_log(WLR_ERROR, "vulkan_preferred_read_format: Failed to get dmabuf of current render buffer"); - return DRM_FORMAT_INVALID; - } - return dmabuf.format; -} - static void vulkan_destroy(struct wlr_renderer *wlr_renderer) { struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); struct wlr_vk_device *dev = renderer->dev; @@ -1598,8 +1059,6 @@ static void vulkan_destroy(struct wlr_renderer *wlr_renderer) { return; } - assert(!renderer->current_render_buffer); - VkResult res = vkDeviceWaitIdle(renderer->dev->dev); if (res != VK_SUCCESS) { wlr_vk_error("vkDeviceWaitIdle", res); @@ -1633,6 +1092,12 @@ static void vulkan_destroy(struct wlr_renderer *wlr_renderer) { destroy_render_buffer(render_buffer); } + struct wlr_vk_color_transform *color_transform, *color_transform_tmp; + wl_list_for_each_safe(color_transform, color_transform_tmp, + &renderer->color_transforms, link) { + vk_color_transform_destroy(&color_transform->addon); + } + struct wlr_vk_render_format_setup *setup, *tmp_setup; wl_list_for_each_safe(setup, tmp_setup, &renderer->render_format_setups, link) { @@ -1661,12 +1126,19 @@ static void vulkan_destroy(struct wlr_renderer *wlr_renderer) { vkDestroyDescriptorSetLayout(dev->dev, pipeline_layout->ds, NULL); vkDestroySampler(dev->dev, pipeline_layout->sampler, NULL); vkDestroySamplerYcbcrConversion(dev->dev, pipeline_layout->ycbcr.conversion, NULL); + free(pipeline_layout); } + vkDestroyImageView(dev->dev, renderer->dummy3d_image_view, NULL); + vkDestroyImage(dev->dev, renderer->dummy3d_image, NULL); + vkFreeMemory(dev->dev, renderer->dummy3d_mem, NULL); + vkDestroySemaphore(dev->dev, renderer->timeline_semaphore, NULL); vkDestroyPipelineLayout(dev->dev, renderer->output_pipe_layout, NULL); - vkDestroyDescriptorSetLayout(dev->dev, renderer->output_ds_layout, NULL); + vkDestroyDescriptorSetLayout(dev->dev, renderer->output_ds_srgb_layout, NULL); + vkDestroyDescriptorSetLayout(dev->dev, renderer->output_ds_lut3d_layout, NULL); vkDestroyCommandPool(dev->dev, renderer->command_pool, NULL); + vkDestroySampler(dev->dev, renderer->output_sampler_lut3d, NULL); if (renderer->read_pixels_cache.initialized) { vkFreeMemory(dev->dev, renderer->read_pixels_cache.dst_img_memory, NULL); @@ -1679,13 +1151,12 @@ static void vulkan_destroy(struct wlr_renderer *wlr_renderer) { free(renderer); } -static bool vulkan_read_pixels(struct wlr_renderer *wlr_renderer, +bool vulkan_read_pixels(struct wlr_vk_renderer *vk_renderer, + VkFormat src_format, VkImage src_image, uint32_t drm_format, uint32_t stride, uint32_t width, uint32_t height, uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y, void *data) { - struct wlr_vk_renderer *vk_renderer = vulkan_get_renderer(wlr_renderer); VkDevice dev = vk_renderer->dev->dev; - VkImage src_image = vk_renderer->current_render_buffer->image; const struct wlr_pixel_format_info *pixel_format_info = drm_get_pixel_format_info(drm_format); if (!pixel_format_info) { @@ -1704,7 +1175,6 @@ static bool vulkan_read_pixels(struct wlr_renderer *wlr_renderer, return false; } VkFormat dst_format = wlr_vk_format->vk; - VkFormat src_format = vk_renderer->current_render_buffer->render_setup->render_format->vk; VkFormatProperties dst_format_props = {0}, src_format_props = {0}; vkGetPhysicalDeviceFormatProperties(vk_renderer->dev->phdev, dst_format, &dst_format_props); vkGetPhysicalDeviceFormatProperties(vk_renderer->dev->phdev, src_format, &src_format_props); @@ -1919,10 +1389,6 @@ static int vulkan_get_drm_fd(struct wlr_renderer *wlr_renderer) { return renderer->dev->drm_fd; } -static uint32_t vulkan_get_render_buffer_caps(struct wlr_renderer *wlr_renderer) { - return WLR_BUFFER_CAP_DMABUF; -} - static struct wlr_render_pass *vulkan_begin_buffer_pass(struct wlr_renderer *wlr_renderer, struct wlr_buffer *buffer, const struct wlr_buffer_pass_options *options) { struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); @@ -1935,7 +1401,8 @@ static struct wlr_render_pass *vulkan_begin_buffer_pass(struct wlr_renderer *wlr } } - struct wlr_vk_render_pass *render_pass = vulkan_begin_render_pass(renderer, render_buffer); + struct wlr_vk_render_pass *render_pass = vulkan_begin_render_pass( + renderer, render_buffer, options); if (render_pass == NULL) { return NULL; } @@ -1943,21 +1410,10 @@ static struct wlr_render_pass *vulkan_begin_buffer_pass(struct wlr_renderer *wlr } static const struct wlr_renderer_impl renderer_impl = { - .bind_buffer = vulkan_bind_buffer, - .begin = vulkan_begin, - .end = vulkan_end, - .clear = vulkan_clear, - .scissor = vulkan_scissor, - .render_subtexture_with_matrix = vulkan_render_subtexture_with_matrix, - .render_quad_with_matrix = vulkan_render_quad_with_matrix, - .get_shm_texture_formats = vulkan_get_shm_texture_formats, - .get_dmabuf_texture_formats = vulkan_get_dmabuf_texture_formats, + .get_texture_formats = vulkan_get_texture_formats, .get_render_formats = vulkan_get_render_formats, - .preferred_read_format = vulkan_preferred_read_format, - .read_pixels = vulkan_read_pixels, .destroy = vulkan_destroy, .get_drm_fd = vulkan_get_drm_fd, - .get_render_buffer_caps = vulkan_get_render_buffer_caps, .texture_from_buffer = vulkan_texture_from_buffer, .begin_buffer_pass = vulkan_begin_buffer_pass, }; @@ -2019,13 +1475,11 @@ static bool init_tex_layouts(struct wlr_vk_renderer *renderer, return true; } -static bool init_blend_to_output_layouts(struct wlr_vk_renderer *renderer, - VkDescriptorSetLayout *out_ds_layout, - VkPipelineLayout *out_pipe_layout) { +static bool init_blend_to_output_layouts(struct wlr_vk_renderer *renderer) { VkResult res; VkDevice dev = renderer->dev->dev; - VkDescriptorSetLayoutBinding ds_binding = { + VkDescriptorSetLayoutBinding ds_binding_input = { .binding = 0, .descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, .descriptorCount = 1, @@ -2036,32 +1490,83 @@ static bool init_blend_to_output_layouts(struct wlr_vk_renderer *renderer, VkDescriptorSetLayoutCreateInfo ds_info = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, .bindingCount = 1, - .pBindings = &ds_binding, + .pBindings = &ds_binding_input, }; - res = vkCreateDescriptorSetLayout(dev, &ds_info, NULL, out_ds_layout); + res = vkCreateDescriptorSetLayout(dev, &ds_info, NULL, &renderer->output_ds_srgb_layout); + if (res != VK_SUCCESS) { + wlr_vk_error("vkCreateDescriptorSetLayout", res); + return false; + } + + VkSamplerCreateInfo sampler_create_info = { + .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + .magFilter = VK_FILTER_LINEAR, + .minFilter = VK_FILTER_LINEAR, + .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST, + .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, + .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, + .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, + .minLod = 0.f, + .maxLod = 0.25f, + }; + + res = vkCreateSampler(renderer->dev->dev, &sampler_create_info, NULL, + &renderer->output_sampler_lut3d); + if (res != VK_SUCCESS) { + wlr_vk_error("vkCreateSampler", res); + return false; + } + + VkDescriptorSetLayoutBinding ds_binding_lut3d = { + .binding = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, + .pImmutableSamplers = &renderer->output_sampler_lut3d, + }; + + VkDescriptorSetLayoutCreateInfo ds_lut3d_info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .bindingCount = 1, + .pBindings = &ds_binding_lut3d, + }; + + res = vkCreateDescriptorSetLayout(dev, &ds_lut3d_info, NULL, + &renderer->output_ds_lut3d_layout); if (res != VK_SUCCESS) { wlr_vk_error("vkCreateDescriptorSetLayout", res); return false; } // pipeline layout -- standard vertex uniforms, no shader uniforms - VkPushConstantRange pc_ranges[1] = { + VkPushConstantRange pc_ranges[2] = { { + .offset = 0, .size = sizeof(struct wlr_vk_vert_pcr_data), .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, }, + { + .offset = sizeof(struct wlr_vk_vert_pcr_data), + .size = sizeof(struct wlr_vk_frag_output_pcr_data), + .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, + }, + }; + + VkDescriptorSetLayout out_ds_layouts[2] = { + renderer->output_ds_srgb_layout, + renderer->output_ds_lut3d_layout, }; VkPipelineLayoutCreateInfo pl_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, - .setLayoutCount = 1, - .pSetLayouts = out_ds_layout, - .pushConstantRangeCount = 1, + .setLayoutCount = 2, + .pSetLayouts = out_ds_layouts, + .pushConstantRangeCount = 2, .pPushConstantRanges = pc_ranges, }; - res = vkCreatePipelineLayout(dev, &pl_info, NULL, out_pipe_layout); + res = vkCreatePipelineLayout(dev, &pl_info, NULL, &renderer->output_pipe_layout); if (res != VK_SUCCESS) { wlr_vk_error("vkCreatePipelineLayout", res); return false; @@ -2274,10 +1779,24 @@ struct wlr_vk_pipeline *setup_get_or_create_pipeline( } static bool init_blend_to_output_pipeline(struct wlr_vk_renderer *renderer, - VkRenderPass rp, VkPipelineLayout pipe_layout, VkPipeline *pipe) { + VkRenderPass rp, VkPipelineLayout pipe_layout, VkPipeline *pipe, + enum wlr_vk_output_transform transform) { VkResult res; VkDevice dev = renderer->dev->dev; + uint32_t output_transform_type = transform; + VkSpecializationMapEntry spec_entry = { + .constantID = 0, + .offset = 0, + .size = sizeof(uint32_t), + }; + VkSpecializationInfo specialization = { + .mapEntryCount = 1, + .pMapEntries = &spec_entry, + .dataSize = sizeof(uint32_t), + .pData = &output_transform_type, + }; + VkPipelineShaderStageCreateInfo tex_stages[2] = { { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, @@ -2290,6 +1809,7 @@ static bool init_blend_to_output_pipeline(struct wlr_vk_renderer *renderer, .stage = VK_SHADER_STAGE_FRAGMENT_BIT, .module = renderer->output_module, .pName = "main", + .pSpecializationInfo = &specialization, }, }; @@ -2443,18 +1963,118 @@ struct wlr_vk_pipeline_layout *get_or_create_pipeline_layout( if (res != VK_SUCCESS) { wlr_vk_error("vkCreateSampler", res); free(pipeline_layout); - return false; + return NULL; } if (!init_tex_layouts(renderer, pipeline_layout->sampler, &pipeline_layout->ds, &pipeline_layout->vk)) { free(pipeline_layout); - return false; + return NULL; } wl_list_insert(&renderer->pipeline_layouts, &pipeline_layout->link); return pipeline_layout; } + +/* The fragment shader for the blend->image subpass can be configured to either + * use or not a sampler3d lookup table; however, even if the shader does not use + * the sampler, a valid descriptor set should be bound. Create that here, linked to + * a 1x1x1 image. + */ +static bool init_dummy_images(struct wlr_vk_renderer *renderer) { + VkResult res; + VkDevice dev = renderer->dev->dev; + + VkFormat format = VK_FORMAT_R32G32B32A32_SFLOAT; + + VkImageCreateInfo img_info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .imageType = VK_IMAGE_TYPE_3D, + .format = format, + .mipLevels = 1, + .arrayLayers = 1, + .samples = VK_SAMPLE_COUNT_1_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .extent = (VkExtent3D) { 1, 1, 1 }, + .tiling = VK_IMAGE_TILING_OPTIMAL, + .usage = VK_IMAGE_USAGE_SAMPLED_BIT, + }; + res = vkCreateImage(dev, &img_info, NULL, &renderer->dummy3d_image); + if (res != VK_SUCCESS) { + wlr_vk_error("vkCreateImage failed", res); + return false; + } + + VkMemoryRequirements mem_reqs = {0}; + vkGetImageMemoryRequirements(dev, renderer->dummy3d_image, &mem_reqs); + int mem_type_index = vulkan_find_mem_type(renderer->dev, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, mem_reqs.memoryTypeBits); + if (mem_type_index == -1) { + wlr_log(WLR_ERROR, "Failed to find suitable memory type"); + return false; + } + VkMemoryAllocateInfo mem_info = { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .allocationSize = mem_reqs.size, + .memoryTypeIndex = mem_type_index, + }; + res = vkAllocateMemory(dev, &mem_info, NULL, &renderer->dummy3d_mem); + if (res != VK_SUCCESS) { + wlr_vk_error("vkAllocateMemory failed", res); + return false; + } + res = vkBindImageMemory(dev, renderer->dummy3d_image, renderer->dummy3d_mem, 0); + if (res != VK_SUCCESS) { + wlr_vk_error("vkBindMemory failed", res); + return false; + } + + VkImageViewCreateInfo view_info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .viewType = VK_IMAGE_VIEW_TYPE_3D, + .format = format, + .components.r = VK_COMPONENT_SWIZZLE_IDENTITY, + .components.g = VK_COMPONENT_SWIZZLE_IDENTITY, + .components.b = VK_COMPONENT_SWIZZLE_IDENTITY, + .components.a = VK_COMPONENT_SWIZZLE_IDENTITY, + .subresourceRange = (VkImageSubresourceRange) { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .image = renderer->dummy3d_image, + }; + res = vkCreateImageView(dev, &view_info, NULL, &renderer->dummy3d_image_view); + if (res != VK_SUCCESS) { + wlr_vk_error("vkCreateImageView failed", res); + return false; + } + + renderer->output_ds_lut3d_dummy_pool = vulkan_alloc_texture_ds(renderer, + renderer->output_ds_lut3d_layout, &renderer->output_ds_lut3d_dummy); + if (!renderer->output_ds_lut3d_dummy_pool) { + wlr_log(WLR_ERROR, "Failed to allocate descriptor"); + return false; + } + VkDescriptorImageInfo ds_img_info = { + .imageView = renderer->dummy3d_image_view, + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + }; + VkWriteDescriptorSet ds_write = { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .dstSet = renderer->output_ds_lut3d_dummy, + .pImageInfo = &ds_img_info, + }; + vkUpdateDescriptorSets(dev, 1, &ds_write, 0, NULL); + + return true; +} + // Creates static render data, such as sampler, layouts and shader modules // for the given renderer. // Cleanup is done by destroying the renderer. @@ -2462,8 +2082,11 @@ static bool init_static_render_data(struct wlr_vk_renderer *renderer) { VkResult res; VkDevice dev = renderer->dev->dev; - if (!init_blend_to_output_layouts(renderer, &renderer->output_ds_layout, - &renderer->output_pipe_layout)) { + if (!init_blend_to_output_layouts(renderer)) { + return false; + } + + if (!init_dummy_images(renderer)) { return false; } @@ -2518,10 +2141,11 @@ static bool init_static_render_data(struct wlr_vk_renderer *renderer) { static struct wlr_vk_render_format_setup *find_or_create_render_setup( struct wlr_vk_renderer *renderer, const struct wlr_vk_format *format, - bool has_blending_buffer) { + bool use_blending_buffer) { struct wlr_vk_render_format_setup *setup; wl_list_for_each(setup, &renderer->render_format_setups, link) { - if (setup->render_format == format) { + if (setup->render_format == format && + setup->use_blending_buffer == use_blending_buffer) { return setup; } } @@ -2533,13 +2157,14 @@ static struct wlr_vk_render_format_setup *find_or_create_render_setup( } setup->render_format = format; + setup->use_blending_buffer = use_blending_buffer; setup->renderer = renderer; wl_list_init(&setup->pipelines); VkDevice dev = renderer->dev->dev; VkResult res; - if (has_blending_buffer) { + if (use_blending_buffer) { VkAttachmentDescription attachments[2] = { { .format = VK_FORMAT_R16G16B16A16_SFLOAT, @@ -2652,12 +2277,18 @@ static struct wlr_vk_render_format_setup *find_or_create_render_setup( // this is only well defined if render pass has a 2nd subpass if (!init_blend_to_output_pipeline( renderer, setup->render_pass, renderer->output_pipe_layout, - &setup->output_pipe)) { + &setup->output_pipe_lut3d, WLR_VK_OUTPUT_TRANSFORM_LUT3D)) { + goto error; + } + if (!init_blend_to_output_pipeline( + renderer, setup->render_pass, renderer->output_pipe_layout, + &setup->output_pipe_srgb, WLR_VK_OUTPUT_TRANSFORM_INVERSE_SRGB)) { goto error; } } else { + assert(format->vk_srgb); VkAttachmentDescription attachment = { - .format = format->vk, + .format = format->vk_srgb, .samples = VK_SAMPLE_COUNT_1_BIT, .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, .storeOp = VK_ATTACHMENT_STORE_OP_STORE, @@ -2780,7 +2411,8 @@ struct wlr_renderer *vulkan_renderer_create_for_device(struct wlr_vk_device *dev } renderer->dev = dev; - wlr_renderer_init(&renderer->wlr_renderer, &renderer_impl); + wlr_renderer_init(&renderer->wlr_renderer, &renderer_impl, WLR_BUFFER_CAP_DMABUF); + renderer->wlr_renderer.features.output_color_transform = true; wl_list_init(&renderer->stage.buffers); wl_list_init(&renderer->foreign_textures); wl_list_init(&renderer->textures); @@ -2788,6 +2420,7 @@ struct wlr_renderer *vulkan_renderer_create_for_device(struct wlr_vk_device *dev wl_list_init(&renderer->output_descriptor_pools); wl_list_init(&renderer->render_format_setups); wl_list_init(&renderer->render_buffers); + wl_list_init(&renderer->color_transforms); wl_list_init(&renderer->pipeline_layouts); if (!init_static_render_data(renderer)) { @@ -2886,11 +2519,3 @@ uint32_t wlr_vk_renderer_get_queue_family(struct wlr_renderer *renderer) { struct wlr_vk_renderer *vk_renderer = vulkan_get_renderer(renderer); return vk_renderer->dev->queue_family; } - -void wlr_vk_renderer_get_current_image_attribs(struct wlr_renderer *renderer, - struct wlr_vk_image_attribs *attribs) { - struct wlr_vk_renderer *vk_renderer = vulkan_get_renderer(renderer); - attribs->image = vk_renderer->current_render_buffer->image; - attribs->format = vk_renderer->current_render_buffer->render_setup->render_format->vk; - attribs->layout = VK_IMAGE_LAYOUT_UNDEFINED; -} diff --git a/render/vulkan/shaders/output.frag b/render/vulkan/shaders/output.frag index 263f3e1..2a744aa 100644 --- a/render/vulkan/shaders/output.frag +++ b/render/vulkan/shaders/output.frag @@ -1,10 +1,24 @@ #version 450 -layout (input_attachment_index = 0, binding = 0) uniform subpassInput in_color; +layout (input_attachment_index = 0, set = 0, binding = 0) uniform subpassInput in_color; + +layout(set = 1, binding = 0) uniform sampler3D lut_3d; layout(location = 0) in vec2 uv; layout(location = 0) out vec4 out_color; +/* struct wlr_vk_frag_output_pcr_data */ +layout(push_constant) uniform UBO { + layout(offset = 80) float lut_3d_offset; + float lut_3d_scale; +} data; + +layout (constant_id = 0) const int OUTPUT_TRANSFORM = 0; + +// Matches enum wlr_vk_output_transform +#define OUTPUT_TRANSFORM_INVERSE_SRGB 0 +#define OUTPUT_TRANSFORM_LUT_3D 1 + float linear_channel_to_srgb(float x) { return max(min(x * 12.92, 0.04045), 1.055 * pow(x, 1. / 2.4) - 0.055); } @@ -25,5 +39,22 @@ vec4 linear_color_to_srgb(vec4 color) { void main() { vec4 val = subpassLoad(in_color).rgba; - out_color = linear_color_to_srgb(val); + if (OUTPUT_TRANSFORM == OUTPUT_TRANSFORM_LUT_3D) { + if (val.a == 0) { + out_color = vec4(0); + return; + } + // Convert from pre-multiplied alpha to straight alpha + vec3 rgb = val.rgb / val.a; + + // Apply 3D LUT + vec3 pos = data.lut_3d_offset + rgb * data.lut_3d_scale; + rgb = texture(lut_3d, pos).rgb; + + // Back to pre-multiplied alpha + out_color = vec4(rgb * val.a, val.a); + } else { // OUTPUT_TRANSFORM_INVERSE_SRGB + // Produce post-premultiplied sRGB encoded values + out_color = linear_color_to_srgb(val); + } } diff --git a/render/vulkan/texture.c b/render/vulkan/texture.c index 65ea22b..c405331 100644 --- a/render/vulkan/texture.c +++ b/render/vulkan/texture.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -42,9 +41,7 @@ static bool write_pixels(struct wlr_vk_texture *texture, uint32_t stride, const pixman_region32_t *region, const void *vdata, VkImageLayout old_layout, VkPipelineStageFlags src_stage, VkAccessFlags src_access) { - VkResult res; struct wlr_vk_renderer *renderer = texture->renderer; - VkDevice dev = texture->renderer->dev->dev; const struct wlr_pixel_format_info *format_info = drm_get_pixel_format_info(texture->format->drm); assert(format_info); @@ -80,20 +77,11 @@ static bool write_pixels(struct wlr_vk_texture *texture, free(copies); return false; } - - void *vmap; - res = vkMapMemory(dev, span.buffer->memory, span.alloc.start, - bsize, 0, &vmap); - if (res != VK_SUCCESS) { - wlr_vk_error("vkMapMemory", res); - free(copies); - return false; - } - char *map = (char *)vmap; + char *map = (char*)span.buffer->cpu_mapping + span.alloc.start; // upload data - uint32_t buf_off = span.alloc.start + (map - (char *)vmap); + uint32_t buf_off = span.alloc.start; for (int i = 0; i < rects_len; i++) { pixman_box32_t rect = rects[i]; uint32_t width = rect.x2 - rect.x1; @@ -138,9 +126,6 @@ static bool write_pixels(struct wlr_vk_texture *texture, buf_off += height * packed_stride; } - assert((uint32_t)(map - (char *)vmap) == bsize); - vkUnmapMemory(dev, span.buffer->memory); - // record staging cb // will be executed before next frame VkCommandBuffer cb = vulkan_record_stage_cb(renderer); @@ -249,8 +234,28 @@ static void vulkan_texture_unref(struct wlr_texture *wlr_texture) { } } +static bool vulkan_texture_read_pixels(struct wlr_texture *wlr_texture, + const struct wlr_texture_read_pixels_options *options) { + struct wlr_vk_texture *texture = vulkan_get_texture(wlr_texture); + + struct wlr_box src; + wlr_texture_read_pixels_options_get_src_box(options, wlr_texture, &src); + + void *p = wlr_texture_read_pixel_options_get_data(options); + + return vulkan_read_pixels(texture->renderer, texture->format->vk, texture->image, + options->format, options->stride, src.width, src.height, src.x, src.y, 0, 0, p); +} + +static uint32_t vulkan_texture_preferred_read_format(struct wlr_texture *wlr_texture) { + struct wlr_vk_texture *texture = vulkan_get_texture(wlr_texture); + return texture->format->drm; +} + static const struct wlr_texture_impl texture_impl = { .update_from_buffer = vulkan_texture_update_from_buffer, + .read_pixels = vulkan_texture_read_pixels, + .preferred_read_format = vulkan_texture_preferred_read_format, .destroy = vulkan_texture_unref, }; @@ -291,7 +296,8 @@ struct wlr_vk_texture_view *vulkan_texture_get_or_create_view(struct wlr_vk_text VkImageViewCreateInfo view_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .viewType = VK_IMAGE_VIEW_TYPE_2D, - .format = texture->format->vk, + .format = texture->using_mutable_srgb ? texture->format->vk_srgb + : texture->format->vk, .components.r = VK_COMPONENT_SWIZZLE_IDENTITY, .components.g = VK_COMPONENT_SWIZZLE_IDENTITY, .components.b = VK_COMPONENT_SWIZZLE_IDENTITY, @@ -352,15 +358,16 @@ struct wlr_vk_texture_view *vulkan_texture_get_or_create_view(struct wlr_vk_text } static void texture_set_format(struct wlr_vk_texture *texture, - const struct wlr_vk_format *format) { + const struct wlr_vk_format *format, bool has_mutable_srgb) { texture->format = format; - texture->transform = !format->is_ycbcr && format->is_srgb ? + texture->using_mutable_srgb = has_mutable_srgb; + texture->transform = !format->is_ycbcr && has_mutable_srgb ? WLR_VK_TEXTURE_TRANSFORM_IDENTITY : WLR_VK_TEXTURE_TRANSFORM_SRGB; const struct wlr_pixel_format_info *format_info = drm_get_pixel_format_info(format->drm); if (format_info != NULL) { - texture->has_alpha = format_info->has_alpha; + texture->has_alpha = pixel_format_has_alpha(format->drm); } else { // We don't have format info for multi-planar formats assert(texture->format->is_ycbcr); @@ -388,8 +395,17 @@ static struct wlr_texture *vulkan_texture_from_pixels( return NULL; } - texture_set_format(texture, &fmt->format); + texture_set_format(texture, &fmt->format, fmt->shm.has_mutable_srgb); + VkFormat view_formats[2] = { + fmt->format.vk, + fmt->format.vk_srgb, + }; + VkImageFormatListCreateInfoKHR list_info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR, + .pViewFormats = view_formats, + .viewFormatCount = 2, + }; VkImageCreateInfo img_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .imageType = VK_IMAGE_TYPE_2D, @@ -402,7 +418,11 @@ static struct wlr_texture *vulkan_texture_from_pixels( .extent = (VkExtent3D) { width, height, 1 }, .tiling = VK_IMAGE_TILING_OPTIMAL, .usage = vulkan_shm_tex_usage, + .pNext = fmt->shm.has_mutable_srgb ? &list_info : NULL, }; + if (fmt->shm.has_mutable_srgb) { + img_info.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; + } res = vkCreateImage(dev, &img_info, NULL, &texture->image); if (res != VK_SUCCESS) { @@ -482,7 +502,7 @@ static bool is_dmabuf_disjoint(const struct wlr_dmabuf_attributes *attribs) { VkImage vulkan_import_dmabuf(struct wlr_vk_renderer *renderer, const struct wlr_dmabuf_attributes *attribs, VkDeviceMemory mems[static WLR_DMABUF_MAX_PLANES], uint32_t *n_mems, - bool for_render) { + bool for_render, bool *using_mutable_srgb) { VkResult res; VkDevice dev = renderer->dev->dev; *n_mems = 0u; @@ -550,6 +570,9 @@ VkImage vulkan_import_dmabuf(struct wlr_vk_renderer *renderer, if (disjoint) { img_info.flags = VK_IMAGE_CREATE_DISJOINT_BIT; } + if (mod->has_mutable_srgb) { + img_info.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; + } VkExternalMemoryImageCreateInfo eimg = { .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO, @@ -574,6 +597,19 @@ VkImage vulkan_import_dmabuf(struct wlr_vk_renderer *renderer, }; eimg.pNext = &mod_info; + VkFormat view_formats[2] = { + fmt->format.vk, + fmt->format.vk_srgb, + }; + VkImageFormatListCreateInfoKHR list_info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR, + .pViewFormats = view_formats, + .viewFormatCount = 2, + }; + if (mod->has_mutable_srgb) { + mod_info.pNext = &list_info; + } + VkImage image; res = vkCreateImage(dev, &img_info, NULL, &image); if (res != VK_SUCCESS) { @@ -679,6 +715,7 @@ VkImage vulkan_import_dmabuf(struct wlr_vk_renderer *renderer, goto error_image; } + *using_mutable_srgb = mod->has_mutable_srgb; return image; error_image: @@ -710,13 +747,13 @@ static struct wlr_vk_texture *vulkan_texture_from_dmabuf( return NULL; } - texture_set_format(texture, &fmt->format); - + bool using_mutable_srgb = false; texture->image = vulkan_import_dmabuf(renderer, attribs, - texture->memories, &texture->mem_count, false); + texture->memories, &texture->mem_count, false, &using_mutable_srgb); if (!texture->image) { goto error; } + texture_set_format(texture, &fmt->format, using_mutable_srgb); texture->dmabuf_imported = true; @@ -754,7 +791,7 @@ static struct wlr_texture *vulkan_texture_from_dmabuf_buffer( struct wlr_vk_texture *texture = vulkan_texture_from_dmabuf(renderer, dmabuf); if (texture == NULL) { - return false; + return NULL; } texture->buffer = wlr_buffer_lock(buffer); @@ -791,7 +828,7 @@ void wlr_vk_texture_get_image_attribs(struct wlr_texture *texture, attribs->image = vk_texture->image; attribs->format = vk_texture->format->vk; attribs->layout = vk_texture->transitioned ? - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED; + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_GENERAL; } bool wlr_vk_texture_has_alpha(struct wlr_texture *texture) { diff --git a/render/vulkan/vulkan.c b/render/vulkan/vulkan.c index b8ba0b4..7cdc44a 100644 --- a/render/vulkan/vulkan.c +++ b/render/vulkan/vulkan.c @@ -1,5 +1,5 @@ -#if !defined(__FreeBSD__) -#define _POSIX_C_SOURCE 200809L +#if defined(__FreeBSD__) +#undef _POSIX_C_SOURCE #endif #include #include @@ -31,7 +31,7 @@ static bool check_extension(const VkExtensionProperties *avail, return false; } -static VkBool32 debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT severity, +static VKAPI_ATTR VkBool32 debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT type, const VkDebugUtilsMessengerCallbackDataEXT *debug_data, void *data) { @@ -455,8 +455,8 @@ struct wlr_vk_device *vulkan_device_create(struct wlr_vk_instance *ini, dev->drm_fd = -1; // For dmabuf import we require at least the external_memory_fd, - // external_memory_dma_buf, queue_family_foreign and - // image_drm_format_modifier extensions. + // external_memory_dma_buf, queue_family_foreign, + // image_drm_format_modifier, and image_format_list extensions. // The size is set to a large number to allow for other conditional // extensions before the device is created const char *extensions[32] = {0}; @@ -623,9 +623,8 @@ struct wlr_vk_device *vulkan_device_create(struct wlr_vk_instance *ini, size_t max_fmts; const struct wlr_vk_format *fmts = vulkan_get_format_list(&max_fmts); - dev->shm_formats = calloc(max_fmts, sizeof(*dev->shm_formats)); dev->format_props = calloc(max_fmts, sizeof(*dev->format_props)); - if (!dev->shm_formats || !dev->format_props) { + if (!dev->format_props) { wlr_log_errno(WLR_ERROR, "allocation failed"); goto error; } @@ -657,12 +656,12 @@ void vulkan_device_destroy(struct wlr_vk_device *dev) { wlr_drm_format_set_finish(&dev->dmabuf_render_formats); wlr_drm_format_set_finish(&dev->dmabuf_texture_formats); + wlr_drm_format_set_finish(&dev->shm_texture_formats); for (unsigned i = 0u; i < dev->format_prop_count; ++i) { vulkan_format_props_finish(&dev->format_props[i]); } - free(dev->shm_formats); free(dev->format_props); free(dev); } diff --git a/render/wlr_renderer.c b/render/wlr_renderer.c index 2bdcff5..6a28908 100644 --- a/render/wlr_renderer.c +++ b/render/wlr_renderer.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -9,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -27,25 +25,19 @@ #endif // WLR_HAS_VULKAN_RENDERER #include "backend/backend.h" -#include "render/pass.h" #include "render/pixel_format.h" #include "render/wlr_renderer.h" #include "util/env.h" void wlr_renderer_init(struct wlr_renderer *renderer, - const struct wlr_renderer_impl *impl) { - if (!impl->begin_buffer_pass) { - assert(impl->begin); - assert(impl->clear); - assert(impl->scissor); - assert(impl->render_subtexture_with_matrix); - assert(impl->render_quad_with_matrix); - } - assert(impl->get_shm_texture_formats); - assert(impl->get_render_buffer_caps); + const struct wlr_renderer_impl *impl, uint32_t render_buffer_caps) { + assert(impl->begin_buffer_pass); + assert(impl->get_texture_formats); + assert(render_buffer_caps != 0); *renderer = (struct wlr_renderer){ .impl = impl, + .render_buffer_caps = render_buffer_caps, }; wl_signal_init(&renderer->events.destroy); @@ -57,8 +49,6 @@ void wlr_renderer_destroy(struct wlr_renderer *r) { return; } - assert(!r->rendering); - wl_signal_emit_mutable(&r->events.destroy, r); if (r->impl && r->impl->destroy) { @@ -68,130 +58,9 @@ void wlr_renderer_destroy(struct wlr_renderer *r) { } } -bool renderer_bind_buffer(struct wlr_renderer *r, struct wlr_buffer *buffer) { - assert(!r->rendering); - if (!r->impl->bind_buffer) { - return false; - } - return r->impl->bind_buffer(r, buffer); -} - -bool wlr_renderer_begin(struct wlr_renderer *r, uint32_t width, uint32_t height) { - assert(!r->rendering); - - if (!r->impl->begin(r, width, height)) { - return false; - } - - r->rendering = true; - return true; -} - -bool wlr_renderer_begin_with_buffer(struct wlr_renderer *r, - struct wlr_buffer *buffer) { - if (!renderer_bind_buffer(r, buffer)) { - return false; - } - if (!wlr_renderer_begin(r, buffer->width, buffer->height)) { - renderer_bind_buffer(r, NULL); - return false; - } - r->rendering_with_buffer = true; - return true; -} - -void wlr_renderer_end(struct wlr_renderer *r) { - assert(r->rendering); - - if (r->impl->end) { - r->impl->end(r); - } - - r->rendering = false; - - if (r->rendering_with_buffer) { - renderer_bind_buffer(r, NULL); - r->rendering_with_buffer = false; - } -} - -void wlr_renderer_clear(struct wlr_renderer *r, const float color[static 4]) { - assert(r->rendering); - r->impl->clear(r, color); -} - -void wlr_renderer_scissor(struct wlr_renderer *r, struct wlr_box *box) { - assert(r->rendering); - r->impl->scissor(r, box); -} - -bool wlr_render_texture(struct wlr_renderer *r, struct wlr_texture *texture, - const float projection[static 9], int x, int y, float alpha) { - struct wlr_box box = { - .x = x, - .y = y, - .width = texture->width, - .height = texture->height, - }; - - float matrix[9]; - wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, - projection); - - return wlr_render_texture_with_matrix(r, texture, matrix, alpha); -} - -bool wlr_render_texture_with_matrix(struct wlr_renderer *r, - struct wlr_texture *texture, const float matrix[static 9], - float alpha) { - struct wlr_fbox box = { - .x = 0, - .y = 0, - .width = texture->width, - .height = texture->height, - }; - return wlr_render_subtexture_with_matrix(r, texture, &box, matrix, alpha); -} - -bool wlr_render_subtexture_with_matrix(struct wlr_renderer *r, - struct wlr_texture *texture, const struct wlr_fbox *box, - const float matrix[static 9], float alpha) { - assert(r->rendering); - assert(texture->renderer == r); - return r->impl->render_subtexture_with_matrix(r, texture, - box, matrix, alpha); -} - -void wlr_render_rect(struct wlr_renderer *r, const struct wlr_box *box, - const float color[static 4], const float projection[static 9]) { - if (box->width == 0 || box->height == 0) { - return; - } - assert(box->width > 0 && box->height > 0); - float matrix[9]; - wlr_matrix_project_box(matrix, box, WL_OUTPUT_TRANSFORM_NORMAL, 0, - projection); - - wlr_render_quad_with_matrix(r, color, matrix); -} - -void wlr_render_quad_with_matrix(struct wlr_renderer *r, - const float color[static 4], const float matrix[static 9]) { - assert(r->rendering); - r->impl->render_quad_with_matrix(r, color, matrix); -} - -const uint32_t *wlr_renderer_get_shm_texture_formats(struct wlr_renderer *r, - size_t *len) { - return r->impl->get_shm_texture_formats(r, len); -} - -const struct wlr_drm_format_set *wlr_renderer_get_dmabuf_texture_formats( - struct wlr_renderer *r) { - if (!r->impl->get_dmabuf_texture_formats) { - return NULL; - } - return r->impl->get_dmabuf_texture_formats(r); +const struct wlr_drm_format_set *wlr_renderer_get_texture_formats( + struct wlr_renderer *r, uint32_t buffer_caps) { + return r->impl->get_texture_formats(r, buffer_caps); } const struct wlr_drm_format_set *wlr_renderer_get_render_formats( @@ -202,24 +71,9 @@ const struct wlr_drm_format_set *wlr_renderer_get_render_formats( return r->impl->get_render_formats(r); } -uint32_t renderer_get_render_buffer_caps(struct wlr_renderer *r) { - return r->impl->get_render_buffer_caps(r); -} - -bool wlr_renderer_read_pixels(struct wlr_renderer *r, uint32_t fmt, - uint32_t stride, uint32_t width, uint32_t height, - uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y, - void *data) { - if (!r->impl->read_pixels) { - return false; - } - return r->impl->read_pixels(r, fmt, stride, width, height, - src_x, src_y, dst_x, dst_y, data); -} - bool wlr_renderer_init_wl_shm(struct wlr_renderer *r, struct wl_display *wl_display) { - return wlr_shm_create_with_renderer(wl_display, 1, r) != NULL; + return wlr_shm_create_with_renderer(wl_display, 2, r) != NULL; } bool wlr_renderer_init_wl_display(struct wlr_renderer *r, @@ -228,15 +82,10 @@ bool wlr_renderer_init_wl_display(struct wlr_renderer *r, return false; } - if (wlr_renderer_get_dmabuf_texture_formats(r) != NULL && - wlr_renderer_get_drm_fd(r) >= 0) { - if (wlr_drm_create(wl_display, r) == NULL) { - return false; - } - - if (wlr_linux_dmabuf_v1_create_with_renderer(wl_display, 4, r) == NULL) { - return false; - } + if (wlr_renderer_get_texture_formats(r, WLR_BUFFER_CAP_DMABUF) != NULL && + wlr_renderer_get_drm_fd(r) >= 0 && + wlr_linux_dmabuf_v1_create_with_renderer(wl_display, 4, r) == NULL) { + return false; } return true; @@ -447,10 +296,6 @@ int wlr_renderer_get_drm_fd(struct wlr_renderer *r) { struct wlr_render_pass *wlr_renderer_begin_buffer_pass(struct wlr_renderer *renderer, struct wlr_buffer *buffer, const struct wlr_buffer_pass_options *options) { - if (!renderer->impl->begin_buffer_pass) { - return begin_legacy_buffer_render_pass(renderer, buffer); - } - struct wlr_buffer_pass_options default_options = {0}; if (!options) { options = &default_options; diff --git a/render/wlr_texture.c b/render/wlr_texture.c index 8c003cf..3526ee1 100644 --- a/render/wlr_texture.c +++ b/render/wlr_texture.c @@ -1,9 +1,11 @@ #include +#include #include #include #include #include #include +#include "render/pixel_format.h" #include "types/wlr_buffer.h" void wlr_texture_init(struct wlr_texture *texture, struct wlr_renderer *renderer, @@ -26,6 +28,48 @@ void wlr_texture_destroy(struct wlr_texture *texture) { } } +void wlr_texture_read_pixels_options_get_src_box( + const struct wlr_texture_read_pixels_options *options, + const struct wlr_texture *texture, struct wlr_box *box) { + if (wlr_box_empty(&options->src_box)) { + *box = (struct wlr_box){ + .x = 0, + .y = 0, + .width = texture->width, + .height = texture->height, + }; + return; + } + + *box = options->src_box; +} + +void *wlr_texture_read_pixel_options_get_data( + const struct wlr_texture_read_pixels_options *options) { + const struct wlr_pixel_format_info *fmt = drm_get_pixel_format_info(options->format); + + return (char *)options->data + + pixel_format_info_min_stride(fmt, options->dst_x) + + options->dst_y * options->stride; +} + +bool wlr_texture_read_pixels(struct wlr_texture *texture, + const struct wlr_texture_read_pixels_options *options) { + if (!texture->impl->read_pixels) { + return false; + } + + return texture->impl->read_pixels(texture, options); +} + +uint32_t wlr_texture_preferred_read_format(struct wlr_texture *texture) { + if (!texture->impl->preferred_read_format) { + return DRM_FORMAT_INVALID; + } + + return texture->impl->preferred_read_format(texture); +} + struct wlr_texture *wlr_texture_from_pixels(struct wlr_renderer *renderer, uint32_t fmt, uint32_t stride, uint32_t width, uint32_t height, const void *data) { diff --git a/tinywl/Makefile b/tinywl/Makefile index 8f1df20..82cbebd 100644 --- a/tinywl/Makefile +++ b/tinywl/Makefile @@ -1,9 +1,10 @@ -WAYLAND_PROTOCOLS=$(shell pkg-config --variable=pkgdatadir wayland-protocols) -WAYLAND_SCANNER=$(shell pkg-config --variable=wayland_scanner wayland-scanner) -LIBS=\ - $(shell pkg-config --cflags --libs "wlroots >= 0.17.0") \ - $(shell pkg-config --cflags --libs wayland-server) \ - $(shell pkg-config --cflags --libs xkbcommon) +PKG_CONFIG?=pkg-config +WAYLAND_PROTOCOLS=$(shell $(PKG_CONFIG) --variable=pkgdatadir wayland-protocols) +WAYLAND_SCANNER=$(shell $(PKG_CONFIG) --variable=wayland_scanner wayland-scanner) + +PKGS="wlroots-0.18" wayland-server xkbcommon +CFLAGS+=$(shell $(PKG_CONFIG) --cflags $(PKGS)) +LIBS=$(shell $(PKG_CONFIG) --libs $(PKGS)) # wayland-scanner is a tool which generates C headers and rigging for Wayland # protocols, which are specified in XML. wlroots requires you to rig these up @@ -12,15 +13,13 @@ xdg-shell-protocol.h: $(WAYLAND_SCANNER) server-header \ $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@ -tinywl: tinywl.c xdg-shell-protocol.h - $(CC) $(CFLAGS) \ - -g -Werror -I. \ - -DWLR_USE_UNSTABLE \ - -o $@ $< \ - $(LIBS) +tinywl.o: tinywl.c xdg-shell-protocol.h + $(CC) -g -Werror $(CFLAGS) -I. -DWLR_USE_UNSTABLE -o $@ -c $< +tinywl: tinywl.o + $(CC) $< -g -Werror $(CFLAGS) $(LDFLAGS) $(LIBS) -o $@ clean: - rm -f tinywl xdg-shell-protocol.h xdg-shell-protocol.c + rm -f tinywl tinywl.o xdg-shell-protocol.h .DEFAULT_GOAL=tinywl .PHONY: clean diff --git a/tinywl/tinywl.c b/tinywl/tinywl.c index e3e8f23..6c043ed 100644 --- a/tinywl/tinywl.c +++ b/tinywl/tinywl.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200112L #include #include #include @@ -42,7 +41,8 @@ struct tinywl_server { struct wlr_scene_output_layout *scene_layout; struct wlr_xdg_shell *xdg_shell; - struct wl_listener new_xdg_surface; + struct wl_listener new_xdg_toplevel; + struct wl_listener new_xdg_popup; struct wl_list toplevels; struct wlr_cursor *cursor; @@ -85,6 +85,7 @@ struct tinywl_toplevel { struct wlr_scene_tree *scene_tree; struct wl_listener map; struct wl_listener unmap; + struct wl_listener commit; struct wl_listener destroy; struct wl_listener request_move; struct wl_listener request_resize; @@ -92,6 +93,12 @@ struct tinywl_toplevel { struct wl_listener request_fullscreen; }; +struct tinywl_popup { + struct wlr_xdg_popup *xdg_popup; + struct wl_listener commit; + struct wl_listener destroy; +}; + struct tinywl_keyboard { struct wl_list link; struct tinywl_server *server; @@ -518,7 +525,7 @@ static void server_cursor_button(struct wl_listener *listener, void *data) { struct wlr_surface *surface = NULL; struct tinywl_toplevel *toplevel = desktop_toplevel_at(server, server->cursor->x, server->cursor->y, &surface, &sx, &sy); - if (event->state == WLR_BUTTON_RELEASED) { + if (event->state == WL_POINTER_BUTTON_STATE_RELEASED) { /* If you released any buttons, we exit interactive move/resize mode. */ reset_cursor_mode(server); } else { @@ -536,7 +543,7 @@ static void server_cursor_axis(struct wl_listener *listener, void *data) { /* Notify the client with pointer focus of the axis event. */ wlr_seat_pointer_notify_axis(server->seat, event->time_msec, event->orientation, event->delta, - event->delta_discrete, event->source); + event->delta_discrete, event->source, event->relative_direction); } static void server_cursor_frame(struct wl_listener *listener, void *data) { @@ -671,12 +678,26 @@ static void xdg_toplevel_unmap(struct wl_listener *listener, void *data) { wl_list_remove(&toplevel->link); } +static void xdg_toplevel_commit(struct wl_listener *listener, void *data) { + /* Called when a new surface state is committed. */ + struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, commit); + + if (toplevel->xdg_toplevel->base->initial_commit) { + /* When an xdg_surface performs an initial commit, the compositor must + * reply with a configure so the client can map the surface. tinywl + * configures the xdg_toplevel with 0,0 size to let the client pick the + * dimensions itself. */ + wlr_xdg_toplevel_set_size(toplevel->xdg_toplevel, 0, 0); + } +} + static void xdg_toplevel_destroy(struct wl_listener *listener, void *data) { /* Called when the xdg_toplevel is destroyed. */ struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, destroy); wl_list_remove(&toplevel->map.link); wl_list_remove(&toplevel->unmap.link); + wl_list_remove(&toplevel->commit.link); wl_list_remove(&toplevel->destroy.link); wl_list_remove(&toplevel->request_move.link); wl_list_remove(&toplevel->request_resize.link); @@ -750,13 +771,17 @@ static void xdg_toplevel_request_resize( static void xdg_toplevel_request_maximize( struct wl_listener *listener, void *data) { /* This event is raised when a client would like to maximize itself, - * typically because the user clicked on the maximize button on - * client-side decorations. tinywl doesn't support maximization, but - * to conform to xdg-shell protocol we still must send a configure. - * wlr_xdg_surface_schedule_configure() is used to send an empty reply. */ + * typically because the user clicked on the maximize button on client-side + * decorations. tinywl doesn't support maximization, but to conform to + * xdg-shell protocol we still must send a configure. + * wlr_xdg_surface_schedule_configure() is used to send an empty reply. + * However, if the request was sent before an initial commit, we don't do + * anything and let the client finish the initial surface setup. */ struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, request_maximize); - wlr_xdg_surface_schedule_configure(toplevel->xdg_toplevel->base); + if (toplevel->xdg_toplevel->base->initialized) { + wlr_xdg_surface_schedule_configure(toplevel->xdg_toplevel->base); + } } static void xdg_toplevel_request_fullscreen( @@ -764,61 +789,93 @@ static void xdg_toplevel_request_fullscreen( /* Just as with request_maximize, we must send a configure here. */ struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, request_fullscreen); - wlr_xdg_surface_schedule_configure(toplevel->xdg_toplevel->base); + if (toplevel->xdg_toplevel->base->initialized) { + wlr_xdg_surface_schedule_configure(toplevel->xdg_toplevel->base); + } } -static void server_new_xdg_surface(struct wl_listener *listener, void *data) { - /* This event is raised when wlr_xdg_shell receives a new xdg surface from a - * client, either a toplevel (application window) or popup. */ - struct tinywl_server *server = - wl_container_of(listener, server, new_xdg_surface); - struct wlr_xdg_surface *xdg_surface = data; - - /* We must add xdg popups to the scene graph so they get rendered. The - * wlroots scene graph provides a helper for this, but to use it we must - * provide the proper parent scene node of the xdg popup. To enable this, - * we always set the user data field of xdg_surfaces to the corresponding - * scene node. */ - if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { - struct wlr_xdg_surface *parent = - wlr_xdg_surface_try_from_wlr_surface(xdg_surface->popup->parent); - assert(parent != NULL); - struct wlr_scene_tree *parent_tree = parent->data; - xdg_surface->data = wlr_scene_xdg_surface_create( - parent_tree, xdg_surface); - return; - } - assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); +static void server_new_xdg_toplevel(struct wl_listener *listener, void *data) { + /* This event is raised when a client creates a new toplevel (application window). */ + struct tinywl_server *server = wl_container_of(listener, server, new_xdg_toplevel); + struct wlr_xdg_toplevel *xdg_toplevel = data; /* Allocate a tinywl_toplevel for this surface */ struct tinywl_toplevel *toplevel = calloc(1, sizeof(*toplevel)); toplevel->server = server; - toplevel->xdg_toplevel = xdg_surface->toplevel; - toplevel->scene_tree = wlr_scene_xdg_surface_create( - &toplevel->server->scene->tree, toplevel->xdg_toplevel->base); + toplevel->xdg_toplevel = xdg_toplevel; + toplevel->scene_tree = + wlr_scene_xdg_surface_create(&toplevel->server->scene->tree, xdg_toplevel->base); toplevel->scene_tree->node.data = toplevel; - xdg_surface->data = toplevel->scene_tree; + xdg_toplevel->base->data = toplevel->scene_tree; /* Listen to the various events it can emit */ toplevel->map.notify = xdg_toplevel_map; - wl_signal_add(&xdg_surface->surface->events.map, &toplevel->map); + wl_signal_add(&xdg_toplevel->base->surface->events.map, &toplevel->map); toplevel->unmap.notify = xdg_toplevel_unmap; - wl_signal_add(&xdg_surface->surface->events.unmap, &toplevel->unmap); + wl_signal_add(&xdg_toplevel->base->surface->events.unmap, &toplevel->unmap); + toplevel->commit.notify = xdg_toplevel_commit; + wl_signal_add(&xdg_toplevel->base->surface->events.commit, &toplevel->commit); + toplevel->destroy.notify = xdg_toplevel_destroy; - wl_signal_add(&xdg_surface->events.destroy, &toplevel->destroy); + wl_signal_add(&xdg_toplevel->events.destroy, &toplevel->destroy); /* cotd */ - struct wlr_xdg_toplevel *xdg_toplevel = xdg_surface->toplevel; toplevel->request_move.notify = xdg_toplevel_request_move; wl_signal_add(&xdg_toplevel->events.request_move, &toplevel->request_move); toplevel->request_resize.notify = xdg_toplevel_request_resize; wl_signal_add(&xdg_toplevel->events.request_resize, &toplevel->request_resize); toplevel->request_maximize.notify = xdg_toplevel_request_maximize; - wl_signal_add(&xdg_toplevel->events.request_maximize, - &toplevel->request_maximize); + wl_signal_add(&xdg_toplevel->events.request_maximize, &toplevel->request_maximize); toplevel->request_fullscreen.notify = xdg_toplevel_request_fullscreen; - wl_signal_add(&xdg_toplevel->events.request_fullscreen, - &toplevel->request_fullscreen); + wl_signal_add(&xdg_toplevel->events.request_fullscreen, &toplevel->request_fullscreen); +} + +static void xdg_popup_commit(struct wl_listener *listener, void *data) { + /* Called when a new surface state is committed. */ + struct tinywl_popup *popup = wl_container_of(listener, popup, commit); + + if (popup->xdg_popup->base->initial_commit) { + /* When an xdg_surface performs an initial commit, the compositor must + * reply with a configure so the client can map the surface. + * tinywl sends an empty configure. A more sophisticated compositor + * might change an xdg_popup's geometry to ensure it's not positioned + * off-screen, for example. */ + wlr_xdg_surface_schedule_configure(popup->xdg_popup->base); + } +} + +static void xdg_popup_destroy(struct wl_listener *listener, void *data) { + /* Called when the xdg_popup is destroyed. */ + struct tinywl_popup *popup = wl_container_of(listener, popup, destroy); + + wl_list_remove(&popup->commit.link); + wl_list_remove(&popup->destroy.link); + + free(popup); +} + +static void server_new_xdg_popup(struct wl_listener *listener, void *data) { + /* This event is raised when a client creates a new popup. */ + struct wlr_xdg_popup *xdg_popup = data; + + struct tinywl_popup *popup = calloc(1, sizeof(*popup)); + popup->xdg_popup = xdg_popup; + + /* We must add xdg popups to the scene graph so they get rendered. The + * wlroots scene graph provides a helper for this, but to use it we must + * provide the proper parent scene node of the xdg popup. To enable this, + * we always set the user data field of xdg_surfaces to the corresponding + * scene node. */ + struct wlr_xdg_surface *parent = wlr_xdg_surface_try_from_wlr_surface(xdg_popup->parent); + assert(parent != NULL); + struct wlr_scene_tree *parent_tree = parent->data; + xdg_popup->base->data = wlr_scene_xdg_surface_create(parent_tree, xdg_popup->base); + + popup->commit.notify = xdg_popup_commit; + wl_signal_add(&xdg_popup->base->surface->events.commit, &popup->commit); + + popup->destroy.notify = xdg_popup_destroy; + wl_signal_add(&xdg_popup->events.destroy, &popup->destroy); } int main(int argc, char *argv[]) { @@ -849,7 +906,7 @@ int main(int argc, char *argv[]) { * output hardware. The autocreate option will choose the most suitable * backend based on the current environment, such as opening an X11 window * if an X11 server is running. */ - server.backend = wlr_backend_autocreate(server.wl_display, NULL); + server.backend = wlr_backend_autocreate(wl_display_get_event_loop(server.wl_display), NULL); if (server.backend == NULL) { wlr_log(WLR_ERROR, "failed to create wlr_backend"); return 1; @@ -891,7 +948,7 @@ int main(int argc, char *argv[]) { /* Creates an output layout, which a wlroots utility for working with an * arrangement of screens in a physical layout. */ - server.output_layout = wlr_output_layout_create(); + server.output_layout = wlr_output_layout_create(server.wl_display); /* Configure a listener to be notified when new outputs are available on the * backend. */ @@ -914,9 +971,10 @@ int main(int argc, char *argv[]) { */ wl_list_init(&server.toplevels); server.xdg_shell = wlr_xdg_shell_create(server.wl_display, 3); - server.new_xdg_surface.notify = server_new_xdg_surface; - wl_signal_add(&server.xdg_shell->events.new_surface, - &server.new_xdg_surface); + server.new_xdg_toplevel.notify = server_new_xdg_toplevel; + wl_signal_add(&server.xdg_shell->events.new_toplevel, &server.new_xdg_toplevel); + server.new_xdg_popup.notify = server_new_xdg_popup; + wl_signal_add(&server.xdg_shell->events.new_popup, &server.new_xdg_popup); /* * Creates a cursor, which is a wlroots utility for tracking the cursor @@ -1007,7 +1065,10 @@ int main(int argc, char *argv[]) { wl_display_destroy_clients(server.wl_display); wlr_scene_node_destroy(&server.scene->tree.node); wlr_xcursor_manager_destroy(server.cursor_mgr); - wlr_output_layout_destroy(server.output_layout); + wlr_cursor_destroy(server.cursor); + wlr_allocator_destroy(server.allocator); + wlr_renderer_destroy(server.renderer); + wlr_backend_destroy(server.backend); wl_display_destroy(server.wl_display); return 0; } diff --git a/types/buffer/buffer.c b/types/buffer/buffer.c index a8e9c54..953207a 100644 --- a/types/buffer/buffer.c +++ b/types/buffer/buffer.c @@ -1,4 +1,5 @@ #include +#include #include #include #include "render/pixel_format.h" @@ -111,16 +112,19 @@ bool buffer_is_opaque(struct wlr_buffer *buffer) { format = shm.format; } else if (wlr_buffer_begin_data_ptr_access(buffer, WLR_BUFFER_DATA_PTR_ACCESS_READ, &data, &format, &stride)) { + bool opaque = false; + if (buffer->width == 1 && buffer->height == 1 && format == DRM_FORMAT_ARGB8888) { + // Special case for single-pixel-buffer-v1 + const uint8_t *argb8888 = data; // little-endian byte order + opaque = argb8888[3] == 0xFF; + } wlr_buffer_end_data_ptr_access(buffer); + if (opaque) { + return true; + } } else { return false; } - const struct wlr_pixel_format_info *format_info = - drm_get_pixel_format_info(format); - if (format_info == NULL) { - return false; - } - - return !format_info->has_alpha; + return !pixel_format_has_alpha(format); } diff --git a/types/buffer/client.c b/types/buffer/client.c index 4cfa57a..d96603a 100644 --- a/types/buffer/client.c +++ b/types/buffer/client.c @@ -25,6 +25,7 @@ static struct wlr_client_buffer *client_buffer_from_buffer( static void client_buffer_destroy(struct wlr_buffer *buffer) { struct wlr_client_buffer *client_buffer = client_buffer_from_buffer(buffer); wl_list_remove(&client_buffer->source_destroy.link); + wl_list_remove(&client_buffer->renderer_destroy.link); wlr_texture_destroy(client_buffer->texture); free(client_buffer); } @@ -54,6 +55,15 @@ static void client_buffer_handle_source_destroy(struct wl_listener *listener, client_buffer->source = NULL; } +static void client_buffer_handle_renderer_destroy(struct wl_listener *listener, + void *data) { + struct wlr_client_buffer *client_buffer = + wl_container_of(listener, client_buffer, renderer_destroy); + wl_list_remove(&client_buffer->renderer_destroy.link); + wl_list_init(&client_buffer->renderer_destroy.link); + client_buffer->texture = NULL; +} + struct wlr_client_buffer *wlr_client_buffer_create(struct wlr_buffer *buffer, struct wlr_renderer *renderer) { struct wlr_texture *texture = wlr_texture_from_buffer(renderer, buffer); @@ -75,6 +85,9 @@ struct wlr_client_buffer *wlr_client_buffer_create(struct wlr_buffer *buffer, wl_signal_add(&buffer->events.destroy, &client_buffer->source_destroy); client_buffer->source_destroy.notify = client_buffer_handle_source_destroy; + wl_signal_add(&texture->renderer->events.destroy, &client_buffer->renderer_destroy); + client_buffer->renderer_destroy.notify = client_buffer_handle_renderer_destroy; + // Ensure the buffer will be released before being destroyed wlr_buffer_lock(&client_buffer->base); wlr_buffer_drop(&client_buffer->base); @@ -88,6 +101,9 @@ bool wlr_client_buffer_apply_damage(struct wlr_client_buffer *client_buffer, // Someone else still has a reference to the buffer return false; } + if (client_buffer->texture == NULL) { + return false; + } return wlr_texture_update_from_buffer(client_buffer->texture, next, damage); } diff --git a/types/buffer/resource.c b/types/buffer/resource.c index 294bac1..14d8f82 100644 --- a/types/buffer/resource.c +++ b/types/buffer/resource.c @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include #include "types/wlr_buffer.h" diff --git a/types/data_device/wlr_data_offer.c b/types/data_device/wlr_data_offer.c index b1bff33..4bb9d0c 100644 --- a/types/data_device/wlr_data_offer.c +++ b/types/data_device/wlr_data_offer.c @@ -1,4 +1,5 @@ -#define _XOPEN_SOURCE 700 +#undef _POSIX_C_SOURCE +#define _XOPEN_SOURCE 700 // for ffs() #include #include #include diff --git a/types/data_device/wlr_data_source.c b/types/data_device/wlr_data_source.c index 5fcc617..15fb992 100644 --- a/types/data_device/wlr_data_source.c +++ b/types/data_device/wlr_data_source.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include diff --git a/types/data_device/wlr_drag.c b/types/data_device/wlr_drag.c index c6e9e59..9ccd53c 100644 --- a/types/data_device/wlr_drag.c +++ b/types/data_device/wlr_drag.c @@ -234,8 +234,9 @@ static uint32_t drag_handle_pointer_button(struct wlr_seat_pointer_grab *grab, } static void drag_handle_pointer_axis(struct wlr_seat_pointer_grab *grab, - uint32_t time, enum wlr_axis_orientation orientation, double value, - int32_t value_discrete, enum wlr_axis_source source) { + uint32_t time, enum wl_pointer_axis orientation, double value, + int32_t value_discrete, enum wl_pointer_axis_source source, + enum wl_pointer_axis_relative_direction relative_direction) { // This space is intentionally left blank } @@ -260,11 +261,11 @@ static uint32_t drag_handle_touch_down(struct wlr_seat_touch_grab *grab, return 0; } -static void drag_handle_touch_up(struct wlr_seat_touch_grab *grab, +static uint32_t drag_handle_touch_up(struct wlr_seat_touch_grab *grab, uint32_t time, struct wlr_touch_point *point) { struct wlr_drag *drag = grab->data; if (drag->grab_touch_id != point->touch_id) { - return; + return 0; } if (drag->focus_client) { @@ -272,6 +273,7 @@ static void drag_handle_touch_up(struct wlr_seat_touch_grab *grab, } drag_destroy(drag); + return 0; } static void drag_handle_touch_motion(struct wlr_seat_touch_grab *grab, diff --git a/types/meson.build b/types/meson.build index ba41e3e..ec70d4b 100644 --- a/types/meson.build +++ b/types/meson.build @@ -8,7 +8,6 @@ wlr_files += files( 'output/render.c', 'output/state.c', 'output/swapchain.c', - 'output/transform.c', 'scene/drag_icon.c', 'scene/subsurface_tree.c', 'scene/surface.c', @@ -34,6 +33,7 @@ wlr_files += files( 'buffer/dmabuf.c', 'buffer/readonly_data.c', 'buffer/resource.c', + 'wlr_alpha_modifier_v1.c', 'wlr_compositor.c', 'wlr_content_type_v1.c', 'wlr_cursor_shape_v1.c', @@ -43,23 +43,25 @@ wlr_files += files( 'wlr_drm.c', 'wlr_export_dmabuf_v1.c', 'wlr_foreign_toplevel_management_v1.c', + 'wlr_ext_foreign_toplevel_list_v1.c', 'wlr_fullscreen_shell_v1.c', 'wlr_gamma_control_v1.c', 'wlr_idle_inhibit_v1.c', 'wlr_idle_notify_v1.c', 'wlr_input_device.c', - 'wlr_input_inhibitor.c', 'wlr_input_method_v2.c', 'wlr_keyboard.c', 'wlr_keyboard_group.c', 'wlr_keyboard_shortcuts_inhibit_v1.c', 'wlr_layer_shell_v1.c', 'wlr_linux_dmabuf_v1.c', + 'wlr_linux_drm_syncobj_v1.c', 'wlr_matrix.c', 'wlr_output_layer.c', 'wlr_output_layout.c', 'wlr_output_management_v1.c', 'wlr_output_power_management_v1.c', + 'wlr_output_swapchain_manager.c', 'wlr_pointer_constraints_v1.c', 'wlr_pointer_gestures_v1.c', 'wlr_pointer.c', @@ -81,6 +83,7 @@ wlr_files += files( 'wlr_tablet_tool.c', 'wlr_text_input_v3.c', 'wlr_touch.c', + 'wlr_transient_seat_v1.c', 'wlr_viewporter.c', 'wlr_virtual_keyboard_v1.c', 'wlr_virtual_pointer_v1.c', diff --git a/types/output/cursor.c b/types/output/cursor.c index ad37a04..2bf7852 100644 --- a/types/output/cursor.c +++ b/types/output/cursor.c @@ -5,9 +5,9 @@ #include #include #include -#include #include #include +#include #include "render/allocator/allocator.h" #include "types/wlr_buffer.h" #include "types/wlr_output.h" @@ -34,6 +34,16 @@ static bool output_set_hardware_cursor(struct wlr_output *output, static void output_cursor_damage_whole(struct wlr_output_cursor *cursor); +static void output_disable_hardware_cursor(struct wlr_output *output) { + if (output->hardware_cursor == NULL) { + return; + } + + output_set_hardware_cursor(output, NULL, 0, 0); + output_cursor_damage_whole(output->hardware_cursor); + output->hardware_cursor = NULL; +} + void wlr_output_lock_software_cursors(struct wlr_output *output, bool lock) { if (lock) { ++output->software_cursor_locks; @@ -45,10 +55,8 @@ void wlr_output_lock_software_cursors(struct wlr_output *output, bool lock) { lock ? "Disabling" : "Enabling", output->name, output->software_cursor_locks); - if (output->software_cursor_locks > 0 && output->hardware_cursor != NULL) { - output_set_hardware_cursor(output, NULL, 0, 0); - output_cursor_damage_whole(output->hardware_cursor); - output->hardware_cursor = NULL; + if (output->software_cursor_locks > 0) { + output_disable_hardware_cursor(output); } // If it's possible to use hardware cursors again, don't switch immediately @@ -56,27 +64,6 @@ void wlr_output_lock_software_cursors(struct wlr_output *output, bool lock) { // again. } -static void output_scissor(struct wlr_output *output, pixman_box32_t *rect) { - struct wlr_renderer *renderer = output->renderer; - assert(renderer); - - struct wlr_box box = { - .x = rect->x1, - .y = rect->y1, - .width = rect->x2 - rect->x1, - .height = rect->y2 - rect->y1, - }; - - int ow, oh; - wlr_output_transformed_resolution(output, &ow, &oh); - - enum wl_output_transform transform = - wlr_output_transform_invert(output->transform); - wlr_box_transform(&box, &box, transform, ow, oh); - - wlr_renderer_scissor(renderer, &box); -} - /** * Returns the cursor box, scaled for its output. */ @@ -88,72 +75,6 @@ static void output_cursor_get_box(struct wlr_output_cursor *cursor, box->height = cursor->height; } -static void output_cursor_render(struct wlr_output_cursor *cursor, - pixman_region32_t *damage) { - struct wlr_renderer *renderer = cursor->output->renderer; - assert(renderer); - - struct wlr_texture *texture = cursor->texture; - if (texture == NULL) { - return; - } - - struct wlr_box box; - output_cursor_get_box(cursor, &box); - - pixman_region32_t surface_damage; - pixman_region32_init(&surface_damage); - pixman_region32_union_rect(&surface_damage, &surface_damage, box.x, box.y, - box.width, box.height); - pixman_region32_intersect(&surface_damage, &surface_damage, damage); - if (!pixman_region32_not_empty(&surface_damage)) { - goto surface_damage_finish; - } - - float matrix[9]; - wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, - cursor->output->transform_matrix); - - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(&surface_damage, &nrects); - for (int i = 0; i < nrects; ++i) { - output_scissor(cursor->output, &rects[i]); - wlr_render_texture_with_matrix(renderer, texture, matrix, 1.0f); - } - wlr_renderer_scissor(renderer, NULL); - -surface_damage_finish: - pixman_region32_fini(&surface_damage); -} - -void wlr_output_render_software_cursors(struct wlr_output *output, - const pixman_region32_t *damage) { - int width, height; - wlr_output_transformed_resolution(output, &width, &height); - - pixman_region32_t render_damage; - pixman_region32_init(&render_damage); - pixman_region32_union_rect(&render_damage, &render_damage, 0, 0, - width, height); - if (damage != NULL) { - // Damage tracking supported - pixman_region32_intersect(&render_damage, &render_damage, damage); - } - - if (pixman_region32_not_empty(&render_damage)) { - struct wlr_output_cursor *cursor; - wl_list_for_each(cursor, &output->cursors, link) { - if (!cursor->enabled || !cursor->visible || - output->hardware_cursor == cursor) { - continue; - } - output_cursor_render(cursor, &render_damage); - } - } - - pixman_region32_fini(&render_damage); -} - void wlr_output_add_software_cursors_to_render_pass(struct wlr_output *output, struct wlr_render_pass *render_pass, const pixman_region32_t *damage) { int width, height; @@ -275,13 +196,27 @@ static struct wlr_buffer *render_cursor_buffer(struct wlr_output_cursor *cursor) int width = cursor->width; int height = cursor->height; - if (output->impl->get_cursor_size) { + if (output->impl->get_cursor_sizes) { // Apply hardware limitations on buffer size - output->impl->get_cursor_size(cursor->output, &width, &height); - if ((int)texture->width > width || (int)texture->height > height) { + size_t sizes_len = 0; + const struct wlr_output_cursor_size *sizes = + output->impl->get_cursor_sizes(cursor->output, &sizes_len); + + bool found = false; + for (size_t i = 0; i < sizes_len; i++) { + struct wlr_output_cursor_size size = sizes[i]; + if ((int)texture->width <= size.width && (int)texture->height <= size.height) { + width = size.width; + height = size.height; + found = true; + break; + } + } + + if (!found) { wlr_log(WLR_DEBUG, "Cursor texture too large (%dx%d), " - "exceeds hardware limitations (%dx%d)", texture->width, - texture->height, width, height); + "exceeds hardware limitations", texture->width, + texture->height); return NULL; } } @@ -359,6 +294,8 @@ static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) { return false; } + output->hardware_cursor = NULL; + struct wlr_texture *texture = cursor->texture; // If the cursor was hidden or was a software cursor, the hardware @@ -421,6 +358,13 @@ bool wlr_output_cursor_set_buffer(struct wlr_output_cursor *cursor, dst_width, dst_height, WL_OUTPUT_TRANSFORM_NORMAL, hotspot_x, hotspot_y); } +static void output_cursor_handle_renderer_destroy(struct wl_listener *listener, + void *data) { + struct wlr_output_cursor *cursor = wl_container_of(listener, cursor, renderer_destroy); + output_cursor_set_texture(cursor, NULL, false, NULL, 0, 0, + WL_OUTPUT_TRANSFORM_NORMAL, 0, 0); +} + bool output_cursor_set_texture(struct wlr_output_cursor *cursor, struct wlr_texture *texture, bool own_texture, const struct wlr_fbox *src_box, int dst_width, int dst_height, enum wl_output_transform transform, @@ -451,12 +395,20 @@ bool output_cursor_set_texture(struct wlr_output_cursor *cursor, cursor->texture = texture; cursor->own_texture = own_texture; + wl_list_remove(&cursor->renderer_destroy.link); + if (texture != NULL) { + cursor->renderer_destroy.notify = output_cursor_handle_renderer_destroy; + wl_signal_add(&texture->renderer->events.destroy, &cursor->renderer_destroy); + } else { + wl_list_init(&cursor->renderer_destroy.link); + } + if (output_cursor_attempt_hardware(cursor)) { return true; } - wlr_log(WLR_DEBUG, "Falling back to software cursor on output '%s'", - cursor->output->name); + wlr_log(WLR_DEBUG, "Falling back to software cursor on output '%s'", output->name); + output_disable_hardware_cursor(output); output_cursor_damage_whole(cursor); return true; } @@ -502,6 +454,7 @@ struct wlr_output_cursor *wlr_output_cursor_create(struct wlr_output *output) { cursor->output = output; wl_list_insert(&output->cursors, &cursor->link); cursor->visible = true; // default position is at (0, 0) + wl_list_init(&cursor->renderer_destroy.link); return cursor; } @@ -512,9 +465,9 @@ void wlr_output_cursor_destroy(struct wlr_output_cursor *cursor) { output_cursor_reset(cursor); if (cursor->output->hardware_cursor == cursor) { // If this cursor was the hardware cursor, disable it - output_set_hardware_cursor(cursor->output, NULL, 0, 0); - cursor->output->hardware_cursor = NULL; + output_disable_hardware_cursor(cursor->output); } + wl_list_remove(&cursor->renderer_destroy.link); if (cursor->own_texture) { wlr_texture_destroy(cursor->texture); } diff --git a/types/output/output.c b/types/output/output.c index af2fab5..818f454 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -6,7 +5,6 @@ #include #include #include -#include #include #include #include "render/allocator/allocator.h" @@ -128,15 +126,25 @@ static void output_bind(struct wl_client *wl_client, void *data, wl_signal_emit_mutable(&output->events.bind, &evt); } -void wlr_output_create_global(struct wlr_output *output) { +static void handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_output *output = wl_container_of(listener, output, display_destroy); + wlr_output_destroy_global(output); +} + +void wlr_output_create_global(struct wlr_output *output, struct wl_display *display) { if (output->global != NULL) { return; } - output->global = wl_global_create(output->display, + + output->global = wl_global_create(display, &wl_output_interface, OUTPUT_VERSION, output, output_bind); if (output->global == NULL) { wlr_log(WLR_ERROR, "Failed to allocate wl_output global"); + return; } + + wl_list_remove(&output->display_destroy.link); + wl_display_add_destroy_listener(display, &output->display_destroy); } void wlr_output_destroy_global(struct wlr_output *output) { @@ -152,6 +160,9 @@ void wlr_output_destroy_global(struct wlr_output *output) { wl_list_init(wl_resource_get_link(resource)); } + wl_list_remove(&output->display_destroy.link); + wl_list_init(&output->display_destroy.link); + wlr_global_destroy_safe(output->global); output->global = NULL; } @@ -171,9 +182,8 @@ void wlr_output_schedule_done(struct wlr_output *output) { return; // Already scheduled } - struct wl_event_loop *ev = wl_display_get_event_loop(output->display); - output->idle_done = - wl_event_loop_add_idle(ev, schedule_done_handle_idle_timer, output); + output->idle_done = wl_event_loop_add_idle(output->event_loop, + schedule_done_handle_idle_timer, output); } struct wlr_output *wlr_output_from_resource(struct wl_resource *resource) { @@ -182,66 +192,6 @@ struct wlr_output *wlr_output_from_resource(struct wl_resource *resource) { return wl_resource_get_user_data(resource); } -static void output_update_matrix(struct wlr_output *output) { - wlr_matrix_identity(output->transform_matrix); - if (output->transform != WL_OUTPUT_TRANSFORM_NORMAL) { - int tr_width, tr_height; - wlr_output_transformed_resolution(output, &tr_width, &tr_height); - - wlr_matrix_translate(output->transform_matrix, - output->width / 2.0, output->height / 2.0); - wlr_matrix_transform(output->transform_matrix, output->transform); - wlr_matrix_translate(output->transform_matrix, - - tr_width / 2.0, - tr_height / 2.0); - } -} - -void wlr_output_enable(struct wlr_output *output, bool enable) { - wlr_output_state_set_enabled(&output->pending, enable); -} - -void wlr_output_set_mode(struct wlr_output *output, - struct wlr_output_mode *mode) { - wlr_output_state_set_mode(&output->pending, mode); -} - -void wlr_output_set_custom_mode(struct wlr_output *output, int32_t width, - int32_t height, int32_t refresh) { - // If there is a fixed mode which matches what the user wants, use that - struct wlr_output_mode *mode; - wl_list_for_each(mode, &output->modes, link) { - if (mode->width == width && mode->height == height && - mode->refresh == refresh) { - wlr_output_set_mode(output, mode); - return; - } - } - - wlr_output_state_set_custom_mode(&output->pending, width, height, refresh); -} - -void wlr_output_set_transform(struct wlr_output *output, - enum wl_output_transform transform) { - wlr_output_state_set_transform(&output->pending, transform); -} - -void wlr_output_set_scale(struct wlr_output *output, float scale) { - wlr_output_state_set_scale(&output->pending, scale); -} - -void wlr_output_enable_adaptive_sync(struct wlr_output *output, bool enabled) { - wlr_output_state_set_adaptive_sync_enabled(&output->pending, enabled); -} - -void wlr_output_set_render_format(struct wlr_output *output, uint32_t format) { - wlr_output_state_set_render_format(&output->pending, format); -} - -void wlr_output_set_subpixel(struct wlr_output *output, - enum wl_output_subpixel subpixel) { - wlr_output_state_set_subpixel(&output->pending, subpixel); -} - void wlr_output_set_name(struct wlr_output *output, const char *name) { assert(output->global == NULL); @@ -271,18 +221,6 @@ void wlr_output_set_description(struct wlr_output *output, const char *desc) { wl_signal_emit_mutable(&output->events.description, output); } -static void handle_display_destroy(struct wl_listener *listener, void *data) { - struct wlr_output *output = - wl_container_of(listener, output, display_destroy); - wlr_output_destroy_global(output); -} - -static void output_state_move(struct wlr_output_state *dst, - struct wlr_output_state *src) { - *dst = *src; - wlr_output_state_init(src); -} - static void output_apply_state(struct wlr_output *output, const struct wlr_output_state *state) { if (state->committed & WLR_OUTPUT_STATE_RENDER_FORMAT) { @@ -304,7 +242,6 @@ static void output_apply_state(struct wlr_output *output, if (state->committed & WLR_OUTPUT_STATE_TRANSFORM) { output->transform = state->transform; - output_update_matrix(output); } bool geometry_updated = state->committed & @@ -364,7 +301,6 @@ static void output_apply_state(struct wlr_output *output, output->refresh != refresh) { output->width = width; output->height = height; - output_update_matrix(output); output->refresh = refresh; @@ -397,7 +333,7 @@ static void output_apply_state(struct wlr_output *output, } void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend, - const struct wlr_output_impl *impl, struct wl_display *display, + const struct wlr_output_impl *impl, struct wl_event_loop *event_loop, const struct wlr_output_state *state) { assert(impl->commit); if (impl->set_cursor || impl->move_cursor) { @@ -407,7 +343,7 @@ void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend, *output = (struct wlr_output){ .backend = backend, .impl = impl, - .display = display, + .event_loop = event_loop, .render_format = DRM_FORMAT_XRGB8888, .transform = WL_OUTPUT_TRANSFORM_NORMAL, .scale = 1, @@ -428,7 +364,6 @@ void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend, wl_signal_init(&output->events.description); wl_signal_init(&output->events.request_state); wl_signal_init(&output->events.destroy); - wlr_output_state_init(&output->pending); output->software_cursor_locks = env_parse_bool("WLR_NO_HARDWARE_CURSORS"); if (output->software_cursor_locks) { @@ -437,8 +372,8 @@ void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend, wlr_addon_set_init(&output->addons); + wl_list_init(&output->display_destroy.link); output->display_destroy.notify = handle_display_destroy; - wl_display_add_destroy_listener(display, &output->display_destroy); if (state) { output_apply_state(output, state); @@ -452,9 +387,9 @@ void wlr_output_destroy(struct wlr_output *output) { wl_signal_emit_mutable(&output->events.destroy, output); - wl_list_remove(&output->display_destroy.link); wlr_output_destroy_global(output); - output_clear_back_buffer(output); + + wl_list_remove(&output->display_destroy.link); wlr_addon_set_finish(&output->addons); @@ -489,8 +424,6 @@ void wlr_output_destroy(struct wlr_output *output) { free(output->model); free(output->serial); - wlr_output_state_finish(&output->pending); - if (output->impl && output->impl->destroy) { output->impl->destroy(output); } else { @@ -532,42 +465,6 @@ struct wlr_output_mode *wlr_output_preferred_mode(struct wlr_output *output) { return wl_container_of(output->modes.next, mode, link); } -static void output_state_clear_buffer(struct wlr_output_state *state) { - if (!(state->committed & WLR_OUTPUT_STATE_BUFFER)) { - return; - } - - wlr_buffer_unlock(state->buffer); - state->buffer = NULL; - - state->committed &= ~WLR_OUTPUT_STATE_BUFFER; -} - -void wlr_output_set_damage(struct wlr_output *output, - const pixman_region32_t *damage) { - pixman_region32_intersect_rect(&output->pending.damage, damage, - 0, 0, output->width, output->height); - output->pending.committed |= WLR_OUTPUT_STATE_DAMAGE; -} - -void wlr_output_set_layers(struct wlr_output *output, - struct wlr_output_layer_state *layers, size_t layers_len) { - wlr_output_state_set_layers(&output->pending, layers, layers_len); -} - -static void output_state_clear_gamma_lut(struct wlr_output_state *state) { - free(state->gamma_lut); - state->gamma_lut = NULL; - state->committed &= ~WLR_OUTPUT_STATE_GAMMA_LUT; -} - -static void output_state_clear(struct wlr_output_state *state) { - output_state_clear_buffer(state); - output_state_clear_gamma_lut(state); - pixman_region32_clear(&state->damage); - state->committed = 0; -} - void output_pending_resolution(struct wlr_output *output, const struct wlr_output_state *state, int *width, int *height) { if (state->committed & WLR_OUTPUT_STATE_MODE) { @@ -686,10 +583,7 @@ static bool output_basic_test(struct wlr_output *output, wlr_drm_format_finish(&format); } - bool enabled = output->enabled; - if (state->committed & WLR_OUTPUT_STATE_ENABLED) { - enabled = state->enabled; - } + bool enabled = output_pending_enabled(output, state); if (enabled && (state->committed & (WLR_OUTPUT_STATE_ENABLED | WLR_OUTPUT_STATE_MODE))) { @@ -769,38 +663,13 @@ bool wlr_output_test_state(struct wlr_output *output, return success; } -bool wlr_output_test(struct wlr_output *output) { - struct wlr_output_state state = output->pending; - - if (output->back_buffer != NULL) { - assert((state.committed & WLR_OUTPUT_STATE_BUFFER) == 0); - state.committed |= WLR_OUTPUT_STATE_BUFFER; - state.buffer = output->back_buffer; - } - - return wlr_output_test_state(output, &state); -} - -bool wlr_output_commit_state(struct wlr_output *output, - const struct wlr_output_state *state) { - uint32_t unchanged = output_compare_state(output, state); - - // Create a shallow copy of the state with only the fields which have been - // changed and potentially a new buffer. - struct wlr_output_state pending = *state; - pending.committed &= ~unchanged; - - if (!output_basic_test(output, &pending)) { +bool output_prepare_commit(struct wlr_output *output, const struct wlr_output_state *state) { + if (!output_basic_test(output, state)) { wlr_log(WLR_ERROR, "Basic output test failed for %s", output->name); return false; } - bool new_back_buffer = false; - if (!output_ensure_buffer(output, &pending, &new_back_buffer)) { - return false; - } - - if ((pending.committed & WLR_OUTPUT_STATE_BUFFER) && + if ((state->committed & WLR_OUTPUT_STATE_BUFFER) && output->idle_frame != NULL) { wl_event_source_remove(output->idle_frame); output->idle_frame = NULL; @@ -812,17 +681,14 @@ bool wlr_output_commit_state(struct wlr_output *output, struct wlr_output_event_precommit pre_event = { .output = output, .when = &now, - .state = &pending, + .state = state, }; wl_signal_emit_mutable(&output->events.precommit, &pre_event); - if (!output->impl->commit(output, &pending)) { - if (new_back_buffer) { - wlr_buffer_unlock(pending.buffer); - } - return false; - } + return true; +} +void output_apply_commit(struct wlr_output *output, const struct wlr_output_state *state) { output->commit_seq++; if (output_pending_enabled(output, state)) { @@ -830,49 +696,55 @@ bool wlr_output_commit_state(struct wlr_output *output, output->needs_frame = false; } - output_apply_state(output, &pending); + output_apply_state(output, state); + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); struct wlr_output_event_commit event = { .output = output, .when = &now, - .state = &pending, + .state = state, }; wl_signal_emit_mutable(&output->events.commit, &event); +} - if (new_back_buffer) { - wlr_buffer_unlock(pending.buffer); +bool wlr_output_commit_state(struct wlr_output *output, + const struct wlr_output_state *state) { + uint32_t unchanged = output_compare_state(output, state); + + // Create a shallow copy of the state with only the fields which have been + // changed and potentially a new buffer. + struct wlr_output_state pending = *state; + pending.committed &= ~unchanged; + + if (!output_basic_test(output, &pending)) { + wlr_log(WLR_ERROR, "Basic output test failed for %s", output->name); + return false; } - return true; -} + bool new_back_buffer = false; + if (!output_ensure_buffer(output, &pending, &new_back_buffer)) { + return false; + } -bool wlr_output_commit(struct wlr_output *output) { - // Make sure the pending state is cleared before the output is committed - struct wlr_output_state state = {0}; - output_state_move(&state, &output->pending); + if (!output_prepare_commit(output, &pending)) { + return false; + } - // output_clear_back_buffer detaches the buffer from the renderer. This is - // important to do before calling impl->commit(), because this marks an - // implicit rendering synchronization point. The backend needs it to avoid - // displaying a buffer when asynchronous GPU work isn't finished. - if (output->back_buffer != NULL) { - wlr_output_state_set_buffer(&state, output->back_buffer); - output_clear_back_buffer(output); + if (!output->impl->commit(output, &pending)) { + if (new_back_buffer) { + wlr_buffer_unlock(pending.buffer); + } + return false; } - bool ok = wlr_output_commit_state(output, &state); - wlr_output_state_finish(&state); - return ok; -} + output_apply_commit(output, &pending); -void wlr_output_rollback(struct wlr_output *output) { - output_clear_back_buffer(output); - output_state_clear(&output->pending); -} + if (new_back_buffer) { + wlr_buffer_unlock(pending.buffer); + } -void wlr_output_attach_buffer(struct wlr_output *output, - struct wlr_buffer *buffer) { - wlr_output_state_set_buffer(&output->pending, buffer); + return true; } void wlr_output_send_frame(struct wlr_output *output) { @@ -902,9 +774,8 @@ void wlr_output_schedule_frame(struct wlr_output *output) { // We're using an idle timer here in case a buffer swap happens right after // this function is called - struct wl_event_loop *ev = wl_display_get_event_loop(output->display); - output->idle_frame = - wl_event_loop_add_idle(ev, schedule_frame_handle_idle_timer, output); + output->idle_frame = wl_event_loop_add_idle(output->event_loop, + schedule_frame_handle_idle_timer, output); } void wlr_output_send_present(struct wlr_output *output, @@ -961,8 +832,8 @@ void output_defer_present(struct wlr_output *output, struct wlr_output_event_pre deferred->output_destroy.notify = deferred_present_event_handle_output_destroy; wl_signal_add(&output->events.destroy, &deferred->output_destroy); - struct wl_event_loop *ev = wl_display_get_event_loop(output->display); - deferred->idle_source = wl_event_loop_add_idle(ev, deferred_present_event_handle_idle, deferred); + deferred->idle_source = wl_event_loop_add_idle(output->event_loop, + deferred_present_event_handle_idle, deferred); } void wlr_output_send_request_state(struct wlr_output *output, @@ -981,11 +852,6 @@ void wlr_output_send_request_state(struct wlr_output *output, wl_signal_emit_mutable(&output->events.request_state, &event); } -void wlr_output_set_gamma(struct wlr_output *output, size_t size, - const uint16_t *r, const uint16_t *g, const uint16_t *b) { - wlr_output_state_set_gamma_lut(&output->pending, size, r, g, b); -} - size_t wlr_output_get_gamma_size(struct wlr_output *output) { if (!output->impl->get_gamma_size) { return 0; diff --git a/types/output/render.c b/types/output/render.c index acc6f6b..709646a 100644 --- a/types/output/render.c +++ b/types/output/render.c @@ -16,10 +16,9 @@ bool wlr_output_init_render(struct wlr_output *output, struct wlr_allocator *allocator, struct wlr_renderer *renderer) { assert(allocator != NULL && renderer != NULL); - assert(output->back_buffer == NULL); uint32_t backend_caps = backend_get_buffer_caps(output->backend); - uint32_t renderer_caps = renderer_get_render_buffer_caps(renderer); + uint32_t renderer_caps = renderer->render_buffer_caps; if (!(backend_caps & allocator->buffer_caps)) { wlr_log(WLR_ERROR, "output backend and allocator buffer capabilities " @@ -43,46 +42,6 @@ bool wlr_output_init_render(struct wlr_output *output, return true; } -void output_clear_back_buffer(struct wlr_output *output) { - if (output->back_buffer == NULL) { - return; - } - - struct wlr_renderer *renderer = output->renderer; - assert(renderer != NULL); - - renderer_bind_buffer(renderer, NULL); - - wlr_buffer_unlock(output->back_buffer); - output->back_buffer = NULL; -} - -bool wlr_output_attach_render(struct wlr_output *output, int *buffer_age) { - assert(output->back_buffer == NULL); - - if (!wlr_output_configure_primary_swapchain(output, &output->pending, - &output->swapchain)) { - return false; - } - - struct wlr_renderer *renderer = output->renderer; - assert(renderer != NULL); - - struct wlr_buffer *buffer = - wlr_swapchain_acquire(output->swapchain, buffer_age); - if (buffer == NULL) { - return false; - } - - if (!renderer_bind_buffer(renderer, buffer)) { - wlr_buffer_unlock(buffer); - return false; - } - - output->back_buffer = buffer; - return true; -} - static struct wlr_buffer *output_acquire_empty_buffer(struct wlr_output *output, const struct wlr_output_state *state) { assert(!(state->committed & WLR_OUTPUT_STATE_BUFFER)); @@ -93,12 +52,12 @@ static struct wlr_buffer *output_acquire_empty_buffer(struct wlr_output *output, // wlr_output_test_state(), which will prevent us from being called. if (!wlr_output_configure_primary_swapchain(output, state, &output->swapchain)) { - return false; + return NULL; } struct wlr_buffer *buffer = wlr_swapchain_acquire(output->swapchain, NULL); if (buffer == NULL) { - return false; + return NULL; } struct wlr_render_pass *pass = @@ -239,27 +198,9 @@ bool output_pick_format(struct wlr_output *output, return true; } -uint32_t wlr_output_preferred_read_format(struct wlr_output *output) { - struct wlr_renderer *renderer = output->renderer; - assert(renderer != NULL); - - if (!renderer->impl->preferred_read_format || !renderer->impl->read_pixels) { - return DRM_FORMAT_INVALID; - } - - if (!wlr_output_attach_render(output, NULL)) { - return false; - } - - uint32_t fmt = renderer->impl->preferred_read_format(renderer); - - output_clear_back_buffer(output); - - return fmt; -} - struct wlr_render_pass *wlr_output_begin_render_pass(struct wlr_output *output, - struct wlr_output_state *state, int *buffer_age, struct wlr_render_timer *timer) { + struct wlr_output_state *state, int *buffer_age, + struct wlr_buffer_pass_options *render_options) { if (!wlr_output_configure_primary_swapchain(output, state, &output->swapchain)) { return NULL; } @@ -271,10 +212,7 @@ struct wlr_render_pass *wlr_output_begin_render_pass(struct wlr_output *output, struct wlr_renderer *renderer = output->renderer; assert(renderer != NULL); - struct wlr_render_pass *pass = wlr_renderer_begin_buffer_pass(renderer, buffer, - &(struct wlr_buffer_pass_options){ - .timer = timer, - }); + struct wlr_render_pass *pass = wlr_renderer_begin_buffer_pass(renderer, buffer, render_options); if (pass == NULL) { return NULL; } diff --git a/types/scene/drag_icon.c b/types/scene/drag_icon.c index b508487..84b202c 100644 --- a/types/scene/drag_icon.c +++ b/types/scene/drag_icon.c @@ -10,8 +10,6 @@ struct wlr_scene_drag_icon { struct wl_listener tree_destroy; struct wl_listener drag_icon_surface_commit; - struct wl_listener drag_icon_surface_map; - struct wl_listener drag_icon_surface_unmap; struct wl_listener drag_icon_destroy; }; @@ -24,25 +22,11 @@ static void drag_icon_handle_surface_commit(struct wl_listener *listener, void * node->x + surface->current.dx, node->y + surface->current.dy); } -static void drag_icon_handle_surface_map(struct wl_listener *listener, void *data) { - struct wlr_scene_drag_icon *icon = - wl_container_of(listener, icon, drag_icon_surface_map); - wlr_scene_node_set_enabled(&icon->tree->node, true); -} - -static void drag_icon_handle_surface_unmap(struct wl_listener *listener, void *data) { - struct wlr_scene_drag_icon *icon = - wl_container_of(listener, icon, drag_icon_surface_unmap); - wlr_scene_node_set_enabled(&icon->tree->node, false); -} - static void drag_icon_handle_tree_destroy(struct wl_listener *listener, void *data) { struct wlr_scene_drag_icon *icon = wl_container_of(listener, icon, tree_destroy); wl_list_remove(&icon->tree_destroy.link); wl_list_remove(&icon->drag_icon_surface_commit.link); - wl_list_remove(&icon->drag_icon_surface_map.link); - wl_list_remove(&icon->drag_icon_surface_unmap.link); wl_list_remove(&icon->drag_icon_destroy.link); free(icon); } @@ -76,16 +60,10 @@ struct wlr_scene_tree *wlr_scene_drag_icon_create( return NULL; } - wlr_scene_node_set_enabled(&icon->tree->node, drag_icon->surface->mapped); - icon->tree_destroy.notify = drag_icon_handle_tree_destroy; wl_signal_add(&icon->tree->node.events.destroy, &icon->tree_destroy); icon->drag_icon_surface_commit.notify = drag_icon_handle_surface_commit; wl_signal_add(&drag_icon->surface->events.commit, &icon->drag_icon_surface_commit); - icon->drag_icon_surface_map.notify = drag_icon_handle_surface_map; - wl_signal_add(&drag_icon->surface->events.map, &icon->drag_icon_surface_map); - icon->drag_icon_surface_unmap.notify = drag_icon_handle_surface_unmap; - wl_signal_add(&drag_icon->surface->events.unmap, &icon->drag_icon_surface_unmap); icon->drag_icon_destroy.notify = drag_icon_handle_destroy; wl_signal_add(&drag_icon->events.destroy, &icon->drag_icon_destroy); diff --git a/types/scene/layer_shell_v1.c b/types/scene/layer_shell_v1.c index 67fdda2..4ae736d 100644 --- a/types/scene/layer_shell_v1.c +++ b/types/scene/layer_shell_v1.c @@ -9,8 +9,6 @@ static void scene_layer_surface_handle_tree_destroy( // tree and surface_node will be cleaned up by scene_node_finish wl_list_remove(&scene_layer_surface->tree_destroy.link); wl_list_remove(&scene_layer_surface->layer_surface_destroy.link); - wl_list_remove(&scene_layer_surface->layer_surface_map.link); - wl_list_remove(&scene_layer_surface->layer_surface_unmap.link); free(scene_layer_surface); } @@ -21,20 +19,6 @@ static void scene_layer_surface_handle_layer_surface_destroy( wlr_scene_node_destroy(&scene_layer_surface->tree->node); } -static void scene_layer_surface_handle_layer_surface_map( - struct wl_listener *listener, void *data) { - struct wlr_scene_layer_surface_v1 *scene_layer_surface = - wl_container_of(listener, scene_layer_surface, layer_surface_map); - wlr_scene_node_set_enabled(&scene_layer_surface->tree->node, true); -} - -static void scene_layer_surface_handle_layer_surface_unmap( - struct wl_listener *listener, void *data) { - struct wlr_scene_layer_surface_v1 *scene_layer_surface = - wl_container_of(listener, scene_layer_surface, layer_surface_unmap); - wlr_scene_node_set_enabled(&scene_layer_surface->tree->node, false); -} - static void layer_surface_exclusive_zone( struct wlr_layer_surface_v1_state *state, struct wlr_box *usable_area) { @@ -70,6 +54,13 @@ static void layer_surface_exclusive_zone( usable_area->width -= state->exclusive_zone + state->margin.right; break; } + + if (usable_area->width < 0) { + usable_area->width = 0; + } + if (usable_area->height < 0) { + usable_area->height = 0; + } } void wlr_scene_layer_surface_v1_configure( @@ -169,18 +160,5 @@ struct wlr_scene_layer_surface_v1 *wlr_scene_layer_surface_v1_create( wl_signal_add(&layer_surface->events.destroy, &scene_layer_surface->layer_surface_destroy); - scene_layer_surface->layer_surface_map.notify = - scene_layer_surface_handle_layer_surface_map; - wl_signal_add(&layer_surface->surface->events.map, - &scene_layer_surface->layer_surface_map); - - scene_layer_surface->layer_surface_unmap.notify = - scene_layer_surface_handle_layer_surface_unmap; - wl_signal_add(&layer_surface->surface->events.unmap, - &scene_layer_surface->layer_surface_unmap); - - wlr_scene_node_set_enabled(&scene_layer_surface->tree->node, - layer_surface->surface->mapped); - return scene_layer_surface; } diff --git a/types/scene/output_layout.c b/types/scene/output_layout.c index 42683f8..8818590 100644 --- a/types/scene/output_layout.c +++ b/types/scene/output_layout.c @@ -75,7 +75,9 @@ void wlr_scene_output_layout_add_output(struct wlr_scene_output_layout *sol, struct wlr_scene_output_layout_output *solo; wl_list_for_each(solo, &sol->outputs, link) { - assert(solo->scene_output != so); + if (solo->scene_output == so) { + return; + } } solo = calloc(1, sizeof(*solo)); diff --git a/types/scene/subsurface_tree.c b/types/scene/subsurface_tree.c index a8d301f..4e0a3f9 100644 --- a/types/scene/subsurface_tree.c +++ b/types/scene/subsurface_tree.c @@ -326,7 +326,7 @@ static struct wlr_scene_subsurface_tree *get_subsurface_tree_from_node( } static bool subsurface_tree_set_clip(struct wlr_scene_node *node, - struct wlr_box *clip) { + const struct wlr_box *clip) { if (node->type != WLR_SCENE_NODE_TREE) { return false; } @@ -360,7 +360,7 @@ static bool subsurface_tree_set_clip(struct wlr_scene_node *node, } void wlr_scene_subsurface_tree_set_clip(struct wlr_scene_node *node, - struct wlr_box *clip) { + const struct wlr_box *clip) { #ifndef NDEBUG bool found = #endif diff --git a/types/scene/surface.c b/types/scene/surface.c index b9f5221..1905b4d 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -1,9 +1,11 @@ #include #include +#include #include #include #include #include +#include #include "types/wlr_scene.h" static void handle_scene_buffer_outputs_update( @@ -17,6 +19,8 @@ static void handle_scene_buffer_outputs_update( double scale = surface->buffer->primary_output->output->scale; wlr_fractional_scale_v1_notify_scale(surface->surface, scale); wlr_surface_set_preferred_buffer_scale(surface->surface, ceil(scale)); + wlr_surface_set_preferred_buffer_transform(surface->surface, + surface->buffer->primary_output->output->transform); } static void handle_scene_buffer_output_enter( @@ -47,17 +51,10 @@ static void handle_scene_buffer_output_sample( return; } - struct wlr_scene *root = scene_node_get_root(&surface->buffer->node); - if (!root->presentation) { - return; - } - if (event->direct_scanout) { - wlr_presentation_surface_scanned_out_on_output( - root->presentation, surface->surface, scene_output->output); + wlr_presentation_surface_scanned_out_on_output(surface->surface, scene_output->output); } else { - wlr_presentation_surface_textured_on_output( - root->presentation, surface->surface, scene_output->output); + wlr_presentation_surface_textured_on_output(surface->surface, scene_output->output); } } @@ -129,12 +126,7 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) { wlr_fbox_transform(&src_box, &src_box, state->transform, buffer_width, buffer_height); - - if (state->transform & WL_OUTPUT_TRANSFORM_90) { - int tmp = buffer_width; - buffer_width = buffer_height; - buffer_height = tmp; - } + wlr_output_transform_coords(state->transform, &buffer_width, &buffer_height); src_box.x += (double)(clip->x * buffer_width) / state->width; src_box.y += (double)(clip->y * buffer_height) / state->height; @@ -154,10 +146,18 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) { return; } + float opacity = 1.0; + const struct wlr_alpha_modifier_surface_v1_state *alpha_modifier_state = + wlr_alpha_modifier_v1_get_surface_state(surface); + if (alpha_modifier_state != NULL) { + opacity = (float)alpha_modifier_state->multiplier; + } + wlr_scene_buffer_set_opaque_region(scene_buffer, &opaque); wlr_scene_buffer_set_source_box(scene_buffer, &src_box); wlr_scene_buffer_set_dest_size(scene_buffer, width, height); wlr_scene_buffer_set_transform(scene_buffer, state->transform); + wlr_scene_buffer_set_opacity(scene_buffer, opacity); scene_buffer_unmark_client_buffer(scene_buffer); diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index f81a861..46768aa 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -7,12 +6,12 @@ #include #include #include -#include #include #include #include #include #include +#include #include "types/wlr_buffer.h" #include "types/wlr_output.h" #include "types/wlr_scene.h" @@ -82,6 +81,11 @@ struct highlight_region { struct wl_list link; }; +static void scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer, + struct wlr_buffer *buffer); +static void scene_buffer_set_texture(struct wlr_scene_buffer *scene_buffer, + struct wlr_texture *texture); + void wlr_scene_node_destroy(struct wlr_scene_node *node) { if (node == NULL) { return; @@ -110,8 +114,8 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) { } } - wlr_texture_destroy(scene_buffer->texture); - wlr_buffer_unlock(scene_buffer->buffer); + scene_buffer_set_buffer(scene_buffer, NULL); + scene_buffer_set_texture(scene_buffer, NULL); pixman_region32_fini(&scene_buffer->opaque_region); } else if (node->type == WLR_SCENE_NODE_TREE) { struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); @@ -123,7 +127,6 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) { wlr_scene_output_destroy(scene_output); } - wl_list_remove(&scene->presentation_destroy.link); wl_list_remove(&scene->linux_dmabuf_v1_destroy.link); } else { assert(node->parent); @@ -157,7 +160,6 @@ struct wlr_scene *wlr_scene_create(void) { scene_tree_init(&scene->tree, NULL); wl_list_init(&scene->outputs); - wl_list_init(&scene->presentation_destroy.link); wl_list_init(&scene->linux_dmabuf_v1_destroy.link); const char *debug_damage_options[] = { @@ -170,6 +172,7 @@ struct wlr_scene *wlr_scene_create(void) { scene->debug_damage_option = env_parse_switch("WLR_SCENE_DEBUG_DAMAGE", debug_damage_options); scene->direct_scanout = !env_parse_bool("WLR_SCENE_DISABLE_DIRECT_SCANOUT"); scene->calculate_visibility = !env_parse_bool("WLR_SCENE_DISABLE_VISIBILITY"); + scene->highlight_transparent_region = env_parse_bool("WLR_SCENE_HIGHLIGHT_TRANSPARENT_REGION"); return scene; } @@ -251,7 +254,7 @@ static void scene_node_opaque_region(struct wlr_scene_node *node, int x, int y, return; } - if (!buffer_is_opaque(scene_buffer->buffer)) { + if (!scene_buffer->buffer_is_opaque) { pixman_region32_copy(opaque, &scene_buffer->opaque_region); pixman_region32_intersect_rect(opaque, opaque, 0, 0, width, height); pixman_region32_translate(opaque, x, y); @@ -602,6 +605,62 @@ void wlr_scene_rect_set_color(struct wlr_scene_rect *rect, const float color[sta scene_node_update(&rect->node, NULL); } +static void scene_buffer_handle_buffer_release(struct wl_listener *listener, + void *data) { + struct wlr_scene_buffer *scene_buffer = + wl_container_of(listener, scene_buffer, buffer_release); + + scene_buffer->buffer = NULL; + wl_list_remove(&scene_buffer->buffer_release.link); + wl_list_init(&scene_buffer->buffer_release.link); +} + +static void scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer, + struct wlr_buffer *buffer) { + wl_list_remove(&scene_buffer->buffer_release.link); + wl_list_init(&scene_buffer->buffer_release.link); + if (scene_buffer->own_buffer) { + wlr_buffer_unlock(scene_buffer->buffer); + } + scene_buffer->buffer = NULL; + scene_buffer->own_buffer = false; + scene_buffer->buffer_width = scene_buffer->buffer_height = 0; + scene_buffer->buffer_is_opaque = false; + + if (!buffer) { + return; + } + + scene_buffer->own_buffer = true; + scene_buffer->buffer = wlr_buffer_lock(buffer); + scene_buffer->buffer_width = buffer->width; + scene_buffer->buffer_height = buffer->height; + scene_buffer->buffer_is_opaque = buffer_is_opaque(buffer); + + scene_buffer->buffer_release.notify = scene_buffer_handle_buffer_release; + wl_signal_add(&buffer->events.release, &scene_buffer->buffer_release); +} + +static void scene_buffer_handle_renderer_destroy(struct wl_listener *listener, + void *data) { + struct wlr_scene_buffer *scene_buffer = wl_container_of(listener, scene_buffer, renderer_destroy); + scene_buffer_set_texture(scene_buffer, NULL); +} + +static void scene_buffer_set_texture(struct wlr_scene_buffer *scene_buffer, + struct wlr_texture *texture) { + wl_list_remove(&scene_buffer->renderer_destroy.link); + wlr_texture_destroy(scene_buffer->texture); + scene_buffer->texture = texture; + + if (texture != NULL) { + scene_buffer->renderer_destroy.notify = scene_buffer_handle_renderer_destroy; + wl_signal_add(&texture->renderer->events.destroy, &scene_buffer->renderer_destroy); + } else { + wl_list_init(&scene_buffer->renderer_destroy.link); + } +} + struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent, struct wlr_buffer *buffer) { struct wlr_scene_buffer *scene_buffer = calloc(1, sizeof(*scene_buffer)); @@ -611,18 +670,17 @@ struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent, assert(parent); scene_node_init(&scene_buffer->node, WLR_SCENE_NODE_BUFFER, parent); - if (buffer) { - scene_buffer->buffer = wlr_buffer_lock(buffer); - } - wl_signal_init(&scene_buffer->events.outputs_update); wl_signal_init(&scene_buffer->events.output_enter); wl_signal_init(&scene_buffer->events.output_leave); wl_signal_init(&scene_buffer->events.output_sample); wl_signal_init(&scene_buffer->events.frame_done); pixman_region32_init(&scene_buffer->opaque_region); + wl_list_init(&scene_buffer->buffer_release.link); + wl_list_init(&scene_buffer->renderer_destroy.link); scene_buffer->opacity = 1; + scene_buffer_set_buffer(scene_buffer, buffer); scene_node_update(&scene_buffer->node, NULL); return scene_buffer; @@ -635,28 +693,26 @@ void wlr_scene_buffer_set_buffer_with_damage(struct wlr_scene_buffer *scene_buff // coordinates. assert(buffer || !damage); - bool update = false; - - wlr_texture_destroy(scene_buffer->texture); - scene_buffer->texture = NULL; + bool mapped = buffer != NULL; + bool prev_mapped = scene_buffer->buffer != NULL || scene_buffer->texture != NULL; - if (buffer) { - // if this node used to not be mapped or its previous displayed - // buffer region will be different from what the new buffer would - // produce we need to update the node. - update = !scene_buffer->buffer || - (scene_buffer->dst_width == 0 && scene_buffer->dst_height == 0 && - (scene_buffer->buffer->width != buffer->width || - scene_buffer->buffer->height != buffer->height)); + if (!mapped && !prev_mapped) { + // unmapping already unmapped buffer - noop + return; + } - wlr_buffer_unlock(scene_buffer->buffer); - scene_buffer->buffer = wlr_buffer_lock(buffer); - } else { - wlr_buffer_unlock(scene_buffer->buffer); - update = true; - scene_buffer->buffer = NULL; + // if this node used to not be mapped or its previous displayed + // buffer region will be different from what the new buffer would + // produce we need to update the node. + bool update = mapped != prev_mapped; + if (buffer != NULL && scene_buffer->dst_width == 0 && scene_buffer->dst_height == 0) { + update = update || scene_buffer->buffer_width != buffer->width || + scene_buffer->buffer_height != buffer->height; } + scene_buffer_set_buffer(scene_buffer, buffer); + scene_buffer_set_texture(scene_buffer, NULL); + if (update) { scene_node_update(&scene_buffer->node, NULL); // updating the node will already damage the whole node for us. Return @@ -844,19 +900,24 @@ void wlr_scene_buffer_set_filter_mode(struct wlr_scene_buffer *scene_buffer, static struct wlr_texture *scene_buffer_get_texture( struct wlr_scene_buffer *scene_buffer, struct wlr_renderer *renderer) { + if (scene_buffer->buffer == NULL || scene_buffer->texture != NULL) { + return scene_buffer->texture; + } + struct wlr_client_buffer *client_buffer = wlr_client_buffer_get(scene_buffer->buffer); if (client_buffer != NULL) { return client_buffer->texture; } - if (scene_buffer->texture != NULL) { - return scene_buffer->texture; - } - - scene_buffer->texture = + struct wlr_texture *texture = wlr_texture_from_buffer(renderer, scene_buffer->buffer); - return scene_buffer->texture; + if (texture != NULL && scene_buffer->own_buffer) { + scene_buffer->own_buffer = false; + wlr_buffer_unlock(scene_buffer->buffer); + } + scene_buffer_set_texture(scene_buffer, texture); + return texture; } static void scene_node_get_size(struct wlr_scene_node *node, @@ -877,14 +938,10 @@ static void scene_node_get_size(struct wlr_scene_node *node, if (scene_buffer->dst_width > 0 && scene_buffer->dst_height > 0) { *width = scene_buffer->dst_width; *height = scene_buffer->dst_height; - } else if (scene_buffer->buffer) { - if (scene_buffer->transform & WL_OUTPUT_TRANSFORM_90) { - *height = scene_buffer->buffer->width; - *width = scene_buffer->buffer->height; - } else { - *width = scene_buffer->buffer->width; - *height = scene_buffer->buffer->height; - } + } else { + *width = scene_buffer->buffer_width; + *height = scene_buffer->buffer_height; + wlr_output_transform_coords(scene_buffer->transform, width, height); } break; } @@ -1108,6 +1165,7 @@ struct wlr_scene_node *wlr_scene_node_at(struct wlr_scene_node *node, struct render_list_entry { struct wlr_scene_node *node; bool sent_dmabuf_feedback; + bool highlight_transparent_region; int x, y; }; @@ -1125,16 +1183,19 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren return; } + int x = entry->x - data->logical.x; + int y = entry->y - data->logical.y; + struct wlr_box dst_box = { - .x = entry->x - data->logical.x, - .y = entry->y - data->logical.y, + .x = x, + .y = y, }; scene_node_get_size(node, &dst_box.width, &dst_box.height); scale_box(&dst_box, data->scale); pixman_region32_t opaque; pixman_region32_init(&opaque); - scene_node_opaque_region(node, dst_box.x, dst_box.y, &opaque); + scene_node_opaque_region(node, x, y, &opaque); scale_output_damage(&opaque, data->scale); pixman_region32_subtract(&opaque, &render_region, &opaque); @@ -1161,11 +1222,11 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren break; case WLR_SCENE_NODE_BUFFER:; struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); - assert(scene_buffer->buffer); struct wlr_texture *texture = scene_buffer_get_texture(scene_buffer, data->output->output->renderer); if (texture == NULL) { + wlr_damage_ring_add(&data->output->damage_ring, &render_region); break; } @@ -1190,6 +1251,15 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren .direct_scanout = false, }; wl_signal_emit_mutable(&scene_buffer->events.output_sample, &sample_event); + + if (entry->highlight_transparent_region) { + wlr_render_pass_add_rect(data->render_pass, &(struct wlr_render_rect_options){ + .box = dst_box, + .color = { .r = 0, .g = 0.3, .b = 0, .a = 0.3 }, + .clip = &opaque, + }); + } + break; } @@ -1197,23 +1267,6 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren pixman_region32_fini(&render_region); } -static void scene_handle_presentation_destroy(struct wl_listener *listener, - void *data) { - struct wlr_scene *scene = - wl_container_of(listener, scene, presentation_destroy); - wl_list_remove(&scene->presentation_destroy.link); - wl_list_init(&scene->presentation_destroy.link); - scene->presentation = NULL; -} - -void wlr_scene_set_presentation(struct wlr_scene *scene, - struct wlr_presentation *presentation) { - assert(scene->presentation == NULL); - scene->presentation = presentation; - scene->presentation_destroy.notify = scene_handle_presentation_destroy; - wl_signal_add(&presentation->events.destroy, &scene->presentation_destroy); -} - static void scene_handle_linux_dmabuf_v1_destroy(struct wl_listener *listener, void *data) { struct wlr_scene *scene = @@ -1281,6 +1334,33 @@ static void scene_output_handle_commit(struct wl_listener *listener, void *data) WLR_OUTPUT_STATE_ENABLED)) { scene_output_update_geometry(scene_output, force_update); } + + // if the output has been committed with a certain damage, we know that region + // will be acknowledged by the backend so we don't need to keep track of it + // anymore + if (state->committed & WLR_OUTPUT_STATE_DAMAGE) { + bool tracking_buffer = false; + struct wlr_damage_ring_buffer *buffer; + wl_list_for_each(buffer, &scene_output->damage_ring.buffers, link) { + if (buffer->buffer == state->buffer) { + tracking_buffer = true; + break; + } + } + + if (tracking_buffer) { + pixman_region32_subtract(&scene_output->pending_commit_damage, + &scene_output->pending_commit_damage, &state->damage); + } else { + pixman_region32_union(&scene_output->pending_commit_damage, + &scene_output->pending_commit_damage, &state->damage); + } + } + + if (scene_output->scene->debug_damage_option == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT && + !wl_list_empty(&scene_output->damage_highlight_regions)) { + wlr_output_schedule_frame(scene_output->output); + } } static void scene_output_handle_damage(struct wl_listener *listener, void *data) { @@ -1310,6 +1390,7 @@ struct wlr_scene_output *wlr_scene_output_create(struct wlr_scene *scene, wlr_addon_init(&scene_output->addon, &output->addons, scene, &output_addon_impl); wlr_damage_ring_init(&scene_output->damage_ring); + pixman_region32_init(&scene_output->pending_commit_damage); wl_list_init(&scene_output->damage_highlight_regions); int prev_output_index = -1; @@ -1368,6 +1449,7 @@ void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) { wlr_addon_finish(&scene_output->addon); wlr_damage_ring_finish(&scene_output->damage_ring); + pixman_region32_fini(&scene_output->pending_commit_damage); wl_list_remove(&scene_output->link); wl_list_remove(&scene_output->output_commit.link); wl_list_remove(&scene_output->output_damage.link); @@ -1411,7 +1493,7 @@ static bool scene_node_invisible(struct wlr_scene_node *node) { } else if (node->type == WLR_SCENE_NODE_BUFFER) { struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); - return buffer->buffer == NULL; + return buffer->buffer == NULL && buffer->texture == NULL; } return false; @@ -1421,6 +1503,8 @@ struct render_list_constructor_data { struct wlr_box box; struct wl_array *render_list; bool calculate_visibility; + bool highlight_transparent_region; + bool fractional_scale; }; static bool construct_render_list_iterator(struct wlr_scene_node *node, @@ -1431,10 +1515,12 @@ static bool construct_render_list_iterator(struct wlr_scene_node *node, return false; } - // while rendering, the background should always be black. - // If we see a black rect, we can ignore rendering everything under the rect - // and even the rect itself. - if (node->type == WLR_SCENE_NODE_RECT && data->calculate_visibility) { + // While rendering, the background should always be black. If we see a + // black rect, we can ignore rendering everything under the rect, and + // unless fractional scale is used even the rect itself (to avoid running + // into issues regarding damage region expansion). + if (node->type == WLR_SCENE_NODE_RECT && data->calculate_visibility && + (!data->fractional_scale || data->render_list->size == 0)) { struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node); float *black = (float[4]){ 0.f, 0.f, 0.f, 1.f }; @@ -1464,6 +1550,7 @@ static bool construct_render_list_iterator(struct wlr_scene_node *node, .node = node, .x = lx, .y = ly, + .highlight_transparent_region = data->highlight_transparent_region, }; return false; @@ -1471,12 +1558,17 @@ static bool construct_render_list_iterator(struct wlr_scene_node *node, static void output_state_apply_damage(const struct render_data *data, struct wlr_output_state *state) { + struct wlr_scene_output *output = data->output; + pixman_region32_t frame_damage; pixman_region32_init(&frame_damage); - pixman_region32_copy(&frame_damage, &data->output->damage_ring.current); + pixman_region32_copy(&frame_damage, &output->damage_ring.current); transform_output_damage(&frame_damage, data); - wlr_output_state_set_damage(state, &frame_damage); + pixman_region32_union(&output->pending_commit_damage, + &output->pending_commit_damage, &frame_damage); pixman_region32_fini(&frame_damage); + + wlr_output_state_set_damage(state, &output->pending_commit_damage); } static void scene_buffer_send_dmabuf_feedback(const struct wlr_scene *scene, @@ -1519,13 +1611,6 @@ static bool scene_entry_try_direct_scanout(struct render_list_entry *entry, return false; } - if (scene_output->scene->debug_damage_option == - WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) { - // We don't want to enter direct scan out if we have highlight regions - // enabled. Otherwise, we won't be able to render the damage regions. - return false; - } - if (node->type != WLR_SCENE_NODE_BUFFER) { return false; } @@ -1542,16 +1627,18 @@ static bool scene_entry_try_direct_scanout(struct render_list_entry *entry, } struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); - - struct wlr_fbox default_box = {0}; - if (buffer->transform & WL_OUTPUT_TRANSFORM_90) { - default_box.width = buffer->buffer->height; - default_box.height = buffer->buffer->width; - } else { - default_box.width = buffer->buffer->width; - default_box.height = buffer->buffer->height; + if (buffer->buffer == NULL) { + return false; } + int default_width = buffer->buffer->width; + int default_height = buffer->buffer->height; + wlr_output_transform_coords(buffer->transform, &default_width, &default_height); + struct wlr_fbox default_box = { + .width = default_width, + .height = default_height, + }; + if (!wlr_fbox_empty(&buffer->src_box) && !wlr_fbox_equal(&buffer->src_box, &default_box)) { return false; @@ -1585,7 +1672,6 @@ static bool scene_entry_try_direct_scanout(struct render_list_entry *entry, } wlr_output_state_set_buffer(&pending, buffer->buffer); - output_state_apply_damage(data, &pending); if (!wlr_output_test_state(scene_output->output, &pending)) { wlr_output_state_finish(&pending); @@ -1606,7 +1692,7 @@ static bool scene_entry_try_direct_scanout(struct render_list_entry *entry, bool wlr_scene_output_commit(struct wlr_scene_output *scene_output, const struct wlr_scene_output_state_options *options) { if (!scene_output->output->needs_frame && !pixman_region32_not_empty( - &scene_output->damage_ring.current)) { + &scene_output->pending_commit_damage)) { return true; } @@ -1622,8 +1708,6 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output, goto out; } - wlr_damage_ring_rotate(&scene_output->damage_ring); - out: wlr_output_state_finish(&state); return ok; @@ -1659,8 +1743,9 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, .output = scene_output, }; + int resolution_width, resolution_height; output_pending_resolution(output, state, - &render_data.trans_width, &render_data.trans_height); + &resolution_width, &resolution_height); if (state->committed & WLR_OUTPUT_STATE_TRANSFORM) { if (render_data.transform != state->transform) { @@ -1678,11 +1763,10 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, render_data.scale = state->scale; } - if (render_data.transform & WL_OUTPUT_TRANSFORM_90) { - int tmp = render_data.trans_width; - render_data.trans_width = render_data.trans_height; - render_data.trans_height = tmp; - } + render_data.trans_width = resolution_width; + render_data.trans_height = resolution_height; + wlr_output_transform_coords(render_data.transform, + &render_data.trans_width, &render_data.trans_height); render_data.logical.width = render_data.trans_width / render_data.scale; render_data.logical.height = render_data.trans_height / render_data.scale; @@ -1691,6 +1775,8 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, .box = render_data.logical, .render_list = &scene_output->render_list, .calculate_visibility = scene_output->scene->calculate_visibility, + .highlight_transparent_region = scene_output->scene->highlight_transparent_region, + .fractional_scale = floor(render_data.scale) != render_data.scale, }; list_con.render_list->size = 0; @@ -1701,28 +1787,8 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, struct render_list_entry *list_data = list_con.render_list->data; int list_len = list_con.render_list->size / sizeof(*list_data); - bool scanout = list_len == 1 && - scene_entry_try_direct_scanout(&list_data[0], state, &render_data); - - if (scene_output->prev_scanout != scanout) { - scene_output->prev_scanout = scanout; - wlr_log(WLR_DEBUG, "Direct scan-out %s", - scanout ? "enabled" : "disabled"); - if (!scanout) { - // When exiting direct scan-out, damage everything - wlr_damage_ring_add_whole(&scene_output->damage_ring); - } - } - - if (scanout) { - if (timer) { - struct timespec end_time, duration; - clock_gettime(CLOCK_MONOTONIC, &end_time); - timespec_sub(&duration, &end_time, &start_time); - timer->pre_render_duration = timespec_to_nsec(&duration); - } - return true; - } + wlr_damage_ring_set_bounds(&scene_output->damage_ring, + render_data.trans_width, render_data.trans_height); if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_RERENDER) { wlr_damage_ring_add_whole(&scene_output->damage_ring); @@ -1766,19 +1832,48 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, pixman_region32_fini(&acc_damage); } - wlr_damage_ring_set_bounds(&scene_output->damage_ring, - render_data.trans_width, render_data.trans_height); + output_state_apply_damage(&render_data, state); - if (!wlr_output_configure_primary_swapchain(output, state, &output->swapchain)) { - return false; + // We only want to try direct scanout if: + // - There is only one entry in the render list + // - There are no color transforms that need to be applied + // - Damage highlight debugging is not enabled + bool scanout = options->color_transform == NULL && + list_len == 1 && debug_damage != WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT && + scene_entry_try_direct_scanout(&list_data[0], state, &render_data); + + if (scene_output->prev_scanout != scanout) { + scene_output->prev_scanout = scanout; + wlr_log(WLR_DEBUG, "Direct scan-out %s", + scanout ? "enabled" : "disabled"); + } + + if (scanout) { + if (timer) { + struct timespec end_time, duration; + clock_gettime(CLOCK_MONOTONIC, &end_time); + timespec_sub(&duration, &end_time, &start_time); + timer->pre_render_duration = timespec_to_nsec(&duration); + } + return true; } - int buffer_age; - struct wlr_buffer *buffer = wlr_swapchain_acquire(output->swapchain, &buffer_age); + struct wlr_swapchain *swapchain = options->swapchain; + if (!swapchain) { + if (!wlr_output_configure_primary_swapchain(output, state, &output->swapchain)) { + return false; + } + + swapchain = output->swapchain; + } + + struct wlr_buffer *buffer = wlr_swapchain_acquire(swapchain, NULL); if (buffer == NULL) { return false; } + assert(buffer->width == resolution_width && buffer->height == resolution_height); + if (timer) { timer->render_timer = wlr_render_timer_create(output->renderer); @@ -1791,6 +1886,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, struct wlr_render_pass *render_pass = wlr_renderer_begin_buffer_pass(output->renderer, buffer, &(struct wlr_buffer_pass_options){ .timer = timer ? timer->render_timer : NULL, + .color_transform = options->color_transform, }); if (render_pass == NULL) { wlr_buffer_unlock(buffer); @@ -1798,9 +1894,10 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, } render_data.render_pass = render_pass; + pixman_region32_init(&render_data.damage); - wlr_damage_ring_get_buffer_damage(&scene_output->damage_ring, - buffer_age, &render_data.damage); + wlr_damage_ring_rotate_buffer(&scene_output->damage_ring, buffer, + &render_data.damage); pixman_region32_t background; pixman_region32_init(&background); @@ -1886,17 +1983,15 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, if (!wlr_render_pass_submit(render_pass)) { wlr_buffer_unlock(buffer); + + // if we failed to render the buffer, it will have undefined contents + // Trash the damage ring + wlr_damage_ring_add_whole(&scene_output->damage_ring); return false; } wlr_output_state_set_buffer(state, buffer); wlr_buffer_unlock(buffer); - output_state_apply_damage(&render_data, state); - - if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT && - !wl_list_empty(&scene_output->damage_highlight_regions)) { - wlr_output_schedule_frame(scene_output->output); - } return true; } diff --git a/types/scene/xdg_shell.c b/types/scene/xdg_shell.c index 9e3ffb0..ed79263 100644 --- a/types/scene/xdg_shell.c +++ b/types/scene/xdg_shell.c @@ -9,8 +9,6 @@ struct wlr_scene_xdg_surface { struct wl_listener tree_destroy; struct wl_listener xdg_surface_destroy; - struct wl_listener xdg_surface_map; - struct wl_listener xdg_surface_unmap; struct wl_listener xdg_surface_commit; }; @@ -21,8 +19,6 @@ static void scene_xdg_surface_handle_tree_destroy(struct wl_listener *listener, // tree and surface_node will be cleaned up by scene_node_finish wl_list_remove(&scene_xdg_surface->tree_destroy.link); wl_list_remove(&scene_xdg_surface->xdg_surface_destroy.link); - wl_list_remove(&scene_xdg_surface->xdg_surface_map.link); - wl_list_remove(&scene_xdg_surface->xdg_surface_unmap.link); wl_list_remove(&scene_xdg_surface->xdg_surface_commit.link); free(scene_xdg_surface); } @@ -34,20 +30,6 @@ static void scene_xdg_surface_handle_xdg_surface_destroy(struct wl_listener *lis wlr_scene_node_destroy(&scene_xdg_surface->tree->node); } -static void scene_xdg_surface_handle_xdg_surface_map(struct wl_listener *listener, - void *data) { - struct wlr_scene_xdg_surface *scene_xdg_surface = - wl_container_of(listener, scene_xdg_surface, xdg_surface_map); - wlr_scene_node_set_enabled(&scene_xdg_surface->tree->node, true); -} - -static void scene_xdg_surface_handle_xdg_surface_unmap(struct wl_listener *listener, - void *data) { - struct wlr_scene_xdg_surface *scene_xdg_surface = - wl_container_of(listener, scene_xdg_surface, xdg_surface_unmap); - wlr_scene_node_set_enabled(&scene_xdg_surface->tree->node, false); -} - static void scene_xdg_surface_update_position( struct wlr_scene_xdg_surface *scene_xdg_surface) { struct wlr_xdg_surface *xdg_surface = scene_xdg_surface->xdg_surface; @@ -106,23 +88,11 @@ struct wlr_scene_tree *wlr_scene_xdg_surface_create( scene_xdg_surface_handle_xdg_surface_destroy; wl_signal_add(&xdg_surface->events.destroy, &scene_xdg_surface->xdg_surface_destroy); - scene_xdg_surface->xdg_surface_map.notify = - scene_xdg_surface_handle_xdg_surface_map; - wl_signal_add(&xdg_surface->surface->events.map, - &scene_xdg_surface->xdg_surface_map); - - scene_xdg_surface->xdg_surface_unmap.notify = - scene_xdg_surface_handle_xdg_surface_unmap; - wl_signal_add(&xdg_surface->surface->events.unmap, - &scene_xdg_surface->xdg_surface_unmap); - scene_xdg_surface->xdg_surface_commit.notify = scene_xdg_surface_handle_xdg_surface_commit; wl_signal_add(&xdg_surface->surface->events.commit, &scene_xdg_surface->xdg_surface_commit); - wlr_scene_node_set_enabled(&scene_xdg_surface->tree->node, - xdg_surface->surface->mapped); scene_xdg_surface_update_position(scene_xdg_surface); return scene_xdg_surface->tree; diff --git a/types/seat/wlr_seat.c b/types/seat/wlr_seat.c index 9e91c9b..2e8e4a8 100644 --- a/types/seat/wlr_seat.c +++ b/types/seat/wlr_seat.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -12,7 +11,7 @@ #include "types/wlr_seat.h" #include "util/global.h" -#define SEAT_VERSION 8 +#define SEAT_VERSION 9 static void seat_handle_get_pointer(struct wl_client *client, struct wl_resource *seat_resource, uint32_t id) { diff --git a/types/seat/wlr_seat_keyboard.c b/types/seat/wlr_seat_keyboard.c index 0193d58..cae4c0d 100644 --- a/types/seat/wlr_seat_keyboard.c +++ b/types/seat/wlr_seat_keyboard.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -90,11 +89,8 @@ static void handle_keyboard_keymap(struct wl_listener *listener, void *data) { struct wlr_seat_keyboard_state *state = wl_container_of(listener, state, keyboard_keymap); struct wlr_seat_client *client; - struct wlr_keyboard *keyboard = data; - if (keyboard == state->keyboard) { - wl_list_for_each(client, &state->seat->clients, link) { - seat_client_send_keymap(client, state->keyboard); - } + wl_list_for_each(client, &state->seat->clients, link) { + seat_client_send_keymap(client, state->keyboard); } } diff --git a/types/seat/wlr_seat_pointer.c b/types/seat/wlr_seat_pointer.c index d5f0f9f..9618d52 100644 --- a/types/seat/wlr_seat_pointer.c +++ b/types/seat/wlr_seat_pointer.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -24,15 +23,16 @@ static void default_pointer_motion(struct wlr_seat_pointer_grab *grab, } static uint32_t default_pointer_button(struct wlr_seat_pointer_grab *grab, - uint32_t time, uint32_t button, enum wlr_button_state state) { + uint32_t time, uint32_t button, enum wl_pointer_button_state state) { return wlr_seat_pointer_send_button(grab->seat, time, button, state); } static void default_pointer_axis(struct wlr_seat_pointer_grab *grab, - uint32_t time, enum wlr_axis_orientation orientation, double value, - int32_t value_discrete, enum wlr_axis_source source) { + uint32_t time, enum wl_pointer_axis orientation, double value, + int32_t value_discrete, enum wl_pointer_axis_source source, + enum wl_pointer_axis_relative_direction relative_direction) { wlr_seat_pointer_send_axis(grab->seat, time, orientation, value, - value_discrete, source); + value_discrete, source, relative_direction); } static void default_pointer_frame(struct wlr_seat_pointer_grab *grab) { @@ -260,7 +260,7 @@ void wlr_seat_pointer_send_motion(struct wlr_seat *wlr_seat, uint32_t time, } uint32_t wlr_seat_pointer_send_button(struct wlr_seat *wlr_seat, uint32_t time, - uint32_t button, enum wlr_button_state state) { + uint32_t button, enum wl_pointer_button_state state) { struct wlr_seat_client *client = wlr_seat->pointer_state.focused_client; if (client == NULL) { return 0; @@ -287,7 +287,7 @@ static bool should_reset_value120_accumulators(int32_t current, int32_t last) { } static void update_value120_accumulators(struct wlr_seat_client *client, - enum wlr_axis_orientation orientation, + enum wl_pointer_axis orientation, double value, int32_t value_discrete, double *low_res_value, int32_t *low_res_value_discrete) { if (value_discrete == 0) { @@ -320,8 +320,9 @@ static void update_value120_accumulators(struct wlr_seat_client *client, } void wlr_seat_pointer_send_axis(struct wlr_seat *wlr_seat, uint32_t time, - enum wlr_axis_orientation orientation, double value, - int32_t value_discrete, enum wlr_axis_source source) { + enum wl_pointer_axis orientation, double value, + int32_t value_discrete, enum wl_pointer_axis_source source, + enum wl_pointer_axis_relative_direction relative_direction) { struct wlr_seat_client *client = wlr_seat->pointer_state.focused_client; if (client == NULL) { return; @@ -361,6 +362,10 @@ void wlr_seat_pointer_send_axis(struct wlr_seat *wlr_seat, uint32_t time, wl_pointer_send_axis_source(resource, source); } if (value) { + if (version >= WL_POINTER_AXIS_RELATIVE_DIRECTION_SINCE_VERSION) { + wl_pointer_send_axis_relative_direction(resource, + orientation, relative_direction); + } if (value_discrete) { if (version >= WL_POINTER_AXIS_VALUE120_SINCE_VERSION) { // High resolution discrete scrolling @@ -448,12 +453,12 @@ void wlr_seat_pointer_notify_motion(struct wlr_seat *wlr_seat, uint32_t time, } uint32_t wlr_seat_pointer_notify_button(struct wlr_seat *wlr_seat, - uint32_t time, uint32_t button, enum wlr_button_state state) { + uint32_t time, uint32_t button, enum wl_pointer_button_state state) { clock_gettime(CLOCK_MONOTONIC, &wlr_seat->last_event); struct wlr_seat_pointer_state* pointer_state = &wlr_seat->pointer_state; - if (state == WLR_BUTTON_PRESSED) { + if (state == WL_POINTER_BUTTON_STATE_PRESSED) { if (pointer_state->button_count == 0) { pointer_state->grab_button = button; pointer_state->grab_time = time; @@ -470,7 +475,7 @@ uint32_t wlr_seat_pointer_notify_button(struct wlr_seat *wlr_seat, uint32_t serial = grab->interface->button(grab, time, button, state); if (serial && pointer_state->button_count == 1 && - state == WLR_BUTTON_PRESSED) { + state == WL_POINTER_BUTTON_STATE_PRESSED) { pointer_state->grab_serial = serial; } @@ -478,12 +483,13 @@ uint32_t wlr_seat_pointer_notify_button(struct wlr_seat *wlr_seat, } void wlr_seat_pointer_notify_axis(struct wlr_seat *wlr_seat, uint32_t time, - enum wlr_axis_orientation orientation, double value, - int32_t value_discrete, enum wlr_axis_source source) { + enum wl_pointer_axis orientation, double value, + int32_t value_discrete, enum wl_pointer_axis_source source, + enum wl_pointer_axis_relative_direction relative_direction) { clock_gettime(CLOCK_MONOTONIC, &wlr_seat->last_event); struct wlr_seat_pointer_grab *grab = wlr_seat->pointer_state.grab; grab->interface->axis(grab, time, orientation, value, value_discrete, - source); + source, relative_direction); } void wlr_seat_pointer_notify_frame(struct wlr_seat *wlr_seat) { diff --git a/types/seat/wlr_seat_touch.c b/types/seat/wlr_seat_touch.c index 3843b5e..09f33d6 100644 --- a/types/seat/wlr_seat_touch.c +++ b/types/seat/wlr_seat_touch.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -14,9 +13,9 @@ static uint32_t default_touch_down(struct wlr_seat_touch_grab *grab, point->touch_id, point->sx, point->sy); } -static void default_touch_up(struct wlr_seat_touch_grab *grab, uint32_t time, +static uint32_t default_touch_up(struct wlr_seat_touch_grab *grab, uint32_t time, struct wlr_touch_point *point) { - wlr_seat_touch_send_up(grab->seat, time, point->touch_id); + return wlr_seat_touch_send_up(grab->seat, time, point->touch_id); } static void default_touch_motion(struct wlr_seat_touch_grab *grab, @@ -41,8 +40,8 @@ static void default_touch_cancel(struct wlr_seat_touch_grab *grab) { } static void default_touch_wl_cancel(struct wlr_seat_touch_grab *grab, - struct wlr_surface *surface) { - wlr_seat_touch_send_cancel(grab->seat, surface); + struct wlr_seat_client *seat_client) { + wlr_seat_touch_send_cancel(grab->seat, seat_client); } const struct wlr_touch_grab_interface default_touch_grab_impl = { @@ -206,16 +205,16 @@ uint32_t wlr_seat_touch_notify_down(struct wlr_seat *seat, return serial; } -void wlr_seat_touch_notify_up(struct wlr_seat *seat, uint32_t time, +uint32_t wlr_seat_touch_notify_up(struct wlr_seat *seat, uint32_t time, int32_t touch_id) { clock_gettime(CLOCK_MONOTONIC, &seat->last_event); struct wlr_seat_touch_grab *grab = seat->touch_state.grab; struct wlr_touch_point *point = wlr_seat_touch_get_point(seat, touch_id); if (!point) { - return; + return 0; } - grab->interface->up(grab, time, point); + return grab->interface->up(grab, time, point); touch_point_destroy(point); } @@ -243,17 +242,12 @@ void wlr_seat_touch_notify_frame(struct wlr_seat *seat) { } void wlr_seat_touch_notify_cancel(struct wlr_seat *seat, - struct wlr_surface *surface) { + struct wlr_seat_client *seat_client) { struct wlr_seat_touch_grab *grab = seat->touch_state.grab; if (grab->interface->wl_cancel) { - grab->interface->wl_cancel(grab, surface); + grab->interface->wl_cancel(grab, seat_client); } - struct wl_client *client = wl_resource_get_client(surface->resource); - struct wlr_seat_client *seat_client = wlr_seat_client_for_wl_client(seat, client); - if (seat_client == NULL) { - return; - } struct wlr_touch_point *point, *tmp; wl_list_for_each_safe(point, tmp, &seat->touch_state.touch_points, link) { if (point->client == seat_client) { @@ -346,11 +340,11 @@ uint32_t wlr_seat_touch_send_down(struct wlr_seat *seat, return serial; } -void wlr_seat_touch_send_up(struct wlr_seat *seat, uint32_t time, int32_t touch_id) { +uint32_t wlr_seat_touch_send_up(struct wlr_seat *seat, uint32_t time, int32_t touch_id) { struct wlr_touch_point *point = wlr_seat_touch_get_point(seat, touch_id); if (!point) { wlr_log(WLR_ERROR, "got touch up for unknown touch point"); - return; + return 0; } uint32_t serial = wlr_seat_client_next_serial(point->client); @@ -363,6 +357,7 @@ void wlr_seat_touch_send_up(struct wlr_seat *seat, uint32_t time, int32_t touch_ } point->client->needs_touch_frame = true; + return serial; } void wlr_seat_touch_send_motion(struct wlr_seat *seat, uint32_t time, int32_t touch_id, @@ -400,13 +395,8 @@ void wlr_seat_touch_send_frame(struct wlr_seat *seat) { } } -void wlr_seat_touch_send_cancel(struct wlr_seat *seat, struct wlr_surface *surface) { - struct wl_client *client = wl_resource_get_client(surface->resource); - struct wlr_seat_client *seat_client = wlr_seat_client_for_wl_client(seat, client); - if (seat_client == NULL) { - return; - } - +void wlr_seat_touch_send_cancel(struct wlr_seat *seat, + struct wlr_seat_client *seat_client) { struct wl_resource *resource; wl_resource_for_each(resource, &seat_client->touches) { if (seat_client_from_touch_resource(resource) == NULL) { diff --git a/types/tablet_v2/wlr_tablet_v2.c b/types/tablet_v2/wlr_tablet_v2.c index d93e92d..28671c9 100644 --- a/types/tablet_v2/wlr_tablet_v2.c +++ b/types/tablet_v2/wlr_tablet_v2.c @@ -1,7 +1,3 @@ -#ifndef _POSIX_C_SOURCE -#define _POSIX_C_SOURCE 200809L -#endif - #include #include #include @@ -12,7 +8,7 @@ #include #include #include -#include "tablet-unstable-v2-protocol.h" +#include "tablet-v2-protocol.h" #define TABLET_MANAGER_VERSION 1 @@ -275,6 +271,12 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); wl_list_remove(&manager->display_destroy.link); + + struct wlr_tablet_seat_v2 *seat, *tmp; + wl_list_for_each_safe(seat, tmp, &manager->seats, link) { + tablet_seat_destroy(seat); + } + wl_global_destroy(manager->wl_global); free(manager); } diff --git a/types/tablet_v2/wlr_tablet_v2_pad.c b/types/tablet_v2/wlr_tablet_v2_pad.c index bf8fb70..ab7c3fc 100644 --- a/types/tablet_v2/wlr_tablet_v2_pad.c +++ b/types/tablet_v2/wlr_tablet_v2_pad.c @@ -1,7 +1,3 @@ -#ifndef _POSIX_C_SOURCE -#define _POSIX_C_SOURCE 200809L -#endif - #include #include #include @@ -11,7 +7,7 @@ #include #include #include -#include "tablet-unstable-v2-protocol.h" +#include "tablet-v2-protocol.h" static const struct wlr_tablet_pad_v2_grab_interface default_pad_grab_interface; diff --git a/types/tablet_v2/wlr_tablet_v2_tablet.c b/types/tablet_v2/wlr_tablet_v2_tablet.c index af374b6..36609e8 100644 --- a/types/tablet_v2/wlr_tablet_v2_tablet.c +++ b/types/tablet_v2/wlr_tablet_v2_tablet.c @@ -1,7 +1,3 @@ -#ifndef _POSIX_C_SOURCE -#define _POSIX_C_SOURCE 200809L -#endif - #include #include #include @@ -11,7 +7,7 @@ #include #include -#include "tablet-unstable-v2-protocol.h" +#include "tablet-v2-protocol.h" void destroy_tablet_v2(struct wl_resource *resource) { struct wlr_tablet_client_v2 *tablet = tablet_client_from_resource(resource); @@ -37,7 +33,7 @@ static const struct zwp_tablet_v2_interface tablet_impl = { static void handle_wlr_tablet_destroy(struct wl_listener *listener, void *data) { struct wlr_tablet_v2_tablet *tablet = - wl_container_of(listener, tablet, tool_destroy); + wl_container_of(listener, tablet, tablet_destroy); struct wlr_tablet_client_v2 *pos; struct wlr_tablet_client_v2 *tmp; @@ -47,7 +43,7 @@ static void handle_wlr_tablet_destroy(struct wl_listener *listener, void *data) wl_list_remove(&tablet->clients); wl_list_remove(&tablet->link); - wl_list_remove(&tablet->tool_destroy.link); + wl_list_remove(&tablet->tablet_destroy.link); free(tablet); } @@ -55,7 +51,7 @@ struct wlr_tablet_v2_tablet *wlr_tablet_create( struct wlr_tablet_manager_v2 *manager, struct wlr_seat *wlr_seat, struct wlr_input_device *wlr_device) { - assert(wlr_device->type == WLR_INPUT_DEVICE_TABLET_TOOL); + assert(wlr_device->type == WLR_INPUT_DEVICE_TABLET); struct wlr_tablet_seat_v2 *seat = get_or_create_tablet_seat(manager, wlr_seat); if (!seat) { return NULL; @@ -71,8 +67,8 @@ struct wlr_tablet_v2_tablet *wlr_tablet_create( wl_list_init(&tablet->clients); - tablet->tool_destroy.notify = handle_wlr_tablet_destroy; - wl_signal_add(&wlr_device->events.destroy, &tablet->tool_destroy); + tablet->tablet_destroy.notify = handle_wlr_tablet_destroy; + wl_signal_add(&wlr_device->events.destroy, &tablet->tablet_destroy); wl_list_insert(&seat->tablets, &tablet->link); // We need to create a tablet client for all clients on the seat @@ -106,13 +102,14 @@ void add_tablet_client(struct wlr_tablet_seat_client_v2 *seat, client, destroy_tablet_v2); zwp_tablet_seat_v2_send_tablet_added(seat->resource, client->resource); - // Send the expected events if (tablet->wlr_tablet->base.name) { zwp_tablet_v2_send_name(client->resource, tablet->wlr_tablet->base.name); } - zwp_tablet_v2_send_id(client->resource, - tablet->wlr_device->vendor, tablet->wlr_device->product); + if (tablet->wlr_tablet->usb_vendor_id != 0) { + zwp_tablet_v2_send_id(client->resource, + tablet->wlr_tablet->usb_vendor_id, tablet->wlr_tablet->usb_product_id); + } const char **path_ptr; wl_array_for_each(path_ptr, &tablet->wlr_tablet->paths) { diff --git a/types/tablet_v2/wlr_tablet_v2_tool.c b/types/tablet_v2/wlr_tablet_v2_tool.c index a5a4548..8574a9a 100644 --- a/types/tablet_v2/wlr_tablet_v2_tool.c +++ b/types/tablet_v2/wlr_tablet_v2_tool.c @@ -1,7 +1,3 @@ -#ifndef _POSIX_C_SOURCE -#define _POSIX_C_SOURCE 200809L -#endif - #include #include #include @@ -12,7 +8,7 @@ #include #include "util/set.h" #include "util/time.h" -#include "tablet-unstable-v2-protocol.h" +#include "tablet-v2-protocol.h" static const struct wlr_tablet_tool_v2_grab_interface default_tool_grab_interface; diff --git a/types/wlr_alpha_modifier_v1.c b/types/wlr_alpha_modifier_v1.c new file mode 100644 index 0000000..7344e18 --- /dev/null +++ b/types/wlr_alpha_modifier_v1.c @@ -0,0 +1,188 @@ +#include +#include +#include +#include +#include +#include "alpha-modifier-v1-protocol.h" + +#define ALPHA_MODIFIER_VERSION 1 + +struct wlr_alpha_modifier_surface_v1 { + struct wl_resource *resource; + struct wlr_surface *surface; + struct wlr_addon addon; + struct wlr_surface_synced synced; + struct wlr_alpha_modifier_surface_v1_state pending, current; +}; + +static const struct wp_alpha_modifier_surface_v1_interface surface_impl; + +// Returns NULL if the wl_surface was destroyed +static struct wlr_alpha_modifier_surface_v1 *surface_from_resource(struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, &wp_alpha_modifier_surface_v1_interface, + &surface_impl)); + return wl_resource_get_user_data(resource); +} + +static void surface_destroy(struct wlr_alpha_modifier_surface_v1 *surface) { + if (surface == NULL) { + return; + } + wlr_surface_synced_finish(&surface->synced); + wlr_addon_finish(&surface->addon); + wl_resource_set_user_data(surface->resource, NULL); + free(surface); +} + +static void surface_handle_resource_destroy(struct wl_resource *resource) { + struct wlr_alpha_modifier_surface_v1 *surface = surface_from_resource(resource); + surface_destroy(surface); +} + +static void surface_handle_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static void surface_handle_set_multiplier(struct wl_client *client, + struct wl_resource *resource, uint32_t multiplier) { + struct wlr_alpha_modifier_surface_v1 *surface = surface_from_resource(resource); + if (surface == NULL) { + wl_resource_post_error(resource, + WP_ALPHA_MODIFIER_SURFACE_V1_ERROR_NO_SURFACE, + "The wl_surface object has been destroyed"); + return; + } + surface->pending.multiplier = (double)multiplier / UINT32_MAX; +} + +static const struct wp_alpha_modifier_surface_v1_interface surface_impl = { + .destroy = surface_handle_destroy, + .set_multiplier = surface_handle_set_multiplier, +}; + +static void surface_synced_init_state(void *_state) { + struct wlr_alpha_modifier_surface_v1_state *state = _state; + state->multiplier = 1; +} + +static const struct wlr_surface_synced_impl surface_synced_impl = { + .state_size = sizeof(struct wlr_alpha_modifier_surface_v1_state), + .init_state = surface_synced_init_state, +}; + +static void surface_addon_destroy(struct wlr_addon *addon) { + struct wlr_alpha_modifier_surface_v1 *surface = wl_container_of(addon, surface, addon); + surface_destroy(surface); +} + +static const struct wlr_addon_interface surface_addon_impl = { + .name = "wp_alpha_modifier_surface_v1", + .destroy = surface_addon_destroy, +}; + +static struct wlr_alpha_modifier_surface_v1 *surface_from_wlr_surface( + struct wlr_surface *wlr_surface) { + struct wlr_addon *addon = wlr_addon_find(&wlr_surface->addons, NULL, &surface_addon_impl); + if (addon == NULL) { + return NULL; + } + struct wlr_alpha_modifier_surface_v1 *surface = wl_container_of(addon, surface, addon); + return surface; +} + +static void manager_handle_destroy(struct wl_client *client, struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static void manager_handle_get_surface(struct wl_client *client, + struct wl_resource *manager_resource, uint32_t id, + struct wl_resource *surface_resource) { + struct wlr_surface *wlr_surface = wlr_surface_from_resource(surface_resource); + + if (surface_from_wlr_surface(wlr_surface) != NULL) { + wl_resource_post_error(manager_resource, + WP_ALPHA_MODIFIER_V1_ERROR_ALREADY_CONSTRUCTED, + "The wl_surface object already has a wp_alpha_modifier_surface_v1 object"); + return; + } + + struct wlr_alpha_modifier_surface_v1 *surface = calloc(1, sizeof(*surface)); + if (surface == NULL) { + wl_resource_post_no_memory(manager_resource); + return; + } + + if (!wlr_surface_synced_init(&surface->synced, wlr_surface, + &surface_synced_impl, &surface->pending, &surface->current)) { + free(surface); + wl_resource_post_no_memory(manager_resource); + return; + } + + uint32_t version = wl_resource_get_version(manager_resource); + surface->resource = wl_resource_create(client, + &wp_alpha_modifier_surface_v1_interface, version, id); + if (surface->resource == NULL) { + wlr_surface_synced_finish(&surface->synced); + free(surface); + wl_resource_post_no_memory(manager_resource); + return; + } + wl_resource_set_implementation(surface->resource, &surface_impl, + surface, surface_handle_resource_destroy); + + surface->surface = wlr_surface; + wlr_addon_init(&surface->addon, &wlr_surface->addons, NULL, &surface_addon_impl); +} + +static const struct wp_alpha_modifier_v1_interface manager_impl = { + .destroy = manager_handle_destroy, + .get_surface = manager_handle_get_surface, +}; + +static void manager_bind(struct wl_client *client, void *data, + uint32_t version, uint32_t id) { + struct wl_resource *resource = wl_resource_create(client, + &wp_alpha_modifier_v1_interface, version, id); + if (resource == NULL) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &manager_impl, NULL, NULL); +} + +static void handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_alpha_modifier_v1 *manager = wl_container_of(listener, manager, display_destroy); + wl_list_remove(&manager->display_destroy.link); + wl_global_destroy(manager->global); + free(manager); +} + +struct wlr_alpha_modifier_v1 *wlr_alpha_modifier_v1_create(struct wl_display *display) { + struct wlr_alpha_modifier_v1 *manager = calloc(1, sizeof(*manager)); + if (manager == NULL) { + return NULL; + } + + manager->global = wl_global_create(display, &wp_alpha_modifier_v1_interface, + ALPHA_MODIFIER_VERSION, NULL, manager_bind); + if (manager->global == NULL) { + free(manager); + return NULL; + } + + manager->display_destroy.notify = handle_display_destroy; + wl_display_add_destroy_listener(display, &manager->display_destroy); + + return manager; +} + +const struct wlr_alpha_modifier_surface_v1_state *wlr_alpha_modifier_v1_get_surface_state( + struct wlr_surface *wlr_surface) { + struct wlr_alpha_modifier_surface_v1 *surface = surface_from_wlr_surface(wlr_surface); + if (surface == NULL) { + return NULL; + } + return &surface->current; +} diff --git a/types/wlr_compositor.c b/types/wlr_compositor.c index 70c1f9c..c5044ce 100644 --- a/types/wlr_compositor.c +++ b/types/wlr_compositor.c @@ -1,18 +1,19 @@ #include +#include #include #include #include #include #include -#include -#include #include #include #include #include +#include #include "types/wlr_buffer.h" #include "types/wlr_region.h" #include "types/wlr_subcompositor.h" +#include "util/array.h" #include "util/time.h" #define COMPOSITOR_VERSION 6 @@ -34,6 +35,24 @@ static int max(int fst, int snd) { } } +static void set_pending_buffer_resource(struct wlr_surface *surface, + struct wl_resource *resource) { + wl_list_remove(&surface->pending_buffer_resource_destroy.link); + surface->pending_buffer_resource = resource; + if (resource != NULL) { + wl_resource_add_destroy_listener(resource, &surface->pending_buffer_resource_destroy); + } else { + wl_list_init(&surface->pending_buffer_resource_destroy.link); + } +} + +static void pending_buffer_resource_handle_destroy(struct wl_listener *listener, void *data) { + struct wlr_surface *surface = + wl_container_of(listener, surface, pending_buffer_resource_destroy); + + set_pending_buffer_resource(surface, NULL); +} + static void surface_handle_destroy(struct wl_client *client, struct wl_resource *resource) { struct wlr_surface *surface = wlr_surface_from_resource(resource); @@ -59,19 +78,8 @@ static void surface_handle_attach(struct wl_client *client, return; } - struct wlr_buffer *buffer = NULL; - if (buffer_resource != NULL) { - buffer = wlr_buffer_try_from_resource(buffer_resource); - if (buffer == NULL) { - wl_resource_post_error(buffer_resource, 0, "unknown buffer type"); - return; - } - } - surface->pending.committed |= WLR_SURFACE_STATE_BUFFER; - - wlr_buffer_unlock(surface->pending.buffer); - surface->pending.buffer = buffer; + set_pending_buffer_resource(surface, buffer_resource); if (wl_resource_get_version(resource) < WL_SURFACE_OFFSET_SINCE_VERSION) { surface->pending.committed |= WLR_SURFACE_STATE_OFFSET; @@ -145,16 +153,10 @@ static void surface_handle_set_input_region(struct wl_client *client, } static void surface_state_transformed_buffer_size(struct wlr_surface_state *state, - int *out_width, int *out_height) { - int width = state->buffer_width; - int height = state->buffer_height; - if ((state->transform & WL_OUTPUT_TRANSFORM_90) != 0) { - int tmp = width; - width = height; - height = tmp; - } - *out_width = width; - *out_height = height; + int *width, int *height) { + *width = state->buffer_width; + *height = state->buffer_height; + wlr_output_transform_coords(state->transform, width, height); } /** @@ -185,6 +187,17 @@ static void surface_finalize_pending(struct wlr_surface *surface) { struct wlr_surface_state *pending = &surface->pending; if ((pending->committed & WLR_SURFACE_STATE_BUFFER)) { + struct wl_resource *buffer_resource = surface->pending_buffer_resource; + if (buffer_resource != NULL) { + set_pending_buffer_resource(surface, NULL); + + pending->buffer = wlr_buffer_try_from_resource(buffer_resource); + if (pending->buffer == NULL) { + wlr_surface_reject_pending(surface, + buffer_resource, -1, "unknown buffer type"); + } + } + if (pending->buffer != NULL) { pending->buffer_width = pending->buffer->width; pending->buffer_height = pending->buffer->height; @@ -206,7 +219,7 @@ static void surface_finalize_pending(struct wlr_surface *surface) { "is not divisible by scale (%d)", pending->buffer_width, pending->buffer_height, pending->scale); } else { - wl_resource_post_error(surface->resource, + wlr_surface_reject_pending(surface, surface->resource, WL_SURFACE_ERROR_INVALID_SIZE, "Buffer size (%dx%d) is not divisible by scale (%d)", pending->buffer_width, pending->buffer_height, pending->scale); @@ -236,54 +249,77 @@ static void surface_update_damage(pixman_region32_t *buffer_damage, struct wlr_surface_state *current, struct wlr_surface_state *pending) { pixman_region32_clear(buffer_damage); - if (pending->width != current->width || - pending->height != current->height || - !wlr_fbox_equal(&pending->viewport.src, ¤t->viewport.src)) { - // Damage the whole buffer on resize or viewport source box change - pixman_region32_union_rect(buffer_damage, buffer_damage, 0, 0, - pending->buffer_width, pending->buffer_height); - } else { - // Copy over surface damage + buffer damage - pixman_region32_t surface_damage; - pixman_region32_init(&surface_damage); - - pixman_region32_copy(&surface_damage, &pending->surface_damage); - - if (pending->viewport.has_dst) { - int src_width, src_height; - surface_state_viewport_src_size(pending, &src_width, &src_height); - float scale_x = (float)pending->viewport.dst_width / src_width; - float scale_y = (float)pending->viewport.dst_height / src_height; - wlr_region_scale_xy(&surface_damage, &surface_damage, - 1.0 / scale_x, 1.0 / scale_y); - } - if (pending->viewport.has_src) { - // This is lossy: do a best-effort conversion - pixman_region32_translate(&surface_damage, - floor(pending->viewport.src.x), - floor(pending->viewport.src.y)); - } + // Copy over surface damage + buffer damage + pixman_region32_t surface_damage; + pixman_region32_init(&surface_damage); - wlr_region_scale(&surface_damage, &surface_damage, pending->scale); + pixman_region32_copy(&surface_damage, &pending->surface_damage); - int width, height; - surface_state_transformed_buffer_size(pending, &width, &height); - wlr_region_transform(&surface_damage, &surface_damage, - wlr_output_transform_invert(pending->transform), - width, height); + if (pending->viewport.has_dst) { + int src_width, src_height; + surface_state_viewport_src_size(pending, &src_width, &src_height); + float scale_x = (float)pending->viewport.dst_width / src_width; + float scale_y = (float)pending->viewport.dst_height / src_height; + wlr_region_scale_xy(&surface_damage, &surface_damage, + 1.0 / scale_x, 1.0 / scale_y); + } + if (pending->viewport.has_src) { + // This is lossy: do a best-effort conversion + pixman_region32_translate(&surface_damage, + floor(pending->viewport.src.x), + floor(pending->viewport.src.y)); + } + + wlr_region_scale(&surface_damage, &surface_damage, pending->scale); + + int width, height; + surface_state_transformed_buffer_size(pending, &width, &height); + wlr_region_transform(&surface_damage, &surface_damage, + wlr_output_transform_invert(pending->transform), + width, height); + + pixman_region32_union(buffer_damage, + &pending->buffer_damage, &surface_damage); - pixman_region32_union(buffer_damage, - &pending->buffer_damage, &surface_damage); + pixman_region32_fini(&surface_damage); +} + +static void *surface_synced_create_state(struct wlr_surface_synced *synced) { + void *state = calloc(1, synced->impl->state_size); + if (state == NULL) { + return NULL; + } + if (synced->impl->init_state) { + synced->impl->init_state(state); + } + return state; +} + +static void surface_synced_destroy_state(struct wlr_surface_synced *synced, + void *state) { + if (state == NULL) { + return; + } + if (synced->impl->finish_state) { + synced->impl->finish_state(state); + } + free(state); +} - pixman_region32_fini(&surface_damage); +static void surface_synced_move_state(struct wlr_surface_synced *synced, + void *dst, void *src) { + if (synced->impl->move_state) { + synced->impl->move_state(dst, src); + } else { + memcpy(dst, src, synced->impl->state_size); } } /** - * Append pending state to current state and clear pending state. + * Overwrite state with a copy of the next state, then clear the next state. */ static void surface_state_move(struct wlr_surface_state *state, - struct wlr_surface_state *next) { + struct wlr_surface_state *next, struct wlr_surface *surface) { state->width = next->width; state->height = next->height; state->buffer_width = next->buffer_width; @@ -338,7 +374,28 @@ static void surface_state_move(struct wlr_surface_state *state, wl_list_init(&next->frame_callback_list); } - state->committed |= next->committed; + void **state_synced = state->synced.data; + void **next_synced = next->synced.data; + struct wlr_surface_synced *synced; + wl_list_for_each(synced, &surface->synced, link) { + surface_synced_move_state(synced, + state_synced[synced->index], next_synced[synced->index]); + } + + // commit subsurface order + struct wlr_subsurface_parent_state *sub_state_next, *sub_state; + wl_list_for_each(sub_state_next, &next->subsurfaces_below, link) { + sub_state = wlr_surface_synced_get_state(sub_state_next->synced, state); + wl_list_remove(&sub_state->link); + wl_list_insert(state->subsurfaces_below.prev, &sub_state->link); + } + wl_list_for_each(sub_state_next, &next->subsurfaces_above, link) { + sub_state = wlr_surface_synced_get_state(sub_state_next->synced, state); + wl_list_remove(&sub_state->link); + wl_list_insert(state->subsurfaces_above.prev, &sub_state->link); + } + + state->committed = next->committed; next->committed = 0; state->seq = next->seq; @@ -348,8 +405,6 @@ static void surface_state_move(struct wlr_surface_state *state, } static void surface_apply_damage(struct wlr_surface *surface) { - surface->has_buffer = surface->current.buffer; - if (surface->current.buffer == NULL) { // NULL commit if (surface->buffer != NULL) { @@ -371,12 +426,12 @@ static void surface_apply_damage(struct wlr_surface *surface) { } } - if (surface->renderer == NULL) { + if (surface->compositor->renderer == NULL) { return; } struct wlr_client_buffer *buffer = wlr_client_buffer_create( - surface->current.buffer, surface->renderer); + surface->current.buffer, surface->compositor->renderer); if (buffer == NULL) { wlr_log(WLR_ERROR, "Failed to upload buffer"); @@ -390,7 +445,7 @@ static void surface_apply_damage(struct wlr_surface *surface) { } static void surface_update_opaque_region(struct wlr_surface *surface) { - if (!surface->has_buffer) { + if (!wlr_surface_has_buffer(surface)) { pixman_region32_clear(&surface->opaque_region); return; } @@ -413,29 +468,50 @@ static void surface_update_input_region(struct wlr_surface *surface) { 0, 0, surface->current.width, surface->current.height); } -static void surface_state_init(struct wlr_surface_state *state); +static bool surface_state_init(struct wlr_surface_state *state, + struct wlr_surface *surface); +static void surface_state_finish(struct wlr_surface_state *state); static void surface_cache_pending(struct wlr_surface *surface) { struct wlr_surface_state *cached = calloc(1, sizeof(*cached)); if (!cached) { - wl_resource_post_no_memory(surface->resource); - return; + goto error; + } + + if (!surface_state_init(cached, surface)) { + goto error_cached; + } + + void **cached_synced = cached->synced.data; + struct wlr_surface_synced *synced; + wl_list_for_each(synced, &surface->synced, link) { + void *synced_state = surface_synced_create_state(synced); + if (synced_state == NULL) { + goto error_state; + } + cached_synced[synced->index] = synced_state; } - surface_state_init(cached); - surface_state_move(cached, &surface->pending); + surface_state_move(cached, &surface->pending, surface); wl_list_insert(surface->cached.prev, &cached->cached_state_link); surface->pending.seq++; + + return; + +error_state: + surface_state_finish(cached); +error_cached: + free(cached); +error: + wl_resource_post_no_memory(surface->resource); } static void surface_commit_state(struct wlr_surface *surface, struct wlr_surface_state *next) { assert(next->cached_state_locks == 0); - wl_signal_emit_mutable(&surface->events.precommit, next); - bool invalid_buffer = next->committed & WLR_SURFACE_STATE_BUFFER; if (invalid_buffer && next->buffer == NULL) { @@ -447,15 +523,6 @@ static void surface_commit_state(struct wlr_surface *surface, surface_update_damage(&surface->buffer_damage, &surface->current, next); - pixman_region32_clear(&surface->external_damage); - if (surface->current.width > next->width || - surface->current.height > next->height || - next->dx != 0 || next->dy != 0) { - pixman_region32_union_rect(&surface->external_damage, - &surface->external_damage, -next->dx, -next->dy, - surface->current.width, surface->current.height); - } - surface->previous.scale = surface->current.scale; surface->previous.transform = surface->current.transform; surface->previous.width = surface->current.width; @@ -463,7 +530,7 @@ static void surface_commit_state(struct wlr_surface *surface, surface->previous.buffer_width = surface->current.buffer_width; surface->previous.buffer_height = surface->current.buffer_height; - surface_state_move(&surface->current, next); + surface_state_move(&surface->current, next, surface); if (invalid_buffer) { surface_apply_damage(surface); @@ -471,20 +538,11 @@ static void surface_commit_state(struct wlr_surface *surface, surface_update_opaque_region(surface); surface_update_input_region(surface); - // commit subsurface order struct wlr_subsurface *subsurface; - wl_list_for_each(subsurface, &surface->pending.subsurfaces_below, pending.link) { - wl_list_remove(&subsurface->current.link); - wl_list_insert(surface->current.subsurfaces_below.prev, - &subsurface->current.link); - + wl_list_for_each(subsurface, &surface->current.subsurfaces_below, current.link) { subsurface_handle_parent_commit(subsurface); } - wl_list_for_each(subsurface, &surface->pending.subsurfaces_above, pending.link) { - wl_list_remove(&subsurface->current.link); - wl_list_insert(surface->current.subsurfaces_above.prev, - &subsurface->current.link); - + wl_list_for_each(subsurface, &surface->current.subsurfaces_above, current.link) { subsurface_handle_parent_commit(subsurface); } @@ -511,10 +569,22 @@ static void surface_commit_state(struct wlr_surface *surface, static void surface_handle_commit(struct wl_client *client, struct wl_resource *resource) { struct wlr_surface *surface = wlr_surface_from_resource(resource); + surface->handling_commit = true; + surface_finalize_pending(surface); + if (surface->role != NULL && surface->role->client_commit != NULL && + (surface->role_resource != NULL || surface->role->no_object)) { + surface->role->client_commit(surface); + } + wl_signal_emit_mutable(&surface->events.client_commit, NULL); + surface->handling_commit = false; + if (surface->pending_rejected) { + return; + } + if (surface->pending.cached_state_locks > 0 || !wl_list_empty(&surface->cached)) { surface_cache_pending(surface); } else { @@ -590,7 +660,8 @@ struct wlr_surface *wlr_surface_from_resource(struct wl_resource *resource) { return wl_resource_get_user_data(resource); } -static void surface_state_init(struct wlr_surface_state *state) { +static bool surface_state_init(struct wlr_surface_state *state, + struct wlr_surface *surface) { *state = (struct wlr_surface_state){ .scale = 1, .transform = WL_OUTPUT_TRANSFORM_NORMAL, @@ -606,6 +677,10 @@ static void surface_state_init(struct wlr_surface_state *state) { pixman_region32_init(&state->opaque); pixman_region32_init_rect(&state->input, INT32_MIN, INT32_MIN, UINT32_MAX, UINT32_MAX); + + wl_array_init(&state->synced); + void *ptr = wl_array_add(&state->synced, surface->synced_len * sizeof(void *)); + return ptr != NULL; } static void surface_state_finish(struct wlr_surface_state *state) { @@ -620,9 +695,18 @@ static void surface_state_finish(struct wlr_surface_state *state) { pixman_region32_fini(&state->buffer_damage); pixman_region32_fini(&state->opaque); pixman_region32_fini(&state->input); + + wl_array_release(&state->synced); } -static void surface_state_destroy_cached(struct wlr_surface_state *state) { +static void surface_state_destroy_cached(struct wlr_surface_state *state, + struct wlr_surface *surface) { + void **synced_states = state->synced.data; + struct wlr_surface_synced *synced; + wl_list_for_each(synced, &surface->synced, link) { + surface_synced_destroy_state(synced, synced_states[synced->index]); + } + surface_state_finish(state); wl_list_remove(&state->cached_state_link); free(state); @@ -645,18 +729,20 @@ static void surface_handle_resource_destroy(struct wl_resource *resource) { wl_signal_emit_mutable(&surface->events.destroy, surface); wlr_addon_set_finish(&surface->addons); + assert(wl_list_empty(&surface->synced)); struct wlr_surface_state *cached, *cached_tmp; wl_list_for_each_safe(cached, cached_tmp, &surface->cached, cached_state_link) { - surface_state_destroy_cached(cached); + surface_state_destroy_cached(cached, surface); } - wl_list_remove(&surface->renderer_destroy.link); wl_list_remove(&surface->role_resource_destroy.link); + + wl_list_remove(&surface->pending_buffer_resource_destroy.link); + surface_state_finish(&surface->pending); surface_state_finish(&surface->current); pixman_region32_fini(&surface->buffer_damage); - pixman_region32_fini(&surface->external_damage); pixman_region32_fini(&surface->opaque_region); pixman_region32_fini(&surface->input_region); if (surface->buffer != NULL) { @@ -665,15 +751,8 @@ static void surface_handle_resource_destroy(struct wl_resource *resource) { free(surface); } -static void surface_handle_renderer_destroy(struct wl_listener *listener, - void *data) { - struct wlr_surface *surface = - wl_container_of(listener, surface, renderer_destroy); - wl_resource_destroy(surface->resource); -} - static struct wlr_surface *surface_create(struct wl_client *client, - uint32_t version, uint32_t id, struct wlr_renderer *renderer) { + uint32_t version, uint32_t id, struct wlr_compositor *compositor) { struct wlr_surface *surface = calloc(1, sizeof(*surface)); if (!surface) { wl_client_post_no_memory(client); @@ -691,14 +770,13 @@ static struct wlr_surface *surface_create(struct wl_client *client, wlr_log(WLR_DEBUG, "New wlr_surface %p (res %p)", surface, surface->resource); - surface->renderer = renderer; + surface->compositor = compositor; - surface_state_init(&surface->current); - surface_state_init(&surface->pending); + surface_state_init(&surface->current, surface); + surface_state_init(&surface->pending, surface); surface->pending.seq = 1; wl_signal_init(&surface->events.client_commit); - wl_signal_init(&surface->events.precommit); wl_signal_init(&surface->events.commit); wl_signal_init(&surface->events.map); wl_signal_init(&surface->events.unmap); @@ -707,20 +785,16 @@ static struct wlr_surface *surface_create(struct wl_client *client, wl_list_init(&surface->current_outputs); wl_list_init(&surface->cached); pixman_region32_init(&surface->buffer_damage); - pixman_region32_init(&surface->external_damage); pixman_region32_init(&surface->opaque_region); pixman_region32_init(&surface->input_region); wlr_addon_set_init(&surface->addons); - - if (renderer != NULL) { - wl_signal_add(&renderer->events.destroy, &surface->renderer_destroy); - surface->renderer_destroy.notify = surface_handle_renderer_destroy; - } else { - wl_list_init(&surface->renderer_destroy.link); - } + wl_list_init(&surface->synced); wl_list_init(&surface->role_resource_destroy.link); + surface->pending_buffer_resource_destroy.notify = pending_buffer_resource_handle_destroy; + wl_list_init(&surface->pending_buffer_resource_destroy.link); + return surface; } @@ -732,7 +806,11 @@ struct wlr_texture *wlr_surface_get_texture(struct wlr_surface *surface) { } bool wlr_surface_has_buffer(struct wlr_surface *surface) { - return surface->has_buffer; + return wlr_surface_state_has_buffer(&surface->current); +} + +bool wlr_surface_state_has_buffer(const struct wlr_surface_state *state) { + return state->buffer_width > 0 && state->buffer_height > 0; } void wlr_surface_map(struct wlr_surface *surface) { @@ -773,6 +851,26 @@ void wlr_surface_unmap(struct wlr_surface *surface) { } } +void wlr_surface_reject_pending(struct wlr_surface *surface, struct wl_resource *resource, + uint32_t code, const char *msg, ...) { + assert(surface->handling_commit); + if (surface->pending_rejected) { + return; + } + + va_list args; + va_start(args, msg); + + // XXX: libwayland could expose wl_resource_post_error_vargs() instead + char buffer[128]; // Matches the size of the buffer used in libwayland + vsnprintf(buffer, sizeof(buffer), msg, args); + + wl_resource_post_error(resource, code, "%s", buffer); + surface->pending_rejected = true; + + va_end(args); +} + bool wlr_surface_set_role(struct wlr_surface *surface, const struct wlr_surface_role *role, struct wl_resource *error_resource, uint32_t error_code) { assert(role != NULL); @@ -868,7 +966,7 @@ void wlr_surface_unlock_cached(struct wlr_surface *surface, uint32_t seq) { } surface_commit_state(surface, next); - surface_state_destroy_cached(next); + surface_state_destroy_cached(next, surface); } } @@ -1127,8 +1225,6 @@ void wlr_surface_get_effective_damage(struct wlr_surface *surface, float scale_y = (float)surface->current.viewport.dst_height / src_height; wlr_region_scale_xy(damage, damage, scale_x, scale_y); } - - pixman_region32_union(damage, damage, &surface->external_damage); } void wlr_surface_get_buffer_source_box(struct wlr_surface *surface, @@ -1199,7 +1295,7 @@ static void compositor_create_surface(struct wl_client *client, struct wlr_compositor *compositor = compositor_from_resource(resource); struct wlr_surface *surface = surface_create(client, - wl_resource_get_version(resource), id, compositor->renderer); + wl_resource_get_version(resource), id, compositor); if (surface == NULL) { wl_client_post_no_memory(client); return; @@ -1237,10 +1333,18 @@ static void compositor_handle_display_destroy( wl_container_of(listener, compositor, display_destroy); wl_signal_emit_mutable(&compositor->events.destroy, NULL); wl_list_remove(&compositor->display_destroy.link); + wl_list_remove(&compositor->renderer_destroy.link); wl_global_destroy(compositor->global); free(compositor); } +static void compositor_handle_renderer_destroy( + struct wl_listener *listener, void *data) { + struct wlr_compositor *compositor = + wl_container_of(listener, compositor, renderer_destroy); + wlr_compositor_set_renderer(compositor, NULL); +} + struct wlr_compositor *wlr_compositor_create(struct wl_display *display, uint32_t version, struct wlr_renderer *renderer) { assert(version <= COMPOSITOR_VERSION); @@ -1256,13 +1360,150 @@ struct wlr_compositor *wlr_compositor_create(struct wl_display *display, free(compositor); return NULL; } - compositor->renderer = renderer; wl_signal_init(&compositor->events.new_surface); wl_signal_init(&compositor->events.destroy); + wl_list_init(&compositor->renderer_destroy.link); compositor->display_destroy.notify = compositor_handle_display_destroy; wl_display_add_destroy_listener(display, &compositor->display_destroy); + wlr_compositor_set_renderer(compositor, renderer); + return compositor; } + +void wlr_compositor_set_renderer(struct wlr_compositor *compositor, + struct wlr_renderer *renderer) { + wl_list_remove(&compositor->renderer_destroy.link); + compositor->renderer = renderer; + + if (renderer != NULL) { + compositor->renderer_destroy.notify = compositor_handle_renderer_destroy; + wl_signal_add(&renderer->events.destroy, &compositor->renderer_destroy); + } else { + wl_list_init(&compositor->renderer_destroy.link); + } +} + +static bool surface_state_add_synced(struct wlr_surface_state *state, void *value) { + void **ptr = wl_array_add(&state->synced, sizeof(void *)); + if (ptr == NULL) { + return false; + } + *ptr = value; + return true; +} + +static void *surface_state_remove_synced(struct wlr_surface_state *state, + struct wlr_surface_synced *synced) { + void **synced_states = state->synced.data; + void *synced_state = synced_states[synced->index]; + array_remove_at(&state->synced, synced->index * sizeof(void *), sizeof(void *)); + return synced_state; +} + +static void surface_state_remove_and_destroy_synced(struct wlr_surface_state *state, + struct wlr_surface_synced *synced) { + void *synced_state = surface_state_remove_synced(state, synced); + surface_synced_destroy_state(synced, synced_state); +} + +bool wlr_surface_synced_init(struct wlr_surface_synced *synced, + struct wlr_surface *surface, const struct wlr_surface_synced_impl *impl, + void *pending, void *current) { + assert(impl->state_size > 0); + + struct wlr_surface_synced *other; + wl_list_for_each(other, &surface->synced, link) { + assert(synced != other); + } + + memset(pending, 0, impl->state_size); + memset(current, 0, impl->state_size); + if (impl->init_state) { + impl->init_state(pending); + impl->init_state(current); + } + if (!surface_state_add_synced(&surface->pending, pending)) { + goto error_init; + } + if (!surface_state_add_synced(&surface->current, current)) { + goto error_pending; + } + + *synced = (struct wlr_surface_synced){ + .surface = surface, + .impl = impl, + .index = surface->synced_len, + }; + + struct wlr_surface_state *cached; + wl_list_for_each(cached, &surface->cached, cached_state_link) { + void *synced_state = surface_synced_create_state(synced); + if (synced_state == NULL || + !surface_state_add_synced(cached, synced_state)) { + surface_synced_destroy_state(synced, synced_state); + goto error_cached; + } + } + + wl_list_insert(&surface->synced, &synced->link); + surface->synced_len++; + + return true; + +error_cached:; + struct wlr_surface_state *failed_at = cached; + wl_list_for_each(cached, &surface->cached, cached_state_link) { + if (cached == failed_at) { + break; + } + surface_state_remove_and_destroy_synced(cached, synced); + } + surface_state_remove_synced(&surface->current, synced); +error_pending: + surface_state_remove_synced(&surface->pending, synced); +error_init: + if (synced->impl->finish_state) { + synced->impl->finish_state(pending); + synced->impl->finish_state(current); + } + return false; +} + +void wlr_surface_synced_finish(struct wlr_surface_synced *synced) { + struct wlr_surface *surface = synced->surface; + + bool found = false; + struct wlr_surface_synced *other; + wl_list_for_each(other, &surface->synced, link) { + if (other == synced) { + found = true; + } else if (other->index > synced->index) { + other->index--; + } + } + assert(found); + + struct wlr_surface_state *cached; + wl_list_for_each(cached, &surface->cached, cached_state_link) { + surface_state_remove_and_destroy_synced(cached, synced); + } + + void *pending = surface_state_remove_synced(&surface->pending, synced); + void *current = surface_state_remove_synced(&surface->current, synced); + if (synced->impl->finish_state) { + synced->impl->finish_state(pending); + synced->impl->finish_state(current); + } + + wl_list_remove(&synced->link); + synced->surface->synced_len--; +} + +void *wlr_surface_synced_get_state(struct wlr_surface_synced *synced, + const struct wlr_surface_state *state) { + void **synced_states = state->synced.data; + return synced_states[synced->index]; +} diff --git a/types/wlr_content_type_v1.c b/types/wlr_content_type_v1.c index 36996ce..0c59859 100644 --- a/types/wlr_content_type_v1.c +++ b/types/wlr_content_type_v1.c @@ -8,8 +8,9 @@ struct wlr_content_type_v1_surface { struct wl_resource *resource; struct wlr_addon addon; - struct wl_listener commit; enum wp_content_type_v1_type pending, current; + + struct wlr_surface_synced synced; }; static void resource_handle_destroy(struct wl_client *client, @@ -56,7 +57,7 @@ static void content_type_surface_destroy( return; } wlr_addon_finish(&content_type_surface->addon); - wl_list_remove(&content_type_surface->commit.link); + wlr_surface_synced_finish(&content_type_surface->synced); wl_resource_set_user_data(content_type_surface->resource, NULL); free(content_type_surface); } @@ -72,12 +73,9 @@ static const struct wlr_addon_interface surface_addon_impl = { .destroy = surface_addon_destroy, }; -static void content_type_surface_handle_commit(struct wl_listener *listener, - void *data) { - struct wlr_content_type_v1_surface *content_type_surface = - wl_container_of(listener, content_type_surface, commit); - content_type_surface->current = content_type_surface->pending; -} +static const struct wlr_surface_synced_impl surface_synced_impl = { + .state_size = sizeof(enum wp_content_type_v1_type), +}; static void content_type_surface_handle_resource_destroy( struct wl_resource *resource) { @@ -107,10 +105,19 @@ static void manager_handle_get_surface_content_type(struct wl_client *client, return; } + if (!wlr_surface_synced_init(&content_type_surface->synced, surface, + &surface_synced_impl, &content_type_surface->pending, + &content_type_surface->current)) { + free(content_type_surface); + wl_resource_post_no_memory(manager_resource); + return; + } + uint32_t version = wl_resource_get_version(manager_resource); content_type_surface->resource = wl_resource_create(client, &wp_content_type_v1_interface, version, id); if (content_type_surface->resource == NULL) { + wlr_surface_synced_finish(&content_type_surface->synced); free(content_type_surface); wl_resource_post_no_memory(manager_resource); return; @@ -121,9 +128,6 @@ static void manager_handle_get_surface_content_type(struct wl_client *client, wlr_addon_init(&content_type_surface->addon, &surface->addons, manager, &surface_addon_impl); - - content_type_surface->commit.notify = content_type_surface_handle_commit; - wl_signal_add(&surface->events.destroy, &content_type_surface->commit); } static const struct wp_content_type_manager_v1_interface manager_impl = { diff --git a/types/wlr_cursor.c b/types/wlr_cursor.c index c651565..77ab2fb 100644 --- a/types/wlr_cursor.c +++ b/types/wlr_cursor.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -185,7 +184,8 @@ static void cursor_detach_output_layout(struct wlr_cursor *cur) { static void cursor_device_destroy(struct wlr_cursor_device *c_device) { struct wlr_input_device *dev = c_device->device; - if (dev->type == WLR_INPUT_DEVICE_POINTER) { + switch (dev->type) { + case WLR_INPUT_DEVICE_POINTER: wl_list_remove(&c_device->motion.link); wl_list_remove(&c_device->motion_absolute.link); wl_list_remove(&c_device->button.link); @@ -199,17 +199,22 @@ static void cursor_device_destroy(struct wlr_cursor_device *c_device) { wl_list_remove(&c_device->pinch_end.link); wl_list_remove(&c_device->hold_begin.link); wl_list_remove(&c_device->hold_end.link); - } else if (dev->type == WLR_INPUT_DEVICE_TOUCH) { + break; + case WLR_INPUT_DEVICE_TOUCH: wl_list_remove(&c_device->touch_down.link); wl_list_remove(&c_device->touch_up.link); wl_list_remove(&c_device->touch_motion.link); wl_list_remove(&c_device->touch_cancel.link); wl_list_remove(&c_device->touch_frame.link); - } else if (dev->type == WLR_INPUT_DEVICE_TABLET_TOOL) { + break; + case WLR_INPUT_DEVICE_TABLET: wl_list_remove(&c_device->tablet_tool_axis.link); wl_list_remove(&c_device->tablet_tool_proximity.link); wl_list_remove(&c_device->tablet_tool_tip.link); wl_list_remove(&c_device->tablet_tool_button.link); + break; + default: + abort(); // unreachable } wl_list_remove(&c_device->link); @@ -480,8 +485,7 @@ static void output_cursor_set_xcursor_image(struct wlr_cursor_output_cursor *out } if (output_cursor->xcursor_timer == NULL) { - struct wl_event_loop *event_loop = - wl_display_get_event_loop(output_cursor->output_cursor->output->display); + struct wl_event_loop *event_loop = output_cursor->output_cursor->output->event_loop; output_cursor->xcursor_timer = wl_event_loop_add_timer(event_loop, handle_xcursor_timer, output_cursor); if (output_cursor->xcursor_timer == NULL) { @@ -952,7 +956,8 @@ static struct wlr_cursor_device *cursor_device_create( wl_signal_add(&device->events.destroy, &c_device->destroy); c_device->destroy.notify = handle_device_destroy; - if (device->type == WLR_INPUT_DEVICE_POINTER) { + switch (device->type) { + case WLR_INPUT_DEVICE_POINTER:; struct wlr_pointer *pointer = wlr_pointer_from_input_device(device); wl_signal_add(&pointer->events.motion, &c_device->motion); @@ -994,7 +999,9 @@ static struct wlr_cursor_device *cursor_device_create( wl_signal_add(&pointer->events.hold_end, &c_device->hold_end); c_device->hold_end.notify = handle_pointer_hold_end; - } else if (device->type == WLR_INPUT_DEVICE_TOUCH) { + + break; + case WLR_INPUT_DEVICE_TOUCH:; struct wlr_touch *touch = wlr_touch_from_input_device(device); wl_signal_add(&touch->events.motion, &c_device->touch_motion); @@ -1011,7 +1018,9 @@ static struct wlr_cursor_device *cursor_device_create( wl_signal_add(&touch->events.frame, &c_device->touch_frame); c_device->touch_frame.notify = handle_touch_frame; - } else if (device->type == WLR_INPUT_DEVICE_TABLET_TOOL) { + + break; + case WLR_INPUT_DEVICE_TABLET:; struct wlr_tablet *tablet = wlr_tablet_from_input_device(device); wl_signal_add(&tablet->events.tip, &c_device->tablet_tool_tip); @@ -1026,6 +1035,11 @@ static struct wlr_cursor_device *cursor_device_create( wl_signal_add(&tablet->events.button, &c_device->tablet_tool_button); c_device->tablet_tool_button.notify = handle_tablet_tool_button; + + break; + + default: + abort(); // unreachable } wl_list_insert(&cursor->state->devices, &c_device->link); @@ -1035,9 +1049,12 @@ static struct wlr_cursor_device *cursor_device_create( void wlr_cursor_attach_input_device(struct wlr_cursor *cur, struct wlr_input_device *dev) { - if (dev->type != WLR_INPUT_DEVICE_POINTER && - dev->type != WLR_INPUT_DEVICE_TOUCH && - dev->type != WLR_INPUT_DEVICE_TABLET_TOOL) { + switch (dev->type) { + case WLR_INPUT_DEVICE_POINTER: + case WLR_INPUT_DEVICE_TOUCH: + case WLR_INPUT_DEVICE_TABLET: + break; + default: wlr_log(WLR_ERROR, "only device types of pointer, touch or tablet tool" "are supported"); return; @@ -1074,7 +1091,6 @@ static void handle_layout_output_destroy(struct wl_listener *listener, void *data) { struct wlr_cursor_output_cursor *output_cursor = wl_container_of(listener, output_cursor, layout_output_destroy); - //struct wlr_output_layout_output *l_output = data; output_cursor_destroy(output_cursor); } diff --git a/types/wlr_cursor_shape_v1.c b/types/wlr_cursor_shape_v1.c index ca33cf1..1bd1126 100644 --- a/types/wlr_cursor_shape_v1.c +++ b/types/wlr_cursor_shape_v1.c @@ -1,7 +1,10 @@ #include #include +#include +#include #include #include +#include #include "types/wlr_tablet_v2.h" #define CURSOR_SHAPE_MANAGER_V1_VERSION 1 @@ -11,8 +14,11 @@ struct wlr_cursor_shape_device_v1 { struct wlr_cursor_shape_manager_v1 *manager; enum wlr_cursor_shape_manager_v1_device_type type; struct wlr_seat_client *seat_client; + // NULL if device_type is not TABLET_TOOL + struct wlr_tablet_v2_tablet_tool *tablet_tool; struct wl_listener seat_client_destroy; + struct wl_listener tablet_tool_destroy; }; static const struct wp_cursor_shape_device_v1_interface device_impl; @@ -49,6 +55,7 @@ static void device_handle_set_shape(struct wl_client *client, struct wl_resource struct wlr_cursor_shape_manager_v1_request_set_shape_event event = { .seat_client = device->seat_client, .device_type = device->type, + .tablet_tool = device->tablet_tool, .serial = serial, .shape = shape, }; @@ -65,6 +72,7 @@ static void device_destroy(struct wlr_cursor_shape_device_v1 *device) { return; } wl_list_remove(&device->seat_client_destroy.link); + wl_list_remove(&device->tablet_tool_destroy.link); wl_resource_set_user_data(device->resource, NULL); // make inert free(device); } @@ -79,9 +87,15 @@ static void device_handle_seat_client_destroy(struct wl_listener *listener, void device_destroy(device); } +static void device_handle_tablet_tool_destroy(struct wl_listener *listener, void *data) { + struct wlr_cursor_shape_device_v1 *device = wl_container_of(listener, device, tablet_tool_destroy); + device_destroy(device); +} + static void create_device(struct wl_resource *manager_resource, uint32_t id, struct wlr_seat_client *seat_client, - enum wlr_cursor_shape_manager_v1_device_type type) { + enum wlr_cursor_shape_manager_v1_device_type type, + struct wlr_tablet_v2_tablet_tool *tablet_tool) { struct wlr_cursor_shape_manager_v1 *manager = manager_from_resource(manager_resource); struct wl_client *client = wl_resource_get_client(manager_resource); @@ -105,14 +119,25 @@ static void create_device(struct wl_resource *manager_resource, uint32_t id, return; } + assert((type == WLR_CURSOR_SHAPE_MANAGER_V1_DEVICE_TYPE_TABLET_TOOL) == + (tablet_tool != NULL)); + device->resource = device_resource; device->manager = manager; device->type = type; + device->tablet_tool = tablet_tool; device->seat_client = seat_client; device->seat_client_destroy.notify = device_handle_seat_client_destroy; wl_signal_add(&seat_client->events.destroy, &device->seat_client_destroy); + if (tablet_tool != NULL) { + device->tablet_tool_destroy.notify = device_handle_tablet_tool_destroy; + wl_signal_add(&tablet_tool->wlr_tool->events.destroy, &device->tablet_tool_destroy); + } else { + wl_list_init(&device->tablet_tool_destroy.link); + } + wl_resource_set_user_data(device_resource, device); } @@ -120,18 +145,22 @@ static void manager_handle_get_pointer(struct wl_client *client, struct wl_resou uint32_t id, struct wl_resource *pointer_resource) { struct wlr_seat_client *seat_client = wlr_seat_client_from_pointer_resource(pointer_resource); create_device(manager_resource, id, seat_client, - WLR_CURSOR_SHAPE_MANAGER_V1_DEVICE_TYPE_POINTER); + WLR_CURSOR_SHAPE_MANAGER_V1_DEVICE_TYPE_POINTER, NULL); } static void manager_handle_get_tablet_tool_v2(struct wl_client *client, struct wl_resource *manager_resource, uint32_t id, struct wl_resource *tablet_tool_resource) { struct wlr_tablet_tool_client_v2 *tablet_tool_client = tablet_tool_client_from_resource(tablet_tool_resource); + struct wlr_seat_client *seat_client = NULL; - if (tablet_tool_client != NULL) { + struct wlr_tablet_v2_tablet_tool *tablet_tool = NULL; + if (tablet_tool_client != NULL && tablet_tool_client->tool != NULL) { seat_client = tablet_tool_client->seat->seat_client; + tablet_tool = tablet_tool_client->tool; } + create_device(manager_resource, id, seat_client, - WLR_CURSOR_SHAPE_MANAGER_V1_DEVICE_TYPE_TABLET_TOOL); + WLR_CURSOR_SHAPE_MANAGER_V1_DEVICE_TYPE_TABLET_TOOL, tablet_tool); } static const struct wp_cursor_shape_manager_v1_interface manager_impl = { diff --git a/types/wlr_damage_ring.c b/types/wlr_damage_ring.c index 439cceb..2934de8 100644 --- a/types/wlr_damage_ring.c +++ b/types/wlr_damage_ring.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -17,6 +18,15 @@ void wlr_damage_ring_init(struct wlr_damage_ring *ring) { for (size_t i = 0; i < WLR_DAMAGE_RING_PREVIOUS_LEN; ++i) { pixman_region32_init(&ring->previous[i]); } + + wl_list_init(&ring->buffers); +} + +static void buffer_destroy(struct wlr_damage_ring_buffer *entry) { + wl_list_remove(&entry->destroy.link); + wl_list_remove(&entry->link); + pixman_region32_fini(&entry->damage); + free(entry); } void wlr_damage_ring_finish(struct wlr_damage_ring *ring) { @@ -24,6 +34,10 @@ void wlr_damage_ring_finish(struct wlr_damage_ring *ring) { for (size_t i = 0; i < WLR_DAMAGE_RING_PREVIOUS_LEN; ++i) { pixman_region32_fini(&ring->previous[i]); } + struct wlr_damage_ring_buffer *entry, *tmp_entry; + wl_list_for_each_safe(entry, tmp_entry, &ring->buffers, link) { + buffer_destroy(entry); + } } void wlr_damage_ring_set_bounds(struct wlr_damage_ring *ring, @@ -114,3 +128,75 @@ void wlr_damage_ring_get_buffer_damage(struct wlr_damage_ring *ring, } } } + +static void entry_squash_damage(struct wlr_damage_ring_buffer *entry) { + pixman_region32_t *prev; + if (entry->link.prev == &entry->ring->buffers) { + // this entry is the first in the list + prev = &entry->ring->current; + } else { + struct wlr_damage_ring_buffer *last = + wl_container_of(entry->link.prev, last, link); + prev = &last->damage; + } + + pixman_region32_union(prev, prev, &entry->damage); +} + +static void buffer_handle_destroy(struct wl_listener *listener, void *data) { + struct wlr_damage_ring_buffer *entry = wl_container_of(listener, entry, destroy); + entry_squash_damage(entry); + buffer_destroy(entry); +} + +void wlr_damage_ring_rotate_buffer(struct wlr_damage_ring *ring, + struct wlr_buffer *buffer, pixman_region32_t *damage) { + pixman_region32_copy(damage, &ring->current); + + struct wlr_damage_ring_buffer *entry; + wl_list_for_each(entry, &ring->buffers, link) { + if (entry->buffer != buffer) { + pixman_region32_union(damage, damage, &entry->damage); + continue; + } + + // Check the number of rectangles + int n_rects = pixman_region32_n_rects(damage); + if (n_rects > WLR_DAMAGE_RING_MAX_RECTS) { + pixman_box32_t *extents = pixman_region32_extents(damage); + pixman_region32_union_rect(damage, damage, + extents->x1, extents->y1, + extents->x2 - extents->x1, + extents->y2 - extents->y1); + } + + // rotate + entry_squash_damage(entry); + pixman_region32_copy(&entry->damage, &ring->current); + pixman_region32_clear(&ring->current); + + wl_list_remove(&entry->link); + wl_list_insert(&ring->buffers, &entry->link); + return; + } + + pixman_region32_clear(damage); + pixman_region32_union_rect(damage, damage, + 0, 0, ring->width, ring->height); + + entry = calloc(1, sizeof(*entry)); + if (!entry) { + return; + } + + pixman_region32_init(&entry->damage); + pixman_region32_copy(&entry->damage, &ring->current); + pixman_region32_clear(&ring->current); + + wl_list_insert(&ring->buffers, &entry->link); + entry->buffer = buffer; + entry->ring = ring; + + entry->destroy.notify = buffer_handle_destroy; + wl_signal_add(&buffer->events.destroy, &entry->destroy); +} diff --git a/types/wlr_data_control_v1.c b/types/wlr_data_control_v1.c index e4f3223..88f23ae 100644 --- a/types/wlr_data_control_v1.c +++ b/types/wlr_data_control_v1.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include diff --git a/types/wlr_drm.c b/types/wlr_drm.c index a75f304..c58cdb4 100644 --- a/types/wlr_drm.c +++ b/types/wlr_drm.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -237,7 +236,8 @@ struct wlr_drm *wlr_drm_create(struct wl_display *display, drm->node_name = node_name; wl_signal_init(&drm->events.destroy); - const struct wlr_drm_format_set *formats = wlr_renderer_get_dmabuf_texture_formats(renderer); + const struct wlr_drm_format_set *formats = + wlr_renderer_get_texture_formats(renderer, WLR_BUFFER_CAP_DMABUF); if (formats == NULL) { goto error; } diff --git a/types/wlr_drm_lease_v1.c b/types/wlr_drm_lease_v1.c index 74de857..d3c9ba1 100644 --- a/types/wlr_drm_lease_v1.c +++ b/types/wlr_drm_lease_v1.c @@ -177,6 +177,7 @@ struct wlr_drm_lease_v1 *wlr_drm_lease_request_v1_grant( if (!lease->drm_lease) { wlr_log(WLR_ERROR, "wlr_drm_create_lease failed"); wp_drm_lease_v1_send_finished(lease->resource); + free(lease); return NULL; } @@ -185,6 +186,7 @@ struct wlr_drm_lease_v1 *wlr_drm_lease_request_v1_grant( wlr_log(WLR_ERROR, "Failed to allocate lease connectors list"); close(fd); wp_drm_lease_v1_send_finished(lease->resource); + free(lease); return NULL; } lease->n_connectors = request->n_connectors; diff --git a/types/wlr_ext_foreign_toplevel_list_v1.c b/types/wlr_ext_foreign_toplevel_list_v1.c new file mode 100644 index 0000000..6c059e6 --- /dev/null +++ b/types/wlr_ext_foreign_toplevel_list_v1.c @@ -0,0 +1,265 @@ + +#include +#include +#include +#include +#include +#include +#include +#include "ext-foreign-toplevel-list-v1-protocol.h" + +#include "util/token.h" + +#define FOREIGN_TOPLEVEL_LIST_V1_VERSION 1 + +static const struct ext_foreign_toplevel_list_v1_interface toplevel_handle_impl; + +static void foreign_toplevel_handle_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static const struct ext_foreign_toplevel_list_v1_interface toplevel_handle_impl = { + .destroy = foreign_toplevel_handle_destroy, +}; + +// Returns true if clients need to be notified about the update +static bool update_string(struct wlr_ext_foreign_toplevel_handle_v1 *toplevel, + char **dst, const char *src) { + if (src == NULL) { + if (*dst == NULL) { + return false; + } + } else if (*dst != NULL && strcmp(*dst, src) == 0) { + return false; + } + + free(*dst); + if (src != NULL) { + *dst = strdup(src); + if (*dst == NULL) { + struct wl_resource *resource; + wl_resource_for_each(resource, &toplevel->resources) { + wl_resource_post_no_memory(resource); + } + return false; + } + } else { + *dst = NULL; + } + return true; +} + +void wlr_ext_foreign_toplevel_handle_v1_update_state( + struct wlr_ext_foreign_toplevel_handle_v1 *toplevel, + const struct wlr_ext_foreign_toplevel_handle_v1_state *state) { + bool changed_app_id = update_string(toplevel, &toplevel->app_id, state->app_id); + bool changed_title = update_string(toplevel, &toplevel->title, state->title); + + if (!changed_app_id && !changed_title) { + return; + } + + struct wl_resource *resource; + wl_resource_for_each(resource, &toplevel->resources) { + if (changed_app_id) { + ext_foreign_toplevel_handle_v1_send_app_id(resource, state->app_id ? state->app_id : ""); + } + if (changed_title) { + ext_foreign_toplevel_handle_v1_send_title(resource, state->title ? state->title : ""); + } + ext_foreign_toplevel_handle_v1_send_done(resource); + } +} + +void wlr_ext_foreign_toplevel_handle_v1_destroy( + struct wlr_ext_foreign_toplevel_handle_v1 *toplevel) { + if (!toplevel) { + return; + } + + wl_signal_emit_mutable(&toplevel->events.destroy, NULL); + + struct wl_resource *resource, *tmp; + wl_resource_for_each_safe(resource, tmp, &toplevel->resources) { + ext_foreign_toplevel_handle_v1_send_closed(resource); + wl_resource_set_user_data(resource, NULL); + wl_list_remove(wl_resource_get_link(resource)); + wl_list_init(wl_resource_get_link(resource)); + } + + wl_list_remove(&toplevel->link); + + free(toplevel->title); + free(toplevel->app_id); + free(toplevel->identifier); + free(toplevel); +} + +static void foreign_toplevel_resource_destroy(struct wl_resource *resource) { + wl_list_remove(wl_resource_get_link(resource)); +} + +static struct wl_resource *create_toplevel_resource_for_resource( + struct wlr_ext_foreign_toplevel_handle_v1 *toplevel, + struct wl_resource *list_resource) { + struct wl_client *client = wl_resource_get_client(list_resource); + struct wl_resource *resource = wl_resource_create(client, + &ext_foreign_toplevel_handle_v1_interface, + wl_resource_get_version(list_resource), 0); + if (!resource) { + wl_client_post_no_memory(client); + return NULL; + } + + wl_resource_set_implementation(resource, &toplevel_handle_impl, toplevel, + foreign_toplevel_resource_destroy); + + wl_list_insert(&toplevel->resources, wl_resource_get_link(resource)); + ext_foreign_toplevel_list_v1_send_toplevel(list_resource, resource); + return resource; +} + +static void toplevel_send_details_to_toplevel_resource( + struct wlr_ext_foreign_toplevel_handle_v1 *toplevel, + struct wl_resource *resource) { + if (toplevel->title) { + ext_foreign_toplevel_handle_v1_send_title(resource, toplevel->title); + } + if (toplevel->app_id) { + ext_foreign_toplevel_handle_v1_send_app_id(resource, toplevel->app_id); + } + assert(toplevel->identifier); // Required to exist by protocol. + ext_foreign_toplevel_handle_v1_send_identifier(resource, toplevel->identifier); + ext_foreign_toplevel_handle_v1_send_done(resource); +} + +struct wlr_ext_foreign_toplevel_handle_v1 * +wlr_ext_foreign_toplevel_handle_v1_create(struct wlr_ext_foreign_toplevel_list_v1 *list, + const struct wlr_ext_foreign_toplevel_handle_v1_state *state) { + struct wlr_ext_foreign_toplevel_handle_v1 *toplevel = calloc(1, sizeof(*toplevel)); + if (!toplevel) { + wlr_log(WLR_ERROR, "failed to allocate memory for toplevel handle"); + return NULL; + } + + toplevel->identifier = calloc(TOKEN_SIZE, sizeof(char)); + if (toplevel->identifier == NULL) { + wlr_log(WLR_ERROR, "failed to allocate memory for toplevel identifier"); + free(toplevel); + return NULL; + } + + if (!generate_token(toplevel->identifier)) { + free(toplevel->identifier); + free(toplevel); + return NULL; + } + + wl_list_insert(&list->toplevels, &toplevel->link); + toplevel->list = list; + if (state->app_id) { + toplevel->app_id = strdup(state->app_id); + } + if (state->title) { + toplevel->title = strdup(state->title); + } + + wl_list_init(&toplevel->resources); + + wl_signal_init(&toplevel->events.destroy); + + struct wl_resource *list_resource, *toplevel_resource; + wl_resource_for_each(list_resource, &list->resources) { + toplevel_resource = create_toplevel_resource_for_resource(toplevel, list_resource); + if (!toplevel_resource) { + continue; + } + toplevel_send_details_to_toplevel_resource(toplevel, toplevel_resource); + } + + return toplevel; +} + +static const struct ext_foreign_toplevel_list_v1_interface + foreign_toplevel_list_impl; + +static void foreign_toplevel_list_handle_stop(struct wl_client *client, + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &ext_foreign_toplevel_list_v1_interface, + &foreign_toplevel_list_impl)); + + ext_foreign_toplevel_list_v1_send_finished(resource); + wl_resource_destroy(resource); +} + +static const struct ext_foreign_toplevel_list_v1_interface + foreign_toplevel_list_impl = { + .stop = foreign_toplevel_list_handle_stop +}; + +static void foreign_toplevel_list_resource_destroy( + struct wl_resource *resource) { + wl_list_remove(wl_resource_get_link(resource)); +} + +static void foreign_toplevel_list_bind(struct wl_client *client, void *data, + uint32_t version, uint32_t id) { + struct wlr_ext_foreign_toplevel_list_v1 *list = data; + struct wl_resource *resource = wl_resource_create(client, + &ext_foreign_toplevel_list_v1_interface, version, id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &foreign_toplevel_list_impl, + list, foreign_toplevel_list_resource_destroy); + + wl_list_insert(&list->resources, wl_resource_get_link(resource)); + + struct wlr_ext_foreign_toplevel_handle_v1 *toplevel; + wl_list_for_each(toplevel, &list->toplevels, link) { + struct wl_resource *toplevel_resource = + create_toplevel_resource_for_resource(toplevel, resource); + toplevel_send_details_to_toplevel_resource(toplevel, + toplevel_resource); + } +} + +static void handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_ext_foreign_toplevel_list_v1 *list = + wl_container_of(listener, list, display_destroy); + wl_signal_emit_mutable(&list->events.destroy, NULL); + wl_list_remove(&list->display_destroy.link); + wl_global_destroy(list->global); + free(list); +} + +struct wlr_ext_foreign_toplevel_list_v1 *wlr_ext_foreign_toplevel_list_v1_create( + struct wl_display *display, uint32_t version) { + assert(version <= FOREIGN_TOPLEVEL_LIST_V1_VERSION); + + struct wlr_ext_foreign_toplevel_list_v1 *list = calloc(1, sizeof(*list)); + if (!list) { + return NULL; + } + + list->global = wl_global_create(display, + &ext_foreign_toplevel_list_v1_interface, + version, list, + foreign_toplevel_list_bind); + if (!list->global) { + free(list); + return NULL; + } + + wl_signal_init(&list->events.destroy); + wl_list_init(&list->resources); + wl_list_init(&list->toplevels); + + list->display_destroy.notify = handle_display_destroy; + wl_display_add_destroy_listener(display, &list->display_destroy); + + return list; +} diff --git a/types/wlr_foreign_toplevel_management_v1.c b/types/wlr_foreign_toplevel_management_v1.c index 80b1fc5..23935d4 100644 --- a/types/wlr_foreign_toplevel_management_v1.c +++ b/types/wlr_foreign_toplevel_management_v1.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include diff --git a/types/wlr_fractional_scale_v1.c b/types/wlr_fractional_scale_v1.c index d985275..6079746 100644 --- a/types/wlr_fractional_scale_v1.c +++ b/types/wlr_fractional_scale_v1.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include diff --git a/types/wlr_gamma_control_v1.c b/types/wlr_gamma_control_v1.c index c24da9a..732439d 100644 --- a/types/wlr_gamma_control_v1.c +++ b/types/wlr_gamma_control_v1.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -67,8 +66,7 @@ static void gamma_control_handle_set_gamma(struct wl_client *client, goto error_fd; } - uint32_t ramp_size = wlr_output_get_gamma_size(gamma_control->output); - size_t table_size = ramp_size * 3 * sizeof(uint16_t); + size_t table_size = gamma_control->ramp_size * 3 * sizeof(uint16_t); // Refuse to block when reading int fd_flags = fcntl(fd, F_GETFL, 0); @@ -102,11 +100,9 @@ static void gamma_control_handle_set_gamma(struct wl_client *client, goto error_table; } close(fd); - fd = -1; free(gamma_control->table); gamma_control->table = table; - gamma_control->ramp_size = ramp_size; struct wlr_gamma_control_manager_v1_set_gamma_event event = { .output = gamma_control->output, @@ -178,6 +174,7 @@ static void gamma_control_manager_get_gamma_control(struct wl_client *client, gamma_control->output = output; gamma_control->manager = manager; gamma_control->resource = resource; + gamma_control->ramp_size = gamma_size; wl_resource_set_user_data(resource, gamma_control); wl_signal_add(&output->events.destroy, diff --git a/types/wlr_idle_notify_v1.c b/types/wlr_idle_notify_v1.c index 6283dc6..686ddcc 100644 --- a/types/wlr_idle_notify_v1.c +++ b/types/wlr_idle_notify_v1.c @@ -6,15 +6,6 @@ #define IDLE_NOTIFIER_VERSION 1 -struct wlr_idle_notifier_v1 { - struct wl_global *global; - - bool inhibited; - struct wl_list notifications; // wlr_idle_notification_v1.link - - struct wl_listener display_destroy; -}; - struct wlr_idle_notification_v1 { struct wl_resource *resource; struct wl_list link; // wlr_idle_notifier_v1.notifications diff --git a/types/wlr_input_device.c b/types/wlr_input_device.c index 7de98b7..ede2e04 100644 --- a/types/wlr_input_device.c +++ b/types/wlr_input_device.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include diff --git a/types/wlr_input_inhibitor.c b/types/wlr_input_inhibitor.c deleted file mode 100644 index 44a7f09..0000000 --- a/types/wlr_input_inhibitor.c +++ /dev/null @@ -1,137 +0,0 @@ -#include -#include -#include -#include -#include "wlr/types/wlr_input_inhibitor.h" -#include "wlr-input-inhibitor-unstable-v1-protocol.h" - -static const struct zwlr_input_inhibit_manager_v1_interface inhibit_manager_implementation; -static const struct zwlr_input_inhibitor_v1_interface input_inhibitor_implementation; - -static struct wlr_input_inhibit_manager *input_inhibit_manager_from_resource( - struct wl_resource *resource) { - assert(wl_resource_instance_of(resource, - &zwlr_input_inhibit_manager_v1_interface, - &inhibit_manager_implementation) - || wl_resource_instance_of(resource, - &zwlr_input_inhibitor_v1_interface, - &input_inhibitor_implementation)); - return wl_resource_get_user_data(resource); -} - -static void input_inhibit_manager_deactivate( - struct wlr_input_inhibit_manager *manager) { - if (manager->active_client == NULL && manager->active_inhibitor == NULL) { - return; - } - manager->active_client = NULL; - manager->active_inhibitor = NULL; - wl_signal_emit_mutable(&manager->events.deactivate, manager); -} - -static void input_inhibitor_destroy(struct wl_client *client, - struct wl_resource *resource) { - struct wlr_input_inhibit_manager *manager = - input_inhibit_manager_from_resource(resource); - input_inhibit_manager_deactivate(manager); - wl_resource_destroy(resource); -} - -static void input_inhibitor_resource_destroy(struct wl_resource *resource) { - struct wlr_input_inhibit_manager *manager = - input_inhibit_manager_from_resource(resource); - input_inhibit_manager_deactivate(manager); -} - -static const struct zwlr_input_inhibitor_v1_interface input_inhibitor_implementation = { - .destroy = input_inhibitor_destroy, -}; - -static void inhibit_manager_get_inhibitor(struct wl_client *client, - struct wl_resource *resource, uint32_t id) { - struct wlr_input_inhibit_manager *manager = - input_inhibit_manager_from_resource(resource); - if (manager->active_client || manager->active_inhibitor) { - wl_resource_post_error(resource, - ZWLR_INPUT_INHIBIT_MANAGER_V1_ERROR_ALREADY_INHIBITED, - "this compositor already has input inhibited"); - return; - } - - struct wl_resource *wl_resource = wl_resource_create(client, - &zwlr_input_inhibitor_v1_interface, - wl_resource_get_version(resource), id); - if (!wl_resource) { - wl_client_post_no_memory(client); - } - wl_resource_set_implementation(wl_resource, &input_inhibitor_implementation, - manager, input_inhibitor_resource_destroy); - - manager->active_client = client; - manager->active_inhibitor = wl_resource; - - wl_signal_emit_mutable(&manager->events.activate, manager); -} - -static const struct zwlr_input_inhibit_manager_v1_interface inhibit_manager_implementation = { - .get_inhibitor = inhibit_manager_get_inhibitor -}; - -static void input_manager_resource_destroy(struct wl_resource *resource) { - struct wlr_input_inhibit_manager *manager = - input_inhibit_manager_from_resource(resource); - struct wl_client *client = wl_resource_get_client(resource); - if (manager->active_client == client) { - input_inhibit_manager_deactivate(manager); - } -} - -static void inhibit_manager_bind(struct wl_client *wl_client, void *data, - uint32_t version, uint32_t id) { - struct wlr_input_inhibit_manager *manager = data; - - struct wl_resource *wl_resource = wl_resource_create(wl_client, - &zwlr_input_inhibit_manager_v1_interface, version, id); - if (wl_resource == NULL) { - wl_client_post_no_memory(wl_client); - return; - } - wl_resource_set_implementation(wl_resource, - &inhibit_manager_implementation, manager, - input_manager_resource_destroy); -} - -static void handle_display_destroy(struct wl_listener *listener, void *data) { - struct wlr_input_inhibit_manager *manager = - wl_container_of(listener, manager, display_destroy); - wl_signal_emit_mutable(&manager->events.destroy, manager); - wl_list_remove(&manager->display_destroy.link); - wl_global_destroy(manager->global); - free(manager); -} - -struct wlr_input_inhibit_manager *wlr_input_inhibit_manager_create( - struct wl_display *display) { - // TODO: Client destroy - struct wlr_input_inhibit_manager *manager = calloc(1, sizeof(*manager)); - if (!manager) { - return NULL; - } - - manager->global = wl_global_create(display, - &zwlr_input_inhibit_manager_v1_interface, - 1, manager, inhibit_manager_bind); - if (manager->global == NULL){ - free(manager); - return NULL; - } - - wl_signal_init(&manager->events.activate); - wl_signal_init(&manager->events.deactivate); - wl_signal_init(&manager->events.destroy); - - manager->display_destroy.notify = handle_display_destroy; - wl_display_add_destroy_listener(display, &manager->display_destroy); - - return manager; -} diff --git a/types/wlr_input_method_v2.c b/types/wlr_input_method_v2.c index 512adb3..257666c 100644 --- a/types/wlr_input_method_v2.c +++ b/types/wlr_input_method_v2.c @@ -1,6 +1,3 @@ -#ifndef _POSIX_C_SOURCE -#define _POSIX_C_SOURCE 200809L -#endif #include #include #include @@ -19,6 +16,12 @@ static const struct zwp_input_method_v2_interface input_method_impl; static const struct zwp_input_method_keyboard_grab_v2_interface keyboard_grab_impl; +static void input_state_reset(struct wlr_input_method_v2_state *state) { + free(state->commit_text); + free(state->preedit.text); + *state = (struct wlr_input_method_v2_state){0}; +} + static void popup_surface_destroy(struct wlr_input_popup_surface_v2 *popup_surface) { wlr_surface_unmap(popup_surface->surface); @@ -53,10 +56,8 @@ static void input_method_destroy(struct wlr_input_method_v2 *input_method) { wl_list_remove(wl_resource_get_link(input_method->resource)); wl_list_remove(&input_method->seat_client_destroy.link); wlr_input_method_keyboard_grab_v2_destroy(input_method->keyboard_grab); - free(input_method->pending.commit_text); - free(input_method->pending.preedit.text); - free(input_method->current.commit_text); - free(input_method->current.preedit.text); + input_state_reset(&input_method->pending); + input_state_reset(&input_method->current); free(input_method); } @@ -81,15 +82,16 @@ static void im_commit(struct wl_client *client, struct wl_resource *resource, return; } if (serial != input_method->current_serial) { - free(input_method->pending.commit_text); - free(input_method->pending.preedit.text); - input_method->pending = (struct wlr_input_method_v2_state){0}; + input_state_reset(&input_method->pending); return; } - free(input_method->current.commit_text); - free(input_method->current.preedit.text); + input_state_reset(&input_method->current); + + // This transfers ownership of the current commit_text and + // preedit.text from pending to current: input_method->current = input_method->pending; input_method->pending = (struct wlr_input_method_v2_state){0}; + wl_signal_emit_mutable(&input_method->events.commit, input_method); } @@ -145,10 +147,13 @@ void wlr_input_popup_surface_v2_send_text_input_rectangle( popup_surface->resource, sbox->x, sbox->y, sbox->width, sbox->height); } -static void popup_surface_consider_map(struct wlr_input_popup_surface_v2 *popup_surface) { - if (wlr_surface_has_buffer(popup_surface->surface) && - popup_surface->input_method->client_active) { - wlr_surface_map(popup_surface->surface); +static void popup_surface_update_mapped(struct wlr_input_popup_surface_v2 *popup_surface) { + if (popup_surface->input_method->client_active) { + if (wlr_surface_has_buffer(popup_surface->surface)) { + wlr_surface_map(popup_surface->surface); + } + } else { + wlr_surface_unmap(popup_surface->surface); } } @@ -159,7 +164,7 @@ static void popup_surface_surface_role_commit(struct wlr_surface *surface) { return; } - popup_surface_consider_map(popup_surface); + popup_surface_update_mapped(popup_surface); } static void popup_surface_surface_role_destroy(struct wlr_surface *surface) { @@ -245,7 +250,7 @@ static void im_get_input_popup_surface(struct wl_client *client, wl_signal_init(&popup_surface->events.destroy); - popup_surface_consider_map(popup_surface); + popup_surface_update_mapped(popup_surface); wl_list_insert(&input_method->popup_surfaces, &popup_surface->link); wl_signal_emit_mutable(&input_method->events.new_popup_surface, popup_surface); @@ -489,7 +494,7 @@ void wlr_input_method_v2_send_done(struct wlr_input_method_v2 *input_method) { input_method->current_serial++; struct wlr_input_popup_surface_v2 *popup_surface; wl_list_for_each(popup_surface, &input_method->popup_surfaces, link) { - popup_surface_consider_map(popup_surface); + popup_surface_update_mapped(popup_surface); } } diff --git a/types/wlr_keyboard.c b/types/wlr_keyboard.c index 6aea09d..98978ee 100644 --- a/types/wlr_keyboard.c +++ b/types/wlr_keyboard.c @@ -48,7 +48,7 @@ bool keyboard_modifier_update(struct wlr_keyboard *keyboard) { XKB_STATE_MODS_LATCHED); xkb_mod_mask_t locked = xkb_state_serialize_mods(keyboard->xkb_state, XKB_STATE_MODS_LOCKED); - xkb_mod_mask_t group = xkb_state_serialize_layout(keyboard->xkb_state, + xkb_layout_index_t group = xkb_state_serialize_layout(keyboard->xkb_state, XKB_STATE_LAYOUT_EFFECTIVE); if (depressed == keyboard->modifiers.depressed && latched == keyboard->modifiers.latched && diff --git a/types/wlr_keyboard_group.c b/types/wlr_keyboard_group.c index b4c7e55..ef557a9 100644 --- a/types/wlr_keyboard_group.c +++ b/types/wlr_keyboard_group.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include diff --git a/types/wlr_layer_shell_v1.c b/types/wlr_layer_shell_v1.c index fd19a67..a59f110 100644 --- a/types/wlr_layer_shell_v1.c +++ b/types/wlr_layer_shell_v1.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -51,6 +50,7 @@ static void layer_surface_destroy(struct wlr_layer_surface_v1 *surface) { layer_surface_reset(surface); wl_signal_emit_mutable(&surface->events.destroy, surface); + wlr_surface_synced_finish(&surface->synced); wl_resource_set_user_data(surface->resource, NULL); free(surface->namespace); free(surface); @@ -136,13 +136,12 @@ static void layer_surface_handle_set_size(struct wl_client *client, return; } - if (surface->current.desired_width == width - && surface->current.desired_height == height) { - surface->pending.committed &= ~WLR_LAYER_SURFACE_V1_STATE_DESIRED_SIZE; - } else { - surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_DESIRED_SIZE; + if (surface->pending.desired_width == width + && surface->pending.desired_height == height) { + return; } + surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_DESIRED_SIZE; surface->pending.desired_width = width; surface->pending.desired_height = height; } @@ -166,12 +165,11 @@ static void layer_surface_handle_set_anchor(struct wl_client *client, return; } - if (surface->current.anchor == anchor) { - surface->pending.committed &= ~WLR_LAYER_SURFACE_V1_STATE_ANCHOR; - } else { - surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_ANCHOR; + if (surface->pending.anchor == anchor) { + return; } + surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_ANCHOR; surface->pending.anchor = anchor; } @@ -183,12 +181,11 @@ static void layer_surface_handle_set_exclusive_zone(struct wl_client *client, return; } - if (surface->current.exclusive_zone == zone) { - surface->pending.committed &= ~WLR_LAYER_SURFACE_V1_STATE_EXCLUSIVE_ZONE; - } else { - surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_EXCLUSIVE_ZONE; + if (surface->pending.exclusive_zone == zone) { + return; } + surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_EXCLUSIVE_ZONE; surface->pending.exclusive_zone = zone; } @@ -202,15 +199,14 @@ static void layer_surface_handle_set_margin( return; } - if (surface->current.margin.top == top - && surface->current.margin.right == right - && surface->current.margin.bottom == bottom - && surface->current.margin.left == left) { - surface->pending.committed &= ~WLR_LAYER_SURFACE_V1_STATE_MARGIN; - } else { - surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_MARGIN; + if (surface->pending.margin.top == top + && surface->pending.margin.right == right + && surface->pending.margin.bottom == bottom + && surface->pending.margin.left == left) { + return; } + surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_MARGIN; surface->pending.margin.top = top; surface->pending.margin.right = right; surface->pending.margin.bottom = bottom; @@ -274,12 +270,11 @@ static void layer_surface_set_layer(struct wl_client *client, return; } - if (surface->current.layer == layer) { - surface->pending.committed &= ~WLR_LAYER_SURFACE_V1_STATE_LAYER; - } else { - surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_LAYER; + if (surface->pending.layer == layer) { + return; } + surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_LAYER; surface->pending.layer = layer; } @@ -327,15 +322,15 @@ void wlr_layer_surface_v1_destroy(struct wlr_layer_surface_v1 *surface) { layer_surface_destroy(surface); } -static void layer_surface_role_commit(struct wlr_surface *wlr_surface) { +static void layer_surface_role_client_commit(struct wlr_surface *wlr_surface) { struct wlr_layer_surface_v1 *surface = wlr_layer_surface_v1_try_from_wlr_surface(wlr_surface); if (surface == NULL) { return; } - if (wlr_surface_has_buffer(surface->surface) && !surface->configured) { - wl_resource_post_error(surface->resource, + if (wlr_surface_state_has_buffer(&wlr_surface->pending) && !surface->configured) { + wlr_surface_reject_pending(wlr_surface, surface->resource, ZWLR_LAYER_SHELL_V1_ERROR_ALREADY_CONSTRUCTED, "layer_surface has never been configured"); return; @@ -344,8 +339,8 @@ static void layer_surface_role_commit(struct wlr_surface *wlr_surface) { const uint32_t horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; if (surface->pending.desired_width == 0 && - (surface->pending.anchor & horiz) != horiz) { - wl_resource_post_error(surface->resource, + (surface->pending.anchor & horiz) != horiz) { + wlr_surface_reject_pending(wlr_surface, surface->resource, ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_SIZE, "width 0 requested without setting left and right anchors"); return; @@ -354,12 +349,20 @@ static void layer_surface_role_commit(struct wlr_surface *wlr_surface) { const uint32_t vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; if (surface->pending.desired_height == 0 && - (surface->pending.anchor & vert) != vert) { - wl_resource_post_error(surface->resource, + (surface->pending.anchor & vert) != vert) { + wlr_surface_reject_pending(wlr_surface, surface->resource, ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_SIZE, "height 0 requested without setting top and bottom anchors"); return; } +} + +static void layer_surface_role_commit(struct wlr_surface *wlr_surface) { + struct wlr_layer_surface_v1 *surface = + wlr_layer_surface_v1_try_from_wlr_surface(wlr_surface); + if (surface == NULL) { + return; + } if (surface->surface->unmap_commit) { layer_surface_reset(surface); @@ -371,17 +374,6 @@ static void layer_surface_role_commit(struct wlr_surface *wlr_surface) { surface->initialized = true; } - surface->current = surface->pending; - surface->pending.committed = 0; - - if (!surface->added) { - surface->added = true; - wl_signal_emit_mutable(&surface->shell->events.new_surface, surface); - // Return early here as the compositor may have closed this layer surface - // in response to the new_surface event. - return; - } - if (wlr_surface_has_buffer(wlr_surface)) { wlr_surface_map(wlr_surface); } @@ -399,10 +391,22 @@ static void layer_surface_role_destroy(struct wlr_surface *wlr_surface) { static const struct wlr_surface_role layer_surface_role = { .name = "zwlr_layer_surface_v1", + .client_commit = layer_surface_role_client_commit, .commit = layer_surface_role_commit, .destroy = layer_surface_role_destroy, }; +static void surface_synced_move_state(void *_dst, void *_src) { + struct wlr_layer_surface_v1_state *dst = _dst, *src = _src; + *dst = *src; + src->committed = 0; +} + +static const struct wlr_surface_synced_impl surface_synced_impl = { + .state_size = sizeof(struct wlr_layer_surface_v1_state), + .move_state = surface_synced_move_state, +}; + static void layer_shell_handle_get_layer_surface(struct wl_client *wl_client, struct wl_resource *client_resource, uint32_t id, struct wl_resource *surface_resource, @@ -413,6 +417,13 @@ static void layer_shell_handle_get_layer_surface(struct wl_client *wl_client, struct wlr_surface *wlr_surface = wlr_surface_from_resource(surface_resource); + if (layer > ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY) { + wl_resource_post_error(client_resource, + ZWLR_LAYER_SHELL_V1_ERROR_INVALID_LAYER, + "Invalid layer %" PRIu32, layer); + return; + } + struct wlr_layer_surface_v1 *surface = calloc(1, sizeof(*surface)); if (surface == NULL) { wl_client_post_no_memory(wl_client); @@ -430,29 +441,29 @@ static void layer_shell_handle_get_layer_surface(struct wl_client *wl_client, if (output_resource) { surface->output = wlr_output_from_resource(output_resource); } - surface->current.layer = surface->pending.layer = layer; - if (layer > ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY) { - free(surface); - wl_resource_post_error(client_resource, - ZWLR_LAYER_SHELL_V1_ERROR_INVALID_LAYER, - "Invalid layer %" PRIu32, layer); - return; - } surface->namespace = strdup(namespace); if (surface->namespace == NULL) { - free(surface); - wl_client_post_no_memory(wl_client); - return; + goto error_surface; } - surface->resource = wl_resource_create(wl_client, - &zwlr_layer_surface_v1_interface, - wl_resource_get_version(client_resource), - id); + + if (!wlr_surface_synced_init(&surface->synced, wlr_surface, + &surface_synced_impl, &surface->pending, &surface->current)) { + goto error_namespace; + } + + surface->current.layer = surface->pending.layer = layer; + + struct wlr_surface_state *cached; + wl_list_for_each(cached, &wlr_surface->cached, cached_state_link) { + struct wlr_layer_surface_v1_state *state = + wlr_surface_synced_get_state(&surface->synced, cached); + state->layer = layer; + } + + surface->resource = wl_resource_create(wl_client, &zwlr_layer_surface_v1_interface, + wl_resource_get_version(client_resource), id); if (surface->resource == NULL) { - free(surface->namespace); - free(surface); - wl_client_post_no_memory(wl_client); - return; + goto error_synced; } wl_list_init(&surface->configure_list); @@ -467,6 +478,18 @@ static void layer_shell_handle_get_layer_surface(struct wl_client *wl_client, &layer_surface_implementation, surface, NULL); wlr_surface_set_role_object(wlr_surface, surface->resource); + + wl_signal_emit_mutable(&surface->shell->events.new_surface, surface); + + return; + +error_synced: + wlr_surface_synced_finish(&surface->synced); +error_namespace: + free(surface->namespace); +error_surface: + free(surface); + wl_client_post_no_memory(wl_client); } static const struct zwlr_layer_shell_v1_interface layer_shell_implementation = { diff --git a/types/wlr_linux_dmabuf_v1.c b/types/wlr_linux_dmabuf_v1.c index d018d77..bfd9763 100644 --- a/types/wlr_linux_dmabuf_v1.c +++ b/types/wlr_linux_dmabuf_v1.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -6,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -13,11 +13,15 @@ #include #include #include -#include "linux-dmabuf-unstable-v1-protocol.h" +#include "linux-dmabuf-v1-protocol.h" #include "render/drm_format_set.h" #include "util/shm.h" -#define LINUX_DMABUF_VERSION 4 +#if WLR_HAS_DRM_BACKEND +#include +#endif + +#define LINUX_DMABUF_VERSION 5 struct wlr_linux_buffer_params_v1 { struct wl_resource *resource; @@ -200,8 +204,9 @@ static void buffer_handle_resource_destroy(struct wl_resource *buffer_resource) wlr_buffer_drop(&buffer->base); } -static bool check_import_dmabuf(struct wlr_linux_dmabuf_v1 *linux_dmabuf, - struct wlr_dmabuf_attributes *attribs) { +static bool check_import_dmabuf(struct wlr_dmabuf_attributes *attribs, void *data) { + struct wlr_linux_dmabuf_v1 *linux_dmabuf = data; + if (linux_dmabuf->main_device_fd < 0) { return true; } @@ -339,7 +344,8 @@ static void params_create_common(struct wl_resource *params_resource, } /* Check if dmabuf is usable */ - if (!check_import_dmabuf(linux_dmabuf, &attribs)) { + if (!linux_dmabuf->check_dmabuf_callback(&attribs, + linux_dmabuf->check_dmabuf_callback_data)) { goto err_failed; } @@ -969,6 +975,9 @@ struct wlr_linux_dmabuf_v1 *wlr_linux_dmabuf_v1_create(struct wl_display *displa linux_dmabuf->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &linux_dmabuf->display_destroy); + wlr_linux_dmabuf_v1_set_check_dmabuf_callback(linux_dmabuf, + check_import_dmabuf, linux_dmabuf); + wlr_buffer_register_resource_interface(&buffer_resource_interface); return linux_dmabuf; @@ -995,6 +1004,13 @@ struct wlr_linux_dmabuf_v1 *wlr_linux_dmabuf_v1_create_with_renderer(struct wl_d return linux_dmabuf; } +void wlr_linux_dmabuf_v1_set_check_dmabuf_callback(struct wlr_linux_dmabuf_v1 *linux_dmabuf, + bool (*callback)(struct wlr_dmabuf_attributes *attribs, void *data), void *data) { + assert(callback); + linux_dmabuf->check_dmabuf_callback = callback; + linux_dmabuf->check_dmabuf_callback_data = data; +} + bool wlr_linux_dmabuf_v1_set_surface_feedback( struct wlr_linux_dmabuf_v1 *linux_dmabuf, struct wlr_surface *wlr_surface, @@ -1054,6 +1070,15 @@ static bool devid_from_fd(int fd, dev_t *devid) { return true; } +static bool is_secondary_drm_backend(struct wlr_backend *backend) { +#if WLR_HAS_DRM_BACKEND + return wlr_backend_is_drm(backend) && + wlr_drm_backend_get_parent(backend) != NULL; +#else + return false; +#endif +} + bool wlr_linux_dmabuf_feedback_v1_init_with_options(struct wlr_linux_dmabuf_feedback_v1 *feedback, const struct wlr_linux_dmabuf_feedback_v1_init_options *options) { assert(options->main_renderer != NULL); @@ -1075,7 +1100,7 @@ bool wlr_linux_dmabuf_feedback_v1_init_with_options(struct wlr_linux_dmabuf_feed feedback->main_device = renderer_dev; const struct wlr_drm_format_set *renderer_formats = - wlr_renderer_get_dmabuf_texture_formats(options->main_renderer); + wlr_renderer_get_texture_formats(options->main_renderer, WLR_BUFFER_CAP_DMABUF); if (renderer_formats == NULL) { wlr_log(WLR_ERROR, "Failed to get renderer DMA-BUF texture formats"); goto error; @@ -1096,7 +1121,8 @@ bool wlr_linux_dmabuf_feedback_v1_init_with_options(struct wlr_linux_dmabuf_feed wlr_log(WLR_ERROR, "Failed to intersect renderer and scanout formats"); goto error; } - } else if (options->scanout_primary_output != NULL) { + } else if (options->scanout_primary_output != NULL && + !is_secondary_drm_backend(options->scanout_primary_output->backend)) { int backend_drm_fd = wlr_backend_get_drm_fd(options->scanout_primary_output->backend); if (backend_drm_fd < 0) { wlr_log(WLR_ERROR, "Failed to get backend DRM FD"); diff --git a/types/wlr_linux_drm_syncobj_v1.c b/types/wlr_linux_drm_syncobj_v1.c new file mode 100644 index 0000000..82ace2d --- /dev/null +++ b/types/wlr_linux_drm_syncobj_v1.c @@ -0,0 +1,469 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "config.h" +#include "linux-drm-syncobj-v1-protocol.h" + +#define LINUX_DRM_SYNCOBJ_V1_VERSION 1 + +struct wlr_linux_drm_syncobj_surface_v1 { + struct wl_resource *resource; + struct wlr_surface *surface; + struct wlr_linux_drm_syncobj_surface_v1_state pending, current; + + struct wlr_addon addon; + struct wlr_surface_synced synced; + + struct wl_listener client_commit; +}; + +struct wlr_linux_drm_syncobj_surface_v1_commit { + struct wlr_linux_drm_syncobj_surface_v1 *surface; + struct wlr_drm_syncobj_timeline_waiter waiter; + uint32_t cached_seq; + + struct wl_listener waiter_ready; + struct wl_listener surface_destroy; +}; + +static const struct wp_linux_drm_syncobj_manager_v1_interface manager_impl; +static const struct wp_linux_drm_syncobj_timeline_v1_interface timeline_impl; +static const struct wp_linux_drm_syncobj_surface_v1_interface surface_impl; + +static struct wlr_linux_drm_syncobj_manager_v1 *manager_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &wp_linux_drm_syncobj_manager_v1_interface, &manager_impl)); + return wl_resource_get_user_data(resource); +} + +static struct wlr_drm_syncobj_timeline *timeline_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &wp_linux_drm_syncobj_timeline_v1_interface, &timeline_impl)); + return wl_resource_get_user_data(resource); +} + +// Returns NULL if the syncobj surface is inert +static struct wlr_linux_drm_syncobj_surface_v1 *surface_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &wp_linux_drm_syncobj_surface_v1_interface, &surface_impl)); + return wl_resource_get_user_data(resource); +} + +static void timeline_handle_resource_destroy(struct wl_resource *resource) { + struct wlr_drm_syncobj_timeline *timeline = timeline_from_resource(resource); + wlr_drm_syncobj_timeline_unref(timeline); +} + +static void timeline_handle_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static const struct wp_linux_drm_syncobj_timeline_v1_interface timeline_impl = { + .destroy = timeline_handle_destroy, +}; + +static void surface_destroy(struct wlr_linux_drm_syncobj_surface_v1 *surface) { + if (surface == NULL) { + return; + } + wl_list_remove(&surface->client_commit.link); + wlr_addon_finish(&surface->addon); + wlr_surface_synced_finish(&surface->synced); + wl_resource_set_user_data(surface->resource, NULL); + free(surface); +} + +static void surface_handle_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static void surface_handle_set_acquire_point(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *timeline_resource, + uint32_t point_hi, uint32_t point_lo) { + struct wlr_linux_drm_syncobj_surface_v1 *surface = + surface_from_resource(resource); + if (surface == NULL) { + wl_resource_post_error(resource, + WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_SURFACE, + "The surface has been destroyed"); + return; + } + + struct wlr_drm_syncobj_timeline *timeline = + timeline_from_resource(timeline_resource); + uint64_t point = (uint64_t)point_hi << 32 | point_lo; + + wlr_drm_syncobj_timeline_unref(surface->pending.acquire_timeline); + surface->pending.acquire_timeline = wlr_drm_syncobj_timeline_ref(timeline); + surface->pending.acquire_point = point; +} + +static void surface_handle_set_release_point(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *timeline_resource, + uint32_t point_hi, uint32_t point_lo) { + struct wlr_linux_drm_syncobj_surface_v1 *surface = + surface_from_resource(resource); + if (surface == NULL) { + wl_resource_post_error(resource, + WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_SURFACE, + "The surface has been destroyed"); + return; + } + + struct wlr_drm_syncobj_timeline *timeline = + timeline_from_resource(timeline_resource); + uint64_t point = (uint64_t)point_hi << 32 | point_lo; + + wlr_drm_syncobj_timeline_unref(surface->pending.release_timeline); + surface->pending.release_timeline = wlr_drm_syncobj_timeline_ref(timeline); + surface->pending.release_point = point; +} + +static const struct wp_linux_drm_syncobj_surface_v1_interface surface_impl = { + .destroy = surface_handle_destroy, + .set_acquire_point = surface_handle_set_acquire_point, + .set_release_point = surface_handle_set_release_point, +}; + +static void surface_handle_resource_destroy(struct wl_resource *resource) { + struct wlr_linux_drm_syncobj_surface_v1 *surface = + surface_from_resource(resource); + surface_destroy(surface); +} + +static void surface_addon_handle_surface_destroy(struct wlr_addon *addon) { + struct wlr_linux_drm_syncobj_surface_v1 *surface = + wl_container_of(addon, surface, addon); + surface_destroy(surface); +} + +static const struct wlr_addon_interface surface_addon_impl = { + .name = "wp_linux_drm_syncobj_surface_v1", + .destroy = surface_addon_handle_surface_destroy, +}; + +static void surface_synced_finish_state(void *_state) { + struct wlr_linux_drm_syncobj_surface_v1_state *state = _state; + wlr_drm_syncobj_timeline_unref(state->acquire_timeline); + wlr_drm_syncobj_timeline_unref(state->release_timeline); +} + +static void surface_synced_move_state(void *_dst, void *_src) { + struct wlr_linux_drm_syncobj_surface_v1_state *dst = _dst, *src = _src; + // TODO: immediately signal dst.release_timeline if necessary + surface_synced_finish_state(dst); + *dst = *src; + *src = (struct wlr_linux_drm_syncobj_surface_v1_state){0}; +} + +static const struct wlr_surface_synced_impl surface_synced_impl = { + .state_size = sizeof(struct wlr_linux_drm_syncobj_surface_v1_state), + .finish_state = surface_synced_finish_state, + .move_state = surface_synced_move_state, +}; + +static void manager_handle_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static struct wlr_linux_drm_syncobj_surface_v1 *surface_from_wlr_surface( + struct wlr_surface *wlr_surface) { + struct wlr_addon *addon = + wlr_addon_find(&wlr_surface->addons, NULL, &surface_addon_impl); + if (addon == NULL) { + return NULL; + } + struct wlr_linux_drm_syncobj_surface_v1 *surface = + wl_container_of(addon, surface, addon); + return surface; +} + +static void surface_commit_destroy(struct wlr_linux_drm_syncobj_surface_v1_commit *commit) { + wlr_surface_unlock_cached(commit->surface->surface, commit->cached_seq); + wl_list_remove(&commit->surface_destroy.link); + wl_list_remove(&commit->waiter_ready.link); + wlr_drm_syncobj_timeline_waiter_finish(&commit->waiter); + free(commit); +} + +static void surface_commit_handle_waiter_ready(struct wl_listener *listener, void *data) { + struct wlr_linux_drm_syncobj_surface_v1_commit *commit = + wl_container_of(listener, commit, waiter_ready); + surface_commit_destroy(commit); +} + +static void surface_commit_handle_surface_destroy(struct wl_listener *listener, + void *data) { + struct wlr_linux_drm_syncobj_surface_v1_commit *commit = + wl_container_of(listener, commit, surface_destroy); + surface_commit_destroy(commit); +} + +// Block the surface commit until the fence materializes +static bool lock_surface_commit(struct wlr_linux_drm_syncobj_surface_v1 *surface, + struct wlr_drm_syncobj_timeline *timeline, uint64_t point) { + uint32_t flags = DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE; + + bool already_materialized = false; + if (!wlr_drm_syncobj_timeline_check(timeline, point, flags, &already_materialized)) { + return false; + } else if (already_materialized) { + return true; + } + + struct wlr_linux_drm_syncobj_surface_v1_commit *commit = calloc(1, sizeof(*commit)); + if (commit == NULL) { + return false; + } + + struct wl_client *client = wl_resource_get_client(surface->resource); + struct wl_display *display = wl_client_get_display(client); + struct wl_event_loop *loop = wl_display_get_event_loop(display); + if (!wlr_drm_syncobj_timeline_waiter_init(&commit->waiter, timeline, point, + flags, loop)) { + free(commit); + return false; + } + + commit->surface = surface; + commit->cached_seq = wlr_surface_lock_pending(surface->surface); + + commit->waiter_ready.notify = surface_commit_handle_waiter_ready; + wl_signal_add(&commit->waiter.events.ready, &commit->waiter_ready); + + commit->surface_destroy.notify = surface_commit_handle_surface_destroy; + wl_signal_add(&surface->surface->events.destroy, &commit->surface_destroy); + + return true; +} + +static void surface_handle_client_commit(struct wl_listener *listener, + void *data) { + struct wlr_linux_drm_syncobj_surface_v1 *surface = + wl_container_of(listener, surface, client_commit); + + if (surface->pending.acquire_timeline != NULL && + surface->surface->pending.buffer == NULL) { + wlr_surface_reject_pending(surface->surface, surface->resource, + WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_BUFFER, + "Acquire point set but no buffer attached"); + return; + } + if (surface->pending.release_timeline != NULL && + surface->surface->pending.buffer == NULL) { + wlr_surface_reject_pending(surface->surface, surface->resource, + WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_BUFFER, + "Release point set but no buffer attached"); + return; + } + + if (surface->pending.acquire_timeline == NULL && + surface->surface->pending.buffer != NULL) { + wlr_surface_reject_pending(surface->surface, surface->resource, + WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_ACQUIRE_POINT, + "Buffer attached but no acquire point set"); + return; + } + if (surface->pending.release_timeline == NULL && + surface->surface->pending.buffer != NULL) { + wlr_surface_reject_pending(surface->surface, surface->resource, + WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_RELEASE_POINT, + "Buffer attached but no release point set"); + return; + } + + if (surface->pending.acquire_timeline != NULL && + surface->pending.release_timeline != NULL && + surface->pending.acquire_timeline == surface->pending.release_timeline && + surface->pending.acquire_point >= surface->pending.release_point) { + wlr_surface_reject_pending(surface->surface, surface->resource, + WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_CONFLICTING_POINTS, + "Acquire and release points conflict"); + return; + } + + if (surface->pending.acquire_timeline != NULL && !lock_surface_commit( + surface, surface->pending.acquire_timeline, surface->pending.acquire_point)) { + wl_resource_post_no_memory(surface->resource); + return; + } +} + +static void manager_handle_get_surface(struct wl_client *client, + struct wl_resource *resource, uint32_t id, + struct wl_resource *surface_resource) { + struct wlr_surface *wlr_surface = wlr_surface_from_resource(surface_resource); + + if (surface_from_wlr_surface(wlr_surface) != NULL) { + wl_resource_post_error(resource, + WP_LINUX_DRM_SYNCOBJ_MANAGER_V1_ERROR_SURFACE_EXISTS, + "wp_linux_drm_syncobj_surface_v1 already created for this surface"); + return; + } + + struct wlr_linux_drm_syncobj_surface_v1 *surface = calloc(1, sizeof(*surface)); + if (surface == NULL) { + wl_resource_post_no_memory(resource); + return; + } + + if (!wlr_surface_synced_init(&surface->synced, wlr_surface, + &surface_synced_impl, &surface->pending, &surface->current)) { + goto error_surface; + } + + uint32_t version = wl_resource_get_version(resource); + surface->resource = wl_resource_create(client, + &wp_linux_drm_syncobj_surface_v1_interface, version, id); + if (surface->resource == NULL) { + goto error_surface_synced; + } + wl_resource_set_implementation(surface->resource, + &surface_impl, surface, surface_handle_resource_destroy); + + surface->surface = wlr_surface; + + surface->client_commit.notify = surface_handle_client_commit; + wl_signal_add(&wlr_surface->events.client_commit, &surface->client_commit); + + wlr_addon_init(&surface->addon, &wlr_surface->addons, NULL, &surface_addon_impl); + + return; + +error_surface_synced: + wlr_surface_synced_finish(&surface->synced); +error_surface: + free(surface); + wl_resource_post_no_memory(resource); +} + +static void manager_handle_import_timeline(struct wl_client *client, + struct wl_resource *resource, uint32_t id, int drm_syncobj_fd) { + struct wlr_linux_drm_syncobj_manager_v1 *manager = + manager_from_resource(resource); + + struct wlr_drm_syncobj_timeline *timeline = + wlr_drm_syncobj_timeline_import(manager->drm_fd, drm_syncobj_fd); + close(drm_syncobj_fd); + if (timeline == NULL) { + wl_resource_post_error(resource, + WP_LINUX_DRM_SYNCOBJ_MANAGER_V1_ERROR_INVALID_TIMELINE, + "Failed to import drm_syncobj timeline"); + return; + } + + uint32_t version = wl_resource_get_version(resource); + struct wl_resource *timeline_resource = wl_resource_create(client, + &wp_linux_drm_syncobj_timeline_v1_interface, version, id); + if (timeline_resource == NULL) { + wl_resource_post_no_memory(resource); + return; + } + wl_resource_set_implementation(timeline_resource, + &timeline_impl, timeline, timeline_handle_resource_destroy); +} + +static const struct wp_linux_drm_syncobj_manager_v1_interface manager_impl = { + .destroy = manager_handle_destroy, + .get_surface = manager_handle_get_surface, + .import_timeline = manager_handle_import_timeline, +}; + +static void manager_bind(struct wl_client *client, void *data, + uint32_t version, uint32_t id) { + struct wlr_linux_drm_syncobj_manager_v1 *manager = data; + + struct wl_resource *resource = wl_resource_create(client, + &wp_linux_drm_syncobj_manager_v1_interface, version, id); + if (resource == NULL) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &manager_impl, manager, NULL); +} + +static void handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_linux_drm_syncobj_manager_v1 *manager = + wl_container_of(listener, manager, display_destroy); + wl_list_remove(&manager->display_destroy.link); + wl_global_destroy(manager->global); + close(manager->drm_fd); + free(manager); +} + +static bool check_syncobj_eventfd(int drm_fd) { + if (!HAVE_EVENTFD) { + return false; + } + + uint64_t value = 0; + if (drmGetCap(drm_fd, DRM_CAP_SYNCOBJ_TIMELINE, &value) != 0 || value == 0) { + return false; + } + + // Try waiting with invalid syncobj should fail with ENOENT + struct drm_syncobj_eventfd syncobj_eventfd = { + .handle = 0, + .fd = -1, + }; + return drmIoctl(drm_fd, DRM_IOCTL_SYNCOBJ_EVENTFD, &syncobj_eventfd) != 0 && errno == ENOENT; +} + +struct wlr_linux_drm_syncobj_manager_v1 *wlr_linux_drm_syncobj_manager_v1_create( + struct wl_display *display, uint32_t version, int drm_fd) { + if (!check_syncobj_eventfd(drm_fd)) { + wlr_log(WLR_INFO, "DRM syncobj eventfd unavailable, disabling linux-drm-syncobj-v1"); + return NULL; + } + + struct wlr_linux_drm_syncobj_manager_v1 *manager = calloc(1, sizeof(*manager)); + if (manager == NULL) { + return NULL; + } + + manager->drm_fd = fcntl(drm_fd, F_DUPFD_CLOEXEC, 0); + if (manager->drm_fd < 0) { + goto error_manager; + } + + manager->global = wl_global_create(display, + &wp_linux_drm_syncobj_manager_v1_interface, + LINUX_DRM_SYNCOBJ_V1_VERSION, manager, manager_bind); + if (manager->global == NULL) { + goto error_drm_fd; + } + + manager->display_destroy.notify = handle_display_destroy; + wl_display_add_destroy_listener(display, &manager->display_destroy); + + return manager; + +error_drm_fd: + close(manager->drm_fd); +error_manager: + free(manager); + return NULL; +} + +struct wlr_linux_drm_syncobj_surface_v1_state * +wlr_linux_drm_syncobj_v1_get_surface_state(struct wlr_surface *wlr_surface) { + struct wlr_linux_drm_syncobj_surface_v1 *surface = + surface_from_wlr_surface(wlr_surface); + if (surface == NULL) { + return NULL; + } + return &surface->current; +} diff --git a/types/wlr_output_layout.c b/types/wlr_output_layout.c index 23d1334..853ff59 100644 --- a/types/wlr_output_layout.c +++ b/types/wlr_output_layout.c @@ -9,17 +9,27 @@ static const struct wlr_addon_interface addon_impl; -struct wlr_output_layout *wlr_output_layout_create(void) { +static void output_layout_handle_display_destroy(struct wl_listener *listener, + void *data) { + struct wlr_output_layout *layout = wl_container_of(listener, layout, display_destroy); + wlr_output_layout_destroy(layout); +} + +struct wlr_output_layout *wlr_output_layout_create(struct wl_display *display) { struct wlr_output_layout *layout = calloc(1, sizeof(*layout)); if (layout == NULL) { return NULL; } wl_list_init(&layout->outputs); + layout->display = display; wl_signal_init(&layout->events.add); wl_signal_init(&layout->events.change); wl_signal_init(&layout->events.destroy); + layout->display_destroy.notify = output_layout_handle_display_destroy; + wl_display_add_destroy_listener(display, &layout->display_destroy); + return layout; } @@ -45,6 +55,7 @@ void wlr_output_layout_destroy(struct wlr_output_layout *layout) { output_layout_output_destroy(l_output); } + wl_list_remove(&layout->display_destroy.link); free(layout); } @@ -104,10 +115,11 @@ static void output_layout_reconfigure(struct wlr_output_layout *layout) { wl_signal_emit_mutable(&layout->events.change, layout); } -static void output_update_global(struct wlr_output *output) { +static void output_update_global(struct wlr_output_layout *layout, + struct wlr_output *output) { // Don't expose the output if it doesn't have a current mode if (output->width > 0 && output->height > 0) { - wlr_output_create_global(output); + wlr_output_create_global(output, layout->display); } else { wlr_output_destroy_global(output); } @@ -122,7 +134,7 @@ static void handle_output_commit(struct wl_listener *listener, void *data) { WLR_OUTPUT_STATE_TRANSFORM | WLR_OUTPUT_STATE_MODE)) { output_layout_reconfigure(l_output->layout); - output_update_global(l_output->output); + output_update_global(l_output->layout, l_output->output); } } @@ -182,7 +194,7 @@ static struct wlr_output_layout_output *output_layout_add(struct wlr_output_layo l_output->auto_configured = auto_configured; output_layout_reconfigure(layout); - output_update_global(output); + output_update_global(layout, output); if (is_new) { wl_signal_emit_mutable(&layout->events.add, l_output); diff --git a/types/wlr_output_management_v1.c b/types/wlr_output_management_v1.c index b90d227..ecc32de 100644 --- a/types/wlr_output_management_v1.c +++ b/types/wlr_output_management_v1.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include "wlr-output-management-unstable-v1-protocol.h" @@ -1027,3 +1028,25 @@ void wlr_output_head_v1_state_apply( wlr_output_state_set_adaptive_sync_enabled(output_state, head_state->adaptive_sync_enabled); } + +struct wlr_backend_output_state *wlr_output_configuration_v1_build_state( + const struct wlr_output_configuration_v1 *config, size_t *states_len) { + *states_len = wl_list_length(&config->heads); + struct wlr_backend_output_state *states = calloc(*states_len, sizeof(states[0])); + if (states == NULL) { + return NULL; + } + + size_t i = 0; + const struct wlr_output_configuration_head_v1 *config_head; + wl_list_for_each(config_head, &config->heads, link) { + struct wlr_backend_output_state *state = &states[i]; + i++; + + state->output = config_head->state.output; + wlr_output_state_init(&state->base); + wlr_output_head_v1_state_apply(&config_head->state, &state->base); + } + + return states; +} diff --git a/types/wlr_output_swapchain_manager.c b/types/wlr_output_swapchain_manager.c new file mode 100644 index 0000000..c2cca43 --- /dev/null +++ b/types/wlr_output_swapchain_manager.c @@ -0,0 +1,251 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "render/drm_format_set.h" +#include "types/wlr_output.h" + +struct wlr_output_swapchain_manager_output { + struct wlr_output *output; + // Newly allocated swapchain. Can be NULL if the old swapchain is re-used + // or if the output is disabled. + struct wlr_swapchain *new_swapchain; + // True if the output was included in the last successful call to + // wlr_output_swapchain_manager_prepare(). + bool test_success; + // Pending swapchain which will replace the old one when + // wlr_output_swapchain_manager_apply() is called. Can be either a pointer + // to the newly allocated swapchain, or the old swapchain, or NULL. + struct wlr_swapchain *pending_swapchain; +}; + +void wlr_output_swapchain_manager_init(struct wlr_output_swapchain_manager *manager, + struct wlr_backend *backend) { + *manager = (struct wlr_output_swapchain_manager){ + .backend = backend, + }; +} + +static struct wlr_output_swapchain_manager_output *manager_get_output( + struct wlr_output_swapchain_manager *manager, struct wlr_output *output) { + struct wlr_output_swapchain_manager_output *manager_output; + wl_array_for_each(manager_output, &manager->outputs) { + if (manager_output->output == output) { + return manager_output; + } + } + return NULL; +} + +static struct wlr_output_swapchain_manager_output *manager_get_or_add_output( + struct wlr_output_swapchain_manager *manager, struct wlr_output *output) { + struct wlr_output_swapchain_manager_output *manager_output = manager_get_output(manager, output); + if (manager_output != NULL) { + return manager_output; + } + + manager_output = wl_array_add(&manager->outputs, sizeof(*manager_output)); + if (manager_output == NULL) { + return NULL; + } + *manager_output = (struct wlr_output_swapchain_manager_output){ + .output = output, + }; + return manager_output; +} + +static bool swapchain_is_compatible(struct wlr_swapchain *swapchain, + int width, int height, const struct wlr_drm_format *format) { + if (swapchain == NULL) { + return false; + } + if (swapchain->width != width || swapchain->height != height) { + return false; + } + if (swapchain->format.format != format->format || swapchain->format.len != format->len) { + return false; + } + assert(format->len > 0); + return memcmp(swapchain->format.modifiers, format->modifiers, format->len * sizeof(format->modifiers[0])) == 0; +} + +static struct wlr_swapchain *manager_output_get_swapchain( + struct wlr_output_swapchain_manager_output *manager_output, + int width, int height, const struct wlr_drm_format *format) { + struct wlr_output *output = manager_output->output; + + if (swapchain_is_compatible(output->swapchain, width, height, format)) { + return output->swapchain; + } + if (swapchain_is_compatible(manager_output->new_swapchain, width, height, format)) { + return manager_output->new_swapchain; + } + + struct wlr_swapchain *swapchain = wlr_swapchain_create(output->allocator, width, height, format); + if (swapchain == NULL) { + return NULL; + } + wlr_swapchain_destroy(manager_output->new_swapchain); + manager_output->new_swapchain = swapchain; + return swapchain; +} + +static bool manager_output_prepare(struct wlr_output_swapchain_manager_output *manager_output, + struct wlr_output_state *state, bool explicit_modifiers) { + struct wlr_output *output = manager_output->output; + struct wlr_allocator *allocator = output->allocator; + assert(allocator != NULL); + + if (!output_pending_enabled(output, state)) { + manager_output->pending_swapchain = NULL; + return true; + } + + int width, height; + output_pending_resolution(output, state, &width, &height); + + uint32_t fmt = output->render_format; + if (state->committed & WLR_OUTPUT_STATE_RENDER_FORMAT) { + fmt = state->render_format; + } + + const struct wlr_drm_format_set *display_formats = + wlr_output_get_primary_formats(output, allocator->buffer_caps); + struct wlr_drm_format format = {0}; + if (!output_pick_format(output, display_formats, &format, fmt)) { + return false; + } + + if (!explicit_modifiers && (format.len != 1 || format.modifiers[0] != DRM_FORMAT_MOD_LINEAR)) { + if (!wlr_drm_format_has(&format, DRM_FORMAT_MOD_INVALID)) { + wlr_log(WLR_DEBUG, "Implicit modifiers not supported"); + wlr_drm_format_finish(&format); + return false; + } + + format.len = 0; + if (!wlr_drm_format_add(&format, DRM_FORMAT_MOD_INVALID)) { + wlr_log(WLR_DEBUG, "Failed to add implicit modifier to format"); + wlr_drm_format_finish(&format); + return false; + } + } + + struct wlr_swapchain *swapchain = + manager_output_get_swapchain(manager_output, width, height, &format); + wlr_drm_format_finish(&format); + if (swapchain == NULL) { + return false; + } + + struct wlr_buffer *buffer = wlr_swapchain_acquire(swapchain, NULL); + if (buffer == NULL) { + return false; + } + + wlr_output_state_set_buffer(state, buffer); + wlr_buffer_unlock(buffer); + manager_output->pending_swapchain = swapchain; + return true; +} + +static bool manager_test(struct wlr_output_swapchain_manager *manager, + struct wlr_backend_output_state *states, size_t states_len, + bool explicit_modifiers) { + wlr_log(WLR_DEBUG, "Preparing test commit for %zu outputs with %s modifiers", + states_len, explicit_modifiers ? "explicit": "implicit"); + + struct wlr_output_swapchain_manager_output *manager_output; + wl_array_for_each(manager_output, &manager->outputs) { + manager_output->test_success = false; + } + + for (size_t i = 0; i < states_len; i++) { + struct wlr_backend_output_state *state = &states[i]; + struct wlr_output_swapchain_manager_output *manager_output = + manager_get_or_add_output(manager, state->output); + if (manager_output == NULL) { + return false; + } + if (!manager_output_prepare(manager_output, &state->base, explicit_modifiers)) { + return false; + } + } + + bool ok = wlr_backend_test(manager->backend, states, states_len); + wlr_log(WLR_DEBUG, "Test commit for %zu outputs %s", + states_len, ok ? "succeeded" : "failed"); + if (!ok) { + return false; + } + + for (size_t i = 0; i < states_len; i++) { + struct wlr_output_swapchain_manager_output *manager_output = + manager_get_output(manager, states[i].output); + assert(manager_output != NULL); + manager_output->test_success = true; + } + + return true; +} + +bool wlr_output_swapchain_manager_prepare(struct wlr_output_swapchain_manager *manager, + const struct wlr_backend_output_state *states, size_t states_len) { + bool ok = false; + struct wlr_backend_output_state *pending = malloc(states_len * sizeof(states[0])); + if (pending == NULL) { + return false; + } + for (size_t i = 0; i < states_len; i++) { + pending[i] = states[i]; + pending[i].base.buffer = NULL; + } + + ok = manager_test(manager, pending, states_len, true); + if (!ok) { + ok = manager_test(manager, pending, states_len, false); + } + + for (size_t i = 0; i < states_len; i++) { + wlr_buffer_unlock(pending[i].base.buffer); + } + free(pending); + + return ok; +} + +struct wlr_swapchain *wlr_output_swapchain_manager_get_swapchain( + struct wlr_output_swapchain_manager *manager, struct wlr_output *output) { + struct wlr_output_swapchain_manager_output *manager_output = + manager_get_output(manager, output); + assert(manager_output != NULL && manager_output->test_success); + return manager_output->pending_swapchain; +} + +void wlr_output_swapchain_manager_apply(struct wlr_output_swapchain_manager *manager) { + struct wlr_output_swapchain_manager_output *manager_output; + wl_array_for_each(manager_output, &manager->outputs) { + struct wlr_output *output = manager_output->output; + if (!manager_output->test_success || manager_output->pending_swapchain == output->swapchain) { + continue; + } + + wlr_swapchain_destroy(output->swapchain); + output->swapchain = manager_output->new_swapchain; + manager_output->new_swapchain = NULL; + manager_output->test_success = false; + } +} + +void wlr_output_swapchain_manager_finish(struct wlr_output_swapchain_manager *manager) { + struct wlr_output_swapchain_manager_output *manager_output; + wl_array_for_each(manager_output, &manager->outputs) { + wlr_swapchain_destroy(manager_output->new_swapchain); + } + wl_array_release(&manager->outputs); +} diff --git a/types/wlr_pointer_constraints_v1.c b/types/wlr_pointer_constraints_v1.c index 9f4bb2e..f3c8fb7 100644 --- a/types/wlr_pointer_constraints_v1.c +++ b/types/wlr_pointer_constraints_v1.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include @@ -49,6 +48,7 @@ static void pointer_constraint_destroy(struct wlr_pointer_constraint_v1 *constra wl_signal_emit_mutable(&constraint->events.destroy, constraint); wl_resource_set_user_data(constraint->resource, NULL); + wlr_surface_synced_finish(&constraint->synced); wl_list_remove(&constraint->link); wl_list_remove(&constraint->surface_commit.link); wl_list_remove(&constraint->surface_destroy.link); @@ -98,6 +98,7 @@ static void pointer_constraint_set_cursor_position_hint(struct wl_client *client return; } + constraint->pending.cursor_hint.enabled = true; constraint->pending.cursor_hint.x = wl_fixed_to_double(x); constraint->pending.cursor_hint.y = wl_fixed_to_double(y); constraint->pending.committed |= WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT; @@ -105,20 +106,6 @@ static void pointer_constraint_set_cursor_position_hint(struct wl_client *client static void pointer_constraint_commit( struct wlr_pointer_constraint_v1 *constraint) { - if (constraint->pending.committed & - WLR_POINTER_CONSTRAINT_V1_STATE_REGION) { - pixman_region32_copy(&constraint->current.region, - &constraint->pending.region); - } - if (constraint->pending.committed & - WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT) { - constraint->current.cursor_hint = constraint->pending.cursor_hint; - } - constraint->current.committed |= constraint->pending.committed; - - bool updated_region = !!constraint->pending.committed; - constraint->pending.committed = 0; - pixman_region32_clear(&constraint->region); if (pixman_region32_not_empty(&constraint->current.region)) { pixman_region32_intersect(&constraint->region, @@ -128,7 +115,7 @@ static void pointer_constraint_commit( &constraint->surface->input_region); } - if (updated_region) { + if (constraint->current.committed & WLR_POINTER_CONSTRAINT_V1_STATE_REGION) { wl_signal_emit_mutable(&constraint->events.set_region, NULL); } } @@ -165,6 +152,37 @@ static const struct zwp_locked_pointer_v1_interface locked_pointer_impl = { .set_cursor_position_hint = pointer_constraint_set_cursor_position_hint, }; +static void surface_synced_init_state(void *_state) { + struct wlr_pointer_constraint_v1_state *state = _state; + pixman_region32_init(&state->region); +} + +static void surface_synced_finish_state(void *_state) { + struct wlr_pointer_constraint_v1_state *state = _state; + pixman_region32_fini(&state->region); +} + +static void surface_synced_move_state(void *_dst, void *_src) { + struct wlr_pointer_constraint_v1_state *dst = _dst, *src = _src; + + if (src->committed & WLR_POINTER_CONSTRAINT_V1_STATE_REGION) { + pixman_region32_copy(&dst->region, &src->region); + } + if (src->committed & WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT) { + dst->cursor_hint = src->cursor_hint; + } + + dst->committed = src->committed; + src->committed = 0; +} + +static const struct wlr_surface_synced_impl surface_synced_impl = { + .state_size = sizeof(struct wlr_pointer_constraint_v1_state), + .init_state = surface_synced_init_state, + .finish_state = surface_synced_finish_state, + .move_state = surface_synced_move_state, +}; + static void pointer_constraint_create(struct wl_client *client, struct wl_resource *pointer_constraints_resource, uint32_t id, struct wl_resource *surface_resource, @@ -220,6 +238,14 @@ static void pointer_constraint_create(struct wl_client *client, return; } + if (!wlr_surface_synced_init(&constraint->synced, surface, + &surface_synced_impl, &constraint->pending, &constraint->current)) { + free(constraint); + wl_resource_destroy(resource); + wl_client_post_no_memory(client); + return; + } + constraint->resource = resource; constraint->surface = surface; constraint->seat = seat; diff --git a/types/wlr_pointer_gestures_v1.c b/types/wlr_pointer_gestures_v1.c index 0628bb5..ebb1c86 100644 --- a/types/wlr_pointer_gestures_v1.c +++ b/types/wlr_pointer_gestures_v1.c @@ -1,7 +1,3 @@ -#ifndef _POSIX_C_SOURCE -#define _POSIX_C_SOURCE 200809L -#endif - #include #include #include @@ -400,6 +396,7 @@ static void pointer_gestures_v1_bind(struct wl_client *wl_client, void *data, static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_pointer_gestures_v1 *gestures = wl_container_of(listener, gestures, display_destroy); + wl_signal_emit_mutable(&gestures->events.destroy, NULL); wl_list_remove(&gestures->display_destroy.link); wl_global_destroy(gestures->global); free(gestures); @@ -424,6 +421,8 @@ struct wlr_pointer_gestures_v1 *wlr_pointer_gestures_v1_create( return NULL; } + wl_signal_init(&gestures->events.destroy); + gestures->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &gestures->display_destroy); diff --git a/types/wlr_presentation_time.c b/types/wlr_presentation_time.c index fcfa0f7..35b960d 100644 --- a/types/wlr_presentation_time.c +++ b/types/wlr_presentation_time.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 199309L #include #include #include @@ -18,8 +17,7 @@ struct wlr_presentation_surface { struct wlr_presentation_surface_state current, pending; struct wlr_addon addon; // wlr_surface.addons - - struct wl_listener surface_commit; + struct wlr_surface_synced synced; }; static void feedback_handle_resource_destroy(struct wl_resource *resource) { @@ -62,11 +60,8 @@ static void presentation_surface_addon_destroy(struct wlr_addon *addon) { wl_container_of(addon, p_surface, addon); wlr_addon_finish(addon); + wlr_surface_synced_finish(&p_surface->synced); - wlr_presentation_feedback_destroy(p_surface->current.feedback); - wlr_presentation_feedback_destroy(p_surface->pending.feedback); - - wl_list_remove(&p_surface->surface_commit.link); free(p_surface); } @@ -75,34 +70,31 @@ static const struct wlr_addon_interface presentation_surface_addon_impl = { .destroy = presentation_surface_addon_destroy, }; -static void presentation_surface_handle_surface_commit( - struct wl_listener *listener, void *data) { - struct wlr_presentation_surface *p_surface = - wl_container_of(listener, p_surface, surface_commit); - - wlr_presentation_feedback_destroy(p_surface->current.feedback); - p_surface->current.feedback = p_surface->pending.feedback; - p_surface->pending.feedback = NULL; +static void surface_synced_finish_state(void *_state) { + struct wlr_presentation_surface_state *state = _state; + wlr_presentation_feedback_destroy(state->feedback); } -static const struct wp_presentation_interface presentation_impl; - -static struct wlr_presentation *presentation_from_resource( - struct wl_resource *resource) { - assert(wl_resource_instance_of(resource, &wp_presentation_interface, - &presentation_impl)); - return wl_resource_get_user_data(resource); +static void surface_synced_move_state(void *_dst, void *_src) { + struct wlr_presentation_surface_state *dst = _dst, *src = _src; + surface_synced_finish_state(dst); + dst->feedback = src->feedback; + src->feedback = NULL; } +static const struct wlr_surface_synced_impl surface_synced_impl = { + .state_size = sizeof(struct wlr_presentation_surface_state), + .finish_state = surface_synced_finish_state, + .move_state = surface_synced_move_state, +}; + static void presentation_handle_feedback(struct wl_client *client, struct wl_resource *presentation_resource, struct wl_resource *surface_resource, uint32_t id) { - struct wlr_presentation *presentation = - presentation_from_resource(presentation_resource); struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); - struct wlr_addon *addon = wlr_addon_find(&surface->addons, - presentation, &presentation_surface_addon_impl); + struct wlr_addon *addon = + wlr_addon_find(&surface->addons, NULL, &presentation_surface_addon_impl); struct wlr_presentation_surface *p_surface = NULL; if (addon != NULL) { p_surface = wl_container_of(addon, p_surface, addon); @@ -113,11 +105,13 @@ static void presentation_handle_feedback(struct wl_client *client, return; } wlr_addon_init(&p_surface->addon, &surface->addons, - presentation, &presentation_surface_addon_impl); - p_surface->surface_commit.notify = - presentation_surface_handle_surface_commit; - wl_signal_add(&surface->events.commit, - &p_surface->surface_commit); + NULL, &presentation_surface_addon_impl); + if (!wlr_surface_synced_init(&p_surface->synced, surface, + &surface_synced_impl, &p_surface->pending, &p_surface->current)) { + free(p_surface); + wl_client_post_no_memory(client); + return; + } } struct wlr_presentation_feedback *feedback = p_surface->pending.feedback; @@ -157,16 +151,13 @@ static const struct wp_presentation_interface presentation_impl = { static void presentation_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { - struct wlr_presentation *presentation = data; - struct wl_resource *resource = wl_resource_create(client, &wp_presentation_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(client); return; } - wl_resource_set_implementation(resource, &presentation_impl, presentation, - NULL); + wl_resource_set_implementation(resource, &presentation_impl, NULL, NULL); wp_presentation_send_clock_id(resource, CLOCK_MONOTONIC); } @@ -188,7 +179,7 @@ struct wlr_presentation *wlr_presentation_create(struct wl_display *display, } presentation->global = wl_global_create(display, &wp_presentation_interface, - PRESENTATION_VERSION, presentation, presentation_bind); + PRESENTATION_VERSION, NULL, presentation_bind); if (presentation->global == NULL) { free(presentation); return NULL; @@ -212,9 +203,9 @@ void wlr_presentation_feedback_send_presented( } struct wlr_presentation_feedback *wlr_presentation_surface_sampled( - struct wlr_presentation *presentation, struct wlr_surface *surface) { - struct wlr_addon *addon = wlr_addon_find(&surface->addons, - presentation, &presentation_surface_addon_impl); + struct wlr_surface *surface) { + struct wlr_addon *addon = + wlr_addon_find(&surface->addons, NULL, &presentation_surface_addon_impl); if (addon != NULL) { struct wlr_presentation_surface *p_surface = wl_container_of(addon, p_surface, addon); @@ -307,11 +298,10 @@ static void feedback_handle_output_destroy(struct wl_listener *listener, wlr_presentation_feedback_destroy(feedback); } -static void presentation_surface_queued_on_output( - struct wlr_presentation *presentation, struct wlr_surface *surface, +static void presentation_surface_queued_on_output(struct wlr_surface *surface, struct wlr_output *output, bool zero_copy) { struct wlr_presentation_feedback *feedback = - wlr_presentation_surface_sampled(presentation, surface); + wlr_presentation_surface_sampled(surface); if (feedback == NULL) { return; } @@ -328,16 +318,12 @@ static void presentation_surface_queued_on_output( wl_signal_add(&output->events.destroy, &feedback->output_destroy); } -void wlr_presentation_surface_textured_on_output( - struct wlr_presentation *presentation, struct wlr_surface *surface, +void wlr_presentation_surface_textured_on_output(struct wlr_surface *surface, struct wlr_output *output) { - return presentation_surface_queued_on_output(presentation, surface, - output, false); + return presentation_surface_queued_on_output(surface, output, false); } -void wlr_presentation_surface_scanned_out_on_output( - struct wlr_presentation *presentation, struct wlr_surface *surface, +void wlr_presentation_surface_scanned_out_on_output(struct wlr_surface *surface, struct wlr_output *output) { - return presentation_surface_queued_on_output(presentation, surface, - output, true); + return presentation_surface_queued_on_output(surface, output, true); } diff --git a/types/wlr_primary_selection_v1.c b/types/wlr_primary_selection_v1.c index af232ef..0a5f0fb 100644 --- a/types/wlr_primary_selection_v1.c +++ b/types/wlr_primary_selection_v1.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include diff --git a/types/wlr_region.c b/types/wlr_region.c index a886827..59f6dfc 100644 --- a/types/wlr_region.c +++ b/types/wlr_region.c @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include "types/wlr_region.h" static const struct wl_region_interface region_impl; diff --git a/types/wlr_screencopy_v1.c b/types/wlr_screencopy_v1.c index 99ed769..acd584d 100644 --- a/types/wlr_screencopy_v1.c +++ b/types/wlr_screencopy_v1.c @@ -3,12 +3,13 @@ #include #include #include +#include #include -#include #include #include #include #include +#include #include "wlr-screencopy-unstable-v1-protocol.h" #include "render/pixel_format.h" #include "render/wlr_renderer.h" @@ -194,11 +195,6 @@ static bool frame_shm_copy(struct wlr_screencopy_frame_v1 *frame, struct wlr_renderer *renderer = output->renderer; assert(renderer); - int x = frame->box.x; - int y = frame->box.y; - int width = frame->box.width; - int height = frame->box.height; - void *data; uint32_t format; size_t stride; @@ -208,15 +204,29 @@ static bool frame_shm_copy(struct wlr_screencopy_frame_v1 *frame, } bool ok = false; - if (!renderer_bind_buffer(renderer, src_buffer)) { + + struct wlr_texture *texture = wlr_texture_from_buffer(renderer, src_buffer); + if (!texture) { + wlr_log(WLR_DEBUG, "Failed to grab a texture from a buffer during shm screencopy"); goto out; } - ok = wlr_renderer_read_pixels(renderer, format, - stride, width, height, x, y, 0, 0, data); - renderer_bind_buffer(renderer, NULL); + + ok = wlr_texture_read_pixels(texture, &(struct wlr_texture_read_pixels_options) { + .data = data, + .format = format, + .stride = stride, + .src_box = frame->box, + }); + + wlr_texture_destroy(texture); out: wlr_buffer_end_data_ptr_access(frame->buffer); + + if (!ok) { + wlr_log(WLR_DEBUG, "Failed to copy to destination during shm screencopy"); + } + return ok; } @@ -227,10 +237,10 @@ static bool frame_dma_copy(struct wlr_screencopy_frame_v1 *frame, struct wlr_renderer *renderer = output->renderer; assert(renderer); - struct wlr_texture *src_tex = wlr_texture_from_buffer(renderer, src_buffer); if (src_tex == NULL) { + wlr_log(WLR_DEBUG, "Failed to grab a texture from a buffer during dma screencopy"); return false; } @@ -261,6 +271,11 @@ static bool frame_dma_copy(struct wlr_screencopy_frame_v1 *frame, out: wlr_texture_destroy(src_tex); + + if (!ok) { + wlr_log(WLR_DEBUG, "Failed to render to destination during dma screencopy"); + } + return ok; } @@ -270,8 +285,6 @@ static void frame_handle_output_commit(struct wl_listener *listener, wl_container_of(listener, frame, output_commit); struct wlr_output_event_commit *event = data; struct wlr_output *output = frame->output; - struct wlr_renderer *renderer = output->renderer; - assert(renderer); if (!(event->state->committed & WLR_OUTPUT_STATE_BUFFER)) { return; @@ -525,7 +538,25 @@ static void capture_output(struct wl_client *wl_client, struct wlr_renderer *renderer = output->renderer; assert(renderer); - frame->shm_format = wlr_output_preferred_read_format(frame->output); + if (!wlr_output_configure_primary_swapchain(output, NULL, &output->swapchain)) { + goto error; + } + + int buffer_age; + struct wlr_buffer *buffer = wlr_swapchain_acquire(output->swapchain, &buffer_age); + if (buffer == NULL) { + goto error; + } + + struct wlr_texture *texture = wlr_texture_from_buffer(renderer, buffer); + wlr_buffer_unlock(buffer); + if (!texture) { + goto error; + } + + frame->shm_format = wlr_texture_preferred_read_format(texture); + wlr_texture_destroy(texture); + if (frame->shm_format == DRM_FORMAT_INVALID) { wlr_log(WLR_ERROR, "Failed to capture output: no read format supported by renderer"); diff --git a/types/wlr_security_context_v1.c b/types/wlr_security_context_v1.c index c675e9b..3371144 100644 --- a/types/wlr_security_context_v1.c +++ b/types/wlr_security_context_v1.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -100,13 +99,18 @@ static void security_context_destroy( free(security_context); } +static void security_context_client_destroy( + struct wlr_security_context_v1_client *security_context_client) { + wl_list_remove(&security_context_client->destroy.link); + security_context_state_finish(&security_context_client->state); + free(security_context_client); +} + static void security_context_client_handle_destroy(struct wl_listener *listener, void *data) { struct wlr_security_context_v1_client *security_context_client = wl_container_of(listener, security_context_client, destroy); - wl_list_remove(&security_context_client->destroy.link); - security_context_state_finish(&security_context_client->state); - free(security_context_client); + security_context_client_destroy(security_context_client); } static int security_context_handle_listen_fd_event(int listen_fd, uint32_t mask, @@ -139,17 +143,19 @@ static int security_context_handle_listen_fd_event(int listen_fd, uint32_t mask, if (client == NULL) { wlr_log(WLR_ERROR, "wl_client_create failed"); close(client_fd); + free(security_context_client); return 0; } + security_context_client->destroy.notify = security_context_client_handle_destroy; + wl_client_add_destroy_listener(client, &security_context_client->destroy); + if (!security_context_state_copy(&security_context_client->state, &security_context->state)) { + security_context_client_destroy(security_context_client); wl_client_post_no_memory(client); return 0; } - - security_context_client->destroy.notify = security_context_client_handle_destroy; - wl_client_add_destroy_listener(client, &security_context_client->destroy); } return 0; @@ -423,8 +429,8 @@ struct wlr_security_context_manager_v1 *wlr_security_context_manager_v1_create( } const struct wlr_security_context_v1_state *wlr_security_context_manager_v1_lookup_client( - struct wlr_security_context_manager_v1 *manager, struct wl_client *client) { - struct wl_listener *listener = wl_client_get_destroy_listener(client, + struct wlr_security_context_manager_v1 *manager, const struct wl_client *client) { + struct wl_listener *listener = wl_client_get_destroy_listener((struct wl_client *)client, security_context_client_handle_destroy); if (listener == NULL) { return NULL; diff --git a/types/wlr_session_lock_v1.c b/types/wlr_session_lock_v1.c index b2c297e..73874f8 100644 --- a/types/wlr_session_lock_v1.c +++ b/types/wlr_session_lock_v1.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -38,7 +37,7 @@ static void lock_surface_destroy(struct wlr_session_lock_surface_v1 *lock_surfac assert(wl_list_empty(&lock_surface->events.destroy.listener_list)); wl_list_remove(&lock_surface->output_destroy.link); - + wlr_surface_synced_finish(&lock_surface->synced); wl_resource_set_user_data(lock_surface->resource, NULL); free(lock_surface); } @@ -149,37 +148,42 @@ static const struct ext_session_lock_surface_v1_interface lock_surface_implement .ack_configure = lock_surface_handle_ack_configure, }; -static void lock_surface_role_commit(struct wlr_surface *surface) { +static void lock_surface_role_client_commit(struct wlr_surface *surface) { struct wlr_session_lock_surface_v1 *lock_surface = wlr_session_lock_surface_v1_try_from_wlr_surface(surface); if (lock_surface == NULL) { return; } - if (!wlr_surface_has_buffer(surface)) { - wl_resource_post_error(lock_surface->resource, + if (!wlr_surface_state_has_buffer(&surface->pending)) { + wlr_surface_reject_pending(surface, lock_surface->resource, EXT_SESSION_LOCK_SURFACE_V1_ERROR_NULL_BUFFER, "session lock surface is committed with a null buffer"); return; } if (!lock_surface->configured) { - wl_resource_post_error(lock_surface->resource, + wlr_surface_reject_pending(surface, lock_surface->resource, EXT_SESSION_LOCK_SURFACE_V1_ERROR_COMMIT_BEFORE_FIRST_ACK, "session lock surface has never been configured"); return; } - if (surface->current.width < 0 || surface->current.height < 0 || - (uint32_t)surface->current.width != lock_surface->pending.width || - (uint32_t)surface->current.height != lock_surface->pending.height) { - wl_resource_post_error(lock_surface->resource, + if ((uint32_t)surface->pending.width != lock_surface->pending.width || + (uint32_t)surface->pending.height != lock_surface->pending.height) { + wlr_surface_reject_pending(surface, lock_surface->resource, EXT_SESSION_LOCK_SURFACE_V1_ERROR_DIMENSIONS_MISMATCH, "committed surface dimensions do not match last acked configure"); return; } +} - lock_surface->current = lock_surface->pending; +static void lock_surface_role_commit(struct wlr_surface *surface) { + struct wlr_session_lock_surface_v1 *lock_surface = + wlr_session_lock_surface_v1_try_from_wlr_surface(surface); + if (lock_surface == NULL) { + return; + } wlr_surface_map(surface); } @@ -195,10 +199,15 @@ static void lock_surface_role_destroy(struct wlr_surface *surface) { static const struct wlr_surface_role lock_surface_role = { .name = "ext_session_lock_surface_v1", + .client_commit = lock_surface_role_client_commit, .commit = lock_surface_role_commit, .destroy = lock_surface_role_destroy, }; +static const struct wlr_surface_synced_impl surface_synced_impl = { + .state_size = sizeof(struct wlr_session_lock_surface_v1_state), +}; + static void lock_surface_handle_output_destroy(struct wl_listener *listener, void *data) { struct wlr_session_lock_surface_v1 *lock_surface = @@ -269,6 +278,13 @@ static void lock_handle_get_lock_surface(struct wl_client *client, return; } + if (!wlr_surface_synced_init(&lock_surface->synced, surface, + &surface_synced_impl, &lock_surface->pending, &lock_surface->current)) { + free(lock_surface); + wl_client_post_no_memory(client); + return; + } + lock_surface->resource = lock_surface_resource; wl_resource_set_user_data(lock_surface_resource, lock_surface); diff --git a/types/wlr_shm.c b/types/wlr_shm.c index ac41e87..1f45c88 100644 --- a/types/wlr_shm.c +++ b/types/wlr_shm.c @@ -1,3 +1,4 @@ +#undef _POSIX_C_SOURCE #define _DEFAULT_SOURCE // for MAP_ANONYMOUS #include #include @@ -6,8 +7,9 @@ #include #include #include -#include +#include #include +#include #include #include #include @@ -20,15 +22,7 @@ #error "Lock-free C11 atomic pointers are required" #endif -#define SHM_VERSION 1 - -struct wlr_shm { - struct wl_global *global; - uint32_t *formats; - size_t formats_len; - - struct wl_listener display_destroy; -}; +#define SHM_VERSION 2 struct wlr_shm_pool { struct wl_resource *resource; // may be NULL @@ -466,8 +460,14 @@ static void shm_handle_create_pool(struct wl_client *client, close(fd); } +static void shm_handle_release(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + static const struct wl_shm_interface shm_impl = { .create_pool = shm_handle_create_pool, + .release = shm_handle_release, }; static void shm_bind(struct wl_client *client, void *data, uint32_t version, @@ -497,6 +497,8 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_shm *wlr_shm_create(struct wl_display *display, uint32_t version, const uint32_t *formats, size_t formats_len) { + assert(version <= SHM_VERSION); + // ARGB8888 and XRGB8888 must be supported per the wl_shm spec bool has_argb8888 = false, has_xrgb8888 = false; for (size_t i = 0; i < formats_len; i++) { @@ -528,7 +530,7 @@ struct wlr_shm *wlr_shm_create(struct wl_display *display, uint32_t version, shm->formats[i] = convert_drm_format_to_wl_shm(formats[i]); } - shm->global = wl_global_create(display, &wl_shm_interface, SHM_VERSION, + shm->global = wl_global_create(display, &wl_shm_interface, version, shm, shm_bind); if (shm->global == NULL) { wlr_log(WLR_ERROR, "wl_global_create failed"); @@ -547,16 +549,27 @@ struct wlr_shm *wlr_shm_create(struct wl_display *display, uint32_t version, struct wlr_shm *wlr_shm_create_with_renderer(struct wl_display *display, uint32_t version, struct wlr_renderer *renderer) { - size_t formats_len; - const uint32_t *formats = - wlr_renderer_get_shm_texture_formats(renderer, &formats_len); - if (formats == NULL) { + const struct wlr_drm_format_set *format_set = + wlr_renderer_get_texture_formats(renderer, WLR_BUFFER_CAP_DATA_PTR); + if (format_set == NULL || format_set->len == 0) { wlr_log(WLR_ERROR, "Failed to initialize wl_shm: " "cannot get renderer formats"); return NULL; } - return wlr_shm_create(display, version, formats, formats_len); + size_t formats_len = format_set->len; + uint32_t *formats = calloc(formats_len, sizeof(formats[0])); + if (formats == NULL) { + return NULL; + } + + for (size_t i = 0; i < format_set->len; i++) { + formats[i] = format_set->formats[i].format; + } + + struct wlr_shm *shm = wlr_shm_create(display, version, formats, formats_len); + free(formats); + return shm; } static bool shm_has_format(struct wlr_shm *shm, uint32_t shm_format) { diff --git a/types/wlr_single_pixel_buffer_v1.c b/types/wlr_single_pixel_buffer_v1.c index e4dac82..2dab6c5 100644 --- a/types/wlr_single_pixel_buffer_v1.c +++ b/types/wlr_single_pixel_buffer_v1.c @@ -8,17 +8,13 @@ #define SINGLE_PIXEL_MANAGER_VERSION 1 -struct wlr_single_pixel_buffer_manager_v1 { - struct wl_global *global; - - struct wl_listener display_destroy; -}; - struct wlr_single_pixel_buffer_v1 { struct wlr_buffer base; struct wl_resource *resource; uint32_t r, g, b, a; uint8_t argb8888[4]; // packed little-endian DRM_FORMAT_ARGB8888 + + struct wl_listener release; }; static void destroy_resource(struct wl_client *client, @@ -60,6 +56,7 @@ static void buffer_destroy(struct wlr_buffer *wlr_buffer) { if (buffer->resource != NULL) { wl_resource_set_user_data(buffer->resource, NULL); } + wl_list_remove(&buffer->release.link); free(buffer); } @@ -92,6 +89,13 @@ static void buffer_handle_resource_destroy(struct wl_resource *resource) { wlr_buffer_drop(&buffer->base); } +static void buffer_handle_release(struct wl_listener *listener, void *data) { + struct wlr_single_pixel_buffer_v1 *buffer = wl_container_of(listener, buffer, release); + if (buffer->resource != NULL) { + wl_buffer_send_release(buffer->resource); + } +} + static void manager_handle_create_u32_rgba_buffer(struct wl_client *client, struct wl_resource *resource, uint32_t id, uint32_t r, uint32_t g, uint32_t b, uint32_t a) { @@ -122,6 +126,9 @@ static void manager_handle_create_u32_rgba_buffer(struct wl_client *client, buffer->argb8888[1] = (uint8_t)((double)buffer->g * f); buffer->argb8888[2] = (uint8_t)((double)buffer->r * f); buffer->argb8888[3] = (uint8_t)((double)buffer->a * f); + + buffer->release.notify = buffer_handle_release; + wl_signal_add(&buffer->base.events.release, &buffer->release); } static const struct wp_single_pixel_buffer_manager_v1_interface manager_impl = { diff --git a/types/wlr_subcompositor.c b/types/wlr_subcompositor.c index f78f7a4..771b987 100644 --- a/types/wlr_subcompositor.c +++ b/types/wlr_subcompositor.c @@ -33,9 +33,9 @@ static void subsurface_destroy(struct wlr_subsurface *subsurface) { wl_signal_emit_mutable(&subsurface->events.destroy, subsurface); + wlr_surface_synced_finish(&subsurface->parent_synced); + wl_list_remove(&subsurface->surface_client_commit.link); - wl_list_remove(&subsurface->current.link); - wl_list_remove(&subsurface->pending.link); wl_list_remove(&subsurface->parent_destroy.link); wl_resource_set_user_data(subsurface->resource, NULL); @@ -118,8 +118,6 @@ static void subsurface_handle_place_above(struct wl_client *client, wl_list_remove(&subsurface->pending.link); wl_list_insert(node, &subsurface->pending.link); - - subsurface->reordered = true; } static void subsurface_handle_place_below(struct wl_client *client, @@ -150,8 +148,6 @@ static void subsurface_handle_place_below(struct wl_client *client, wl_list_remove(&subsurface->pending.link); wl_list_insert(node->prev, &subsurface->pending.link); - - subsurface->reordered = true; } static void subsurface_handle_set_sync(struct wl_client *client, @@ -195,7 +191,8 @@ static const struct wl_subsurface_interface subsurface_implementation = { const struct wlr_surface_role subsurface_role; void subsurface_consider_map(struct wlr_subsurface *subsurface) { - if (subsurface->parent->mapped && wlr_surface_has_buffer(subsurface->surface)) { + if (subsurface->added && subsurface->parent->mapped && + wlr_surface_has_buffer(subsurface->surface)) { wlr_surface_map(subsurface->surface); } } @@ -224,6 +221,33 @@ const struct wlr_surface_role subsurface_role = { .destroy = subsurface_role_destroy, }; +static void surface_synced_init_state(void *_state) { + struct wlr_subsurface_parent_state *state = _state; + wl_list_init(&state->link); +} + +static void surface_synced_finish_state(void *_state) { + struct wlr_subsurface_parent_state *state = _state; + wl_list_remove(&state->link); +} + +static void surface_synced_move_state(void *_dst, void *_src) { + struct wlr_subsurface_parent_state *dst = _dst, *src = _src; + dst->x = src->x; + dst->y = src->y; + dst->synced = src->synced; + + // For the sake of simplicity, copying the position in list is done by the + // parent itself +} + +static struct wlr_surface_synced_impl surface_synced_impl = { + .state_size = sizeof(struct wlr_subsurface_parent_state), + .init_state = surface_synced_init_state, + .finish_state = surface_synced_finish_state, + .move_state = surface_synced_move_state, +}; + static void subsurface_handle_parent_destroy(struct wl_listener *listener, void *data) { struct wlr_subsurface *subsurface = @@ -253,45 +277,21 @@ static void subsurface_handle_surface_client_commit( } } -static void collect_damage_iter(struct wlr_surface *surface, - int sx, int sy, void *data) { - struct wlr_subsurface *subsurface = data; - pixman_region32_t *damage = &subsurface->parent->external_damage; - pixman_region32_union_rect(damage, damage, - subsurface->current.x + sx, - subsurface->current.y + sy, - surface->current.width, surface->current.height); -} - void subsurface_handle_parent_commit(struct wlr_subsurface *subsurface) { - struct wlr_surface *surface = subsurface->surface; - - bool moved = subsurface->current.x != subsurface->pending.x || - subsurface->current.y != subsurface->pending.y; - if (subsurface->surface->mapped && moved) { - wlr_surface_for_each_surface(surface, - collect_damage_iter, subsurface); - } - if (subsurface->synchronized && subsurface->has_cache) { - wlr_surface_unlock_cached(surface, subsurface->cached_seq); + wlr_surface_unlock_cached(subsurface->surface, subsurface->cached_seq); subsurface->has_cache = false; } - subsurface->current.x = subsurface->pending.x; - subsurface->current.y = subsurface->pending.y; - if (subsurface->surface->mapped && (moved || subsurface->reordered)) { - subsurface->reordered = false; - wlr_surface_for_each_surface(surface, - collect_damage_iter, subsurface); - } - if (!subsurface->added) { subsurface->added = true; wl_signal_emit_mutable(&subsurface->parent->events.new_subsurface, subsurface); subsurface_consider_map(subsurface); } + + subsurface->previous.x = subsurface->current.x; + subsurface->previous.y = subsurface->current.y; } struct wlr_subsurface *wlr_subsurface_try_from_wlr_surface(struct wlr_surface *surface) { @@ -334,11 +334,19 @@ static void subcompositor_handle_get_subsurface(struct wl_client *client, return; } + if (!wlr_surface_synced_init(&subsurface->parent_synced, parent, &surface_synced_impl, + &subsurface->pending, &subsurface->current)) { + free(subsurface); + wl_client_post_no_memory(client); + return; + } + subsurface->synchronized = true; subsurface->surface = surface; subsurface->resource = wl_resource_create(client, &wl_subsurface_interface, wl_resource_get_version(resource), id); if (subsurface->resource == NULL) { + wlr_surface_synced_finish(&subsurface->parent_synced); free(subsurface); wl_client_post_no_memory(client); return; @@ -346,6 +354,19 @@ static void subcompositor_handle_get_subsurface(struct wl_client *client, wl_resource_set_implementation(subsurface->resource, &subsurface_implementation, subsurface, NULL); + // In surface_state_move() we commit sub-surface order. To do so we need to + // iterate over the list of sub-surfaces from a struct wlr_surface_state. + // Store a pointer to struct wlr_surface_synced to facilitate this. + subsurface->pending.synced = &subsurface->parent_synced; + subsurface->current.synced = &subsurface->parent_synced; + + struct wlr_surface_state *cached; + wl_list_for_each(cached, &parent->cached, cached_state_link) { + struct wlr_subsurface_parent_state *sub_state = + wlr_surface_synced_get_state(&subsurface->parent_synced, cached); + sub_state->synced = &subsurface->parent_synced; + } + wlr_surface_set_role_object(surface, subsurface->resource); wl_signal_init(&subsurface->events.destroy); @@ -360,7 +381,7 @@ static void subcompositor_handle_get_subsurface(struct wl_client *client, wl_signal_add(&parent->events.destroy, &subsurface->parent_destroy); subsurface->parent_destroy.notify = subsurface_handle_parent_destroy; - wl_list_init(&subsurface->current.link); + wl_list_remove(&subsurface->pending.link); wl_list_insert(parent->pending.subsurfaces_above.prev, &subsurface->pending.link); } diff --git a/types/wlr_tablet_tool.c b/types/wlr_tablet_tool.c index fbab01e..77f4631 100644 --- a/types/wlr_tablet_tool.c +++ b/types/wlr_tablet_tool.c @@ -9,7 +9,7 @@ struct wlr_tablet *wlr_tablet_from_input_device( struct wlr_input_device *input_device) { - assert(input_device->type == WLR_INPUT_DEVICE_TABLET_TOOL); + assert(input_device->type == WLR_INPUT_DEVICE_TABLET); return wl_container_of(input_device, (struct wlr_tablet *)NULL, base); } @@ -18,7 +18,7 @@ void wlr_tablet_init(struct wlr_tablet *tablet, *tablet = (struct wlr_tablet){ .impl = impl, }; - wlr_input_device_init(&tablet->base, WLR_INPUT_DEVICE_TABLET_TOOL, name); + wlr_input_device_init(&tablet->base, WLR_INPUT_DEVICE_TABLET, name); wl_signal_init(&tablet->events.axis); wl_signal_init(&tablet->events.proximity); diff --git a/types/wlr_tearing_control_v1.c b/types/wlr_tearing_control_v1.c index f22eb6e..cd7cd5a 100644 --- a/types/wlr_tearing_control_v1.c +++ b/types/wlr_tearing_control_v1.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include @@ -39,6 +38,8 @@ static void destroy_tearing_hint(struct wlr_tearing_control_v1 *hint) { wl_resource_set_user_data(hint->resource, NULL); wlr_addon_finish(&hint->addon); + wlr_surface_synced_finish(&hint->synced); + wl_list_remove(&hint->surface_commit.link); free(hint); } @@ -64,21 +65,32 @@ static void destroy_tearing_resource_impl(struct wl_resource *resource) { destroy_tearing_hint(hint); } -static void tearing_control_handle_presentation_hint(struct wl_client *client, +static void tearing_control_handle_set_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); + surface_hint->pending = hint; } static const struct wp_tearing_control_v1_interface tearing_control_impl = { .destroy = resource_handle_destroy, - .set_presentation_hint = tearing_control_handle_presentation_hint + .set_presentation_hint = tearing_control_handle_set_presentation_hint, +}; + +static const struct wlr_surface_synced_impl surface_synced_impl = { + .state_size = sizeof(enum wp_tearing_control_v1_presentation_hint), }; +static void hint_handle_surface_commit(struct wl_listener *listener, void *data) { + struct wlr_tearing_control_v1 *hint = wl_container_of(listener, hint, surface_commit); + + if (hint->current != hint->previous) { + wl_signal_emit_mutable(&hint->events.set_hint, NULL); + } + + hint->previous = hint->current; +} + 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) { @@ -98,6 +110,13 @@ static void tearing_control_manager_handle_get_tearing_control( return; } + if (!wlr_surface_synced_init(&hint->synced, surface, + &surface_synced_impl, &hint->pending, &hint->current)) { + free(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); @@ -110,7 +129,6 @@ static void tearing_control_manager_handle_get_tearing_control( 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; @@ -119,6 +137,9 @@ static void tearing_control_manager_handle_get_tearing_control( wl_signal_init(&hint->events.set_hint); wl_signal_init(&hint->events.destroy); + hint->surface_commit.notify = hint_handle_surface_commit; + wl_signal_add(&surface->events.commit, &hint->surface_commit); + wl_list_insert(&manager->surface_hints, &hint->link); wl_signal_emit_mutable(&manager->events.new_object, hint); @@ -198,5 +219,5 @@ wlr_tearing_control_manager_v1_surface_hint_from_surface(struct wlr_tearing_cont struct wlr_tearing_control_v1 *hint = wl_container_of(addon, hint, addon); - return hint->hint; + return hint->current; } diff --git a/types/wlr_text_input_v3.c b/types/wlr_text_input_v3.c index 11944f2..0b05ca6 100644 --- a/types/wlr_text_input_v3.c +++ b/types/wlr_text_input_v3.c @@ -1,6 +1,3 @@ -#ifndef _POSIX_C_SOURCE -#define _POSIX_C_SOURCE 200809L -#endif #include #include #include @@ -28,6 +25,7 @@ void wlr_text_input_v3_send_enter(struct wlr_text_input_v3 *text_input, struct wlr_surface *surface) { assert(wl_resource_get_client(text_input->resource) == wl_resource_get_client(surface->resource)); + assert(text_input->focused_surface == NULL); text_input->focused_surface = surface; wl_signal_add(&text_input->focused_surface->events.destroy, &text_input->surface_destroy); @@ -36,6 +34,7 @@ void wlr_text_input_v3_send_enter(struct wlr_text_input_v3 *text_input, } void wlr_text_input_v3_send_leave(struct wlr_text_input_v3 *text_input) { + assert(text_input->focused_surface != NULL); zwp_text_input_v3_send_leave(text_input->resource, text_input->focused_surface->resource); text_input_clear_focused_surface(text_input); diff --git a/types/wlr_transient_seat_v1.c b/types/wlr_transient_seat_v1.c new file mode 100644 index 0000000..379ce0a --- /dev/null +++ b/types/wlr_transient_seat_v1.c @@ -0,0 +1,160 @@ +#include +#include +#include +#include +#include +#include "ext-transient-seat-v1-protocol.h" + +static const struct ext_transient_seat_manager_v1_interface manager_impl; +static const struct ext_transient_seat_v1_interface transient_seat_impl; + +static struct wlr_transient_seat_manager_v1 *manager_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &ext_transient_seat_manager_v1_interface, &manager_impl)); + return wl_resource_get_user_data(resource); +} + +static struct wlr_transient_seat_v1 *transient_seat_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &ext_transient_seat_v1_interface, &transient_seat_impl)); + return wl_resource_get_user_data(resource); +} + +static void transient_seat_destroy(struct wlr_transient_seat_v1 *seat) { + wl_list_remove(&seat->seat_destroy.link); + wlr_seat_destroy(seat->seat); + free(seat); +} + +static void transient_seat_handle_resource_destroy( + struct wl_resource *resource) { + struct wlr_transient_seat_v1 *seat = + transient_seat_from_resource(resource); + transient_seat_destroy(seat); +} + +static void transient_seat_handle_destroy(struct wl_client *client, + struct wl_resource *seat_resource) { + wl_resource_destroy(seat_resource); +} + +static const struct ext_transient_seat_v1_interface transient_seat_impl = { + .destroy = transient_seat_handle_destroy, +}; + +static void manager_create_transient_seat(struct wl_client *client, + struct wl_resource *manager_resource, uint32_t id) { + struct wlr_transient_seat_manager_v1 *manager = + manager_from_resource(manager_resource); + + struct wlr_transient_seat_v1 *seat = calloc(1, sizeof(*seat)); + if (!seat) { + goto failure; + } + + int version = wl_resource_get_version(manager_resource); + seat->resource = wl_resource_create(client, + &ext_transient_seat_v1_interface, version, id); + if (!seat->resource) { + goto failure; + } + + wl_resource_set_implementation(seat->resource, &transient_seat_impl, + seat, transient_seat_handle_resource_destroy); + + wl_signal_emit_mutable(&manager->events.create_seat, seat); + + return; + +failure: + free(seat); + wl_client_post_no_memory(client); +} + +static void transient_seat_handle_seat_destroy(struct wl_listener *listener, + void *data) { + struct wlr_transient_seat_v1 *seat = wl_container_of(listener, seat, + seat_destroy); + seat->seat = NULL; + wl_resource_set_user_data(seat->resource, NULL); + transient_seat_destroy(seat); +} + +void wlr_transient_seat_v1_ready(struct wlr_transient_seat_v1 *seat, + struct wlr_seat *wlr_seat) { + assert(wlr_seat); + + seat->seat = wlr_seat; + + seat->seat_destroy.notify = transient_seat_handle_seat_destroy; + wl_signal_add(&wlr_seat->events.destroy, &seat->seat_destroy); + + struct wl_client *client = wl_resource_get_client(seat->resource); + uint32_t global_name = wl_global_get_name(seat->seat->global, client); + assert(global_name != 0); + ext_transient_seat_v1_send_ready(seat->resource, global_name); +} + +void wlr_transient_seat_v1_deny(struct wlr_transient_seat_v1 *seat) { + ext_transient_seat_v1_send_denied(seat->resource); +} + +static void manager_handle_destroy(struct wl_client *client, + struct wl_resource *manager_resource) { + wl_resource_destroy(manager_resource); +} + +static const struct ext_transient_seat_manager_v1_interface manager_impl = { + .create = manager_create_transient_seat, + .destroy = manager_handle_destroy, +}; + +static void handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_transient_seat_manager_v1 *manager = + wl_container_of(listener, manager, display_destroy); + + wl_list_remove(&manager->display_destroy.link); + wl_global_destroy(manager->global); + free(manager); +} + +static void transient_seat_manager_bind(struct wl_client *client, void *data, + uint32_t version, uint32_t id) { + struct wlr_transient_seat_manager_v1 *manager = data; + + struct wl_resource *resource = wl_resource_create(client, + &ext_transient_seat_manager_v1_interface, version, id); + + if (!resource) { + wl_client_post_no_memory(client); + return; + } + + wl_resource_set_implementation(resource, &manager_impl, manager, NULL); +} + +struct wlr_transient_seat_manager_v1 *wlr_transient_seat_manager_v1_create( + struct wl_display *display) { + struct wlr_transient_seat_manager_v1 *manager = + calloc(1, sizeof(*manager)); + if (!manager) { + return NULL; + } + + manager->global = wl_global_create(display, + &ext_transient_seat_manager_v1_interface, 1, manager, + transient_seat_manager_bind); + if (!manager->global) { + free(manager); + return NULL; + } + + manager->display_destroy.notify = handle_display_destroy; + wl_display_add_destroy_listener(display, &manager->display_destroy); + + wl_signal_init(&manager->events.create_seat); + + return manager; +} diff --git a/types/wlr_viewporter.c b/types/wlr_viewporter.c index 0e5069b..38c2043 100644 --- a/types/wlr_viewporter.c +++ b/types/wlr_viewporter.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "viewporter-protocol.h" #define VIEWPORTER_VERSION 1 @@ -134,11 +135,7 @@ static void viewport_handle_resource_destroy(struct wl_resource *resource) { static bool check_src_buffer_bounds(const struct wlr_surface_state *state) { int width = state->buffer_width / state->scale; int height = state->buffer_height / state->scale; - if (state->transform & WL_OUTPUT_TRANSFORM_90) { - int tmp = width; - width = height; - height = tmp; - } + wlr_output_transform_coords(state->transform, &width, &height); struct wlr_fbox box = state->viewport.src; return box.x + box.width <= width && box.y + box.height <= height; @@ -154,15 +151,17 @@ static void viewport_handle_surface_client_commit(struct wl_listener *listener, if (!state->viewport.has_dst && (floor(state->viewport.src.width) != state->viewport.src.width || floor(state->viewport.src.height) != state->viewport.src.height)) { - wl_resource_post_error(viewport->resource, WP_VIEWPORT_ERROR_BAD_SIZE, + wlr_surface_reject_pending(viewport->surface, + viewport->resource, WP_VIEWPORT_ERROR_BAD_SIZE, "wl_viewport.set_source width and height must be integers " "when the destination rectangle is unset"); return; } - if (state->viewport.has_src && state->buffer != NULL && + if (state->viewport.has_src && wlr_surface_state_has_buffer(state) && !check_src_buffer_bounds(state)) { - wl_resource_post_error(viewport->resource, WP_VIEWPORT_ERROR_OUT_OF_BUFFER, + wlr_surface_reject_pending(viewport->surface, + viewport->resource, WP_VIEWPORT_ERROR_OUT_OF_BUFFER, "source rectangle out of buffer bounds"); return; } diff --git a/types/wlr_virtual_keyboard_v1.c b/types/wlr_virtual_keyboard_v1.c index 639156f..8a6d107 100644 --- a/types/wlr_virtual_keyboard_v1.c +++ b/types/wlr_virtual_keyboard_v1.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 199309L #include #include #include diff --git a/types/wlr_virtual_pointer_v1.c b/types/wlr_virtual_pointer_v1.c index 6fcd0e9..2ae39a0 100644 --- a/types/wlr_virtual_pointer_v1.c +++ b/types/wlr_virtual_pointer_v1.c @@ -71,7 +71,7 @@ static void virtual_pointer_button(struct wl_client *client, .pointer = &pointer->pointer, .time_msec = time, .button = button, - .state = state ? WLR_BUTTON_PRESSED : WLR_BUTTON_RELEASED + .state = state ? WL_POINTER_BUTTON_STATE_PRESSED : WL_POINTER_BUTTON_STATE_RELEASED, }; wl_signal_emit_mutable(&pointer->pointer.events.button, &event); } diff --git a/types/wlr_xcursor_manager.c b/types/wlr_xcursor_manager.c index 96679d5..b45dcd7 100644 --- a/types/wlr_xcursor_manager.c +++ b/types/wlr_xcursor_manager.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include diff --git a/types/wlr_xdg_activation_v1.c b/types/wlr_xdg_activation_v1.c index cd38271..8e0fb06 100644 --- a/types/wlr_xdg_activation_v1.c +++ b/types/wlr_xdg_activation_v1.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include diff --git a/types/wlr_xdg_decoration_v1.c b/types/wlr_xdg_decoration_v1.c index 0cc8f6d..d16ac7c 100644 --- a/types/wlr_xdg_decoration_v1.c +++ b/types/wlr_xdg_decoration_v1.c @@ -62,8 +62,8 @@ static void toplevel_decoration_handle_resource_destroy( struct wlr_xdg_toplevel_decoration_v1 *decoration = toplevel_decoration_from_resource(resource); wl_signal_emit_mutable(&decoration->events.destroy, decoration); - wl_list_remove(&decoration->surface_commit.link); - wl_list_remove(&decoration->surface_destroy.link); + wlr_surface_synced_finish(&decoration->synced); + wl_list_remove(&decoration->toplevel_destroy.link); wl_list_remove(&decoration->surface_configure.link); wl_list_remove(&decoration->surface_ack_configure.link); struct wlr_xdg_toplevel_decoration_v1_configure *configure, *tmp; @@ -74,10 +74,10 @@ static void toplevel_decoration_handle_resource_destroy( free(decoration); } -static void toplevel_decoration_handle_surface_destroy( +static void toplevel_decoration_handle_toplevel_destroy( struct wl_listener *listener, void *data) { struct wlr_xdg_toplevel_decoration_v1 *decoration = - wl_container_of(listener, decoration, surface_destroy); + wl_container_of(listener, decoration, toplevel_destroy); wl_resource_post_error(decoration->resource, ZXDG_TOPLEVEL_DECORATION_V1_ERROR_ORPHANED, @@ -141,20 +141,9 @@ static void toplevel_decoration_handle_surface_ack_configure( free(configure); } -static void toplevel_decoration_handle_surface_commit( - struct wl_listener *listener, void *data) { - struct wlr_xdg_toplevel_decoration_v1 *decoration = - wl_container_of(listener, decoration, surface_commit); - struct wlr_xdg_decoration_manager_v1 *manager = decoration->manager; - - decoration->current = decoration->pending; - - if (decoration->toplevel->base->added && !decoration->added) { - decoration->added = true; - wl_signal_emit_mutable(&manager->events.new_toplevel_decoration, - decoration); - } -} +static const struct wlr_surface_synced_impl surface_synced_impl = { + .state_size = sizeof(struct wlr_xdg_toplevel_decoration_v1_state), +}; static const struct zxdg_decoration_manager_v1_interface decoration_manager_impl; @@ -204,10 +193,18 @@ static void decoration_manager_handle_get_toplevel_decoration( decoration->manager = manager; decoration->toplevel = toplevel; + if (!wlr_surface_synced_init(&decoration->synced, toplevel->base->surface, + &surface_synced_impl, &decoration->pending, &decoration->current)) { + free(decoration); + wl_client_post_no_memory(client); + return; + } + uint32_t version = wl_resource_get_version(manager_resource); decoration->resource = wl_resource_create(client, &zxdg_toplevel_decoration_v1_interface, version, id); if (decoration->resource == NULL) { + wlr_surface_synced_finish(&decoration->synced); free(decoration); wl_client_post_no_memory(client); return; @@ -223,30 +220,16 @@ static void decoration_manager_handle_get_toplevel_decoration( wl_signal_init(&decoration->events.destroy); wl_signal_init(&decoration->events.request_mode); - wl_signal_add(&toplevel->base->events.destroy, - &decoration->surface_destroy); - decoration->surface_destroy.notify = - toplevel_decoration_handle_surface_destroy; - wl_signal_add(&toplevel->base->events.configure, - &decoration->surface_configure); - decoration->surface_configure.notify = - toplevel_decoration_handle_surface_configure; - wl_signal_add(&toplevel->base->events.ack_configure, - &decoration->surface_ack_configure); - decoration->surface_ack_configure.notify = - toplevel_decoration_handle_surface_ack_configure; - wl_signal_add(&toplevel->base->surface->events.commit, - &decoration->surface_commit); - decoration->surface_commit.notify = - toplevel_decoration_handle_surface_commit; + wl_signal_add(&toplevel->events.destroy, &decoration->toplevel_destroy); + decoration->toplevel_destroy.notify = toplevel_decoration_handle_toplevel_destroy; + wl_signal_add(&toplevel->base->events.configure, &decoration->surface_configure); + decoration->surface_configure.notify = toplevel_decoration_handle_surface_configure; + wl_signal_add(&toplevel->base->events.ack_configure, &decoration->surface_ack_configure); + decoration->surface_ack_configure.notify = toplevel_decoration_handle_surface_ack_configure; wl_list_insert(&manager->decorations, &decoration->link); - if (toplevel->base->added) { - decoration->added = true; - wl_signal_emit_mutable(&manager->events.new_toplevel_decoration, - decoration); - } + wl_signal_emit_mutable(&manager->events.new_toplevel_decoration, decoration); } static const struct zxdg_decoration_manager_v1_interface diff --git a/types/wlr_xdg_foreign_v1.c b/types/wlr_xdg_foreign_v1.c index a6cb01f..12cd4fc 100644 --- a/types/wlr_xdg_foreign_v1.c +++ b/types/wlr_xdg_foreign_v1.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include @@ -40,15 +39,15 @@ static struct wlr_xdg_toplevel *verify_is_toplevel(struct wl_resource *resource, static void destroy_imported_child(struct wlr_xdg_imported_child_v1 *child) { wl_list_remove(&child->xdg_toplevel_set_parent.link); - wl_list_remove(&child->xdg_surface_destroy.link); + wl_list_remove(&child->xdg_toplevel_destroy.link); wl_list_remove(&child->link); free(child); } -static void handle_child_xdg_surface_destroy( +static void handle_child_xdg_toplevel_destroy( struct wl_listener *listener, void *data) { struct wlr_xdg_imported_child_v1 *child = - wl_container_of(listener, child, xdg_surface_destroy); + wl_container_of(listener, child, xdg_toplevel_destroy); destroy_imported_child(child); } @@ -98,7 +97,7 @@ static void xdg_imported_handle_set_parent_of(struct wl_client *client, return; } child->surface = wlr_surface_child; - child->xdg_surface_destroy.notify = handle_child_xdg_surface_destroy; + child->xdg_toplevel_destroy.notify = handle_child_xdg_toplevel_destroy; child->xdg_toplevel_set_parent.notify = handle_xdg_toplevel_set_parent; if (!wlr_xdg_toplevel_set_parent(child_toplevel, surface->toplevel)) { @@ -110,10 +109,8 @@ static void xdg_imported_handle_set_parent_of(struct wl_client *client, } wlr_xdg_toplevel_set_parent(child_toplevel, surface->toplevel); - wl_signal_add(&child_toplevel->base->events.destroy, - &child->xdg_surface_destroy); - wl_signal_add(&child_toplevel->events.set_parent, - &child->xdg_toplevel_set_parent); + wl_signal_add(&child_toplevel->events.destroy, &child->xdg_toplevel_destroy); + wl_signal_add(&child_toplevel->events.set_parent, &child->xdg_toplevel_set_parent); wl_list_insert(&imported->children, &child->link); } @@ -173,7 +170,7 @@ static void destroy_imported(struct wlr_xdg_imported_v1 *imported) { static void destroy_exported(struct wlr_xdg_exported_v1 *exported) { wlr_xdg_foreign_exported_finish(&exported->base); - wl_list_remove(&exported->xdg_surface_destroy.link); + wl_list_remove(&exported->xdg_toplevel_destroy.link); wl_list_remove(&exported->link); wl_resource_set_user_data(exported->resource, NULL); free(exported); @@ -189,10 +186,9 @@ static void xdg_exported_handle_resource_destroy( } } -static void handle_xdg_surface_destroy( - struct wl_listener *listener, void *data) { +static void handle_xdg_toplevel_destroy(struct wl_listener *listener, void *data) { struct wlr_xdg_exported_v1 *exported = - wl_container_of(listener, exported, xdg_surface_destroy); + wl_container_of(listener, exported, xdg_toplevel_destroy); destroy_exported(exported); } @@ -240,8 +236,8 @@ static void xdg_exporter_handle_export(struct wl_client *wl_client, zxdg_exported_v1_send_handle(exported->resource, exported->base.handle); - exported->xdg_surface_destroy.notify = handle_xdg_surface_destroy; - wl_signal_add(&xdg_toplevel->base->events.destroy, &exported->xdg_surface_destroy); + exported->xdg_toplevel_destroy.notify = handle_xdg_toplevel_destroy; + wl_signal_add(&xdg_toplevel->base->events.destroy, &exported->xdg_toplevel_destroy); } static const struct zxdg_exporter_v1_interface xdg_exporter_impl = { diff --git a/types/wlr_xdg_foreign_v2.c b/types/wlr_xdg_foreign_v2.c index eb0368a..4cfbe77 100644 --- a/types/wlr_xdg_foreign_v2.c +++ b/types/wlr_xdg_foreign_v2.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include @@ -45,15 +44,15 @@ static struct wlr_xdg_toplevel *verify_is_toplevel(struct wl_resource *resource, static void destroy_imported_child(struct wlr_xdg_imported_child_v2 *child) { wl_list_remove(&child->xdg_toplevel_set_parent.link); - wl_list_remove(&child->xdg_surface_destroy.link); + wl_list_remove(&child->xdg_toplevel_destroy.link); wl_list_remove(&child->link); free(child); } -static void handle_child_xdg_surface_destroy( +static void handle_child_xdg_toplevel_destroy( struct wl_listener *listener, void *data) { struct wlr_xdg_imported_child_v2 *child = - wl_container_of(listener, child, xdg_surface_destroy); + wl_container_of(listener, child, xdg_toplevel_destroy); destroy_imported_child(child); } @@ -101,7 +100,7 @@ static void xdg_imported_handle_set_parent_of(struct wl_client *client, return; } child->surface = wlr_surface_child; - child->xdg_surface_destroy.notify = handle_child_xdg_surface_destroy; + child->xdg_toplevel_destroy.notify = handle_child_xdg_toplevel_destroy; child->xdg_toplevel_set_parent.notify = handle_xdg_toplevel_set_parent; if (!wlr_xdg_toplevel_set_parent(child_toplevel, surface->toplevel)) { @@ -113,10 +112,8 @@ static void xdg_imported_handle_set_parent_of(struct wl_client *client, } wlr_xdg_toplevel_set_parent(child_toplevel, surface->toplevel); - wl_signal_add(&child_toplevel->base->events.destroy, - &child->xdg_surface_destroy); - wl_signal_add(&child_toplevel->events.set_parent, - &child->xdg_toplevel_set_parent); + wl_signal_add(&child_toplevel->events.destroy, &child->xdg_toplevel_destroy); + wl_signal_add(&child_toplevel->events.set_parent, &child->xdg_toplevel_set_parent); wl_list_insert(&imported->children, &child->link); } @@ -176,7 +173,7 @@ static void destroy_imported(struct wlr_xdg_imported_v2 *imported) { static void destroy_exported(struct wlr_xdg_exported_v2 *exported) { wlr_xdg_foreign_exported_finish(&exported->base); - wl_list_remove(&exported->xdg_surface_destroy.link); + wl_list_remove(&exported->xdg_toplevel_destroy.link); wl_list_remove(&exported->link); wl_resource_set_user_data(exported->resource, NULL); free(exported); @@ -192,10 +189,9 @@ static void xdg_exported_handle_resource_destroy( } } -static void handle_xdg_surface_destroy( - struct wl_listener *listener, void *data) { +static void handle_xdg_toplevel_destroy(struct wl_listener *listener, void *data) { struct wlr_xdg_exported_v2 *exported = - wl_container_of(listener, exported, xdg_surface_destroy); + wl_container_of(listener, exported, xdg_toplevel_destroy); destroy_exported(exported); } @@ -243,8 +239,8 @@ static void xdg_exporter_handle_export(struct wl_client *wl_client, zxdg_exported_v2_send_handle(exported->resource, exported->base.handle); - exported->xdg_surface_destroy.notify = handle_xdg_surface_destroy; - wl_signal_add(&xdg_toplevel->base->events.destroy, &exported->xdg_surface_destroy); + exported->xdg_toplevel_destroy.notify = handle_xdg_toplevel_destroy; + wl_signal_add(&xdg_toplevel->base->events.destroy, &exported->xdg_toplevel_destroy); } static const struct zxdg_exporter_v2_interface xdg_exporter_impl = { diff --git a/types/xdg_shell/wlr_xdg_popup.c b/types/xdg_shell/wlr_xdg_popup.c index e803587..082e06f 100644 --- a/types/xdg_shell/wlr_xdg_popup.c +++ b/types/xdg_shell/wlr_xdg_popup.c @@ -28,6 +28,7 @@ struct wlr_xdg_popup_configure *send_xdg_popup_configure( } struct wlr_box *geometry = &configure->geometry; + assert(geometry->width > 0 && geometry->height > 0); xdg_popup_send_configure(popup->resource, geometry->x, geometry->y, geometry->width, geometry->height); @@ -80,10 +81,11 @@ static uint32_t xdg_pointer_grab_button(struct wlr_seat_pointer_grab *grab, } static void xdg_pointer_grab_axis(struct wlr_seat_pointer_grab *grab, - uint32_t time, enum wlr_axis_orientation orientation, double value, - int32_t value_discrete, enum wlr_axis_source source) { + uint32_t time, enum wl_pointer_axis orientation, double value, + int32_t value_discrete, enum wl_pointer_axis_source source, + enum wl_pointer_axis_relative_direction relative_direction) { wlr_seat_pointer_send_axis(grab->seat, time, orientation, value, - value_discrete, source); + value_discrete, source, relative_direction); } static void xdg_pointer_grab_frame(struct wlr_seat_pointer_grab *grab) { @@ -149,9 +151,9 @@ static uint32_t xdg_touch_grab_down(struct wlr_seat_touch_grab *grab, point->touch_id, point->sx, point->sy); } -static void xdg_touch_grab_up(struct wlr_seat_touch_grab *grab, +static uint32_t xdg_touch_grab_up(struct wlr_seat_touch_grab *grab, uint32_t time, struct wlr_touch_point *point) { - wlr_seat_touch_send_up(grab->seat, time, point->touch_id); + return wlr_seat_touch_send_up(grab->seat, time, point->touch_id); } static void xdg_touch_grab_motion(struct wlr_seat_touch_grab *grab, @@ -236,20 +238,12 @@ static struct wlr_xdg_popup_grab *get_xdg_shell_popup_grab_from_seat( return xdg_grab; } -void handle_xdg_popup_committed(struct wlr_xdg_popup *popup) { +void handle_xdg_popup_client_commit(struct wlr_xdg_popup *popup) { if (!popup->parent) { - wl_resource_post_error(popup->base->resource, - XDG_SURFACE_ERROR_NOT_CONSTRUCTED, - "xdg_popup has no parent"); + wlr_surface_reject_pending(popup->base->surface, popup->base->resource, + XDG_SURFACE_ERROR_NOT_CONSTRUCTED, "xdg_popup has no parent"); return; } - - popup->current = popup->pending; - - if (popup->base->initial_commit && !popup->sent_initial_configure) { - wlr_xdg_surface_schedule_configure(popup->base); - popup->sent_initial_configure = true; - } } static const struct xdg_popup_interface xdg_popup_implementation; @@ -284,7 +278,7 @@ static void xdg_popup_handle_grab(struct wl_client *client, wlr_xdg_popup_destroy(popup); return; } - if (popup->sent_initial_configure) { + if (popup->base->surface->mapped) { wl_resource_post_error(popup->resource, XDG_POPUP_ERROR_INVALID_GRAB, "xdg_popup is already mapped"); @@ -323,8 +317,13 @@ static void xdg_popup_handle_reposition( return; } - struct wlr_xdg_positioner *positioner = - wlr_xdg_positioner_from_resource(positioner_resource); + struct wlr_xdg_positioner *positioner = wlr_xdg_positioner_from_resource(positioner_resource); + if (!wlr_xdg_positioner_is_complete(positioner)) { + wl_resource_post_error(popup->base->client->resource, + XDG_WM_BASE_ERROR_INVALID_POSITIONER, "positioner object is not complete"); + return; + } + wlr_xdg_positioner_rules_get_geometry( &positioner->rules, &popup->scheduled.geometry); popup->scheduled.rules = positioner->rules; @@ -358,6 +357,10 @@ static const struct xdg_popup_interface xdg_popup_implementation = { .reposition = xdg_popup_handle_reposition, }; +static const struct wlr_surface_synced_impl surface_synced_impl = { + .state_size = sizeof(struct wlr_xdg_popup_state), +}; + static void xdg_popup_handle_resource_destroy(struct wl_resource *resource) { struct wlr_xdg_popup *popup = wlr_xdg_popup_from_resource(resource); @@ -367,14 +370,11 @@ static void xdg_popup_handle_resource_destroy(struct wl_resource *resource) { wlr_xdg_popup_destroy(popup); } -void create_xdg_popup(struct wlr_xdg_surface *surface, - struct wlr_xdg_surface *parent, +void create_xdg_popup(struct wlr_xdg_surface *surface, struct wlr_xdg_surface *parent, struct wlr_xdg_positioner *positioner, uint32_t id) { - if (positioner->rules.size.width == 0 || - positioner->rules.anchor_rect.width == 0) { + if (!wlr_xdg_positioner_is_complete(positioner)) { wl_resource_post_error(surface->client->resource, - XDG_WM_BASE_ERROR_INVALID_POSITIONER, - "positioner object is not complete"); + XDG_WM_BASE_ERROR_INVALID_POSITIONER, "positioner object is not complete"); return; } @@ -396,14 +396,17 @@ void create_xdg_popup(struct wlr_xdg_surface *surface, } surface->popup->base = surface; + if (!wlr_surface_synced_init(&surface->popup->synced, surface->surface, + &surface_synced_impl, &surface->popup->pending, + &surface->popup->current)) { + goto error_popup; + } + surface->popup->resource = wl_resource_create( surface->client->client, &xdg_popup_interface, wl_resource_get_version(surface->resource), id); if (surface->popup->resource == NULL) { - free(surface->popup); - surface->popup = NULL; - wl_resource_post_no_memory(surface->resource); - return; + goto error_synced; } wl_resource_set_implementation(surface->popup->resource, &xdg_popup_implementation, surface->popup, @@ -415,17 +418,31 @@ void create_xdg_popup(struct wlr_xdg_surface *surface, &positioner->rules, &surface->popup->scheduled.geometry); surface->popup->scheduled.rules = positioner->rules; + wl_signal_init(&surface->popup->events.destroy); wl_signal_init(&surface->popup->events.reposition); if (parent) { surface->popup->parent = parent->surface; wl_list_insert(&parent->popups, &surface->popup->link); - wl_signal_emit_mutable(&parent->events.new_popup, surface->popup); } else { wl_list_init(&surface->popup->link); } set_xdg_surface_role_object(surface, surface->popup->resource); + + wl_signal_emit_mutable(&surface->client->shell->events.new_popup, surface->popup); + if (parent) { + wl_signal_emit_mutable(&parent->events.new_popup, surface->popup); + } + + return; + +error_synced: + wlr_surface_synced_finish(&surface->popup->synced); +error_popup: + free(surface->popup); + surface->popup = NULL; + wl_resource_post_no_memory(surface->resource); } void reset_xdg_popup(struct wlr_xdg_popup *popup) { @@ -452,22 +469,16 @@ void reset_xdg_popup(struct wlr_xdg_popup *popup) { popup->seat = NULL; } - - popup->sent_initial_configure = false; } void destroy_xdg_popup(struct wlr_xdg_popup *popup) { wlr_surface_unmap(popup->base->surface); reset_xdg_popup(popup); - // TODO: improve events - if (popup->base->added) { - wl_signal_emit_mutable(&popup->base->events.destroy, NULL); - popup->base->added = false; - } + wl_signal_emit_mutable(&popup->events.destroy, NULL); + wlr_surface_synced_finish(&popup->synced); popup->base->popup = NULL; - wl_list_remove(&popup->link); wl_resource_set_user_data(popup->resource, NULL); free(popup); diff --git a/types/xdg_shell/wlr_xdg_positioner.c b/types/xdg_shell/wlr_xdg_positioner.c index 6a991bb..380c113 100644 --- a/types/xdg_shell/wlr_xdg_positioner.c +++ b/types/xdg_shell/wlr_xdg_positioner.c @@ -203,6 +203,11 @@ static uint32_t xdg_positioner_gravity_to_wlr_edges( return xdg_positioner_anchor_to_wlr_edges((enum xdg_positioner_anchor)gravity); } +bool wlr_xdg_positioner_is_complete(struct wlr_xdg_positioner *positioner) { + struct wlr_xdg_positioner_rules *rules = &positioner->rules; + return rules->size.width > 0 && rules->anchor_rect.width > 0; +} + void wlr_xdg_positioner_rules_get_geometry( const struct wlr_xdg_positioner_rules *rules, struct wlr_box *box) { box->x = rules->offset.x; diff --git a/types/xdg_shell/wlr_xdg_shell.c b/types/xdg_shell/wlr_xdg_shell.c index 33e99e3..3baea04 100644 --- a/types/xdg_shell/wlr_xdg_shell.c +++ b/types/xdg_shell/wlr_xdg_shell.c @@ -158,6 +158,8 @@ struct wlr_xdg_shell *wlr_xdg_shell_create(struct wl_display *display, xdg_shell->global = global; wl_signal_init(&xdg_shell->events.new_surface); + wl_signal_init(&xdg_shell->events.new_toplevel); + wl_signal_init(&xdg_shell->events.new_popup); wl_signal_init(&xdg_shell->events.destroy); xdg_shell->display_destroy.notify = handle_display_destroy; diff --git a/types/xdg_shell/wlr_xdg_surface.c b/types/xdg_shell/wlr_xdg_surface.c index 28d5dba..029ab81 100644 --- a/types/xdg_shell/wlr_xdg_surface.c +++ b/types/xdg_shell/wlr_xdg_surface.c @@ -256,23 +256,42 @@ static const struct xdg_surface_interface xdg_surface_implementation = { .set_window_geometry = xdg_surface_handle_set_window_geometry, }; -static void xdg_surface_role_commit(struct wlr_surface *wlr_surface) { +static void xdg_surface_role_client_commit(struct wlr_surface *wlr_surface) { struct wlr_xdg_surface *surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface); assert(surface != NULL); - if (wlr_surface_has_buffer(wlr_surface) && !surface->configured) { - wl_resource_post_error(surface->resource, - XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER, - "xdg_surface has never been configured"); + if (wlr_surface_state_has_buffer(&wlr_surface->pending) && !surface->configured) { + wlr_surface_reject_pending(wlr_surface, surface->resource, + XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER, "xdg_surface has never been configured"); return; } if (surface->role_resource == NULL) { - wl_resource_post_error(surface->resource, - XDG_SURFACE_ERROR_NOT_CONSTRUCTED, - "xdg_surface must have a role object"); + wlr_surface_reject_pending(wlr_surface, surface->resource, + XDG_SURFACE_ERROR_NOT_CONSTRUCTED, "xdg_surface must have a role object"); + return; + } + + switch (surface->role) { + case WLR_XDG_SURFACE_ROLE_NONE: + assert(0 && "not reached"); return; + case WLR_XDG_SURFACE_ROLE_TOPLEVEL: + if (surface->toplevel != NULL) { + handle_xdg_toplevel_client_commit(surface->toplevel); + } + break; + case WLR_XDG_SURFACE_ROLE_POPUP: + if (surface->popup != NULL) { + handle_xdg_popup_client_commit(surface->popup); + } + break; } +} + +static void xdg_surface_role_commit(struct wlr_surface *wlr_surface) { + struct wlr_xdg_surface *surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface); + assert(surface != NULL); if (surface->surface->unmap_commit) { reset_xdg_surface_role_object(surface); @@ -285,34 +304,22 @@ static void xdg_surface_role_commit(struct wlr_surface *wlr_surface) { surface->initialized = true; } - surface->current = surface->pending; - switch (surface->role) { case WLR_XDG_SURFACE_ROLE_NONE: assert(0 && "not reached"); return; case WLR_XDG_SURFACE_ROLE_TOPLEVEL: - if (surface->toplevel != NULL) { - handle_xdg_toplevel_committed(surface->toplevel); - } else { + if (surface->toplevel == NULL) { return; } break; case WLR_XDG_SURFACE_ROLE_POPUP: - if (surface->popup != NULL) { - handle_xdg_popup_committed(surface->popup); - } else { + if (surface->popup == NULL) { return; } break; } - if (!surface->added) { - surface->added = true; - wl_signal_emit_mutable(&surface->client->shell->events.new_surface, - surface); - } - if (wlr_surface_has_buffer(wlr_surface)) { wlr_surface_map(wlr_surface); } @@ -328,12 +335,17 @@ static void xdg_surface_role_destroy(struct wlr_surface *wlr_surface) { destroy_xdg_surface(surface); } -static struct wlr_surface_role xdg_surface_role = { +static const struct wlr_surface_role xdg_surface_role = { .name = "xdg_surface", + .client_commit = xdg_surface_role_client_commit, .commit = xdg_surface_role_commit, .destroy = xdg_surface_role_destroy, }; +static const struct wlr_surface_synced_impl surface_synced_impl = { + .state_size = sizeof(struct wlr_xdg_surface_state), +}; + struct wlr_xdg_surface *wlr_xdg_surface_try_from_wlr_surface( struct wlr_surface *surface) { if (surface->role != &xdg_surface_role || surface->role_resource == NULL) { @@ -349,12 +361,24 @@ void create_xdg_surface(struct wlr_xdg_client *client, struct wlr_surface *wlr_s return; } + if (wlr_surface_has_buffer(wlr_surface)) { + wl_resource_post_error(client->resource, + XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER, + "xdg_surface must not have a buffer at creation"); + return; + } + struct wlr_xdg_surface *surface = calloc(1, sizeof(*surface)); if (surface == NULL) { wl_client_post_no_memory(client->client); return; } + if (!wlr_surface_synced_init(&surface->synced, wlr_surface, + &surface_synced_impl, &surface->pending, &surface->current)) { + goto error_surface; + } + surface->client = client; surface->role = WLR_XDG_SURFACE_ROLE_NONE; surface->surface = wlr_surface; @@ -362,18 +386,7 @@ void create_xdg_surface(struct wlr_xdg_client *client, struct wlr_surface *wlr_s &xdg_surface_interface, wl_resource_get_version(client->resource), id); if (surface->resource == NULL) { - free(surface); - wl_client_post_no_memory(client->client); - return; - } - - if (wlr_surface_has_buffer(surface->surface)) { - wl_resource_destroy(surface->resource); - free(surface); - wl_resource_post_error(client->resource, - XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER, - "xdg_surface must not have a buffer at creation"); - return; + goto error_synced; } wl_list_init(&surface->configure_list); @@ -392,6 +405,16 @@ void create_xdg_surface(struct wlr_xdg_client *client, struct wlr_surface *wlr_s wl_list_insert(&client->surfaces, &surface->link); wlr_surface_set_role_object(wlr_surface, surface->resource); + + wl_signal_emit_mutable(&surface->client->shell->events.new_surface, surface); + + return; + +error_synced: + wlr_surface_synced_finish(&surface->synced); +error_surface: + free(surface); + wl_client_post_no_memory(client->client); } bool set_xdg_surface_role(struct wlr_xdg_surface *surface, enum wlr_xdg_surface_role role) { @@ -466,8 +489,10 @@ void destroy_xdg_surface(struct wlr_xdg_surface *surface) { destroy_xdg_surface_role_object(surface); reset_xdg_surface(surface); - wl_list_remove(&surface->link); + wl_signal_emit_mutable(&surface->events.destroy, NULL); + wl_list_remove(&surface->link); + wlr_surface_synced_finish(&surface->synced); wl_resource_set_user_data(surface->resource, NULL); free(surface); } diff --git a/types/xdg_shell/wlr_xdg_toplevel.c b/types/xdg_shell/wlr_xdg_toplevel.c index fef9b21..1c4fa30 100644 --- a/types/xdg_shell/wlr_xdg_toplevel.c +++ b/types/xdg_shell/wlr_xdg_toplevel.c @@ -1,10 +1,10 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include "types/wlr_xdg_shell.h" +#include "util/utf8.h" void handle_xdg_toplevel_ack_configure( struct wlr_xdg_toplevel *toplevel, @@ -115,7 +115,7 @@ struct wlr_xdg_toplevel_configure *send_xdg_toplevel_configure( return configure; } -void handle_xdg_toplevel_committed(struct wlr_xdg_toplevel *toplevel) { +void handle_xdg_toplevel_client_commit(struct wlr_xdg_toplevel *toplevel) { struct wlr_xdg_toplevel_state *pending = &toplevel->pending; // 1) Negative values are prohibited @@ -124,29 +124,10 @@ void handle_xdg_toplevel_committed(struct wlr_xdg_toplevel *toplevel) { pending->max_width < 0 || pending->max_height < 0 || (pending->max_width != 0 && pending->max_width < pending->min_width) || (pending->max_height != 0 && pending->max_height < pending->min_height)) { - wl_resource_post_error(toplevel->resource, - XDG_TOPLEVEL_ERROR_INVALID_SIZE, - "client provided an invalid min or max size"); + wlr_surface_reject_pending(toplevel->base->surface, toplevel->resource, + XDG_TOPLEVEL_ERROR_INVALID_SIZE, "client provided an invalid min or max size"); return; } - - toplevel->current = toplevel->pending; - - if (toplevel->base->initial_commit) { - // On the initial commit, send a configure request to tell the client it - // is added - wlr_xdg_surface_schedule_configure(toplevel->base); - - if (toplevel->base->client->shell->version >= - XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION) { - // The first configure event must carry WM capabilities - wlr_xdg_toplevel_set_wm_capabilities(toplevel, - WLR_XDG_TOPLEVEL_WM_CAPABILITIES_WINDOW_MENU | - WLR_XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE | - WLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN | - WLR_XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE); - } - } } static const struct xdg_toplevel_interface xdg_toplevel_implementation; @@ -224,6 +205,12 @@ static void xdg_toplevel_handle_set_title(struct wl_client *client, wlr_xdg_toplevel_from_resource(resource); char *tmp; + if (!is_utf8(title)) { + // TODO: update when xdg_toplevel has a dedicated error code for this + wl_resource_post_error(resource, (uint32_t)-1, "xdg_toplevel title is not valid UTF-8"); + return; + } + tmp = strdup(title); if (tmp == NULL) { wl_resource_post_no_memory(resource); @@ -311,6 +298,7 @@ static void xdg_toplevel_handle_resize(struct wl_client *client, wlr_seat_client_from_resource(seat_resource); switch (edges) { + case XDG_TOPLEVEL_RESIZE_EDGE_NONE: case XDG_TOPLEVEL_RESIZE_EDGE_TOP: case XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM: case XDG_TOPLEVEL_RESIZE_EDGE_LEFT: @@ -455,6 +443,10 @@ static const struct xdg_toplevel_interface xdg_toplevel_implementation = { .set_minimized = xdg_toplevel_handle_set_minimized, }; +static const struct wlr_surface_synced_impl surface_synced_impl = { + .state_size = sizeof(struct wlr_xdg_toplevel_state), +}; + void create_xdg_toplevel(struct wlr_xdg_surface *surface, uint32_t id) { if (!set_xdg_surface_role(surface, WLR_XDG_SURFACE_ROLE_TOPLEVEL)) { @@ -469,6 +461,7 @@ void create_xdg_toplevel(struct wlr_xdg_surface *surface, } surface->toplevel->base = surface; + wl_signal_init(&surface->toplevel->events.destroy); wl_signal_init(&surface->toplevel->events.request_maximize); wl_signal_init(&surface->toplevel->events.request_fullscreen); wl_signal_init(&surface->toplevel->events.request_minimize); @@ -479,19 +472,42 @@ void create_xdg_toplevel(struct wlr_xdg_surface *surface, wl_signal_init(&surface->toplevel->events.set_title); wl_signal_init(&surface->toplevel->events.set_app_id); + if (!wlr_surface_synced_init(&surface->toplevel->synced, surface->surface, + &surface_synced_impl, &surface->toplevel->pending, &surface->toplevel->current)) { + goto error_toplevel; + } + surface->toplevel->resource = wl_resource_create( surface->client->client, &xdg_toplevel_interface, wl_resource_get_version(surface->resource), id); if (surface->toplevel->resource == NULL) { - free(surface->toplevel); - surface->toplevel = NULL; - wl_resource_post_no_memory(surface->resource); - return; + goto error_synced; } wl_resource_set_implementation(surface->toplevel->resource, &xdg_toplevel_implementation, surface->toplevel, NULL); set_xdg_surface_role_object(surface, surface->toplevel->resource); + + if (surface->client->shell->version >= XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION) { + // The first configure event must carry WM capabilities + surface->toplevel->scheduled.wm_capabilities = + WLR_XDG_TOPLEVEL_WM_CAPABILITIES_WINDOW_MENU | + WLR_XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE | + WLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN | + WLR_XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE; + surface->toplevel->scheduled.fields |= WLR_XDG_TOPLEVEL_CONFIGURE_WM_CAPABILITIES; + } + + wl_signal_emit_mutable(&surface->client->shell->events.new_toplevel, surface->toplevel); + + return; + +error_synced: + wlr_surface_synced_finish(&surface->toplevel->synced); +error_toplevel: + free(surface->toplevel); + surface->toplevel = NULL; + wl_resource_post_no_memory(surface->resource); } void reset_xdg_toplevel(struct wlr_xdg_toplevel *toplevel) { @@ -517,12 +533,9 @@ void destroy_xdg_toplevel(struct wlr_xdg_toplevel *toplevel) { wlr_surface_unmap(toplevel->base->surface); reset_xdg_toplevel(toplevel); - // TODO: improve events - if (toplevel->base->added) { - wl_signal_emit_mutable(&toplevel->base->events.destroy, NULL); - toplevel->base->added = false; - } + wl_signal_emit_mutable(&toplevel->events.destroy, NULL); + wlr_surface_synced_finish(&toplevel->synced); toplevel->base->toplevel = NULL; wl_resource_set_user_data(toplevel->resource, NULL); free(toplevel); @@ -534,6 +547,7 @@ void wlr_xdg_toplevel_send_close(struct wlr_xdg_toplevel *toplevel) { uint32_t wlr_xdg_toplevel_set_size(struct wlr_xdg_toplevel *toplevel, int32_t width, int32_t height) { + assert(width >= 0 && height >= 0); toplevel->scheduled.width = width; toplevel->scheduled.height = height; return wlr_xdg_surface_schedule_configure(toplevel->base); diff --git a/util/box.c b/util/box.c index bc2c60d..a615e2f 100644 --- a/util/box.c +++ b/util/box.c @@ -1,4 +1,3 @@ -#include #include #include #include @@ -14,11 +13,22 @@ void wlr_box_closest_point(const struct wlr_box *box, double x, double y, return; } + // Note: the width and height of the box are exclusive; that is, + // for a 100x100 box at (0,0), the point (99.9,99.9) is inside it + // while the point (100,100) is outside it. + // + // In order to be consistent with e.g. wlr_box_contains_point(), + // this function returns a point inside the bottom and right edges + // of the box by at least 1/65536 of a unit (pixel). 1/65536 is + // small enough to avoid a "dead zone" with high-resolution mice + // but large enough to avoid rounding to zero (due to loss of + // significant digits) in simple floating-point calculations. + // find the closest x point if (x < box->x) { *dest_x = box->x; - } else if (x >= box->x + box->width) { - *dest_x = box->x + box->width - 1; + } else if (x > box->x + box->width - 1/65536.0) { + *dest_x = box->x + box->width - 1/65536.0; } else { *dest_x = x; } @@ -26,8 +36,8 @@ void wlr_box_closest_point(const struct wlr_box *box, double x, double y, // find closest y point if (y < box->y) { *dest_y = box->y; - } else if (y >= box->y + box->height) { - *dest_y = box->y + box->height - 1; + } else if (y > box->y + box->height - 1/65536.0) { + *dest_y = box->y + box->height - 1/65536.0; } else { *dest_y = y; } diff --git a/util/log.c b/util/log.c index ae84f7e..a329b43 100644 --- a/util/log.c +++ b/util/log.c @@ -1,4 +1,3 @@ -#define _XOPEN_SOURCE 700 // for snprintf #include #include #include diff --git a/util/meson.build b/util/meson.build index 1c3dcd5..053e2c5 100644 --- a/util/meson.build +++ b/util/meson.build @@ -11,5 +11,6 @@ wlr_files += files( 'shm.c', 'time.c', 'token.c', + 'transform.c', + 'utf8.c', ) - diff --git a/util/shm.c b/util/shm.c index 6fddebb..5eb87bb 100644 --- a/util/shm.c +++ b/util/shm.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200112L #include #include #include diff --git a/util/time.c b/util/time.c index 78faac5..bc4a106 100644 --- a/util/time.c +++ b/util/time.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include diff --git a/util/token.c b/util/token.c index 09702e5..2d58d16 100644 --- a/util/token.c +++ b/util/token.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include "util/token.h" #include "wlr/util/log.h" diff --git a/types/output/transform.c b/util/transform.c similarity index 80% rename from types/output/transform.c rename to util/transform.c index 2b18d48..de3f8ef 100644 --- a/types/output/transform.c +++ b/util/transform.c @@ -1,4 +1,4 @@ -#include +#include enum wl_output_transform wlr_output_transform_invert( enum wl_output_transform tr) { @@ -23,3 +23,11 @@ enum wl_output_transform wlr_output_transform_compose( } return flipped | rotated; } + +void wlr_output_transform_coords(enum wl_output_transform tr, int *x, int *y) { + if (tr & WL_OUTPUT_TRANSFORM_90) { + int tmp = *x; + *x = *y; + *y = tmp; + } +} diff --git a/util/utf8.c b/util/utf8.c new file mode 100644 index 0000000..802fd01 --- /dev/null +++ b/util/utf8.c @@ -0,0 +1,66 @@ +#include +#include "util/utf8.h" + +static bool in_range(char x, uint8_t low, uint8_t high) { + uint8_t v = (uint8_t)x; + return low <= v && v <= high; +} + +bool is_utf8(const char *string) { + /* Returns true iff the string is 'well-formed', as defined by + * Unicode Standard 15.0.0. See Chapter 3, D92 and Table 3.7. + * + * UTF-8 strings are sequences of code points encoded in one of the + * following ways. The first byte determines the pattern. + * + * 00..7F + * C2..DF 80..BF + * E0 A0..BF 80..BF + * E1..EC 80..BF 80..BF + * ED 80..9F 80..BF + * EE..EF 80..BF 80..BF + * F0 90..BF 80..BF 80..BF + * F1..F3 80..BF 80..BF 80..BF + * F4 80..8F 80..BF 80..BF + */ + uint8_t range_table[9][8] = { + {0x00, 0x7F}, + {0xC2, 0xDF, 0x80, 0xBF}, + {0xE0, 0xE0, 0xA0, 0xBF, 0x80, 0xBF}, + {0xE1, 0xEC, 0x80, 0xBF, 0x80, 0xBF}, + {0xED, 0xED, 0x80, 0x9F, 0x80, 0xBF}, + {0xEE, 0xEF, 0x80, 0xBF, 0x80, 0xBF}, + {0xF0, 0xF0, 0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF}, + {0xF1, 0xF3, 0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF}, + {0xF4, 0xF4, 0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF}, + }; + int lengths[9] = { + 1, 2, 3, 3, 3, 3, 4, 4, 4 + }; + + while (string[0]) { + bool accept = false; + for (int i = 0; i < 9; i++) { + if (!in_range(string[0], range_table[i][0], + range_table[i][1])) { + continue; + } + for (int j = 1; j < lengths[i]; j++) { + if (!in_range(string[j], range_table[i][2 * j], + range_table[i][2 * j + 1])) { + // Early exit is necessary to avoid + // reading past the null terminator + return false; + } + } + string += lengths[i]; + accept = true; + break; + } + if (!accept) { + return false; + } + } + + return true; +} diff --git a/xcursor/wlr_xcursor.c b/xcursor/wlr_xcursor.c index 620b6c7..33729f1 100644 --- a/xcursor/wlr_xcursor.c +++ b/xcursor/wlr_xcursor.c @@ -23,7 +23,6 @@ * SOFTWARE. */ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -188,7 +187,7 @@ static void load_callback(struct xcursor_images *images, void *data) { theme->cursor_count * sizeof(theme->cursors[0])); if (cursors == NULL) { theme->cursor_count--; - free(cursor); + xcursor_destroy(cursor); } else { theme->cursors = cursors; theme->cursors[theme->cursor_count - 1] = cursor; diff --git a/xcursor/xcursor.c b/xcursor/xcursor.c index 7e1f310..b5edb9d 100644 --- a/xcursor/xcursor.c +++ b/xcursor/xcursor.c @@ -23,7 +23,8 @@ * SOFTWARE. */ -#define _DEFAULT_SOURCE +#undef _POSIX_C_SOURCE +#define _DEFAULT_SOURCE // for d_type in struct dirent #include #include #include @@ -722,38 +723,44 @@ load_all_cursors_from_dir(const char *path, int size, closedir(dir); } -/** Load all the cursor of a theme - * - * This function loads all the cursor images of a given theme and its - * inherited themes. Each cursor is loaded into an struct xcursor_images object - * which is passed to the caller's load callback. If a cursor appears - * more than once across all the inherited themes, the load callback - * will be called multiple times, with possibly different struct xcursor_images - * object which have the same name. The user is expected to destroy the - * struct xcursor_images objects passed to the callback with - * xcursor_images_destroy(). - * - * \param theme The name of theme that should be loaded - * \param size The desired size of the cursor images - * \param load_callback A callback function that will be called - * for each cursor loaded. The first parameter is the struct xcursor_images - * object representing the loaded cursor and the second is a pointer - * to data provided by the user. - * \param user_data The data that should be passed to the load callback - */ -void -xcursor_load_theme(const char *theme, int size, - void (*load_callback)(struct xcursor_images *, void *), - void *user_data) +struct xcursor_nodelist { + size_t nodelen; + const char *node; + struct xcursor_nodelist *next; +}; + +static bool +nodelist_contains(struct xcursor_nodelist *nodelist, const char *s, size_t ss) { + struct xcursor_nodelist *vi; + for (vi = nodelist; vi && vi->node; vi = vi->next) { + if (vi->nodelen == ss && !strncmp(s, vi->node, vi->nodelen)) + return true; + } + return false; +} + +static void +xcursor_load_theme_protected(const char *theme, + int size, + void (*load_callback)(struct xcursor_images *, void *), + void *user_data, + struct xcursor_nodelist *visited_nodes) { char *full, *dir; char *inherits = NULL; const char *path, *i; char *xcursor_path; + size_t si; + struct xcursor_nodelist current_node; if (!theme) theme = "default"; + current_node.next = visited_nodes; + current_node.node = theme; + current_node.nodelen = strlen(theme); + visited_nodes = ¤t_node; + xcursor_path = xcursor_library_path(); for (path = xcursor_path; path; @@ -778,9 +785,39 @@ xcursor_load_theme(const char *theme, int size, free(dir); } - for (i = inherits; i; i = xcursor_next_path(i)) - xcursor_load_theme(i, size, load_callback, user_data); + for (i = inherits; i; i = xcursor_next_path(i)) { + si = strlen(i); + if (nodelist_contains(visited_nodes, i, si)) + continue; + xcursor_load_theme_protected(i, size, load_callback, user_data, visited_nodes); + } free(inherits); free(xcursor_path); } + +/** Load all the cursor of a theme + * + * This function loads all the cursor images of a given theme and its + * inherited themes. Each cursor is loaded into an struct xcursor_images object + * which is passed to the caller's load callback. If a cursor appears + * more than once across all the inherited themes, the load callback + * will be called multiple times, with possibly different struct xcursor_images + * object which have the same name. The user is expected to destroy the + * struct xcursor_images objects passed to the callback with + * xcursor_images_destroy(). + * + * \param theme The name of theme that should be loaded + * \param size The desired size of the cursor images + * \param load_callback A callback function that will be called + * for each cursor loaded. The first parameter is the struct xcursor_images + * object representing the loaded cursor and the second is a pointer + * to data provided by the user. + * \param user_data The data that should be passed to the load callback + */ +void +xcursor_load_theme(const char *theme, int size, + void (*load_callback)(struct xcursor_images *, void *), + void *user_data) { + return xcursor_load_theme_protected(theme, size, load_callback, user_data, NULL); +} diff --git a/xwayland/selection/dnd.c b/xwayland/selection/dnd.c index 1c053e5..e9cc63d 100644 --- a/xwayland/selection/dnd.c +++ b/xwayland/selection/dnd.c @@ -49,11 +49,12 @@ static void xwm_dnd_send_event(struct wlr_xwm *xwm, xcb_atom_t type, .data = *data, }; - xcb_send_event(xwm->xcb_conn, + xwm_send_event_with_size(xwm->xcb_conn, 0, // propagate dest->window_id, XCB_EVENT_MASK_NO_EVENT, - (const char *)&event); + &event, + sizeof(event)); xcb_flush(xwm->xcb_conn); } diff --git a/xwayland/selection/incoming.c b/xwayland/selection/incoming.c index c2e1e71..4a0b450 100644 --- a/xwayland/selection/incoming.c +++ b/xwayland/selection/incoming.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include diff --git a/xwayland/selection/outgoing.c b/xwayland/selection/outgoing.c index d2d0932..3f2e2c7 100644 --- a/xwayland/selection/outgoing.c +++ b/xwayland/selection/outgoing.c @@ -27,11 +27,12 @@ static void xwm_selection_send_notify(struct wlr_xwm *xwm, " requestor=%" PRIu32 " selection=%" PRIu32 " target=%" PRIu32 " property=%" PRIu32, req->requestor, req->time, req->requestor, req->selection, req->target, selection_notify.property); - xcb_send_event(xwm->xcb_conn, + xwm_send_event_with_size(xwm->xcb_conn, 0, // propagate req->requestor, XCB_EVENT_MASK_NO_EVENT, - (const char *)&selection_notify); + &selection_notify, + sizeof(selection_notify)); xcb_flush(xwm->xcb_conn); } diff --git a/xwayland/selection/selection.c b/xwayland/selection/selection.c index 0079df2..4d1e366 100644 --- a/xwayland/selection/selection.c +++ b/xwayland/selection/selection.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include diff --git a/xwayland/server.c b/xwayland/server.c index fde3d61..4f92879 100644 --- a/xwayland/server.c +++ b/xwayland/server.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -358,6 +357,8 @@ static bool server_start(struct wlr_xwayland_server *server) { } if (!set_cloexec(notify_fd[0], true)) { wlr_log(WLR_ERROR, "Failed to set CLOEXEC on FD"); + close(notify_fd[0]); + close(notify_fd[1]); server_finish_process(server); return false; } diff --git a/xwayland/sockets.c b/xwayland/sockets.c index 873fde8..9e287f3 100644 --- a/xwayland/sockets.c +++ b/xwayland/sockets.c @@ -1,4 +1,5 @@ -#define _XOPEN_SOURCE 700 +#undef _POSIX_C_SOURCE +#define _XOPEN_SOURCE 700 // for S_ISVTX #include #include #include diff --git a/xwayland/xwayland.c b/xwayland/xwayland.c index 0ffc1fe..c4ca9ae 100644 --- a/xwayland/xwayland.c +++ b/xwayland/xwayland.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include diff --git a/xwayland/xwm.c b/xwayland/xwm.c index 7f19871..c65851b 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -1,6 +1,3 @@ -#ifndef _POSIX_C_SOURCE -#define _POSIX_C_SOURCE 200809L -#endif #include #include #include @@ -74,6 +71,7 @@ static const char *const atom_map[ATOM_LAST] = { [NET_WM_WINDOW_TYPE_MENU] = "_NET_WM_WINDOW_TYPE_MENU", [NET_WM_WINDOW_TYPE_NOTIFICATION] = "_NET_WM_WINDOW_TYPE_NOTIFICATION", [NET_WM_WINDOW_TYPE_SPLASH] = "_NET_WM_WINDOW_TYPE_SPLASH", + [NET_WM_WINDOW_TYPE_DESKTOP] = "_NET_WM_WINDOW_TYPE_DESKTOP", [DND_SELECTION] = "XdndSelection", [DND_AWARE] = "XdndAware", [DND_STATUS] = "XdndStatus", @@ -173,7 +171,7 @@ static struct wlr_xwayland_surface *xwayland_surface_create( xcb_get_geometry_cookie_t geometry_cookie = xcb_get_geometry(xwm->xcb_conn, window_id); - xcb_res_query_client_ids_cookie_t client_id_cookie; + xcb_res_query_client_ids_cookie_t client_id_cookie = { 0 }; if (xwm->xres) { xcb_res_client_id_spec_t spec = { .client = window_id, @@ -220,8 +218,9 @@ static struct wlr_xwayland_surface *xwayland_surface_create( wl_signal_init(&surface->events.set_decorations); wl_signal_init(&surface->events.set_strut_partial); wl_signal_init(&surface->events.set_override_redirect); - wl_signal_init(&surface->events.ping_timeout); wl_signal_init(&surface->events.set_geometry); + wl_signal_init(&surface->events.map_request); + wl_signal_init(&surface->events.ping_timeout); xcb_get_geometry_reply_t *geometry_reply = xcb_get_geometry_reply(xwm->xcb_conn, geometry_cookie, NULL); @@ -258,6 +257,25 @@ static void xwm_set_net_active_window(struct wlr_xwm *xwm, xwm->atoms[WINDOW], 32, 1, &window); } +/* + * Wrapper for xcb_send_event, which ensures that the event data is 32 byte big. + */ +xcb_void_cookie_t xwm_send_event_with_size(xcb_connection_t *c, + uint8_t propagate, xcb_window_t destination, + uint32_t event_mask, const void *event, uint32_t length) +{ + if (length == 32) { + return xcb_send_event(c, propagate, destination, event_mask, event); + } else if (length < 32) { + char buf[32]; + memcpy(buf, event, length); + memset(buf + length, 0, 32 - length); + return xcb_send_event(c, propagate, destination, event_mask, buf); + } else { + assert(false && "Event too long"); + } +} + static void xwm_send_wm_message(struct wlr_xwayland_surface *surface, xcb_client_message_data_t *data, uint32_t event_mask) { struct wlr_xwm *xwm = surface->xwm; @@ -271,11 +289,12 @@ static void xwm_send_wm_message(struct wlr_xwayland_surface *surface, .data = *data, }; - xcb_send_event(xwm->xcb_conn, + xwm_send_event_with_size(xwm->xcb_conn, 0, // propagate surface->window_id, event_mask, - (const char *)&event); + &event, + sizeof(event)); xcb_flush(xwm->xcb_conn); } @@ -292,15 +311,18 @@ static void xwm_set_net_client_list(struct wlr_xwm *xwm) { } } - xcb_window_t *windows = malloc(sizeof(xcb_window_t) * mapped_surfaces); - if (!windows) { - return; - } + xcb_window_t *windows = NULL; + if (mapped_surfaces > 0) { + windows = malloc(sizeof(*windows) * mapped_surfaces); + if (!windows) { + return; + } - size_t index = 0; - wl_list_for_each(surface, &xwm->surfaces, link) { - if (surface->surface != NULL && surface->surface->mapped) { - windows[index++] = surface->window_id; + size_t index = 0; + wl_list_for_each(surface, &xwm->surfaces, link) { + if (surface->surface != NULL && surface->surface->mapped) { + windows[index++] = surface->window_id; + } } } @@ -452,6 +474,10 @@ static void xwayland_surface_dissociate(struct wlr_xwayland_surface *xsurface) { wl_list_init(&xsurface->unpaired_link); xsurface->surface_id = 0; xsurface->serial = 0; + + wl_list_remove(&xsurface->stack_link); + wl_list_init(&xsurface->stack_link); + xwm_set_net_client_list_stacking(xsurface->xwm); } static void xwayland_surface_destroy(struct wlr_xwayland_surface *xsurface) { @@ -464,7 +490,6 @@ static void xwayland_surface_destroy(struct wlr_xwayland_surface *xsurface) { } wl_list_remove(&xsurface->link); - wl_list_remove(&xsurface->stack_link); wl_list_remove(&xsurface->parent_link); struct wlr_xwayland_surface *child, *next; @@ -999,6 +1024,24 @@ static void xwm_handle_configure_request(struct wlr_xwm *xwm, wl_signal_emit_mutable(&surface->events.request_configure, &wlr_event); } +static void xwm_update_override_redirect(struct wlr_xwayland_surface *xsurface, + bool override_redirect) { + if (xsurface->override_redirect == override_redirect) { + return; + } + xsurface->override_redirect = override_redirect; + + if (override_redirect) { + wl_list_remove(&xsurface->stack_link); + wl_list_init(&xsurface->stack_link); + xwm_set_net_client_list_stacking(xsurface->xwm); + } else if (xsurface->surface != NULL && xsurface->surface->mapped) { + wlr_xwayland_surface_restack(xsurface, NULL, XCB_STACK_MODE_BELOW); + } + + wl_signal_emit_mutable(&xsurface->events.set_override_redirect, NULL); +} + static void xwm_handle_configure_notify(struct wlr_xwm *xwm, xcb_configure_notify_event_t *ev) { struct wlr_xwayland_surface *xsurface = lookup_surface(xwm, ev->window); @@ -1017,10 +1060,7 @@ static void xwm_handle_configure_notify(struct wlr_xwm *xwm, xsurface->height = ev->height; } - if (xsurface->override_redirect != ev->override_redirect) { - xsurface->override_redirect = ev->override_redirect; - wl_signal_emit_mutable(&xsurface->events.set_override_redirect, NULL); - } + xwm_update_override_redirect(xsurface, ev->override_redirect); if (geometry_changed) { wl_signal_emit_mutable(&xsurface->events.set_geometry, NULL); @@ -1055,6 +1095,18 @@ void wlr_xwayland_surface_restack(struct wlr_xwayland_surface *xsurface, size_t idx = 0; uint32_t flags = XCB_CONFIG_WINDOW_STACK_MODE; + assert(!xsurface->override_redirect); + + // X11 clients expect their override_redirect windows to stay on top. + // Avoid interfering by restacking above the topmost managed surface. + if (mode == XCB_STACK_MODE_ABOVE && !sibling) { + sibling = wl_container_of(xwm->surfaces_in_stack_order.prev, sibling, stack_link); + } + + if (sibling == xsurface) { + return; + } + if (sibling != NULL) { values[idx++] = sibling->window_id; flags |= XCB_CONFIG_WINDOW_SIBLING; @@ -1067,11 +1119,7 @@ void wlr_xwayland_surface_restack(struct wlr_xwayland_surface *xsurface, struct wl_list *node; if (mode == XCB_STACK_MODE_ABOVE) { - if (sibling) { - node = &sibling->stack_link; - } else { - node = xwm->surfaces_in_stack_order.prev; - } + node = &sibling->stack_link; } else if (mode == XCB_STACK_MODE_BELOW) { if (sibling) { node = sibling->stack_link.prev; @@ -1096,8 +1144,7 @@ static void xwm_handle_map_request(struct wlr_xwm *xwm, return; } - wlr_xwayland_surface_set_withdrawn(xsurface, false); - wlr_xwayland_surface_restack(xsurface, NULL, XCB_STACK_MODE_BELOW); + wl_signal_emit_mutable(&xsurface->events.map_request, NULL); xcb_map_window(xwm->xcb_conn, ev->window); } @@ -1108,9 +1155,11 @@ static void xwm_handle_map_notify(struct wlr_xwm *xwm, return; } - if (xsurface->override_redirect != ev->override_redirect) { - xsurface->override_redirect = ev->override_redirect; - wl_signal_emit_mutable(&xsurface->events.set_override_redirect, NULL); + xwm_update_override_redirect(xsurface, ev->override_redirect); + + if (!xsurface->override_redirect) { + wlr_xwayland_surface_set_withdrawn(xsurface, false); + wlr_xwayland_surface_restack(xsurface, NULL, XCB_STACK_MODE_BELOW); } } @@ -1345,29 +1394,14 @@ static void xwm_handle_net_wm_state_message(struct wlr_xwm *xwm, // all other values are set to 0 if (fullscreen != xsurface->fullscreen) { - if (xsurface->fullscreen) { - xsurface->saved_width = xsurface->width; - xsurface->saved_height = xsurface->height; - } - wl_signal_emit_mutable(&xsurface->events.request_fullscreen, NULL); } if (maximized != xsurface_is_maximized(xsurface)) { - if (xsurface_is_maximized(xsurface)) { - xsurface->saved_width = xsurface->width; - xsurface->saved_height = xsurface->height; - } - wl_signal_emit_mutable(&xsurface->events.request_maximize, NULL); } if (minimized != xsurface->minimized) { - if (xsurface->minimized) { - xsurface->saved_width = xsurface->width; - xsurface->saved_height = xsurface->height; - } - struct wlr_xwayland_minimize_event minimize_event = { .surface = xsurface, .minimize = xsurface->minimized, @@ -1795,9 +1829,10 @@ void wlr_xwayland_surface_configure(struct wlr_xwayland_surface *xsurface, .height = height, }; - xcb_send_event(xwm->xcb_conn, 0, xsurface->window_id, + xwm_send_event_with_size(xwm->xcb_conn, 0, xsurface->window_id, XCB_EVENT_MASK_STRUCTURE_NOTIFY, - (const char *)&configure_notify); + &configure_notify, + sizeof(configure_notify)); } xcb_flush(xwm->xcb_conn); @@ -2297,6 +2332,7 @@ bool wlr_xwayland_or_surface_wants_focus( NET_WM_WINDOW_TYPE_NOTIFICATION, NET_WM_WINDOW_TYPE_POPUP_MENU, NET_WM_WINDOW_TYPE_SPLASH, + NET_WM_WINDOW_TYPE_DESKTOP, NET_WM_WINDOW_TYPE_TOOLTIP, NET_WM_WINDOW_TYPE_UTILITY, }; @@ -2350,3 +2386,8 @@ void wlr_xwayland_set_workareas(struct wlr_xwayland *wlr_xwayland, XCB_ATOM_CARDINAL, 32, 4 * num_workareas, data); free(data); } + +xcb_connection_t *wlr_xwayland_get_xwm_connection( + struct wlr_xwayland *wlr_xwayland) { + return wlr_xwayland->xwm ? wlr_xwayland->xwm->xcb_conn : NULL; +}