diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e02463a9c..3b02ef16f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -include: https://git.sr.ht/~emersion/dalligi/blob/master/templates/multi.yml +include: https://gitlab.freedesktop.org/emersion/dalligi/-/raw/master/templates/multi.yml alpine: extends: .dalligi pages: true diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c index 41773d4f5..faa535d44 100644 --- a/backend/drm/atomic.c +++ b/backend/drm/atomic.c @@ -423,12 +423,6 @@ void drm_atomic_connector_apply_commit(struct wlr_drm_connector_state *state) { if (state->primary_in_fence_fd >= 0) { close(state->primary_in_fence_fd); } - if (state->out_fence_fd >= 0) { - // TODO: error handling - wlr_drm_syncobj_timeline_import_sync_file(state->base->signal_timeline, - state->base->signal_point, state->out_fence_fd); - close(state->out_fence_fd); - } conn->colorspace = state->colorspace; } @@ -446,9 +440,6 @@ void drm_atomic_connector_rollback_commit(struct wlr_drm_connector_state *state) if (state->primary_in_fence_fd >= 0) { close(state->primary_in_fence_fd); } - if (state->out_fence_fd >= 0) { - close(state->out_fence_fd); - } } static void plane_disable(struct atomic *atom, struct wlr_drm_plane *plane) { @@ -500,19 +491,6 @@ static void set_plane_in_fence_fd(struct atomic *atom, atomic_add(atom, plane->id, plane->props.in_fence_fd, sync_file_fd); } -static void set_crtc_out_fence_ptr(struct atomic *atom, struct wlr_drm_crtc *crtc, - int *fd_ptr) { - if (!crtc->props.out_fence_ptr) { - wlr_log(WLR_ERROR, - "CRTC %"PRIu32" is missing the OUT_FENCE_PTR property", - crtc->id); - atom->failed = true; - return; - } - - atomic_add(atom, crtc->id, crtc->props.out_fence_ptr, (uintptr_t)fd_ptr); -} - static void atomic_connector_add(struct atomic *atom, struct wlr_drm_connector_state *state, bool modeset) { struct wlr_drm_connector *conn = state->connector; @@ -557,9 +535,6 @@ static void atomic_connector_add(struct atomic *atom, if (state->primary_in_fence_fd >= 0) { set_plane_in_fence_fd(atom, crtc->primary, state->primary_in_fence_fd); } - if (state->base->committed & WLR_OUTPUT_STATE_SIGNAL_TIMELINE) { - set_crtc_out_fence_ptr(atom, crtc, &state->out_fence_fd); - } if (crtc->cursor) { if (drm_connector_is_cursor_visible(conn)) { struct wlr_fbox cursor_src = { diff --git a/backend/drm/drm.c b/backend/drm/drm.c index fe9dcc341..bd29872fb 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -367,7 +367,17 @@ static void drm_plane_finish_surface(struct wlr_drm_plane *plane) { } drm_fb_clear(&plane->queued_fb); + if (plane->queued_release_timeline != NULL) { + wlr_drm_syncobj_timeline_signal(plane->queued_release_timeline, plane->queued_release_point); + wlr_drm_syncobj_timeline_unref(plane->queued_release_timeline); + plane->queued_release_timeline = NULL; + } drm_fb_clear(&plane->current_fb); + if (plane->current_release_timeline != NULL) { + wlr_drm_syncobj_timeline_signal(plane->current_release_timeline, plane->current_release_point); + wlr_drm_syncobj_timeline_unref(plane->current_release_timeline); + plane->current_release_timeline = NULL; + } finish_drm_surface(&plane->mgpu_surf); } @@ -556,6 +566,18 @@ static void drm_connector_apply_commit(const struct wlr_drm_connector_state *sta struct wlr_drm_crtc *crtc = conn->crtc; drm_fb_copy(&crtc->primary->queued_fb, state->primary_fb); + if (crtc->primary->queued_release_timeline != NULL) { + wlr_drm_syncobj_timeline_signal(crtc->primary->queued_release_timeline, crtc->primary->queued_release_point); + wlr_drm_syncobj_timeline_unref(crtc->primary->queued_release_timeline); + } + if (state->base->signal_timeline != NULL) { + crtc->primary->queued_release_timeline = wlr_drm_syncobj_timeline_ref(state->base->signal_timeline); + crtc->primary->queued_release_point = state->base->signal_point; + } else { + crtc->primary->queued_release_timeline = NULL; + crtc->primary->queued_release_point = 0; + } + crtc->primary->viewport = state->primary_viewport; if (crtc->cursor != NULL) { drm_fb_copy(&crtc->cursor->queued_fb, state->cursor_fb); @@ -646,7 +668,6 @@ static void drm_connector_state_init(struct wlr_drm_connector_state *state, .base = base, .active = output_pending_enabled(&conn->output, base), .primary_in_fence_fd = -1, - .out_fence_fd = -1, }; struct wlr_output_mode *mode = conn->output.current_mode; @@ -2018,6 +2039,14 @@ static void handle_page_flip(int fd, unsigned seq, struct wlr_drm_plane *plane = conn->crtc->primary; if (plane->queued_fb) { drm_fb_move(&plane->current_fb, &plane->queued_fb); + if (plane->current_release_timeline != NULL) { + wlr_drm_syncobj_timeline_signal(plane->current_release_timeline, plane->current_release_point); + wlr_drm_syncobj_timeline_unref(plane->current_release_timeline); + } + plane->current_release_timeline = plane->queued_release_timeline; + plane->current_release_point = plane->queued_release_point; + plane->queued_release_timeline = NULL; + plane->queued_release_point = 0; } if (conn->crtc->cursor && conn->crtc->cursor->queued_fb) { drm_fb_move(&conn->crtc->cursor->current_fb, diff --git a/backend/drm/libliftoff.c b/backend/drm/libliftoff.c index 12761afd4..333beacad 100644 --- a/backend/drm/libliftoff.c +++ b/backend/drm/libliftoff.c @@ -352,10 +352,6 @@ static bool add_connector(drmModeAtomicReq *req, liftoff_layer_set_property(crtc->liftoff_composition_layer, "IN_FENCE_FD", state->primary_in_fence_fd); } - if (state->base->committed & WLR_OUTPUT_STATE_SIGNAL_TIMELINE) { - ok = ok && add_prop(req, crtc->id, crtc->props.out_fence_ptr, - (uintptr_t)&state->out_fence_fd); - } if (state->base->committed & WLR_OUTPUT_STATE_LAYERS) { for (size_t i = 0; i < state->base->layers_len; i++) { diff --git a/backend/x11/backend.c b/backend/x11/backend.c index 0983bad0a..4dc4bf16d 100644 --- a/backend/x11/backend.c +++ b/backend/x11/backend.c @@ -115,6 +115,7 @@ static void handle_x11_event(struct wlr_x11_backend *x11, handle_x11_error(x11, ev); break; } + case XCB_DESTROY_NOTIFY: case XCB_UNMAP_NOTIFY: case XCB_MAP_NOTIFY: break; diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h index af4231f54..a8c5e077a 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -29,8 +29,12 @@ struct wlr_drm_plane { /* Buffer submitted to the kernel, will be presented on next vblank */ struct wlr_drm_fb *queued_fb; + struct wlr_drm_syncobj_timeline *queued_release_timeline; + uint64_t queued_release_point; /* Buffer currently displayed on screen */ struct wlr_drm_fb *current_fb; + struct wlr_drm_syncobj_timeline *current_release_timeline; + uint64_t current_release_point; /* Viewport belonging to the last committed fb */ struct wlr_drm_viewport viewport; @@ -156,7 +160,7 @@ struct wlr_drm_connector_state { uint32_t mode_id; uint32_t gamma_lut; uint32_t fb_damage_clips; - int primary_in_fence_fd, out_fence_fd; + int primary_in_fence_fd; bool vrr_enabled; uint32_t colorspace; uint32_t hdr_output_metadata; diff --git a/include/render/drm_syncobj_merger.h b/include/render/drm_syncobj_merger.h new file mode 100644 index 000000000..c55c87314 --- /dev/null +++ b/include/render/drm_syncobj_merger.h @@ -0,0 +1,44 @@ +#ifndef WLR_RENDER_DRM_SYNCOBJ_MERGER_H +#define WLR_RENDER_DRM_SYNCOBJ_MERGER_H + +#include + +/** + * Accumulate timeline points, to have a destination timeline point be + * signalled when all inputs are + */ +struct wlr_drm_syncobj_merger { + int n_ref; + struct wlr_drm_syncobj_timeline *dst_timeline; + uint64_t dst_point; + int sync_fd; +}; + +/** + * Create a new merger. + * + * The given timeline point will be signalled when all input points are + * signalled and the merger is destroyed. + */ +struct wlr_drm_syncobj_merger *wlr_drm_syncobj_merger_create( + struct wlr_drm_syncobj_timeline *dst_timeline, uint64_t dst_point); + +struct wlr_drm_syncobj_merger *wlr_drm_syncobj_merger_ref( + struct wlr_drm_syncobj_merger *merger); + +/** + * Target timeline point is materialized when all inputs are, and the merger is + * destroyed. + */ +void wlr_drm_syncobj_merger_unref(struct wlr_drm_syncobj_merger *merger); +/** + * Add a new timeline point to wait for. + * + * If the point is not materialized, the supplied event loop is used to schedule + * a wait. + */ +bool wlr_drm_syncobj_merger_add(struct wlr_drm_syncobj_merger *merger, + struct wlr_drm_syncobj_timeline *dst_timeline, uint64_t dst_point, + struct wl_event_loop *loop); + +#endif diff --git a/include/wlr/render/drm_syncobj.h b/include/wlr/render/drm_syncobj.h index deef72dc9..fabe23d1c 100644 --- a/include/wlr/render/drm_syncobj.h +++ b/include/wlr/render/drm_syncobj.h @@ -90,6 +90,10 @@ bool wlr_drm_syncobj_timeline_transfer(struct wlr_drm_syncobj_timeline *dst, */ bool wlr_drm_syncobj_timeline_check(struct wlr_drm_syncobj_timeline *timeline, uint64_t point, uint32_t flags, bool *result); +/** + * Signals a timeline point. + */ +bool wlr_drm_syncobj_timeline_signal(struct wlr_drm_syncobj_timeline *timeline, uint64_t point); /** * Asynchronously wait for a timeline point. * diff --git a/include/wlr/types/wlr_linux_drm_syncobj_v1.h b/include/wlr/types/wlr_linux_drm_syncobj_v1.h index 733350412..7fd55ec2e 100644 --- a/include/wlr/types/wlr_linux_drm_syncobj_v1.h +++ b/include/wlr/types/wlr_linux_drm_syncobj_v1.h @@ -19,8 +19,12 @@ 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 { + bool committed; + struct wlr_drm_syncobj_timeline *release_timeline; + uint64_t release_point; + struct wlr_drm_syncobj_merger *release_merger; + } WLR_PRIVATE; }; struct wlr_linux_drm_syncobj_manager_v1 { @@ -55,4 +59,19 @@ struct wlr_linux_drm_syncobj_surface_v1_state *wlr_linux_drm_syncobj_v1_get_surf bool wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer( struct wlr_linux_drm_syncobj_surface_v1_state *state, struct wlr_buffer *buffer); +/** + * Register a release point for buffer usage. + * + * This function may be called multiple times for the same commit. The client's + * release point will be signalled when all registered points are signalled, and + * a new buffer has been committed. + * + * Because the given release point may not be materialized, a wl_event_loop must + * be supplied to schedule a wait internally, if needed + */ +bool wlr_linux_drm_syncobj_v1_state_add_release_point( + struct wlr_linux_drm_syncobj_surface_v1_state *state, + struct wlr_drm_syncobj_timeline *release_timeline, uint64_t release_point, + struct wl_event_loop *event_loop); + #endif diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 46635f4bf..6c627b1f9 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -131,8 +131,6 @@ struct wlr_scene_surface { struct wlr_addon addon; struct wl_listener outputs_update; - struct wl_listener output_enter; - struct wl_listener output_leave; struct wl_listener output_sample; struct wl_listener frame_done; struct wl_listener surface_destroy; @@ -155,6 +153,8 @@ struct wlr_scene_outputs_update_event { struct wlr_scene_output_sample_event { struct wlr_scene_output *output; bool direct_scanout; + struct wlr_drm_syncobj_timeline *release_timeline; + uint64_t release_point; }; struct wlr_scene_frame_done_event { @@ -268,6 +268,8 @@ struct wlr_scene_output { struct wlr_drm_syncobj_timeline *in_timeline; uint64_t in_point; + struct wlr_drm_syncobj_timeline *out_timeline; + uint64_t out_point; } WLR_PRIVATE; }; diff --git a/include/wlr/types/wlr_virtual_keyboard_v1.h b/include/wlr/types/wlr_virtual_keyboard_v1.h index babc535a5..dda1530b1 100644 --- a/include/wlr/types/wlr_virtual_keyboard_v1.h +++ b/include/wlr/types/wlr_virtual_keyboard_v1.h @@ -33,12 +33,27 @@ struct wlr_virtual_keyboard_v1 { bool has_keymap; struct wl_list link; // wlr_virtual_keyboard_manager_v1.virtual_keyboards + + struct { + struct wl_listener seat_destroy; + } WLR_PRIVATE; }; struct wlr_virtual_keyboard_manager_v1* wlr_virtual_keyboard_manager_v1_create( struct wl_display *display); +/** + * Get the struct wlr_virtual_keyboard_v1 corresponding to a zwp_virtual_keyboard_v1 resource. + * + * Asserts that the resource is a valid zwp_virtual_keyboard_v1 resource created by wlroots. + * + * Returns NULL if the resource is inert. + */ +struct wlr_virtual_keyboard_v1 *wlr_virtual_keyboard_v1_from_resource( + struct wl_resource *resource); + struct wlr_virtual_keyboard_v1 *wlr_input_device_get_virtual_keyboard( struct wlr_input_device *wlr_dev); + #endif diff --git a/meson.build b/meson.build index d9e215afd..9d6b21222 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'wlroots', 'c', - version: '0.20.0-rc4', + version: '0.20.0', license: 'MIT', meson_version: '>=1.3', default_options: [ diff --git a/protocol/wlr-export-dmabuf-unstable-v1.xml b/protocol/wlr-export-dmabuf-unstable-v1.xml index 80ea012f5..d41efbb1d 100644 --- a/protocol/wlr-export-dmabuf-unstable-v1.xml +++ b/protocol/wlr-export-dmabuf-unstable-v1.xml @@ -192,7 +192,7 @@ - Unreferences the frame. This request must be called as soon as its no + Unreferences the frame. This request must be called as soon as it's no longer used. It can be called at any time by the client. The client will still have diff --git a/render/drm_syncobj.c b/render/drm_syncobj.c index e1a407a1e..2abe2cff6 100644 --- a/render/drm_syncobj.c +++ b/render/drm_syncobj.c @@ -177,6 +177,14 @@ bool wlr_drm_syncobj_timeline_check(struct wlr_drm_syncobj_timeline *timeline, return true; } +bool wlr_drm_syncobj_timeline_signal(struct wlr_drm_syncobj_timeline *timeline, uint64_t point) { + if (drmSyncobjTimelineSignal(timeline->drm_fd, &timeline->handle, &point, 1) != 0) { + wlr_log(WLR_ERROR, "drmSyncobjTimelineSignal() failed"); + return false; + } + return true; +} + static int handle_eventfd_ready(int ev_fd, uint32_t mask, void *data) { struct wlr_drm_syncobj_timeline_waiter *waiter = data; diff --git a/render/drm_syncobj_merger.c b/render/drm_syncobj_merger.c new file mode 100644 index 000000000..d50d28c28 --- /dev/null +++ b/render/drm_syncobj_merger.c @@ -0,0 +1,133 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "render/drm_syncobj_merger.h" + +#include "config.h" + +#if HAVE_LINUX_SYNC_FILE + +#include +#include + +static int sync_file_merge(int fd1, int fd2) { + // The kernel will automatically prune signalled fences + struct sync_merge_data merge_data = { .fd2 = fd2 }; + if (ioctl(fd1, SYNC_IOC_MERGE, &merge_data) < 0) { + wlr_log_errno(WLR_ERROR, "ioctl(SYNC_IOC_MERGE) failed"); + return -1; + } + + return merge_data.fence; +} + +#else + +static int sync_file_merge(int fd1, int fd2) { + wlr_log(WLR_ERROR, "sync_file support is unavailable"); + return -1; +} + +#endif + +struct wlr_drm_syncobj_merger *wlr_drm_syncobj_merger_create( + struct wlr_drm_syncobj_timeline *dst_timeline, uint64_t dst_point) { + struct wlr_drm_syncobj_merger *merger = calloc(1, sizeof(*merger)); + if (merger == NULL) { + return NULL; + } + merger->n_ref = 1; + merger->dst_timeline = wlr_drm_syncobj_timeline_ref(dst_timeline); + merger->dst_point = dst_point; + merger->sync_fd = -1; + return merger; +} + +struct wlr_drm_syncobj_merger *wlr_drm_syncobj_merger_ref( + struct wlr_drm_syncobj_merger *merger) { + assert(merger->n_ref > 0); + merger->n_ref++; + return merger; +} + +void wlr_drm_syncobj_merger_unref(struct wlr_drm_syncobj_merger *merger) { + if (merger == NULL) { + return; + } + assert(merger->n_ref > 0); + merger->n_ref--; + if (merger->n_ref > 0) { + return; + } + + if (merger->sync_fd != -1) { + wlr_drm_syncobj_timeline_import_sync_file(merger->dst_timeline, + merger->dst_point, merger->sync_fd); + close(merger->sync_fd); + } else { + wlr_drm_syncobj_timeline_signal(merger->dst_timeline, merger->dst_point); + } + wlr_drm_syncobj_timeline_unref(merger->dst_timeline); + free(merger); +} + +static bool merger_add_exportable(struct wlr_drm_syncobj_merger *merger, + struct wlr_drm_syncobj_timeline *src_timeline, uint64_t src_point) { + int new_sync = wlr_drm_syncobj_timeline_export_sync_file(src_timeline, src_point); + if (merger->sync_fd != -1) { + int fd2 = new_sync; + new_sync = sync_file_merge(merger->sync_fd, fd2); + close(fd2); + close(merger->sync_fd); + } + merger->sync_fd = new_sync; + return true; +} + +struct export_waiter { + struct wlr_drm_syncobj_timeline_waiter waiter; + struct wlr_drm_syncobj_merger *merger; + struct wlr_drm_syncobj_timeline *src_timeline; + uint64_t src_point; +}; + +static void export_waiter_handle_ready(struct wlr_drm_syncobj_timeline_waiter *waiter) { + struct export_waiter *add = wl_container_of(waiter, add, waiter); + merger_add_exportable(add->merger, add->src_timeline, add->src_point); + wlr_drm_syncobj_merger_unref(add->merger); + wlr_drm_syncobj_timeline_unref(add->src_timeline); + wlr_drm_syncobj_timeline_waiter_finish(&add->waiter); + free(add); +} + +bool wlr_drm_syncobj_merger_add(struct wlr_drm_syncobj_merger *merger, + struct wlr_drm_syncobj_timeline *src_timeline, uint64_t src_point, + struct wl_event_loop *loop) { + assert(loop != NULL); + bool exportable = false; + int flags = DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE; + if (!wlr_drm_syncobj_timeline_check(src_timeline, src_point, flags, &exportable)) { + return false; + } + if (exportable) { + return merger_add_exportable(merger, src_timeline, src_point); + } + struct export_waiter *add = calloc(1, sizeof(*add)); + if (add == NULL) { + return false; + } + if (!wlr_drm_syncobj_timeline_waiter_init(&add->waiter, src_timeline, src_point, + flags, loop, export_waiter_handle_ready)) { + return false; + } + add->merger = merger; + add->src_timeline = wlr_drm_syncobj_timeline_ref(src_timeline); + add->src_point = src_point; + merger->n_ref++; + return true; +} diff --git a/render/meson.build b/render/meson.build index aaaf2ec48..517d76bf0 100644 --- a/render/meson.build +++ b/render/meson.build @@ -9,6 +9,7 @@ wlr_files += files( 'color.c', 'dmabuf.c', 'drm_format_set.c', + 'drm_syncobj_merger.c', 'drm_syncobj.c', 'pass.c', 'pixel_format.c', @@ -28,6 +29,7 @@ else endif internal_config.set10('HAVE_EVENTFD', cc.has_header('sys/eventfd.h')) +internal_config.set10('HAVE_LINUX_SYNC_FILE', cc.has_header('linux/sync_file.h')) if 'gles2' in renderers or 'auto' in renderers egl = dependency('egl', required: 'gles2' in renderers) diff --git a/render/vulkan/util.c b/render/vulkan/util.c index 8c31dc797..73a14073a 100644 --- a/render/vulkan/util.c +++ b/render/vulkan/util.c @@ -44,7 +44,7 @@ const char *vulkan_strerror(VkResult err) { ERR_STR(ERROR_INVALID_EXTERNAL_HANDLE); ERR_STR(ERROR_FRAGMENTATION); ERR_STR(ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS); - ERR_STR(PIPELINE_COMPILE_REQUIRED); + ERR_STR(PIPELINE_COMPILE_REQUIRED_EXT); ERR_STR(ERROR_SURFACE_LOST_KHR); ERR_STR(ERROR_NATIVE_WINDOW_IN_USE_KHR); ERR_STR(SUBOPTIMAL_KHR); diff --git a/render/vulkan/vulkan.c b/render/vulkan/vulkan.c index ee7adc011..0c0445d80 100644 --- a/render/vulkan/vulkan.c +++ b/render/vulkan/vulkan.c @@ -564,17 +564,17 @@ struct wlr_vk_device *vulkan_device_create(struct wlr_vk_instance *ini, .pQueuePriorities = &prio, }; - VkDeviceQueueGlobalPriorityCreateInfoKHR global_priority; + VkDeviceQueueGlobalPriorityCreateInfoEXT global_priority; bool has_global_priority = check_extension(avail_ext_props, avail_extc, - VK_KHR_GLOBAL_PRIORITY_EXTENSION_NAME); + VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME); if (has_global_priority) { // If global priorities are supported, request a high-priority context - global_priority = (VkDeviceQueueGlobalPriorityCreateInfoKHR){ - .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_KHR, - .globalPriority = VK_QUEUE_GLOBAL_PRIORITY_HIGH_KHR, + global_priority = (VkDeviceQueueGlobalPriorityCreateInfoEXT){ + .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_EXT, + .globalPriority = VK_QUEUE_GLOBAL_PRIORITY_HIGH_EXT, }; qinfo.pNext = &global_priority; - extensions[extensions_len++] = VK_KHR_GLOBAL_PRIORITY_EXTENSION_NAME; + extensions[extensions_len++] = VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME; wlr_log(WLR_DEBUG, "Requesting a high-priority device queue"); } else { wlr_log(WLR_DEBUG, "Global priorities are not supported, " diff --git a/types/ext_image_capture_source_v1/output.c b/types/ext_image_capture_source_v1/output.c index 75fa15f4d..0e3a57823 100644 --- a/types/ext_image_capture_source_v1/output.c +++ b/types/ext_image_capture_source_v1/output.c @@ -110,6 +110,10 @@ static const struct wlr_ext_image_capture_source_v1_interface output_source_impl static void source_update_buffer_constraints(struct wlr_ext_output_image_capture_source_v1 *source) { struct wlr_output *output = source->output; + if (!output->enabled) { + return; + } + if (!wlr_output_configure_primary_swapchain(output, NULL, &output->swapchain)) { return; } @@ -123,7 +127,8 @@ static void source_handle_output_commit(struct wl_listener *listener, struct wlr_ext_output_image_capture_source_v1 *source = wl_container_of(listener, source, output_commit); struct wlr_output_event_commit *event = data; - if (event->state->committed & (WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_RENDER_FORMAT)) { + if (event->state->committed & (WLR_OUTPUT_STATE_MODE | + WLR_OUTPUT_STATE_RENDER_FORMAT | WLR_OUTPUT_STATE_ENABLED)) { source_update_buffer_constraints(source); } diff --git a/types/scene/surface.c b/types/scene/surface.c index bce8c74a6..e6ea1333a 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -38,7 +38,7 @@ static struct wlr_output *get_surface_frame_pacing_output(struct wlr_surface *su return frame_pacing_output; } -static bool get_tf_preference(enum wlr_color_transfer_function tf) { +static int get_tf_preference(enum wlr_color_transfer_function tf) { switch (tf) { case WLR_COLOR_TRANSFER_FUNCTION_GAMMA22: return 0; @@ -52,7 +52,7 @@ static bool get_tf_preference(enum wlr_color_transfer_function tf) { abort(); // unreachable } -static bool get_primaries_preference(enum wlr_color_named_primaries primaries) { +static int get_primaries_preference(enum wlr_color_named_primaries primaries) { switch (primaries) { case WLR_COLOR_NAMED_PRIMARIES_SRGB: return 0; @@ -94,14 +94,44 @@ static void handle_scene_buffer_outputs_update( struct wl_listener *listener, void *data) { struct wlr_scene_surface *surface = wl_container_of(listener, surface, outputs_update); + struct wlr_scene_outputs_update_event *event = data; struct wlr_scene *scene = scene_node_get_root(&surface->buffer->node); // If the surface is no longer visible on any output, keep the last sent // preferred configuration to avoid unnecessary redraws - if (wl_list_empty(&surface->surface->current_outputs)) { + if (event->size == 0) { return; } + // To avoid sending redundant leave/enter events when a surface is hidden and then shown + // without moving to a different output the following policy is implemented: + // + // 1. When a surface transitions from being visible on >0 outputs to being visible on 0 outputs + // don't send any leave events. + // + // 2. When a surface transitions from being visible on 0 outputs to being visible on >0 outputs + // send leave events for all entered outputs on which the surface is no longer visible as + // well as enter events for any outputs not already entered. + struct wlr_surface_output *entered_output, *tmp; + wl_list_for_each_safe(entered_output, tmp, &surface->surface->current_outputs, link) { + bool active = false; + for (size_t i = 0; i < event->size; i++) { + if (entered_output->output == event->active[i]->output) { + active = true; + break; + } + } + if (!active) { + wlr_surface_send_leave(surface->surface, entered_output->output); + } + } + + for (size_t i = 0; i < event->size; i++) { + // This function internally checks if an enter event was already sent for the output + // to avoid sending redundant events. + wlr_surface_send_enter(surface->surface, event->active[i]->output); + } + double scale = get_surface_preferred_buffer_scale(surface->surface); wlr_fractional_scale_v1_notify_scale(surface->surface, scale); wlr_surface_set_preferred_buffer_scale(surface->surface, ceil(scale)); @@ -114,24 +144,6 @@ static void handle_scene_buffer_outputs_update( } } -static void handle_scene_buffer_output_enter( - struct wl_listener *listener, void *data) { - struct wlr_scene_surface *surface = - wl_container_of(listener, surface, output_enter); - struct wlr_scene_output *output = data; - - wlr_surface_send_enter(surface->surface, output->output); -} - -static void handle_scene_buffer_output_leave( - struct wl_listener *listener, void *data) { - struct wlr_scene_surface *surface = - wl_container_of(listener, surface, output_leave); - struct wlr_scene_output *output = data; - - wlr_surface_send_leave(surface->surface, output->output); -} - static void handle_scene_buffer_output_sample( struct wl_listener *listener, void *data) { struct wlr_scene_surface *surface = @@ -147,6 +159,13 @@ static void handle_scene_buffer_output_sample( } else { wlr_presentation_surface_textured_on_output(surface->surface, output); } + + struct wlr_linux_drm_syncobj_surface_v1_state *syncobj_surface_state = + wlr_linux_drm_syncobj_v1_get_surface_state(surface->surface); + if (syncobj_surface_state != NULL && event->release_timeline != NULL) { + wlr_linux_drm_syncobj_v1_state_add_release_point(syncobj_surface_state, + event->release_timeline, event->release_point, output->event_loop); + } } static void handle_scene_buffer_frame_done( @@ -311,27 +330,15 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) { struct wlr_linux_drm_syncobj_surface_v1_state *syncobj_surface_state = wlr_linux_drm_syncobj_v1_get_surface_state(surface); - struct wlr_drm_syncobj_timeline *wait_timeline = NULL; - uint64_t wait_point = 0; - if (syncobj_surface_state != NULL) { - wait_timeline = syncobj_surface_state->acquire_timeline; - wait_point = syncobj_surface_state->acquire_point; - } - struct wlr_scene_buffer_set_buffer_options options = { .damage = &surface->buffer_damage, - .wait_timeline = wait_timeline, - .wait_point = wait_point, }; + if (syncobj_surface_state != NULL) { + options.wait_timeline = syncobj_surface_state->acquire_timeline; + options.wait_point = syncobj_surface_state->acquire_point; + } wlr_scene_buffer_set_buffer_with_options(scene_buffer, &surface->buffer->base, &options); - - if (syncobj_surface_state != NULL && - (surface->current.committed & WLR_SURFACE_STATE_BUFFER) && - surface->buffer->source != NULL) { - wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer(syncobj_surface_state, - surface->buffer->source); - } } else { wlr_scene_buffer_set_buffer(scene_buffer, NULL); } @@ -380,8 +387,6 @@ static void surface_addon_destroy(struct wlr_addon *addon) { wlr_addon_finish(&surface->addon); wl_list_remove(&surface->outputs_update.link); - wl_list_remove(&surface->output_enter.link); - wl_list_remove(&surface->output_leave.link); wl_list_remove(&surface->output_sample.link); wl_list_remove(&surface->frame_done.link); wl_list_remove(&surface->surface_destroy.link); @@ -427,12 +432,6 @@ struct wlr_scene_surface *wlr_scene_surface_create(struct wlr_scene_tree *parent surface->outputs_update.notify = handle_scene_buffer_outputs_update; wl_signal_add(&scene_buffer->events.outputs_update, &surface->outputs_update); - surface->output_enter.notify = handle_scene_buffer_output_enter; - wl_signal_add(&scene_buffer->events.output_enter, &surface->output_enter); - - surface->output_leave.notify = handle_scene_buffer_output_leave; - wl_signal_add(&scene_buffer->events.output_leave, &surface->output_leave); - surface->output_sample.notify = handle_scene_buffer_output_sample; wl_signal_add(&scene_buffer->events.output_sample, &surface->output_sample); diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 51373e15a..54c09bbfe 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1553,6 +1553,8 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren struct wlr_scene_output_sample_event sample_event = { .output = data->output, .direct_scanout = false, + .release_timeline = data->output->in_timeline, + .release_point = data->output->in_point, }; wl_signal_emit_mutable(&scene_buffer->events.output_sample, &sample_event); @@ -1785,7 +1787,10 @@ struct wlr_scene_output *wlr_scene_output_create(struct wlr_scene *scene, if (drm_fd >= 0 && output->backend->features.timeline && output->renderer != NULL && output->renderer->features.timeline) { scene_output->in_timeline = wlr_drm_syncobj_timeline_create(drm_fd); - if (scene_output->in_timeline == NULL) { + scene_output->out_timeline = wlr_drm_syncobj_timeline_create(drm_fd); + if (scene_output->in_timeline == NULL || scene_output->out_timeline == NULL) { + wlr_drm_syncobj_timeline_unref(scene_output->in_timeline); + wlr_drm_syncobj_timeline_unref(scene_output->out_timeline); return NULL; } } @@ -1840,7 +1845,14 @@ void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) { wl_list_remove(&scene_output->output_commit.link); wl_list_remove(&scene_output->output_damage.link); wl_list_remove(&scene_output->output_needs_frame.link); - wlr_drm_syncobj_timeline_unref(scene_output->in_timeline); + if (scene_output->in_timeline != NULL) { + wlr_drm_syncobj_timeline_signal(scene_output->in_timeline, UINT64_MAX); + wlr_drm_syncobj_timeline_unref(scene_output->in_timeline); + } + if (scene_output->out_timeline != NULL) { + wlr_drm_syncobj_timeline_signal(scene_output->out_timeline, UINT64_MAX); + wlr_drm_syncobj_timeline_unref(scene_output->out_timeline); + } wlr_color_transform_unref(scene_output->gamma_lut_color_transform); wlr_color_transform_unref(scene_output->prev_gamma_lut_color_transform); wlr_color_transform_unref(scene_output->prev_supplied_color_transform); @@ -2136,6 +2148,12 @@ static enum scene_direct_scanout_result scene_entry_try_direct_scanout( if (buffer->wait_timeline != NULL) { wlr_output_state_set_wait_timeline(&pending, buffer->wait_timeline, buffer->wait_point); } + + if (scene_output->out_timeline) { + scene_output->out_point++; + wlr_output_state_set_signal_timeline(&pending, scene_output->out_timeline, scene_output->out_point); + } + if (!wlr_output_test_state(scene_output->output, &pending)) { wlr_output_state_finish(&pending); return SCANOUT_CANDIDATE; @@ -2147,6 +2165,8 @@ static enum scene_direct_scanout_result scene_entry_try_direct_scanout( struct wlr_scene_output_sample_event sample_event = { .output = scene_output, .direct_scanout = true, + .release_timeline = data->output->out_timeline, + .release_point = data->output->out_point, }; wl_signal_emit_mutable(&buffer->events.output_sample, &sample_event); return SCANOUT_SUCCESS; @@ -2606,6 +2626,9 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, if (scene_output->in_timeline != NULL) { wlr_output_state_set_wait_timeline(state, scene_output->in_timeline, scene_output->in_point); + scene_output->out_point++; + wlr_output_state_set_signal_timeline(state, scene_output->out_timeline, + scene_output->out_point); } if (!render_gamma_lut) { diff --git a/types/wlr_color_management_v1.c b/types/wlr_color_management_v1.c index 0a19e8e78..aa924e36d 100644 --- a/types/wlr_color_management_v1.c +++ b/types/wlr_color_management_v1.c @@ -74,6 +74,41 @@ static float decode_cie1931_coord(int32_t raw) { return (float)raw / (1000 * 1000); } +static bool cie1931_xy_equal(const struct wlr_color_cie1931_xy *a, + const struct wlr_color_cie1931_xy *b) { + return a->x == b->x && a->y == b->y; +} + +static bool primaries_equal(const struct wlr_color_primaries *a, + const struct wlr_color_primaries *b) { + return cie1931_xy_equal(&a->red, &b->red) && + cie1931_xy_equal(&a->green, &b->green) && + cie1931_xy_equal(&a->blue, &b->blue) && + cie1931_xy_equal(&a->white, &b->white); +} + +static bool img_desc_data_equal(const struct wlr_image_description_v1_data *a, + const struct wlr_image_description_v1_data *b) { + if (a->tf_named != b->tf_named || + a->primaries_named != b->primaries_named || + a->has_mastering_display_primaries != b->has_mastering_display_primaries || + a->has_mastering_luminance != b->has_mastering_luminance || + a->max_cll != b->max_cll || + a->max_fall != b->max_fall) { + return false; + } + if (a->has_mastering_display_primaries && + !primaries_equal(&a->mastering_display_primaries, &b->mastering_display_primaries)) { + return false; + } + if (a->has_mastering_luminance && + (a->mastering_luminance.min != b->mastering_luminance.min || + a->mastering_luminance.max != b->mastering_luminance.max)) { + return false; + } + return true; +} + static const struct wp_image_description_v1_interface image_desc_impl; static struct wlr_image_description_v1 *image_desc_from_resource(struct wl_resource *resource) { @@ -1002,16 +1037,20 @@ void wlr_color_manager_v1_set_surface_preferred_image_description( struct wlr_color_management_surface_feedback_v1 *surface_feedback; wl_list_for_each(surface_feedback, &manager->surface_feedbacks, link) { - if (surface_feedback->surface == surface) { - surface_feedback->data = *data; - uint32_t version = wl_resource_get_version(surface_feedback->resource); - if (version >= WP_COLOR_MANAGEMENT_SURFACE_FEEDBACK_V1_PREFERRED_CHANGED2_SINCE_VERSION) { - wp_color_management_surface_feedback_v1_send_preferred_changed2( - surface_feedback->resource, identity_hi, identity_lo); - } else { - wp_color_management_surface_feedback_v1_send_preferred_changed( - surface_feedback->resource, identity_lo); - } + if (surface_feedback->surface != surface || + img_desc_data_equal(&surface_feedback->data, data)) { + continue; + } + + surface_feedback->data = *data; + + uint32_t version = wl_resource_get_version(surface_feedback->resource); + if (version >= WP_COLOR_MANAGEMENT_SURFACE_FEEDBACK_V1_PREFERRED_CHANGED2_SINCE_VERSION) { + wp_color_management_surface_feedback_v1_send_preferred_changed2( + surface_feedback->resource, identity_hi, identity_lo); + } else { + wp_color_management_surface_feedback_v1_send_preferred_changed( + surface_feedback->resource, identity_lo); } } } diff --git a/types/wlr_ext_image_copy_capture_v1.c b/types/wlr_ext_image_copy_capture_v1.c index c9e7b0079..2e969b4a9 100644 --- a/types/wlr_ext_image_copy_capture_v1.c +++ b/types/wlr_ext_image_copy_capture_v1.c @@ -518,14 +518,16 @@ static void cursor_session_handle_get_capture_session(struct wl_client *client, return; } - cursor_session->capture_session_created = true; - + struct wlr_ext_image_copy_capture_manager_v1 *manager = NULL; struct wlr_ext_image_capture_source_v1 *source = NULL; + if (cursor_session != NULL) { + manager = cursor_session->manager; + cursor_session->capture_session_created = true; source = &cursor_session->source->base; } - session_create(cursor_session_resource, new_id, source, 0, cursor_session->manager); + session_create(cursor_session_resource, new_id, source, 0, manager); } static const struct ext_image_copy_capture_cursor_session_v1_interface cursor_session_impl = { diff --git a/types/wlr_linux_drm_syncobj_v1.c b/types/wlr_linux_drm_syncobj_v1.c index 988d44e01..53fc2fd43 100644 --- a/types/wlr_linux_drm_syncobj_v1.c +++ b/types/wlr_linux_drm_syncobj_v1.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -11,6 +12,7 @@ #include #include "config.h" #include "linux-drm-syncobj-v1-protocol.h" +#include "render/drm_syncobj_merger.h" #define LINUX_DRM_SYNCOBJ_V1_VERSION 1 @@ -158,20 +160,39 @@ 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); + wlr_drm_syncobj_merger_unref(state->release_merger); } 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 + if (src->acquire_timeline == NULL) { + // ignore commits that did not attach a buffer + return; + } surface_synced_finish_state(dst); *dst = *src; + dst->committed = true; *src = (struct wlr_linux_drm_syncobj_surface_v1_state){0}; } +static void surface_synced_commit(struct wlr_surface_synced *synced) { + struct wlr_linux_drm_syncobj_surface_v1 *surface = wl_container_of(synced, surface, synced); + + if (!surface->current.committed) { + return; + } + + surface->current.release_merger = wlr_drm_syncobj_merger_create( + surface->current.release_timeline, surface->current.release_point); + surface->current.committed = false; +} + + 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, + .commit = surface_synced_commit, }; static void manager_handle_destroy(struct wl_client *client, @@ -422,6 +443,11 @@ struct wlr_linux_drm_syncobj_manager_v1 *wlr_linux_drm_syncobj_manager_v1_create struct wl_display *display, uint32_t version, int drm_fd) { assert(version <= LINUX_DRM_SYNCOBJ_V1_VERSION); + if (!HAVE_LINUX_SYNC_FILE) { + wlr_log(WLR_INFO, "Linux sync_file unavailable, disabling linux-drm-syncobj-v1"); + return NULL; + } + if (!check_syncobj_eventfd(drm_fd)) { wlr_log(WLR_INFO, "DRM syncobj eventfd unavailable, disabling linux-drm-syncobj-v1"); return NULL; @@ -467,20 +493,14 @@ wlr_linux_drm_syncobj_v1_get_surface_state(struct wlr_surface *wlr_surface) { } struct release_signaller { - struct wlr_drm_syncobj_timeline *timeline; - uint64_t point; + struct wlr_drm_syncobj_merger *merger; struct wl_listener buffer_release; }; static void release_signaller_handle_buffer_release(struct wl_listener *listener, void *data) { struct release_signaller *signaller = wl_container_of(listener, signaller, buffer_release); - if (drmSyncobjTimelineSignal(signaller->timeline->drm_fd, &signaller->timeline->handle, - &signaller->point, 1) != 0) { - wlr_log(WLR_ERROR, "drmSyncobjTimelineSignal() failed"); - } - - wlr_drm_syncobj_timeline_unref(signaller->timeline); + wlr_drm_syncobj_merger_unref(signaller->merger); wl_list_remove(&signaller->buffer_release.link); free(signaller); } @@ -488,7 +508,7 @@ static void release_signaller_handle_buffer_release(struct wl_listener *listener bool wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer( struct wlr_linux_drm_syncobj_surface_v1_state *state, struct wlr_buffer *buffer) { assert(buffer->n_locks > 0); - if (state->release_timeline == NULL) { + if (state->release_merger == NULL) { // This can happen if an existing surface with a buffer has a // syncobj_surface_v1_state created but no new buffer with release // timeline committed. @@ -500,11 +520,23 @@ bool wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer( return false; } - signaller->timeline = wlr_drm_syncobj_timeline_ref(state->release_timeline); - signaller->point = state->release_point; - + signaller->merger = wlr_drm_syncobj_merger_ref(state->release_merger); signaller->buffer_release.notify = release_signaller_handle_buffer_release; wl_signal_add(&buffer->events.release, &signaller->buffer_release); return true; } + +bool wlr_linux_drm_syncobj_v1_state_add_release_point( + struct wlr_linux_drm_syncobj_surface_v1_state *state, + struct wlr_drm_syncobj_timeline *release_timeline, uint64_t release_point, + struct wl_event_loop *event_loop) { + if (state->release_merger == NULL) { + // This can happen if an existing surface with a buffer has a + // syncobj_surface_v1_state created but no new buffer with release + // timeline committed. + return true; + } + return wlr_drm_syncobj_merger_add(state->release_merger, + release_timeline, release_point, event_loop); +} diff --git a/types/wlr_virtual_keyboard_v1.c b/types/wlr_virtual_keyboard_v1.c index e7dcb3ec3..011fbfec7 100644 --- a/types/wlr_virtual_keyboard_v1.c +++ b/types/wlr_virtual_keyboard_v1.c @@ -15,7 +15,7 @@ static const struct wlr_keyboard_impl keyboard_impl = { static const struct zwp_virtual_keyboard_v1_interface virtual_keyboard_impl; -static struct wlr_virtual_keyboard_v1 *virtual_keyboard_from_resource( +struct wlr_virtual_keyboard_v1 *wlr_virtual_keyboard_v1_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwp_virtual_keyboard_v1_interface, &virtual_keyboard_impl)); @@ -39,7 +39,7 @@ static void virtual_keyboard_keymap(struct wl_client *client, struct wl_resource *resource, uint32_t format, int32_t fd, uint32_t size) { struct wlr_virtual_keyboard_v1 *keyboard = - virtual_keyboard_from_resource(resource); + wlr_virtual_keyboard_v1_from_resource(resource); if (keyboard == NULL) { return; } @@ -76,7 +76,7 @@ static void virtual_keyboard_key(struct wl_client *client, struct wl_resource *resource, uint32_t time, uint32_t key, uint32_t state) { struct wlr_virtual_keyboard_v1 *keyboard = - virtual_keyboard_from_resource(resource); + wlr_virtual_keyboard_v1_from_resource(resource); if (keyboard == NULL) { return; } @@ -99,7 +99,7 @@ static void virtual_keyboard_modifiers(struct wl_client *client, struct wl_resource *resource, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) { struct wlr_virtual_keyboard_v1 *keyboard = - virtual_keyboard_from_resource(resource); + wlr_virtual_keyboard_v1_from_resource(resource); if (keyboard == NULL) { return; } @@ -113,21 +113,24 @@ static void virtual_keyboard_modifiers(struct wl_client *client, mods_depressed, mods_latched, mods_locked, group); } -static void virtual_keyboard_destroy_resource(struct wl_resource *resource) { - struct wlr_virtual_keyboard_v1 *keyboard = - virtual_keyboard_from_resource(resource); - if (keyboard == NULL) { - return; - } - - wlr_keyboard_finish(&keyboard->keyboard); - - wl_resource_set_user_data(keyboard->resource, NULL); - wl_list_remove(&keyboard->link); - free(keyboard); +static void virtual_keyboard_destroy(struct wlr_virtual_keyboard_v1 *virtual_keyboard) { + wlr_keyboard_finish(&virtual_keyboard->keyboard); + wl_resource_set_user_data(virtual_keyboard->resource, NULL); + wl_list_remove(&virtual_keyboard->seat_destroy.link); + wl_list_remove(&virtual_keyboard->link); + free(virtual_keyboard); } -static void virtual_keyboard_destroy(struct wl_client *client, +static void virtual_keyboard_destroy_resource(struct wl_resource *resource) { + struct wlr_virtual_keyboard_v1 *virtual_keyboard = + wlr_virtual_keyboard_v1_from_resource(resource); + if (virtual_keyboard == NULL) { + return; + } + virtual_keyboard_destroy(virtual_keyboard); +} + +static void virtual_keyboard_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } @@ -136,7 +139,7 @@ static const struct zwp_virtual_keyboard_v1_interface virtual_keyboard_impl = { .keymap = virtual_keyboard_keymap, .key = virtual_keyboard_key, .modifiers = virtual_keyboard_modifiers, - .destroy = virtual_keyboard_destroy, + .destroy = virtual_keyboard_handle_destroy, }; static const struct zwp_virtual_keyboard_manager_v1_interface manager_impl; @@ -148,6 +151,13 @@ static struct wlr_virtual_keyboard_manager_v1 *manager_from_resource( return wl_resource_get_user_data(resource); } +static void virtual_keyboard_handle_seat_destroy(struct wl_listener *listener, + void *data) { + struct wlr_virtual_keyboard_v1 *virtual_keyboard = wl_container_of(listener, virtual_keyboard, + seat_destroy); + virtual_keyboard_destroy(virtual_keyboard); +} + static void virtual_keyboard_manager_create_virtual_keyboard( struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat, uint32_t id) { @@ -181,6 +191,9 @@ static void virtual_keyboard_manager_create_virtual_keyboard( virtual_keyboard->seat = seat_client->seat; wl_resource_set_user_data(keyboard_resource, virtual_keyboard); + wl_signal_add(&seat_client->events.destroy, &virtual_keyboard->seat_destroy); + virtual_keyboard->seat_destroy.notify = virtual_keyboard_handle_seat_destroy; + wl_list_insert(&manager->virtual_keyboards, &virtual_keyboard->link); wl_signal_emit_mutable(&manager->events.new_virtual_keyboard, diff --git a/util/box.c b/util/box.c index aae09888f..489c5ad2e 100644 --- a/util/box.c +++ b/util/box.c @@ -4,6 +4,14 @@ #include #include +static int max(int a, int b) { + return a > b ? a : b; +} + +static int min(int a, int b) { + return a < b ? a : b; +} + void wlr_box_closest_point(const struct wlr_box *box, double x, double y, double *dest_x, double *dest_y) { // if box is empty, then it contains no points, so no closest point either @@ -56,10 +64,10 @@ bool wlr_box_intersection(struct wlr_box *dest, const struct wlr_box *box_a, return false; } - int x1 = fmax(box_a->x, box_b->x); - int y1 = fmax(box_a->y, box_b->y); - int x2 = fmin(box_a->x + box_a->width, box_b->x + box_b->width); - int y2 = fmin(box_a->y + box_a->height, box_b->y + box_b->height); + int x1 = max(box_a->x, box_b->x); + int y1 = max(box_a->y, box_b->y); + int x2 = min(box_a->x + box_a->width, box_b->x + box_b->width); + int y2 = min(box_a->y + box_a->height, box_b->y + box_b->height); dest->x = x1; dest->y = y1;