Commit5188a771Recorded29 Oct 2026Repositorysigil-wm
Follow pointer motion natively for explicit scene resources
Changed
Makefile | 2 +-
src/c/host.c | 31 +++++++++++++++++++++++++++----
src/c/lock.h | 1 +
src/c/scene-move.h | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/sigil/wm/runtime.sgl | 1 +
src/sigil/wm/scene.sgl | 14 +++++++++++++-
6 files changed, 140 insertions(+), 6 deletions(-)Diff
Makefilemodified
@@ -32,7 +32,7 @@ build/output-inventory-test: test/output-inventory.c src/c/output-inventory.h sr
32
build/deadlines-test: test/deadlines.c src/c/deadlines.h src/c/host.c 33
$(CC) $(CFLAGS) $(CPPFLAGS) $< $(LDFLAGS) $(LDLIBS) -o $@ 34
−35
build/libsigil-wm.so: src/c/trace-records.h src/c/output-inventory.h src/c/deadlines.h+35
build/libsigil-wm.so: src/c/scene-move.h src/c/trace-records.h src/c/output-inventory.h src/c/deadlines.h 36
37
build/environment-exec: src/c/environment-exec.c 38
mkdir -p buildsrc/c/host.cmodified
@@ -260,6 +260,15 @@ struct input {
260
uint64_t output; 261
double x, y; 262
};+263
struct scene_move_item { int64_t id; int x,y; };+264
struct scene_move {+265
bool retained,active;+266
uint64_t token,window;+267
int button,dx,dy;+268
double pointer_x,pointer_y;+269
unsigned count;+270
struct scene_move_item items[256];+271
}; 272
struct server { 273
struct wl_display *display; 274
struct wlr_security_context_manager_v1 *security_context;@@ -349,6 +358,7 @@ struct server {
358
uint64_t motion_delivered_token; 359
int64_t motion_device, motion_period, motion_last; 360
double motion_x,motion_y;+361
struct scene_move scene_move; 362
struct input pending_motion; 363
bool has_pending_motion; 364
struct wlr_box motion_region;@@ -360,6 +370,10 @@ struct server {
370
uint64_t plan_trace_causes[TRACE_CAUSES]; 371
}; 372
+373
static void scene_move_finish(struct server *s);+374
static bool scene_move_motion(struct server *s,uint32_t time,double x,double y,uint64_t completion,int origin);+375
static void scene_move_position(struct server *s,int64_t resource,struct wlr_scene_node *node,int x,int y);+376
static void scene_move_plan(struct server *s,const int64_t *w); 377
#include "security-context.h" 378
#include "output-inventory.h" 379
@@ -867,6 +881,8 @@ static bool configure_input_intercepts(struct server *s,bool key,int64_t *words,
881
return true; 882
} 883
static void queue_input(struct server *s,struct input input) {+884
if(input.kind==31 && !input.state && input.code==s->scene_move.button)+885
scene_move_finish(s); 886
if(input.kind!=32 && s->has_pending_motion) { 887
struct input pending=s->pending_motion; 888
s->has_pending_motion=false;@@ -1046,6 +1062,7 @@ static void motion_input(struct server *s, uint32_t time, double x, double y, ui
1062
if(s->lock_state && origin!=0) return; 1063
activity(s,2,origin); 1064
s->motion_x=x; s->motion_y=y;+1065
if(scene_move_motion(s,time,x,y,completion,origin)) return; 1066
bool matches = s->motion_hook && (!s->motion_device || device == (uint64_t)s->motion_device) && 1067
(!s->motion_region.width || wlr_box_contains_point(&s->motion_region, x, y)); 1068
if (matches) {@@ -1437,11 +1454,12 @@ static void check_plan(struct server *s) {
1454
} 1455
if(!ready && now_ms()<s->plan_deadline) return; 1456
release_saved(s);+1457
scene_move_plan(s,w); 1458
for(int i=0;i<w[1];i++) { 1459
const int64_t *p=w+4+10*i; 1460
if(p[0]<0) { 1461
struct pixels *image=find_pixels(s,-p[0]);−1444
wlr_scene_node_set_position(&image->scene->node,p[1],p[2]);+1462
scene_move_position(s,p[0],&image->scene->node,p[1],p[2]); 1463
wlr_scene_buffer_set_dest_size(image->scene,p[3],p[4]); 1464
wlr_scene_node_set_enabled(&image->scene->node, 1465
p[5] && pixel_scope_valid(s,image->scope));@@ -1449,7 +1467,7 @@ static void check_plan(struct server *s) {
1467
continue; 1468
} 1469
struct view *v=find_view(s,p[0]);−1452
wlr_scene_node_set_position(&v->tree->node,p[1],p[2]);+1470
scene_move_position(s,p[0],&v->tree->node,p[1],p[2]); 1471
wlr_scene_node_set_enabled(&v->tree->node,p[5] && v->mapped); 1472
if (v->layer && p[5] && v->mapped && !v->layer_presented && !v->layer_present_armed) { 1473
v->layer_present_commit = v->layer->output->commit_seq + 1;@@ -1757,6 +1775,7 @@ static struct wlr_scene_node *scene_resource(struct server *s,int64_t resource)
1775
struct pixels *pixels=find_pixels(s,-resource); 1776
return pixels ? &pixels->scene->node : NULL; 1777
}+1778
#include "scene-move.h" 1779
/* Position only this resource. Copied retired neighbours are harmless; no 1780
* frame or input dispatch can observe creation before its final insertion. */ 1781
static void position_pixels(struct server *s,struct pixels *p,const int64_t *order,size_t count) {@@ -1801,7 +1820,7 @@ static bool apply_pixels(struct server *s,uint64_t id,int64_t *w,struct pixel_bu
1820
memcpy(p->scope,b->scope,sizeof(p->scope)); 1821
wlr_scene_node_set_enabled(&p->scene->node,true); 1822
wlr_scene_buffer_set_dest_size(p->scene,w[2],w[3]);−1804
wlr_scene_node_set_position(&p->scene->node,w[0],w[1]);+1823
scene_move_position(s,-(int64_t)id,&p->scene->node,w[0],w[1]); 1824
if(b->order_count) { 1825
size_t offset=(b->stride*(size_t)b->base.height+7)&~(size_t)7; 1826
position_pixels(s,p,(const int64_t *)(b->data+offset),b->order_count);@@ -2129,6 +2148,7 @@ static bool apply_command(struct server *s,struct wm_message m) {
2148
ok=output_set_gamma(s,w[2],data,w[4]); 2149
if(!ok && w[4]>=1) EVENT(131,data[0],w[2],false); 2150
break;+2151
case 17: ok=scene_move_start(s,w[2],data,w[4]); break; 2152
case 8: ok=restack_scene(s,data,w[4]); break; 2153
case 9: ok=configure_pointer(s,w[2],data,w[4]); break; 2154
case 10: output_batch(s,w[2],data,w[4]); free(data); return true;@@ -2159,6 +2179,9 @@ static bool apply_command(struct server *s,struct wm_message m) {
2179
if (w[1] < 1 || w[1] > 200) return false; 2180
s->scene_update_deadline = now_ms() + w[1]; 2181
return true;+2182
case 43:+2183
if(s->scene_move.token==(uint64_t)w[1]) scene_move_finish(s);+2184
return true; 2185
case 42: { 2186
struct output *o = find_output(s, w[1]); 2187
if (!o) return false;@@ -2519,7 +2542,7 @@ int wm_input_reply(const struct wm_message *m,int kind) {
2542
return result; 2543
} 2544
int wm_submit_traced(int64_t kind,int64_t token,const int64_t *words,int64_t count,const uint64_t *causes,int trace_count) {−2522
if(!words || count<1 || count>65536 || kind<1 || kind>16) return 0;+2545
if(!words || count<1 || count>65536 || kind<1 || kind>17) return 0; 2546
if(trace_count<0 || trace_count>TRACE_CAUSES || (trace_count && !causes)) return 0; 2547
int64_t *copy=malloc((count+trace_count)*sizeof(int64_t)); 2548
if(!copy) return 0;src/c/lock.hmodified
@@ -212,6 +212,7 @@ static void lock_new(struct wl_listener *listener,void *data) {
212
struct server *s=wl_container_of(listener,s,new_lock); 213
struct wlr_session_lock_v1 *owner=data; 214
if(s->lock_owner || s->stopping) { wlr_session_lock_v1_destroy(owner); return; }+215
scene_move_finish(s); 216
s->lock_owner=owner; s->lock_generation++; s->lock_state=1; 217
s->lock_deadline=now_ms()+bridge.request_deadline; 218
wlr_scene_node_set_enabled(&s->normal->node,false);src/c/scene-move.hadded
@@ -0,0 +1,97 @@
+1
/* SPDX-License-Identifier: BSD-3-Clause+2
* Pointer-following is a mechanism over an explicit manager-supplied resource+3
* set. It does not choose floating membership, decoration ownership or order. */+4
static void scene_move_finish(struct server *s) {+5
struct scene_move *move=&s->scene_move;+6
if(!move->active) return;+7
move->active=false;+8
EVENT(38,move->window,move->dx,move->dy,move->token);+9
}+10
static void scene_move_position(struct server *s,int64_t resource,+11
struct wlr_scene_node *node,int x,int y) {+12
struct scene_move *move=&s->scene_move;+13
if(move->retained) {+14
for(unsigned i=0;i<move->count;i++) {+15
if(move->items[i].id!=resource) continue;+16
x=move->items[i].x+move->dx; y=move->items[i].y+move->dy;+17
break;+18
}+19
}+20
wlr_scene_node_set_position(node,x,y);+21
}+22
static void scene_move_update(struct server *s) {+23
struct scene_move *move=&s->scene_move;+24
move->dx=(int)round(s->cursor->x-move->pointer_x);+25
move->dy=(int)round(s->cursor->y-move->pointer_y);+26
for(unsigned i=0;i<move->count;i++) {+27
struct wlr_scene_node *node=scene_resource(s,move->items[i].id);+28
if(node) scene_move_position(s,move->items[i].id,node,0,0);+29
}+30
struct view *view=find_view(s,move->window);+31
if(view && view->x11)+32
wlr_xwayland_surface_configure(view->x11,view->tree->node.x,+33
view->tree->node.y,view->width,view->height);+34
}+35
static bool scene_move_motion(struct server *s,uint32_t time,double x,double y,+36
uint64_t completion,int origin) {+37
struct scene_move *move=&s->scene_move;+38
if(!move->active) return false;+39
struct view *view=find_view(s,move->window);+40
if(s->lock_state || !view || !visible(view)) {+41
scene_move_finish(s); return false;+42
}+43
uint64_t token=++s->next_token;+44
latency_trace(0,token,32,0);+45
constrained_warp(s,x,y);+46
s->motion_x=s->cursor->x; s->motion_y=s->cursor->y;+47
scene_move_update(s);+48
trace_damage(s,token,0);+49
route_motion(s,time,true);+50
if(completion) EVENT(35,completion,1,origin,token);+51
return true;+52
}+53
static bool scene_move_start(struct server *s,uint64_t token,const int64_t *w,size_t n) {+54
if(s->lock_state || n<6 || w[0]<=0 || w[1]<272 || w[1]>=288 ||+55
w[4]<1 || w[4]>256 || n!=5+(size_t)w[4]) return false;+56
struct view *view=find_view(s,w[0]);+57
if(!view || !visible(view) || view->layer) return false;+58
bool held=false,owner=false;+59
for(unsigned i=0;i<QUEUE_SIZE;i++) {+60
if((s->buttons[i].held && s->buttons[i].code==w[1]) ||+61
(s->intercepted_buttons[i].held && s->intercepted_buttons[i].code==w[1])) held=true;+62
}+63
if(!held) return false;+64
struct scene_move next={.retained=true,.active=true,.token=token,+65
.window=w[0],.button=w[1],.pointer_x=w[2]/1000.0,.pointer_y=w[3]/1000.0,.count=w[4]};+66
for(unsigned i=0;i<next.count;i++) {+67
int64_t id=w[5+i];+68
if(!id || id==INT64_MIN) return false;+69
struct wlr_scene_node *node=scene_resource(s,id);+70
if(!node || node->parent!=s->normal) return false;+71
for(unsigned j=0;j<i;j++) if(next.items[j].id==id) return false;+72
next.items[i]=(struct scene_move_item){id,node->x,node->y};+73
if(id==w[0]) owner=true;+74
}+75
if(!owner) return false;+76
scene_move_finish(s);+77
s->scene_move=next;+78
scene_move_update(s);+79
return true;+80
}+81
/* Delayed plans/uploads cannot undo newer pointer motion. Keep the final+82
* position through release until the manager acknowledges it in a full plan. */+83
static void scene_move_plan(struct server *s,const int64_t *w) {+84
struct scene_move *move=&s->scene_move;+85
if(!move->retained) return;+86
if(!find_view(s,move->window)) { scene_move_finish(s); move->retained=false; return; }+87
for(int i=0;i<w[1];i++) {+88
const int64_t *p=w+4+10*i;+89
if(p[0]!=(int64_t)move->window) continue;+90
if(!p[5]) scene_move_finish(s);+91
if(!move->active) {+92
struct wlr_scene_node *node=scene_resource(s,move->window);+93
if(node && p[1]==node->x && p[2]==node->y) move->retained=false;+94
}+95
break;+96
}+97
}src/sigil/wm/runtime.sglmodified
@@ -104,6 +104,7 @@
104
((32) '(motion)) 105
((35) '(input-completed (success?: 2) (origin: 3) (input-token: 4))) 106
((36) '(motion-hook-applied (enabled?: 2)))+107
((38) '(scene-move-ended (delta-x: 2) (delta-y: 3) (move-token: 4))) 108
((37) '(pointer-target-changed (output: 2) (x-milli: 3) (y-milli: 4) (moved?: 5) (generation: 6) (pointer-token: 7))) 109
((40) '(device-added (device-type: 2) (virtual?: 3) (libinput?: 4) (tap-fingers: 5) (nested?: 6))) 110
((41) '(keyboard-state (group: 2) (groups: 3)))src/sigil/wm/scene.sglmodified
@@ -4,7 +4,7 @@
4
(export placement placement? placement-window placement-kind placement-box placement-shown? 5
placement-activated? placement-suspended? placement-fullscreen? placement-tiled placement-clip? placement-resize? 6
scene-plan scene-plan? make-scene-plan scene-plan-placements scene-plan-focus−7
scene-plan-deadline scene-plan-limit validate-plan submit-plan! restack-scene! present-pixels! place-pixels! remove-pixels!)+7
scene-plan-deadline scene-plan-limit validate-plan submit-plan! restack-scene! start-scene-move! stop-scene-move! present-pixels! place-pixels! remove-pixels!) 8
(begin 9
(define-struct placement 10
(window) (kind default: 'window) (box) (shown? default: #t) (activated? default: #f) (suspended? default: #f)@@ -78,6 +78,18 @@
78
(and (<= (length words) 65535) 79
(host-submit! host 8 token (cons (length words) words)) token))) 80
+81
;; Follow the pointer natively until BUTTON is released. The manager supplies+82
;; all resources and the pointer anchor from the initiating press. No scene+83
;; order or window membership is inferred by the native host.+84
(define (start-scene-move! host window button x y resources)+85
(let ((words (scene-resource-words resources)) (token (host-next-token! host)))+86
(and (<= 1 (length words) 256)+87
(host-submit! host 17 token+88
(append (list window button (inexact->exact (round (* x 1000)))+89
(inexact->exact (round (* y 1000))) (length words)) words))+90
token)))+91
(define (stop-scene-move! host token) (host-send! host (list 43 token)))+92
93
;; Pixel descriptor: data, pixel width/height, stride. No draw-module import. 94
(define (stack-code stack) 95
(case stack ((top) 1) ((bottom) -1) ((keep) 0) (else (error "invalid pixel stacking" stack))))