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