Commit20f6ad51Recorded29 Oct 2026Repositorysigil-wm

Complete stale pointer move admissions as cancelled

Changed
 README.md          |  3 +++
 src/c/scene-move.h | 29 ++++++++++++++++++++---------
 test/scene-move.c  | 14 +++++++++++---
 3 files changed, 34 insertions(+), 12 deletions(-)
Diff
README.mdmodified
@@ -5,6 +5,9 @@
5
A toolkit for writing Wayland window managers in Sigil, built on wlroots.
6
The native thread owns Wayland objects, input transport and scene presentation.
7
+8
Copied admissions that arrive after release, lock, or resource retirement complete
+9
with a cancelled `scene-move-ended` event.
+10
11
Interactive scene translation uses `start-scene-move!` with a window, held
12
button, pointer anchor and an explicit set of window/pixel resources (at most
13
256). The native owner follows pointer motion without a manager round trip or
src/c/scene-move.hmodified
@@ -57,12 +57,20 @@ static bool scene_move_motion(struct server *s,uint32_t time,double x,double y,
57
return true;
58
}
59
static bool scene_move_start(struct server *s,uint64_t token,const int64_t *w,size_t n) {
−60
if(s->lock_state || n<6 || w[0]<=0 || w[1]<272 || w[1]>=288 ||
+60
if(n<6 || w[0]<=0 || w[1]<272 || w[1]>=288 ||
61
w[2]<-32768000 || w[2]>32768000 || w[3]<-32768000 || w[3]>32768000 ||
62
w[4]<1 || w[4]>256 || n!=5+(size_t)w[4]) return false;
+63
/* Validate copied shape before checking state that can retire in transit. */
+64
bool owner=false;
+65
for(unsigned i=0;i<(unsigned)w[4];i++) {
+66
int64_t id=w[5+i];
+67
if(!id || id==INT64_MIN) return false;
+68
for(unsigned j=0;j<i;j++) if(w[5+j]==id) return false;
+69
if(id==w[0]) owner=true;
+70
}
+71
if(!owner) return false;
72
struct view *view=find_view(s,w[0]);
−64
if(!view || !visible(view) || view->layer) return false;
−65
bool held=false,owner=false;
+73
bool held=false;
74
uint64_t device=0;
75
for(unsigned i=0;i<QUEUE_SIZE;i++) {
76
if((s->buttons[i].held && s->buttons[i].code==w[1]) ||
@@ -72,19 +80,22 @@ static bool scene_move_start(struct server *s,uint64_t token,const int64_t *w,si
80
s->intercepted_buttons[i].device : s->buttons[i].device;
81
}
82
}
−75
if(!held) return false;
+83
/* A quick release, unmap, lock or decoration retirement can precede this
+84
* asynchronous admission. Complete the manager token without rejecting its
+85
* command or resurrecting a gesture that has already ended. */
+86
if(s->lock_state || !held || !view || !visible(view) || view->layer) {
+87
EVENT(38,w[0],0,0,token,1); return true;
+88
}
89
struct scene_move next={.retained=true,.active=true,.token=token,
90
.window=w[0],.device=device,.button=w[1],.pointer_x=w[2]/1000.0,.pointer_y=w[3]/1000.0,.count=w[4]};
91
for(unsigned i=0;i<next.count;i++) {
92
int64_t id=w[5+i];
−80
if(!id || id==INT64_MIN) return false;
93
struct wlr_scene_node *node=scene_resource(s,id);
−82
if(!node || node->parent!=s->normal) return false;
−83
for(unsigned j=0;j<i;j++) if(next.items[j].id==id) return false;
+94
if(!node || node->parent!=s->normal) {
+95
EVENT(38,w[0],0,0,token,1); return true;
+96
}
97
next.items[i]=(struct scene_move_item){id,node->x,node->y};
−85
if(id==w[0]) owner=true;
98
}
−87
if(!owner) return false;
99
scene_move_finish(s);
100
s->scene_move=next;
101
scene_move_update(s);
test/scene-move.cmodified
@@ -20,7 +20,11 @@ int main(void) {
20
wlr_scene_node_set_position(&view.tree->node,100,100);
21
wlr_scene_node_set_position(&border.scene->node,99,99);
22
int64_t payload[]={1,272,40000,40000,2,1,-2};
−23
assert(!scene_move_start(&s,10,payload,7)); /* no held initiating button */
+23
struct wm_message event;
+24
assert(scene_move_start(&s,10,payload,7)); /* release overtook copied admission */
+25
assert(!s.scene_move.active && pop(&bridge.events,&event));
+26
assert(event.word[0]==38 && event.word[4]==10 && event.word[5]==1);
+27
assert(event.word[2]==0 && event.word[3]==0);
28
s.intercepted_buttons[0].held=true; s.intercepted_buttons[0].code=272;
29
s.intercepted_buttons[0].device=9;
30
assert(scene_move_start(&s,11,payload,7));
@@ -36,7 +40,7 @@ int main(void) {
40
struct input release={.kind=31,.code=272,.device=8};
41
scene_move_button(&s,&release); assert(s.scene_move.active);
42
release.device=9; scene_move_button(&s,&release); assert(!s.scene_move.active);
−39
struct wm_message event; assert(pop(&bridge.events,&event));
+43
assert(pop(&bridge.events,&event));
44
assert(event.word[0]==38 && event.word[1]==1 && event.word[2]==40 && event.word[3]==20 && event.word[4]==11 && event.word[5]==0);
45
scene_move_plan(&s,stale); assert(s.scene_move.retained);
46
stale[5]=140; stale[6]=120; scene_move_plan(&s,stale); assert(!s.scene_move.retained);
@@ -45,6 +49,10 @@ int main(void) {
49
int64_t missing_owner[]={1,272,40000,40000,1,-2};
50
assert(!scene_move_start(&s,12,missing_owner,6));
51
payload[2]=INT64_MAX; assert(!scene_move_start(&s,12,payload,7)); payload[2]=80000; payload[3]=60000;
+52
int64_t retired[]={1,272,80000,60000,2,1,-999};
+53
assert(scene_move_start(&s,99,retired,7));
+54
assert(pop(&bridge.events,&event) && event.word[4]==99 && event.word[5]==1);
+55
assert(!s.scene_move.active);
56
assert(scene_move_start(&s,12,payload,7));
57
stale[5]=0; stale[6]=0; stale[11]=1;
58
scene_move_plan(&s,stale); assert(!s.scene_move.active && !s.scene_move.retained);
@@ -54,6 +62,6 @@ int main(void) {
62
s.lock_state=0; assert(scene_move_start(&s,14,payload,7));
63
view.mapped=false; assert(!scene_move_motion(&s,0,90,60,0,0)); assert(!s.scene_move.active);
64
close(bridge.event_fd); wlr_cursor_destroy(s.cursor); wlr_scene_node_destroy(&s.scene->tree.node);
−57
puts("SCENE_MOVE=PASS admission grouped-geometry stable-order stale-plans release lock unmap");
+65
puts("SCENE_MOVE=PASS admission stale-start retired-resource grouped-geometry stable-order stale-plans release lock unmap");
66
return 0;
67
}