Compare commits

...

24 commits

Author SHA1 Message Date
Simon Ser
c1d38536c9 build: bump version to 0.20.0 2026-03-26 17:38:50 +01:00
Simon Zeni
a945fd8940 render/vulkan: compile against vulkan 1.2 header
Uses the EXT version of VK_PIPELINE_COMPILE_REQUIRED in `vulkan_strerror` func since it requires
Vulkan 1.3, switch to VK_EXT_global_priority instead of VK_KHR_global_priority which is only
promoted to core in Vulkan 1.3 as well.

(cherry picked from commit 413664e0b0)
2026-03-26 17:38:25 +01:00
Félix Poisot
2bfbec4af1 linux_drm_syncobj_v1: fix handling of empty first commit
As reported in
https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4979#note_3385626,
bfd6e619fc did not correctly handle clients
that don't immediately follow their call to
`wp_linux_drm_syncobj_manager_v1.get_surface` with a commit attaching
a buffer

Fixes: bfd6e619fc
(cherry picked from commit fd870f6d27)
2026-03-26 17:38:25 +01:00
Simon Ser
1a9b1292e2 color_management_v1: ignore surface update if no-op
If the new image description is identical to the old one, skip the
event.

(cherry picked from commit 4ca40004fd)
2026-03-26 17:38:25 +01:00
Simon Ser
ad82740518 color_management_v1: use early continue in surface loop
(cherry picked from commit 7287f700ab)
2026-03-26 17:38:25 +01:00
Simon Ser
bba38a0d82 build: bump version to 0.20.0-rc5 2026-03-19 20:02:25 +01:00
Félix Poisot
df1539d9f0 output/drm: don't use OUT_FENCE_PTR
The returned fence is not required to be signalled at the earliest
possible time. It is not intended to replace the drm flip event, and is
expected to be signalled only much later

(cherry picked from commit 8d454e1e34)
2026-03-19 20:01:40 +01:00
Félix Poisot
eff5aa52e6 backend/drm: properly delay syncobj signalling
DRM CRTC signals when scanout begins, but
wlr_output_state_set_signal_timeline() is defined to signal buffer
release. Delay to the next page flip

(cherry picked from commit cd555f9261)
2026-03-19 20:01:40 +01:00
Félix Poisot
8daba3246d scene: transfer sample syncobj to client timeline
(cherry picked from commit b2f6a390a4)
2026-03-19 20:01:40 +01:00
Félix Poisot
52564ea97c linux_drm_syncobj_v1: add release point accumulation
This changes the behavior of wlr_linux_drm_syncobj_surface_v1 to
automatically signal release of previous commits as they are replaced.

Users must call wlr_linux_drm_syncobj_v1_state_add_release_point or
wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer to delay the
signal as appropriate.

(cherry picked from commit bfd6e619fc)
2026-03-19 20:01:40 +01:00
Félix Poisot
288ba9e75b drm/syncobj: add timeline point merger utility
(cherry picked from commit e83a679e23)
2026-03-19 20:01:40 +01:00
Félix Poisot
2f14845ce0 scene: add buffer release point to 'sample' event
(cherry picked from commit 1f3d351abb)
2026-03-19 20:01:23 +01:00
Félix Poisot
c4ff394f7f render/drm_syncobj: add wlr_drm_syncobj_timeline_signal()
(cherry picked from commit 0af9b9d003)
2026-03-19 19:48:36 +01:00
Isaac Freund
88718e84c9 virtual-keyboard: handle seat destroy
We must make the virtual keyboard inert when the seat is destroyed.

(cherry picked from commit 1fa8bb8f7a)
2026-03-19 19:48:14 +01:00
Isaac Freund
6a902237ef virtual-keyboard: add wlr_virtual_keyboard_v1_from_resource()
I want to use the zwp_virtual_keyboard_v1 object in a custom river
protocol and need to be able to obtain the corresponding wlroots struct.

(cherry picked from commit ec746d3e3e)
2026-03-19 19:48:14 +01:00
Scott Moreau
47a43e14ae wlr_ext_image_copy_capture_v1: Fix crash when client creates a cursor session not implemented server side
This guards against a crash where the server implements
wlr_ext_image_capture_source_v1_interface without setting .get_pointer_cursor().
In general, we should install a NULL check here because this is a crash
waiting to happen. Now, instead of crashing, the resource will be created and
the copy capture session will be stopped.

(cherry picked from commit 1fc928d528)
2026-03-19 19:48:09 +01:00
llyyr
ba32abbddb scene: use wl_list_for_each_safe to iterate outputs
The outputs loop in handle_scene_buffer_outputs_update may remove entries
from the list while iterating, so use wl_list_for_each_safe instead of
wl_list_for_each.

Fixes: 39e918edc8 ("scene: avoid redundant wl_surface.enter/leave events")
(cherry picked from commit 3cb2cf9425)
2026-03-19 19:48:05 +01:00
Isaac Freund
9c3bfbeb48 scene: avoid redundant wl_surface.enter/leave events
Currently we send wl_surface.enter/leave when a surface is hidden
and shown again on the same output. In practice, this happens very
often since compositors like river and sway enable and disable
the scene nodes of surfaces as part of their atomic transaction
strategy involving rendering saved buffers while waiting for
clients to submit new buffers of the desired size.

The new strategy documented in the new comments avoids sending
redundant events in this case.

(cherry picked from commit 39e918edc8)
2026-03-19 19:47:55 +01:00
Diego Viola
b727521449 wlr-export-dmabuf-unstable-v1: fix typo
(cherry picked from commit 736c0f3f25)
2026-03-19 19:47:55 +01:00
Jonathan Marler
af228b879a backend/x11: ignore DestroyNotify events
The X11 backend subscribes to StructureNotify events, so when
output_destroy() calls xcb_destroy_window() the server sends a
DestroyNotify back. This is expected and harmless but was logged
as an unhandled event. Silence it the same way MAP_NOTIFY and
UNMAP_NOTIFY are already silenced.

(cherry picked from commit 3c8d199ec1)
2026-03-19 19:47:55 +01:00
Kenny Levinsen
ec26eb250a util/box: Use integer min/max for intersection
wlr_box_intersection only operates on integers, so we shouldn't use
fmin/fmax. Do the usual and add a local integer min/max helper.

(cherry picked from commit 285cee5f3a)
2026-03-19 19:47:55 +01:00
Christopher Snowhill
3105ac2b16 scene: fix color format compare
bool doesn't really support negative values.

Fixes: 7cb3393e7 (scene: send color_management_v1 surface feedback)
(cherry picked from commit 9a931d9ffa)
2026-03-19 19:47:55 +01:00
Simon Zeni
557fde4d01 ci: update dalligi upstream repo
(cherry picked from commit 67ce318b1f)
2026-03-19 19:47:55 +01:00
Andri Yngvason
e444836bf2 image_capture_source/output: Update constraints on enable
Without observing the enable event, clients receive no pixel formats and
buffer dimensions are reported as 0 after an output has been re-enabled.

(cherry picked from commit 3336d28813)
2026-03-19 19:47:55 +01:00
26 changed files with 494 additions and 141 deletions

View file

@ -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

View file

@ -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 = {

View file

@ -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,

View file

@ -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++) {

View file

@ -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;

View file

@ -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;

View file

@ -0,0 +1,44 @@
#ifndef WLR_RENDER_DRM_SYNCOBJ_MERGER_H
#define WLR_RENDER_DRM_SYNCOBJ_MERGER_H
#include <wayland-server-core.h>
/**
* 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

View file

@ -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.
*

View file

@ -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

View file

@ -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;
};

View file

@ -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

View file

@ -1,7 +1,7 @@
project(
'wlroots',
'c',
version: '0.20.0-rc4',
version: '0.20.0',
license: 'MIT',
meson_version: '>=1.3',
default_options: [

View file

@ -192,7 +192,7 @@
<request name="destroy" type="destructor">
<description summary="delete this object, used or not">
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

View file

@ -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;

133
render/drm_syncobj_merger.c Normal file
View file

@ -0,0 +1,133 @@
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <wayland-util.h>
#include <wlr/render/drm_syncobj.h>
#include <wlr/util/log.h>
#include <xf86drm.h>
#include "render/drm_syncobj_merger.h"
#include "config.h"
#if HAVE_LINUX_SYNC_FILE
#include <linux/sync_file.h>
#include <sys/ioctl.h>
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;
}

View file

@ -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)

View file

@ -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);

View file

@ -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, "

View file

@ -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);
}

View file

@ -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);

View file

@ -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) {

View file

@ -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);
}
}
}

View file

@ -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 = {

View file

@ -3,6 +3,7 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wayland-server-core.h>
#include <wlr/render/drm_syncobj.h>
#include <wlr/types/wlr_buffer.h>
#include <wlr/types/wlr_compositor.h>
@ -11,6 +12,7 @@
#include <xf86drm.h>
#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);
}

View file

@ -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,

View file

@ -4,6 +4,14 @@
#include <wlr/util/box.h>
#include <wlr/util/log.h>
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;