Commitfbcd7a10Recorded29 Oct 2026Repositorysigil-wm

Apply hidden placements promptly and hold relocated output presentation

Changed
 src/c/host.c             | 16 ++++++++++------
 src/c/output-batch.h     |  5 +++++
 test/plan-presentation.c | 37 ++++++++++++++++++++++++++++++++++---
 3 files changed, 49 insertions(+), 9 deletions(-)
Diff
src/c/host.cmodified
@@ -1468,7 +1468,7 @@ static void check_plan(struct server *s) {
1468
if(id<0) { if(!find_pixels(s,-id)) { reject_plan_object(s,2,id); return; } continue; }
1469
struct view *v=find_view(s,id);
1470
if(!v) { reject_plan_object(s,2,id); return; }
−1471
if(v->mapped && !view_configured(v)) ready=false;
+1471
if(plan_waits_for_view(s,v)) ready=false;
1472
}
1473
if(!ready && now_ms()<s->plan_deadline) return;
1474
release_saved(s);
@@ -1576,6 +1576,13 @@ static void output_buffer_filter(struct wlr_scene_buffer *buffer,int x,int y,voi
1576
struct output *output=data;
1577
buffer->filter_mode=output->nearest_filter ? WLR_SCALE_FILTER_NEAREST : WLR_SCALE_FILTER_BILINEAR;
1578
}
+1579
static void output_frame_done(struct output *output,struct timespec *now) {
+1580
wlr_scene_output_send_frame_done(output->scene,now);
+1581
struct view *view;
+1582
wl_list_for_each(view,&output->server->views,link)
+1583
if(view->saved || plan_waits_for_view(output->server,view))
+1584
view_each_surface(view,frame_done,now);
+1585
}
1586
static void output_frame(struct wl_listener *listener,void *data) {
1587
(void)data;
1588
struct output *o=wl_container_of(listener,o,frame);
@@ -1586,7 +1593,7 @@ static void output_frame(struct wl_listener *listener,void *data) {
1593
* decorations and their complete scene are being prepared. */
1594
struct timespec now;
1595
clock_gettime(CLOCK_MONOTONIC, &now);
−1589
wlr_scene_output_send_frame_done(o->scene, &now);
+1596
output_frame_done(o,&now);
1597
return;
1598
}
1599
wlr_scene_output_for_each_buffer(o->scene,output_buffer_filter,o);
@@ -1601,10 +1608,7 @@ static void output_frame(struct wl_listener *listener,void *data) {
1608
o->scene_retry_at=0;
1609
struct timespec now;
1610
clock_gettime(CLOCK_MONOTONIC,&now);
−1604
wlr_scene_output_send_frame_done(o->scene,&now);
−1605
struct view *v;
−1606
wl_list_for_each(v,&o->server->views,link)
−1607
if(v->saved || plan_waits_for_view(o->server,v)) view_each_surface(v,frame_done,&now);
+1611
output_frame_done(o,&now);
1612
atomic_fetch_add(&bridge.frames,1);
1613
}
1614
static void output_readback(struct wl_listener *listener,void *data) {
src/c/output-batch.hmodified
@@ -32,6 +32,11 @@ static void finish_backend_states(struct wlr_backend_output_state *states,size_t
32
free(states);
33
}
34
static void place_output_state(struct server *s,struct output *output,int x,int y) {
+35
struct wlr_output_layout_output *previous=wlr_output_layout_get(s->layout,output->native);
+36
/* Keep the last frame until policy moves the scene into the new region. */
+37
if(previous && output->configured_enabled && (previous->x!=x || previous->y!=y) &&
+38
!s->scene_update_deadline)
+39
s->scene_update_deadline=now_ms()+200;
40
output->x=x; output->y=y;
41
if(!output->configured_enabled) {
42
wlr_output_layout_remove(s->layout,output->native);
test/plan-presentation.cmodified
@@ -2,9 +2,14 @@
2
* Exercise native readiness and output placement with real scene/layout nodes.
3
* Observe scene commits without rendering a buffer. */
4
#define wlr_scene_output_commit fixture_scene_commit
+5
#define wlr_xdg_surface_for_each_surface fixture_each_surface
6
#include "../src/c/host.c"
7
#include <assert.h>
−7
static unsigned commits;
+8
static unsigned commits,callbacks;
+9
void fixture_each_surface(struct wlr_xdg_surface *surface,
+10
wlr_surface_iterator_func_t iterator,void *data) {
+11
(void)surface; (void)iterator; assert(data); callbacks++;
+12
}
13
bool fixture_scene_commit(struct wlr_scene_output *output,
14
const struct wlr_scene_output_state_options *options) {
15
(void)output; (void)options; commits++; return true;
@@ -71,14 +76,40 @@ static void relocation(void) {
76
place_output_state(&s,&output,0,0);
77
output_frame(&output.frame,NULL);
78
assert(commits==before && "RELOCATED_OUTPUT_MUST_WAIT_FOR_SCENE_PLAN");
−74
int64_t words[]={1,0,0,200};
+79
struct wlr_xdg_surface surface={0};
+80
struct wlr_xdg_toplevel toplevel={.base=&surface};
+81
struct view view={.id=2,.server=&s,.mapped=true,.native=&toplevel,.serial=24};
+82
view.tree=wlr_scene_tree_create(s.normal); assert(view.tree);
+83
wlr_scene_node_set_position(&view.tree->node,320,0);
+84
wl_list_insert(&s.views,&view.link);
+85
surface.current.configure_serial=21;
+86
int64_t words[]={1,1,0,200,2,0,0,300,200,1,0,0,0,0};
+87
s.plan_token=101; s.plan_deadline=now_ms()+200;
88
s.plan=malloc(sizeof(words)); assert(s.plan); memcpy(s.plan,words,sizeof(words));
+89
check_plan(&s); assert(s.plan);
+90
output_frame(&output.frame,NULL);
+91
assert(commits==before && callbacks==1 && "HELD_OUTPUT_MUST_DELIVER_PLAN_CALLBACKS");
+92
surface.current.configure_serial=24;
93
check_plan(&s);
+94
assert(!s.plan && now_ms()<s.plan_deadline);
+95
struct wm_message event; assert(pop(&bridge.events,&event));
+96
assert(event.word[0]==22 && event.word[2]==0);
97
output_frame(&output.frame,NULL);
98
assert(commits==before+1 && "APPLIED_PLAN_MUST_RELEASE_PRESENTATION");
+99
place_output_state(&s,&output,0,0);
+100
assert(!s.scene_update_deadline && "UNCHANGED_POSITION_MUST_NOT_HOLD");
+101
place_output_state(&s,&output,320,0);
+102
uint64_t hold=s.scene_update_deadline;
+103
assert(hold && hold<=now_ms()+200);
+104
place_output_state(&s,&output,0,0);
+105
assert(s.scene_update_deadline==hold && "RELOCATION_MUST_NOT_EXTEND_HOLD");
+106
s.scene_update_deadline=now_ms();
+107
output_frame(&output.frame,NULL);
+108
assert(commits==before+2 && "EXPIRED_HOLD_MUST_ALLOW_PRESENTATION");
+109
wl_list_remove(&view.link);
110
wl_list_remove(&output.link);
111
wlr_backend_destroy(s.backend); finish(&s);
−81
puts("OUTPUT_RELOCATION=PASS presentation-held-until-plan");
+112
puts("OUTPUT_RELOCATION=PASS presentation-held-until-plan configure-callback-progress");
113
}
114
int main(int argc,char **argv) {
115
assert(argc==2);