Compare commits

...

37 commits

Author SHA1 Message Date
Simon Ser
a7f2006627 build: bump version to 0.20.1 2026-05-19 22:56:13 +02:00
Ronan Pigott
9d3ec642c2 scene: only send leave events to outputs with matching scene root
In handling scene buffer output updates, wlroots would send a leave event to
all entered outputs, even those that the scene root for the scene output update
event did not own. Leaving the output list inaccurate.

Sending leave events only for the given scene introduces a problem, though:
existing logic to de-duplicate leave events stops us from sending a leave event
when we leave all the outputs in a scene, and when the surface then becomes
visible in another scene, the frame pacing output cannot be selected
accurately. This breaks screen capture for off-screen windows in sway.

So, let us also mark outputs that would have been left but were spared by the
deduplication logic as "suspended" indicating they are ineligible as frame
pacing outputs.

Fixes: https://github.com/swaywm/sway/issues/9094
(cherry picked from commit e532b4c26c)
2026-05-19 15:56:40 -04:00
xurui
ac378cea41 linux_drm_syncobj_v1: fix memory leak
Signed-off-by: xurui <xurui@kylinos.cn>
(cherry picked from commit 19df074c16)
2026-05-11 10:36:03 -04:00
Kenny Levinsen
b23a3e8131 wlr_compositor: Apply state before updating surface_damage
When locking surface state, surface_cache_pending will move the pending
surface state to a new, empty `wlr_surface_state`. This new surface
state will only contain the fields committed in the pending state, as
surface_state_move does not copy anything else.

surface_update_damage is called before we move state from pending to
current to merge buffer damage and surface damage, and it expects that
the pending surface state still contains prior committed details such as
scale and transform. This is not the case when we finally commit the
cached surface state.

Move surface_update_damage after surface_state_move and make it operate
purely on the current surface state.

(cherry picked from commit fba00c4a04)
2026-05-11 10:36:03 -04:00
Consolatis
a8b883707c scene/surface: schedule on frame pacing output
Otherwise wlr_scene_output_send_frame_done() drops frame callbacks
if only parts of a window on a non frame-pacing output is damaged.

(cherry picked from commit 700ee83ab8)
2026-05-11 10:36:03 -04:00
Isaac Freund
1688cfb814 keyboard: fix modifiers event when no keymap set
Actually send the modifiers event when there is no keymap set.

Compositors may need lower-level "raw" access to the key/modifiers
events from the backend. Currently it is impossible for a compositor
to get modifier events from the backend without them being filtered
through an xkb_state controlled by wlroots.

I need this feature for river in order to fix some keyboard state
synchronization bugs.

Note that setting a keymap resets the modifiers so I don't think setting
wlr_keyboard->modifiers directly here is a concern.

(cherry picked from commit 9de0ec3089)
2026-04-13 11:10:02 -04:00
Consolatis
5d2172b7fa render/pixman: fix bilinear filtering to match gles2 renderer
Before this patch the pixman renderer would use "constant padding"
for bilinear scaling which meant that the edges would either be dark
or turn transparent. The effect was most obvious when trying to scale
a single row buffer to a height like 100. The center would have the
desired color but the edges to both sides would fade into transparency.

We now use PIXMAN_REPEAT_PAD which clamps out-of-bound pixels and
seems to match the behavior of the gles2 renderer.

(cherry picked from commit c66a910753)
2026-04-13 11:09:50 -04:00
Simon Ser
7310756297 wlr_virtual_keyboard_v1: specify size when creating keymap
xkb_keymap_new_from_string() assumes that the string is
NULL-terminated, but we don't check this so the function might
access outside the mmap'ed region. Use the safer
xkb_keymap_new_from_buffer() function.

Reported-by: Julian Orth <ju.orth@gmail.com>
Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/work_items/4072
(cherry picked from commit c393fb6bfa)
2026-04-13 11:09:41 -04:00
Johan Malm
b9ca8fd6d6 wlr_ext_workspace_v1.c: add new workspaces to end of list
...so that panels/bars like xfce4-panel and waybar show workspaces in the
right order.

(cherry picked from commit e8c03e9ce9)
2026-04-13 11:09:31 -04:00
tokyo4j
61ed450247 ext-foreign-toplevel-list: add new toplevels to end of list
Same as the prior patch for wlr-foreign-toplevel-management.

(cherry picked from commit 8dd1a77615)
2026-04-13 11:09:18 -04:00
tokyo4j
d0fbe1ce56 wlr-foreign-toplevel-management: add new toplevels to end of list
Prior to this patch, when the client binds the manager, the existing
toplevel handles were notified in the opposite order of their creation,
as wl_list_insert() adds a new handle to the head of the list and
wl_list_for_each() iterates from head to tail.

(cherry picked from commit 84b45e4157)
2026-04-13 11:09:10 -04:00
Consolatis
13bac746d6 toplevel_capture: allocate new_request argument on the stack
This fixes a memory leak.
2026-04-13 11:08:10 -04:00
Isaac Freund
7610326b8d ext_image_capture_source_v1/scene: fix extents
Currently the width/height of the extents is too small if the first node
visited has position/dimensions 0,0,100,100 and the second node has
position/dimensions -20,-20,10,10.

In this case the current code calculates total extents as
-20,-20,100,100 but the correct extents are -20,-20,120,120.

References: https://codeberg.org/river/river-classic/issues/17
(cherry picked from commit e22084f639)
2026-03-31 10:10:06 -04:00
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
35 changed files with 566 additions and 191 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: alpine:
extends: .dalligi extends: .dalligi
pages: true 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) { if (state->primary_in_fence_fd >= 0) {
close(state->primary_in_fence_fd); 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; 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) { if (state->primary_in_fence_fd >= 0) {
close(state->primary_in_fence_fd); 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) { 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); 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, static void atomic_connector_add(struct atomic *atom,
struct wlr_drm_connector_state *state, bool modeset) { struct wlr_drm_connector_state *state, bool modeset) {
struct wlr_drm_connector *conn = state->connector; 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) { if (state->primary_in_fence_fd >= 0) {
set_plane_in_fence_fd(atom, crtc->primary, state->primary_in_fence_fd); 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 (crtc->cursor) {
if (drm_connector_is_cursor_visible(conn)) { if (drm_connector_is_cursor_visible(conn)) {
struct wlr_fbox cursor_src = { 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); 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); 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); 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; struct wlr_drm_crtc *crtc = conn->crtc;
drm_fb_copy(&crtc->primary->queued_fb, state->primary_fb); 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; crtc->primary->viewport = state->primary_viewport;
if (crtc->cursor != NULL) { if (crtc->cursor != NULL) {
drm_fb_copy(&crtc->cursor->queued_fb, state->cursor_fb); 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, .base = base,
.active = output_pending_enabled(&conn->output, base), .active = output_pending_enabled(&conn->output, base),
.primary_in_fence_fd = -1, .primary_in_fence_fd = -1,
.out_fence_fd = -1,
}; };
struct wlr_output_mode *mode = conn->output.current_mode; 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; struct wlr_drm_plane *plane = conn->crtc->primary;
if (plane->queued_fb) { if (plane->queued_fb) {
drm_fb_move(&plane->current_fb, &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) { if (conn->crtc->cursor && conn->crtc->cursor->queued_fb) {
drm_fb_move(&conn->crtc->cursor->current_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, liftoff_layer_set_property(crtc->liftoff_composition_layer,
"IN_FENCE_FD", state->primary_in_fence_fd); "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) { if (state->base->committed & WLR_OUTPUT_STATE_LAYERS) {
for (size_t i = 0; i < state->base->layers_len; i++) { 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); handle_x11_error(x11, ev);
break; break;
} }
case XCB_DESTROY_NOTIFY:
case XCB_UNMAP_NOTIFY: case XCB_UNMAP_NOTIFY:
case XCB_MAP_NOTIFY: case XCB_MAP_NOTIFY:
break; break;

View file

@ -29,8 +29,12 @@ struct wlr_drm_plane {
/* Buffer submitted to the kernel, will be presented on next vblank */ /* Buffer submitted to the kernel, will be presented on next vblank */
struct wlr_drm_fb *queued_fb; struct wlr_drm_fb *queued_fb;
struct wlr_drm_syncobj_timeline *queued_release_timeline;
uint64_t queued_release_point;
/* Buffer currently displayed on screen */ /* Buffer currently displayed on screen */
struct wlr_drm_fb *current_fb; 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 */ /* Viewport belonging to the last committed fb */
struct wlr_drm_viewport viewport; struct wlr_drm_viewport viewport;
@ -156,7 +160,7 @@ struct wlr_drm_connector_state {
uint32_t mode_id; uint32_t mode_id;
uint32_t gamma_lut; uint32_t gamma_lut;
uint32_t fb_damage_clips; uint32_t fb_damage_clips;
int primary_in_fence_fd, out_fence_fd; int primary_in_fence_fd;
bool vrr_enabled; bool vrr_enabled;
uint32_t colorspace; uint32_t colorspace;
uint32_t hdr_output_metadata; 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, bool wlr_drm_syncobj_timeline_check(struct wlr_drm_syncobj_timeline *timeline,
uint64_t point, uint32_t flags, bool *result); 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. * Asynchronously wait for a timeline point.
* *

View file

@ -127,6 +127,8 @@ struct wlr_surface_output {
struct { struct {
struct wl_listener bind; struct wl_listener bind;
struct wl_listener destroy; struct wl_listener destroy;
bool suspended;
} WLR_PRIVATE; } WLR_PRIVATE;
}; };

View file

@ -19,8 +19,12 @@ struct wlr_linux_drm_syncobj_surface_v1_state {
struct wlr_drm_syncobj_timeline *acquire_timeline; struct wlr_drm_syncobj_timeline *acquire_timeline;
uint64_t acquire_point; uint64_t acquire_point;
struct {
bool committed;
struct wlr_drm_syncobj_timeline *release_timeline; struct wlr_drm_syncobj_timeline *release_timeline;
uint64_t release_point; uint64_t release_point;
struct wlr_drm_syncobj_merger *release_merger;
} WLR_PRIVATE;
}; };
struct wlr_linux_drm_syncobj_manager_v1 { 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( bool wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer(
struct wlr_linux_drm_syncobj_surface_v1_state *state, struct wlr_buffer *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 #endif

View file

@ -131,8 +131,6 @@ struct wlr_scene_surface {
struct wlr_addon addon; struct wlr_addon addon;
struct wl_listener outputs_update; struct wl_listener outputs_update;
struct wl_listener output_enter;
struct wl_listener output_leave;
struct wl_listener output_sample; struct wl_listener output_sample;
struct wl_listener frame_done; struct wl_listener frame_done;
struct wl_listener surface_destroy; 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_sample_event {
struct wlr_scene_output *output; struct wlr_scene_output *output;
bool direct_scanout; bool direct_scanout;
struct wlr_drm_syncobj_timeline *release_timeline;
uint64_t release_point;
}; };
struct wlr_scene_frame_done_event { struct wlr_scene_frame_done_event {
@ -268,6 +268,8 @@ struct wlr_scene_output {
struct wlr_drm_syncobj_timeline *in_timeline; struct wlr_drm_syncobj_timeline *in_timeline;
uint64_t in_point; uint64_t in_point;
struct wlr_drm_syncobj_timeline *out_timeline;
uint64_t out_point;
} WLR_PRIVATE; } WLR_PRIVATE;
}; };

View file

@ -33,12 +33,27 @@ struct wlr_virtual_keyboard_v1 {
bool has_keymap; bool has_keymap;
struct wl_list link; // wlr_virtual_keyboard_manager_v1.virtual_keyboards 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 wlr_virtual_keyboard_manager_v1* wlr_virtual_keyboard_manager_v1_create(
struct wl_display *display); 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_virtual_keyboard_v1 *wlr_input_device_get_virtual_keyboard(
struct wlr_input_device *wlr_dev); struct wlr_input_device *wlr_dev);
#endif #endif

View file

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

View file

@ -192,7 +192,7 @@
<request name="destroy" type="destructor"> <request name="destroy" type="destructor">
<description summary="delete this object, used or not"> <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. longer used.
It can be called at any time by the client. The client will still have 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; 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) { static int handle_eventfd_ready(int ev_fd, uint32_t mask, void *data) {
struct wlr_drm_syncobj_timeline_waiter *waiter = 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', 'color.c',
'dmabuf.c', 'dmabuf.c',
'drm_format_set.c', 'drm_format_set.c',
'drm_syncobj_merger.c',
'drm_syncobj.c', 'drm_syncobj.c',
'pass.c', 'pass.c',
'pixel_format.c', 'pixel_format.c',
@ -28,6 +29,7 @@ else
endif endif
internal_config.set10('HAVE_EVENTFD', cc.has_header('sys/eventfd.h')) 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 if 'gles2' in renderers or 'auto' in renderers
egl = dependency('egl', required: 'gles2' in renderers) egl = dependency('egl', required: 'gles2' in renderers)

View file

@ -159,6 +159,7 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass,
switch (options->filter_mode) { switch (options->filter_mode) {
case WLR_SCALE_FILTER_BILINEAR: case WLR_SCALE_FILTER_BILINEAR:
pixman_image_set_repeat(texture->image, PIXMAN_REPEAT_PAD);
pixman_image_set_filter(texture->image, PIXMAN_FILTER_BILINEAR, NULL, 0); pixman_image_set_filter(texture->image, PIXMAN_FILTER_BILINEAR, NULL, 0);
break; break;
case WLR_SCALE_FILTER_NEAREST: case WLR_SCALE_FILTER_NEAREST:

View file

@ -44,7 +44,7 @@ const char *vulkan_strerror(VkResult err) {
ERR_STR(ERROR_INVALID_EXTERNAL_HANDLE); ERR_STR(ERROR_INVALID_EXTERNAL_HANDLE);
ERR_STR(ERROR_FRAGMENTATION); ERR_STR(ERROR_FRAGMENTATION);
ERR_STR(ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS); 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_SURFACE_LOST_KHR);
ERR_STR(ERROR_NATIVE_WINDOW_IN_USE_KHR); ERR_STR(ERROR_NATIVE_WINDOW_IN_USE_KHR);
ERR_STR(SUBOPTIMAL_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, .pQueuePriorities = &prio,
}; };
VkDeviceQueueGlobalPriorityCreateInfoKHR global_priority; VkDeviceQueueGlobalPriorityCreateInfoEXT global_priority;
bool has_global_priority = check_extension(avail_ext_props, avail_extc, 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 (has_global_priority) {
// If global priorities are supported, request a high-priority context // If global priorities are supported, request a high-priority context
global_priority = (VkDeviceQueueGlobalPriorityCreateInfoKHR){ global_priority = (VkDeviceQueueGlobalPriorityCreateInfoEXT){
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_KHR, .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_EXT,
.globalPriority = VK_QUEUE_GLOBAL_PRIORITY_HIGH_KHR, .globalPriority = VK_QUEUE_GLOBAL_PRIORITY_HIGH_EXT,
}; };
qinfo.pNext = &global_priority; 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"); wlr_log(WLR_DEBUG, "Requesting a high-priority device queue");
} else { } else {
wlr_log(WLR_DEBUG, "Global priorities are not supported, " wlr_log(WLR_DEBUG, "Global priorities are not supported, "

View file

@ -34,18 +34,13 @@ static void foreign_toplevel_manager_handle_create_source(struct wl_client *clie
return; return;
} }
struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request *request = struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request request = {
calloc(1, sizeof(*request)); .toplevel_handle = toplevel_handle,
if (request == NULL) { .client = client,
wl_resource_post_no_memory(manager_resource); .new_id = new_id,
return; };
}
request->toplevel_handle = toplevel_handle; wl_signal_emit_mutable(&manager->events.new_request, &request);
request->client = client;
request->new_id = new_id;
wl_signal_emit_mutable(&manager->events.new_request, request);
} }
static void foreign_toplevel_manager_handle_destroy(struct wl_client *client, static void foreign_toplevel_manager_handle_destroy(struct wl_client *client,

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) { static void source_update_buffer_constraints(struct wlr_ext_output_image_capture_source_v1 *source) {
struct wlr_output *output = source->output; struct wlr_output *output = source->output;
if (!output->enabled) {
return;
}
if (!wlr_output_configure_primary_swapchain(output, NULL, &output->swapchain)) { if (!wlr_output_configure_primary_swapchain(output, NULL, &output->swapchain)) {
return; 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_ext_output_image_capture_source_v1 *source = wl_container_of(listener, source, output_commit);
struct wlr_output_event_commit *event = data; 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); source_update_buffer_constraints(source);
} }

View file

@ -34,13 +34,14 @@ struct scene_node_source_frame_event {
static size_t last_output_num = 0; static size_t last_output_num = 0;
static void _get_scene_node_extents(struct wlr_scene_node *node, struct wlr_box *box, int lx, int ly) { static void _get_scene_node_extents(struct wlr_scene_node *node, int lx, int ly,
int *x_min, int *y_min, int *x_max, int *y_max) {
switch (node->type) { switch (node->type) {
case WLR_SCENE_NODE_TREE:; case WLR_SCENE_NODE_TREE:;
struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node);
struct wlr_scene_node *child; struct wlr_scene_node *child;
wl_list_for_each(child, &scene_tree->children, link) { wl_list_for_each(child, &scene_tree->children, link) {
_get_scene_node_extents(child, box, lx + child->x, ly + child->y); _get_scene_node_extents(child, lx + child->x, ly + child->y, x_min, y_min, x_max, y_max);
} }
break; break;
case WLR_SCENE_NODE_RECT: case WLR_SCENE_NODE_RECT:
@ -48,27 +49,30 @@ static void _get_scene_node_extents(struct wlr_scene_node *node, struct wlr_box
struct wlr_box node_box = { .x = lx, .y = ly }; struct wlr_box node_box = { .x = lx, .y = ly };
scene_node_get_size(node, &node_box.width, &node_box.height); scene_node_get_size(node, &node_box.width, &node_box.height);
if (node_box.x < box->x) { if (node_box.x < *x_min) {
box->x = node_box.x; *x_min = node_box.x;
} }
if (node_box.y < box->y) { if (node_box.y < *y_min) {
box->y = node_box.y; *y_min = node_box.y;
} }
if (node_box.x + node_box.width > box->x + box->width) { if (node_box.x + node_box.width > *x_max) {
box->width = node_box.x + node_box.width - box->x; *x_max = node_box.x + node_box.width;
} }
if (node_box.y + node_box.height > box->y + box->height) { if (node_box.y + node_box.height > *y_max) {
box->height = node_box.y + node_box.height - box->y; *y_max = node_box.y + node_box.height;
} }
break; break;
} }
} }
static void get_scene_node_extents(struct wlr_scene_node *node, struct wlr_box *box) { static void get_scene_node_extents(struct wlr_scene_node *node, struct wlr_box *box) {
*box = (struct wlr_box){ .x = INT_MAX, .y = INT_MAX };
int lx = 0, ly = 0; int lx = 0, ly = 0;
wlr_scene_node_coords(node, &lx, &ly); wlr_scene_node_coords(node, &lx, &ly);
_get_scene_node_extents(node, box, lx, ly); *box = (struct wlr_box){ .x = INT_MAX, .y = INT_MAX };
int x_max = INT_MIN, y_max = INT_MIN;
_get_scene_node_extents(node, lx, ly, &box->x, &box->y, &x_max, &y_max);
box->width = x_max - box->x;
box->height = y_max - box->y;
} }
static void source_render(struct scene_node_source *source) { static void source_render(struct scene_node_source *source) {

View file

@ -30,15 +30,15 @@ static struct wlr_output *get_surface_frame_pacing_output(struct wlr_surface *su
struct wlr_output *frame_pacing_output = NULL; struct wlr_output *frame_pacing_output = NULL;
struct wlr_surface_output *surface_output; struct wlr_surface_output *surface_output;
wl_list_for_each(surface_output, &surface->current_outputs, link) { wl_list_for_each(surface_output, &surface->current_outputs, link) {
if (frame_pacing_output == NULL || if (!surface_output->suspended && (frame_pacing_output == NULL ||
surface_output->output->refresh > frame_pacing_output->refresh) { surface_output->output->refresh > frame_pacing_output->refresh)) {
frame_pacing_output = surface_output->output; frame_pacing_output = surface_output->output;
} }
} }
return frame_pacing_output; 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) { switch (tf) {
case WLR_COLOR_TRANSFER_FUNCTION_GAMMA22: case WLR_COLOR_TRANSFER_FUNCTION_GAMMA22:
return 0; return 0;
@ -52,7 +52,7 @@ static bool get_tf_preference(enum wlr_color_transfer_function tf) {
abort(); // unreachable 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) { switch (primaries) {
case WLR_COLOR_NAMED_PRIMARIES_SRGB: case WLR_COLOR_NAMED_PRIMARIES_SRGB:
return 0; return 0;
@ -94,14 +94,55 @@ static void handle_scene_buffer_outputs_update(
struct wl_listener *listener, void *data) { struct wl_listener *listener, void *data) {
struct wlr_scene_surface *surface = struct wlr_scene_surface *surface =
wl_container_of(listener, surface, outputs_update); 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); 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 // If the surface is no longer visible on any output in the scene, keep the
// preferred configuration to avoid unnecessary redraws // last sent preferred configuration to avoid unnecessary redraws
if (wl_list_empty(&surface->surface->current_outputs)) { bool suspend = event->size == 0;
// 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;
}
}
struct wlr_scene_output *scene_output;
wl_list_for_each(scene_output, &scene->outputs, link) {
if (scene_output->output == entered_output->output) {
entered_output->suspended = suspend;
if (!suspend && !active) {
wlr_surface_send_leave(surface->surface, entered_output->output);
}
break;
}
}
}
// No reason to update the preferred configuration if we aren't sending leave/enter events.
if (suspend) {
return; return;
} }
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); double scale = get_surface_preferred_buffer_scale(surface->surface);
wlr_fractional_scale_v1_notify_scale(surface->surface, 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_scale(surface->surface, ceil(scale));
@ -114,24 +155,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( static void handle_scene_buffer_output_sample(
struct wl_listener *listener, void *data) { struct wl_listener *listener, void *data) {
struct wlr_scene_surface *surface = struct wlr_scene_surface *surface =
@ -147,6 +170,13 @@ static void handle_scene_buffer_output_sample(
} else { } else {
wlr_presentation_surface_textured_on_output(surface->surface, output); 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( static void handle_scene_buffer_frame_done(
@ -311,27 +341,15 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) {
struct wlr_linux_drm_syncobj_surface_v1_state *syncobj_surface_state = struct wlr_linux_drm_syncobj_surface_v1_state *syncobj_surface_state =
wlr_linux_drm_syncobj_v1_get_surface_state(surface); 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 = { struct wlr_scene_buffer_set_buffer_options options = {
.damage = &surface->buffer_damage, .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, wlr_scene_buffer_set_buffer_with_options(scene_buffer,
&surface->buffer->base, &options); &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 { } else {
wlr_scene_buffer_set_buffer(scene_buffer, NULL); wlr_scene_buffer_set_buffer(scene_buffer, NULL);
} }
@ -354,10 +372,9 @@ static void handle_scene_surface_surface_commit(
// the surface anyway. // the surface anyway.
int lx, ly; int lx, ly;
bool enabled = wlr_scene_node_coords(&scene_buffer->node, &lx, &ly); bool enabled = wlr_scene_node_coords(&scene_buffer->node, &lx, &ly);
struct wlr_output *output = get_surface_frame_pacing_output(surface->surface);
if (!wl_list_empty(&surface->surface->current.frame_callback_list) && if (!wl_list_empty(&surface->surface->current.frame_callback_list) && output && enabled) {
surface->buffer->primary_output != NULL && enabled) { wlr_output_schedule_frame(output);
wlr_output_schedule_frame(surface->buffer->primary_output->output);
} }
} }
@ -380,8 +397,6 @@ static void surface_addon_destroy(struct wlr_addon *addon) {
wlr_addon_finish(&surface->addon); wlr_addon_finish(&surface->addon);
wl_list_remove(&surface->outputs_update.link); 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->output_sample.link);
wl_list_remove(&surface->frame_done.link); wl_list_remove(&surface->frame_done.link);
wl_list_remove(&surface->surface_destroy.link); wl_list_remove(&surface->surface_destroy.link);
@ -427,12 +442,6 @@ struct wlr_scene_surface *wlr_scene_surface_create(struct wlr_scene_tree *parent
surface->outputs_update.notify = handle_scene_buffer_outputs_update; surface->outputs_update.notify = handle_scene_buffer_outputs_update;
wl_signal_add(&scene_buffer->events.outputs_update, &surface->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; surface->output_sample.notify = handle_scene_buffer_output_sample;
wl_signal_add(&scene_buffer->events.output_sample, &surface->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 = { struct wlr_scene_output_sample_event sample_event = {
.output = data->output, .output = data->output,
.direct_scanout = false, .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); 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 && if (drm_fd >= 0 && output->backend->features.timeline &&
output->renderer != NULL && output->renderer->features.timeline) { output->renderer != NULL && output->renderer->features.timeline) {
scene_output->in_timeline = wlr_drm_syncobj_timeline_create(drm_fd); 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; 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_commit.link);
wl_list_remove(&scene_output->output_damage.link); wl_list_remove(&scene_output->output_damage.link);
wl_list_remove(&scene_output->output_needs_frame.link); wl_list_remove(&scene_output->output_needs_frame.link);
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); 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->gamma_lut_color_transform);
wlr_color_transform_unref(scene_output->prev_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); 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) { if (buffer->wait_timeline != NULL) {
wlr_output_state_set_wait_timeline(&pending, buffer->wait_timeline, buffer->wait_point); 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)) { if (!wlr_output_test_state(scene_output->output, &pending)) {
wlr_output_state_finish(&pending); wlr_output_state_finish(&pending);
return SCANOUT_CANDIDATE; 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 = { struct wlr_scene_output_sample_event sample_event = {
.output = scene_output, .output = scene_output,
.direct_scanout = true, .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); wl_signal_emit_mutable(&buffer->events.output_sample, &sample_event);
return SCANOUT_SUCCESS; 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) { if (scene_output->in_timeline != NULL) {
wlr_output_state_set_wait_timeline(state, scene_output->in_timeline, wlr_output_state_set_wait_timeline(state, scene_output->in_timeline,
scene_output->in_point); 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) { if (!render_gamma_lut) {

View file

@ -74,6 +74,41 @@ static float decode_cie1931_coord(int32_t raw) {
return (float)raw / (1000 * 1000); 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 const struct wp_image_description_v1_interface image_desc_impl;
static struct wlr_image_description_v1 *image_desc_from_resource(struct wl_resource *resource) { static struct wlr_image_description_v1 *image_desc_from_resource(struct wl_resource *resource) {
@ -1002,8 +1037,13 @@ void wlr_color_manager_v1_set_surface_preferred_image_description(
struct wlr_color_management_surface_feedback_v1 *surface_feedback; struct wlr_color_management_surface_feedback_v1 *surface_feedback;
wl_list_for_each(surface_feedback, &manager->surface_feedbacks, link) { wl_list_for_each(surface_feedback, &manager->surface_feedbacks, link) {
if (surface_feedback->surface == surface) { if (surface_feedback->surface != surface ||
img_desc_data_equal(&surface_feedback->data, data)) {
continue;
}
surface_feedback->data = *data; surface_feedback->data = *data;
uint32_t version = wl_resource_get_version(surface_feedback->resource); uint32_t version = wl_resource_get_version(surface_feedback->resource);
if (version >= WP_COLOR_MANAGEMENT_SURFACE_FEEDBACK_V1_PREFERRED_CHANGED2_SINCE_VERSION) { if (version >= WP_COLOR_MANAGEMENT_SURFACE_FEEDBACK_V1_PREFERRED_CHANGED2_SINCE_VERSION) {
wp_color_management_surface_feedback_v1_send_preferred_changed2( wp_color_management_surface_feedback_v1_send_preferred_changed2(
@ -1013,7 +1053,6 @@ void wlr_color_manager_v1_set_surface_preferred_image_description(
surface_feedback->resource, identity_lo); surface_feedback->resource, identity_lo);
} }
} }
}
} }
enum wlr_color_transfer_function enum wlr_color_transfer_function

View file

@ -246,40 +246,40 @@ static void surface_finalize_pending(struct wlr_surface *surface) {
} }
static void surface_update_damage(pixman_region32_t *buffer_damage, static void surface_update_damage(pixman_region32_t *buffer_damage,
struct wlr_surface_state *current, struct wlr_surface_state *pending) { struct wlr_surface_state *state) {
pixman_region32_clear(buffer_damage); pixman_region32_clear(buffer_damage);
// Copy over surface damage + buffer damage // Copy over surface damage + buffer damage
pixman_region32_t surface_damage; pixman_region32_t surface_damage;
pixman_region32_init(&surface_damage); pixman_region32_init(&surface_damage);
pixman_region32_copy(&surface_damage, &pending->surface_damage); pixman_region32_copy(&surface_damage, &state->surface_damage);
if (pending->viewport.has_dst) { if (state->viewport.has_dst) {
int src_width, src_height; int src_width, src_height;
surface_state_viewport_src_size(pending, &src_width, &src_height); surface_state_viewport_src_size(state, &src_width, &src_height);
float scale_x = (float)pending->viewport.dst_width / src_width; float scale_x = (float)state->viewport.dst_width / src_width;
float scale_y = (float)pending->viewport.dst_height / src_height; float scale_y = (float)state->viewport.dst_height / src_height;
wlr_region_scale_xy(&surface_damage, &surface_damage, wlr_region_scale_xy(&surface_damage, &surface_damage,
1.0 / scale_x, 1.0 / scale_y); 1.0 / scale_x, 1.0 / scale_y);
} }
if (pending->viewport.has_src) { if (state->viewport.has_src) {
// This is lossy: do a best-effort conversion // This is lossy: do a best-effort conversion
pixman_region32_translate(&surface_damage, pixman_region32_translate(&surface_damage,
floor(pending->viewport.src.x), floor(state->viewport.src.x),
floor(pending->viewport.src.y)); floor(state->viewport.src.y));
} }
wlr_region_scale(&surface_damage, &surface_damage, pending->scale); wlr_region_scale(&surface_damage, &surface_damage, state->scale);
int width, height; int width, height;
surface_state_transformed_buffer_size(pending, &width, &height); surface_state_transformed_buffer_size(state, &width, &height);
wlr_region_transform(&surface_damage, &surface_damage, wlr_region_transform(&surface_damage, &surface_damage,
wlr_output_transform_invert(pending->transform), wlr_output_transform_invert(state->transform),
width, height); width, height);
pixman_region32_union(buffer_damage, pixman_region32_union(buffer_damage,
&pending->buffer_damage, &surface_damage); &state->buffer_damage, &surface_damage);
pixman_region32_fini(&surface_damage); pixman_region32_fini(&surface_damage);
} }
@ -521,8 +521,6 @@ static void surface_commit_state(struct wlr_surface *surface,
surface->unmap_commit = false; surface->unmap_commit = false;
} }
surface_update_damage(&surface->buffer_damage, &surface->current, next);
surface->previous.scale = surface->current.scale; surface->previous.scale = surface->current.scale;
surface->previous.transform = surface->current.transform; surface->previous.transform = surface->current.transform;
surface->previous.width = surface->current.width; surface->previous.width = surface->current.width;
@ -531,6 +529,7 @@ static void surface_commit_state(struct wlr_surface *surface,
surface->previous.buffer_height = surface->current.buffer_height; surface->previous.buffer_height = surface->current.buffer_height;
surface_state_move(&surface->current, next, surface); surface_state_move(&surface->current, next, surface);
surface_update_damage(&surface->buffer_damage, &surface->current);
if (invalid_buffer) { if (invalid_buffer) {
surface_apply_damage(surface); surface_apply_damage(surface);

View file

@ -168,7 +168,7 @@ wlr_ext_foreign_toplevel_handle_v1_create(struct wlr_ext_foreign_toplevel_list_v
return NULL; return NULL;
} }
wl_list_insert(&list->toplevels, &toplevel->link); wl_list_insert(list->toplevels.prev, &toplevel->link);
toplevel->list = list; toplevel->list = list;
if (state->app_id) { if (state->app_id) {
toplevel->app_id = strdup(state->app_id); toplevel->app_id = strdup(state->app_id);

View file

@ -518,14 +518,16 @@ static void cursor_session_handle_get_capture_session(struct wl_client *client,
return; 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; struct wlr_ext_image_capture_source_v1 *source = NULL;
if (cursor_session != NULL) { if (cursor_session != NULL) {
manager = cursor_session->manager;
cursor_session->capture_session_created = true;
source = &cursor_session->source->base; 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 = { static const struct ext_image_copy_capture_cursor_session_v1_interface cursor_session_impl = {

View file

@ -790,7 +790,7 @@ struct wlr_ext_workspace_handle_v1 *wlr_ext_workspace_handle_v1_create(
wl_array_init(&workspace->coordinates); wl_array_init(&workspace->coordinates);
wl_signal_init(&workspace->events.destroy); wl_signal_init(&workspace->events.destroy);
wl_list_insert(&manager->workspaces, &workspace->link); wl_list_insert(manager->workspaces.prev, &workspace->link);
struct wlr_ext_workspace_manager_v1_resource *manager_res; struct wlr_ext_workspace_manager_v1_resource *manager_res;
wl_list_for_each(manager_res, &manager->resources, link) { wl_list_for_each(manager_res, &manager->resources, link) {

View file

@ -530,7 +530,7 @@ wlr_foreign_toplevel_handle_v1_create(
return NULL; return NULL;
} }
wl_list_insert(&manager->toplevels, &toplevel->link); wl_list_insert(manager->toplevels.prev, &toplevel->link);
toplevel->manager = manager; toplevel->manager = manager;
wl_list_init(&toplevel->resources); wl_list_init(&toplevel->resources);

View file

@ -84,6 +84,16 @@ void wlr_keyboard_notify_modifiers(struct wlr_keyboard *keyboard,
uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked,
uint32_t group) { uint32_t group) {
if (keyboard->xkb_state == NULL) { if (keyboard->xkb_state == NULL) {
if (keyboard->modifiers.depressed != mods_depressed ||
keyboard->modifiers.latched != mods_latched ||
keyboard->modifiers.locked != mods_locked ||
keyboard->modifiers.group != group) {
keyboard->modifiers.depressed = mods_depressed;
keyboard->modifiers.latched = mods_latched;
keyboard->modifiers.locked = mods_locked;
keyboard->modifiers.group = group;
wl_signal_emit_mutable(&keyboard->events.modifiers, keyboard);
}
return; return;
} }
xkb_state_update_mask(keyboard->xkb_state, mods_depressed, mods_latched, xkb_state_update_mask(keyboard->xkb_state, mods_depressed, mods_latched,

View file

@ -3,6 +3,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <wayland-server-core.h>
#include <wlr/render/drm_syncobj.h> #include <wlr/render/drm_syncobj.h>
#include <wlr/types/wlr_buffer.h> #include <wlr/types/wlr_buffer.h>
#include <wlr/types/wlr_compositor.h> #include <wlr/types/wlr_compositor.h>
@ -11,6 +12,7 @@
#include <xf86drm.h> #include <xf86drm.h>
#include "config.h" #include "config.h"
#include "linux-drm-syncobj-v1-protocol.h" #include "linux-drm-syncobj-v1-protocol.h"
#include "render/drm_syncobj_merger.h"
#define LINUX_DRM_SYNCOBJ_V1_VERSION 1 #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; struct wlr_linux_drm_syncobj_surface_v1_state *state = _state;
wlr_drm_syncobj_timeline_unref(state->acquire_timeline); wlr_drm_syncobj_timeline_unref(state->acquire_timeline);
wlr_drm_syncobj_timeline_unref(state->release_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) { static void surface_synced_move_state(void *_dst, void *_src) {
struct wlr_linux_drm_syncobj_surface_v1_state *dst = _dst, *src = _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); surface_synced_finish_state(dst);
*dst = *src; *dst = *src;
dst->committed = true;
*src = (struct wlr_linux_drm_syncobj_surface_v1_state){0}; *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 = { static const struct wlr_surface_synced_impl surface_synced_impl = {
.state_size = sizeof(struct wlr_linux_drm_syncobj_surface_v1_state), .state_size = sizeof(struct wlr_linux_drm_syncobj_surface_v1_state),
.finish_state = surface_synced_finish_state, .finish_state = surface_synced_finish_state,
.move_state = surface_synced_move_state, .move_state = surface_synced_move_state,
.commit = surface_synced_commit,
}; };
static void manager_handle_destroy(struct wl_client *client, static void manager_handle_destroy(struct wl_client *client,
@ -365,6 +386,7 @@ static void manager_handle_import_timeline(struct wl_client *client,
struct wl_resource *timeline_resource = wl_resource_create(client, struct wl_resource *timeline_resource = wl_resource_create(client,
&wp_linux_drm_syncobj_timeline_v1_interface, version, id); &wp_linux_drm_syncobj_timeline_v1_interface, version, id);
if (timeline_resource == NULL) { if (timeline_resource == NULL) {
wlr_drm_syncobj_timeline_unref(timeline);
wl_resource_post_no_memory(resource); wl_resource_post_no_memory(resource);
return; return;
} }
@ -422,6 +444,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) { struct wl_display *display, uint32_t version, int drm_fd) {
assert(version <= LINUX_DRM_SYNCOBJ_V1_VERSION); 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)) { if (!check_syncobj_eventfd(drm_fd)) {
wlr_log(WLR_INFO, "DRM syncobj eventfd unavailable, disabling linux-drm-syncobj-v1"); wlr_log(WLR_INFO, "DRM syncobj eventfd unavailable, disabling linux-drm-syncobj-v1");
return NULL; return NULL;
@ -467,20 +494,14 @@ wlr_linux_drm_syncobj_v1_get_surface_state(struct wlr_surface *wlr_surface) {
} }
struct release_signaller { struct release_signaller {
struct wlr_drm_syncobj_timeline *timeline; struct wlr_drm_syncobj_merger *merger;
uint64_t point;
struct wl_listener buffer_release; struct wl_listener buffer_release;
}; };
static void release_signaller_handle_buffer_release(struct wl_listener *listener, void *data) { static void release_signaller_handle_buffer_release(struct wl_listener *listener, void *data) {
struct release_signaller *signaller = wl_container_of(listener, signaller, buffer_release); struct release_signaller *signaller = wl_container_of(listener, signaller, buffer_release);
if (drmSyncobjTimelineSignal(signaller->timeline->drm_fd, &signaller->timeline->handle, wlr_drm_syncobj_merger_unref(signaller->merger);
&signaller->point, 1) != 0) {
wlr_log(WLR_ERROR, "drmSyncobjTimelineSignal() failed");
}
wlr_drm_syncobj_timeline_unref(signaller->timeline);
wl_list_remove(&signaller->buffer_release.link); wl_list_remove(&signaller->buffer_release.link);
free(signaller); free(signaller);
} }
@ -488,7 +509,7 @@ static void release_signaller_handle_buffer_release(struct wl_listener *listener
bool wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer( bool wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer(
struct wlr_linux_drm_syncobj_surface_v1_state *state, struct wlr_buffer *buffer) { struct wlr_linux_drm_syncobj_surface_v1_state *state, struct wlr_buffer *buffer) {
assert(buffer->n_locks > 0); 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 // This can happen if an existing surface with a buffer has a
// syncobj_surface_v1_state created but no new buffer with release // syncobj_surface_v1_state created but no new buffer with release
// timeline committed. // timeline committed.
@ -500,11 +521,23 @@ bool wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer(
return false; return false;
} }
signaller->timeline = wlr_drm_syncobj_timeline_ref(state->release_timeline); signaller->merger = wlr_drm_syncobj_merger_ref(state->release_merger);
signaller->point = state->release_point;
signaller->buffer_release.notify = release_signaller_handle_buffer_release; signaller->buffer_release.notify = release_signaller_handle_buffer_release;
wl_signal_add(&buffer->events.release, &signaller->buffer_release); wl_signal_add(&buffer->events.release, &signaller->buffer_release);
return true; 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 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) { struct wl_resource *resource) {
assert(wl_resource_instance_of(resource, assert(wl_resource_instance_of(resource,
&zwp_virtual_keyboard_v1_interface, &virtual_keyboard_impl)); &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, struct wl_resource *resource, uint32_t format, int32_t fd,
uint32_t size) { uint32_t size) {
struct wlr_virtual_keyboard_v1 *keyboard = struct wlr_virtual_keyboard_v1 *keyboard =
virtual_keyboard_from_resource(resource); wlr_virtual_keyboard_v1_from_resource(resource);
if (keyboard == NULL) { if (keyboard == NULL) {
return; return;
} }
@ -52,7 +52,7 @@ static void virtual_keyboard_keymap(struct wl_client *client,
if (data == MAP_FAILED) { if (data == MAP_FAILED) {
goto fd_fail; goto fd_fail;
} }
struct xkb_keymap *keymap = xkb_keymap_new_from_string(context, data, struct xkb_keymap *keymap = xkb_keymap_new_from_buffer(context, data, size,
XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
munmap(data, size); munmap(data, size);
if (!keymap) { if (!keymap) {
@ -76,7 +76,7 @@ static void virtual_keyboard_key(struct wl_client *client,
struct wl_resource *resource, uint32_t time, uint32_t key, struct wl_resource *resource, uint32_t time, uint32_t key,
uint32_t state) { uint32_t state) {
struct wlr_virtual_keyboard_v1 *keyboard = struct wlr_virtual_keyboard_v1 *keyboard =
virtual_keyboard_from_resource(resource); wlr_virtual_keyboard_v1_from_resource(resource);
if (keyboard == NULL) { if (keyboard == NULL) {
return; return;
} }
@ -99,7 +99,7 @@ static void virtual_keyboard_modifiers(struct wl_client *client,
struct wl_resource *resource, uint32_t mods_depressed, struct wl_resource *resource, uint32_t mods_depressed,
uint32_t mods_latched, uint32_t mods_locked, uint32_t group) { uint32_t mods_latched, uint32_t mods_locked, uint32_t group) {
struct wlr_virtual_keyboard_v1 *keyboard = struct wlr_virtual_keyboard_v1 *keyboard =
virtual_keyboard_from_resource(resource); wlr_virtual_keyboard_v1_from_resource(resource);
if (keyboard == NULL) { if (keyboard == NULL) {
return; return;
} }
@ -113,21 +113,24 @@ static void virtual_keyboard_modifiers(struct wl_client *client,
mods_depressed, mods_latched, mods_locked, group); mods_depressed, mods_latched, mods_locked, group);
} }
static void virtual_keyboard_destroy_resource(struct wl_resource *resource) { static void virtual_keyboard_destroy(struct wlr_virtual_keyboard_v1 *virtual_keyboard) {
struct wlr_virtual_keyboard_v1 *keyboard = wlr_keyboard_finish(&virtual_keyboard->keyboard);
virtual_keyboard_from_resource(resource); wl_resource_set_user_data(virtual_keyboard->resource, NULL);
if (keyboard == NULL) { wl_list_remove(&virtual_keyboard->seat_destroy.link);
return; wl_list_remove(&virtual_keyboard->link);
} free(virtual_keyboard);
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 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) { struct wl_resource *resource) {
wl_resource_destroy(resource); wl_resource_destroy(resource);
} }
@ -136,7 +139,7 @@ static const struct zwp_virtual_keyboard_v1_interface virtual_keyboard_impl = {
.keymap = virtual_keyboard_keymap, .keymap = virtual_keyboard_keymap,
.key = virtual_keyboard_key, .key = virtual_keyboard_key,
.modifiers = virtual_keyboard_modifiers, .modifiers = virtual_keyboard_modifiers,
.destroy = virtual_keyboard_destroy, .destroy = virtual_keyboard_handle_destroy,
}; };
static const struct zwp_virtual_keyboard_manager_v1_interface manager_impl; 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); 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( static void virtual_keyboard_manager_create_virtual_keyboard(
struct wl_client *client, struct wl_resource *resource, struct wl_client *client, struct wl_resource *resource,
struct wl_resource *seat, uint32_t id) { 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; virtual_keyboard->seat = seat_client->seat;
wl_resource_set_user_data(keyboard_resource, virtual_keyboard); 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_list_insert(&manager->virtual_keyboards, &virtual_keyboard->link);
wl_signal_emit_mutable(&manager->events.new_virtual_keyboard, wl_signal_emit_mutable(&manager->events.new_virtual_keyboard,

View file

@ -4,6 +4,14 @@
#include <wlr/util/box.h> #include <wlr/util/box.h>
#include <wlr/util/log.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, void wlr_box_closest_point(const struct wlr_box *box, double x, double y,
double *dest_x, double *dest_y) { double *dest_x, double *dest_y) {
// if box is empty, then it contains no points, so no closest point either // 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; return false;
} }
int x1 = fmax(box_a->x, box_b->x); int x1 = max(box_a->x, box_b->x);
int y1 = fmax(box_a->y, box_b->y); int y1 = max(box_a->y, box_b->y);
int x2 = fmin(box_a->x + box_a->width, box_b->x + box_b->width); int x2 = min(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 y2 = min(box_a->y + box_a->height, box_b->y + box_b->height);
dest->x = x1; dest->x = x1;
dest->y = y1; dest->y = y1;