AtlatestRepositorysigil-wm
1
/* SPDX-License-Identifier: BSD-3-Clause2
* Explicit constraint leases, changed protocol facts and native enforcement. */3
struct constraint {4
struct wl_list link;5
struct server *server;6
struct wlr_pointer_constraint_v1 *native;7
struct wl_listener destroy,region,unmap;8
uint64_t id,scope;9
double origin_x,origin_y;10
int64_t facts[8];11
bool sampled;12
};13
struct pointer_hint {14
struct wl_list link;15
struct server *server;16
struct wlr_surface *surface;17
struct wl_listener destroy;18
uint64_t token,window,generation,deadline;19
double x,y;20
};21
static pixman_region32_t *constraint_region(struct constraint *owner) {22
return pixman_region32_empty(&owner->native->current.region) ?23
&owner->native->surface->input_region : &owner->native->region;24
}25
static bool constraint_contains(struct constraint *owner,double x,double y) {26
return x>=0 && y>=0 && pixman_region32_contains_point(constraint_region(owner),(int)x,(int)y,NULL);27
}28
static bool constraint_position(struct constraint *owner,double *x,double *y) {29
struct server *s=owner->server;30
struct wlr_pointer_constraint_v1 *native=owner->native;31
struct view *view=find_view(s,window_for_surface(s,native->surface));32
if(s->stopping || s->lock_state || native->seat!=s->seat || !native->surface->mapped ||33
!visible(view) || wlr_seat_pointer_has_grab(s->seat) ||34
s->seat->pointer_state.focused_surface!=native->surface) return false;35
struct wlr_surface *under=surface_at(s,s->cursor->x,s->cursor->y,x,y);36
if(under!=native->surface) return false;37
owner->origin_x=s->cursor->x-*x; owner->origin_y=s->cursor->y-*y;38
return true;39
}40
static void constraints_end(struct server *s) {41
struct constraint *owner=s->active_constraint;42
if(!owner) return;43
s->active_constraint=NULL;44
EVENT(107,owner->id,0,s->generation);45
wlr_pointer_constraint_v1_send_deactivated(owner->native);46
}47
static void constraints_observe(struct server *s) {48
if(!s->pointer_constraints || s->observing_constraints || s->stopping) return;49
s->observing_constraints=true;50
struct constraint *active=s->active_constraint;51
double x=0,y=0;52
if(active && (active->scope!=s->generation || !constraint_position(active,&x,&y) ||53
!constraint_contains(active,x,y))) constraints_end(s);54
uint64_t keyboard=window_for_surface(s,s->seat->keyboard_state.focused_surface);55
struct constraint *owner;56
wl_list_for_each(owner,&s->constraints,link) {57
uint64_t window=window_for_surface(s,owner->native->surface);58
bool over=constraint_position(owner,&x,&y);59
struct view *view=find_view(s,window);60
int64_t facts[8]={window,over,keyboard,over && constraint_contains(owner,x,y),61
visible(view),(int64_t)(owner->origin_x*1000),(int64_t)(owner->origin_y*1000),s->generation};62
if(!owner->sampled || memcmp(facts,owner->facts,sizeof(facts))) {63
memcpy(owner->facts,facts,sizeof(facts)); owner->sampled=true;64
EVENT(106,owner->id,facts[0],facts[1],facts[2],facts[3],facts[4],facts[5],facts[6],facts[7]);65
}66
}67
s->observing_constraints=false;68
}69
static void constrained_warp(struct server *s,double x,double y) {70
constraints_observe(s);71
struct constraint *owner=s->active_constraint;72
if(!owner) { wlr_cursor_warp_closest(s->cursor,NULL,x,y); return; }73
if(owner->native->type==WLR_POINTER_CONSTRAINT_V1_LOCKED) return;74
double confined_x,confined_y;75
if(wlr_region_confine(constraint_region(owner),s->cursor->x-owner->origin_x,76
s->cursor->y-owner->origin_y,x-owner->origin_x,y-owner->origin_y,&confined_x,&confined_y))77
wlr_cursor_warp(s->cursor,NULL,owner->origin_x+confined_x,owner->origin_y+confined_y);78
}79
static void pointer_hint_remove(struct pointer_hint *hint) {80
wl_list_remove(&hint->destroy.link); wl_list_remove(&hint->link);81
hint->server->pointer_hint_count--;82
atomic_fetch_sub(&bridge.objects,1);83
free(hint);84
}85
static void pointer_hint_destroyed(struct wl_listener *listener,void *data) {86
(void)data;87
struct pointer_hint *hint=wl_container_of(listener,hint,destroy);88
EVENT(72,hint->token);89
pointer_hint_remove(hint);90
}91
static void pointer_hint_request(struct constraint *owner) {92
struct server *s=owner->server;93
struct wlr_pointer_constraint_v1 *native=owner->native;94
double x,y;95
if(s->pointer_hint_count>=64 || owner->scope!=s->generation ||96
native->type!=WLR_POINTER_CONSTRAINT_V1_LOCKED || !native->current.cursor_hint.enabled ||97
!constraint_position(owner,&x,&y)) return;98
x=native->current.cursor_hint.x; y=native->current.cursor_hint.y;99
if(x<0 || y<0 || !pixman_region32_contains_point(&native->surface->input_region,(int)x,(int)y,NULL)) return;100
struct pointer_hint *hint=calloc(1,sizeof(*hint));101
if(!hint) return;102
hint->server=s; hint->surface=native->surface; hint->window=window_for_surface(s,native->surface);103
hint->token=++s->next_token; hint->generation=s->generation; hint->deadline=now_ms()+bridge.request_deadline;104
hint->x=owner->origin_x+x; hint->y=owner->origin_y+y;105
listen(&native->surface->events.destroy,&hint->destroy,pointer_hint_destroyed);106
wl_list_insert(&s->pointer_hints,&hint->link); s->pointer_hint_count++;107
atomic_fetch_add(&bridge.objects,1);108
EVENT(110,hint->token,hint->window,hint->generation,(int64_t)(hint->x*1000),(int64_t)(hint->y*1000));109
}110
static bool pointer_hint_reply(struct server *s,uint64_t token,bool accept) {111
struct pointer_hint *hint;112
wl_list_for_each(hint,&s->pointer_hints,link) {113
if(hint->token!=token) continue;114
double sx,sy;115
bool valid=accept && !s->lock_state && !s->active_constraint && hint->generation==s->generation &&116
visible(find_view(s,hint->window)) &&117
surface_at(s,s->cursor->x,s->cursor->y,&sx,&sy)==hint->surface;118
double target_x=valid ? hint->x-(s->cursor->x-sx) : -1;119
double target_y=valid ? hint->y-(s->cursor->y-sy) : -1;120
valid=valid && target_x>=0 && target_y>=0 &&121
pixman_region32_contains_point(&hint->surface->input_region,(int)target_x,(int)target_y,NULL);122
bool applied=valid && wlr_cursor_warp(s->cursor,NULL,hint->x,hint->y);123
pointer_hint_remove(hint);124
if(applied) {125
s->motion_x=s->cursor->x; s->motion_y=s->cursor->y;126
route_motion(s,now_ms(),false);127
}128
EVENT(111,token,applied);129
return true;130
}131
EVENT(111,token,0);132
return true;133
}134
static bool set_active_constraint(struct server *s,uint64_t id,uint64_t generation) {135
if(!id) {136
constraints_end(s);137
EVENT(109,0,1);138
return true;139
}140
struct constraint *owner;141
wl_list_for_each(owner,&s->constraints,link) {142
if(owner->id!=id) continue;143
double x,y;144
if(generation!=s->generation || !constraint_position(owner,&x,&y) ||145
!constraint_contains(owner,x,y)) { EVENT(109,id,0); return true; }146
if(s->active_constraint!=owner) {147
constraints_end(s);148
/* Releasing a one-shot lease can synchronously destroy its owner. */149
owner->scope=s->generation;150
s->active_constraint=owner;151
wlr_pointer_constraint_v1_send_activated(owner->native);152
EVENT(107,id,1,s->generation);153
}154
EVENT(109,id,1);155
return true;156
}157
EVENT(109,id,0);158
return true;159
}160
static void constraint_destroyed(struct wl_listener *listener,void *data) {161
(void)data;162
struct constraint *owner=wl_container_of(listener,owner,destroy);163
struct server *s=owner->server;164
if(s->active_constraint==owner) {165
s->active_constraint=NULL;166
EVENT(107,owner->id,0,s->generation);167
if(!s->stopping) pointer_hint_request(owner);168
}169
EVENT(108,owner->id);170
wl_list_remove(&owner->destroy.link); wl_list_remove(&owner->region.link);171
wl_list_remove(&owner->unmap.link); wl_list_remove(&owner->link);172
s->constraint_count--;173
atomic_fetch_sub(&bridge.objects,1);174
free(owner);175
constraints_observe(s);176
}177
static void constraint_region_changed(struct wl_listener *listener,void *data) {178
(void)data;179
struct constraint *owner=wl_container_of(listener,owner,region);180
constraints_observe(owner->server);181
}182
static void constraint_unmapped(struct wl_listener *listener,void *data) {183
(void)data;184
struct constraint *owner=wl_container_of(listener,owner,unmap);185
struct server *s=owner->server;186
if(s->active_constraint==owner) constraints_end(s);187
constraints_observe(s);188
}189
static void new_constraint(struct wl_listener *listener,void *data) {190
struct server *s=wl_container_of(listener,s,new_constraint);191
struct wlr_pointer_constraint_v1 *native=data;192
if(s->constraint_count>=128) { wl_resource_destroy(native->resource); return; }193
struct constraint *owner=calloc(1,sizeof(*owner));194
if(!owner) { wl_resource_destroy(native->resource); return; }195
owner->server=s; owner->native=native; owner->id=++s->next_id;196
wl_list_insert(&s->constraints,&owner->link); s->constraint_count++;197
listen(&native->events.destroy,&owner->destroy,constraint_destroyed);198
listen(&native->events.set_region,&owner->region,constraint_region_changed);199
listen(&native->surface->events.unmap,&owner->unmap,constraint_unmapped);200
atomic_fetch_add(&bridge.objects,1);201
EVENT(105,owner->id,window_for_surface(s,native->surface),native->type,202
native->lifetime==ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT,native->seat==s->seat);203
constraints_observe(s);204
}205
static void pointer_hints_expire(struct server *s) {206
struct pointer_hint *hint,*next;207
wl_list_for_each_safe(hint,next,&s->pointer_hints,link) {208
if(now_ms()<hint->deadline && hint->generation==s->generation) continue;209
EVENT(72,hint->token);210
pointer_hint_remove(hint);211
}212
}213
static void constraint_pointer_focus(struct wl_listener *listener,void *data) {214
(void)data;215
struct server *s=wl_container_of(listener,s,constraint_pointer_focus);216
constraints_observe(s);217
}218
static void constraint_keyboard_focus(struct wl_listener *listener,void *data) {219
(void)data;220
struct server *s=wl_container_of(listener,s,constraint_keyboard_focus);221
constraints_observe(s);222
}223
static bool constraints_init(struct server *s) {224
s->pointer_constraints=wlr_pointer_constraints_v1_create(s->display);225
if(!s->pointer_constraints) return false;226
listen(&s->pointer_constraints->events.new_constraint,&s->new_constraint,new_constraint);227
listen(&s->seat->pointer_state.events.focus_change,&s->constraint_pointer_focus,constraint_pointer_focus);228
listen(&s->seat->keyboard_state.events.focus_change,&s->constraint_keyboard_focus,constraint_keyboard_focus);229
return true;230
}231
static void constraints_finish(struct server *s) {232
if(s->new_constraint.notify) {233
wl_list_remove(&s->new_constraint.link);234
wl_list_remove(&s->constraint_pointer_focus.link);235
wl_list_remove(&s->constraint_keyboard_focus.link);236
}237
s->active_constraint=NULL;238
struct constraint *owner,*next;239
wl_list_for_each_safe(owner,next,&s->constraints,link) {240
wl_list_remove(&owner->destroy.link); wl_list_remove(&owner->region.link);241
wl_list_remove(&owner->unmap.link); wl_list_remove(&owner->link);242
atomic_fetch_sub(&bridge.objects,1);243
free(owner);244
}245
struct pointer_hint *hint,*next_hint;246
wl_list_for_each_safe(hint,next_hint,&s->pointer_hints,link) pointer_hint_remove(hint);247
}