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 build
src/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))))