AtlatestRepositorysigil-wm
1
/*2
* SPDX-License-Identifier: MIT AND BSD-3-Clause3
*4
* This file started from wlroots' compositor example (see UPSTREAM.md). The5
* original portions remain under the MIT license below. sigil-wm changes are6
* Copyright (C) 2026 David Wilson and licensed under the BSD 3-Clause7
* License (see LICENSE).8
*9
* Copyright (c) 2017, 2018 Drew DeVault10
* Copyright (c) 2014 Jari Vetoniemi11
* Copyright (c) 2023 The wlroots contributors12
*13
* Permission is hereby granted, free of charge, to any person obtaining a copy of14
* this software and associated documentation files (the "Software"), to deal in15
* the Software without restriction, including without limitation the rights to16
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies17
* of the Software, and to permit persons to whom the Software is furnished to do18
* so, subject to the following conditions:19
*20
* The above copyright notice and this permission notice shall be included in all21
* copies or substantial portions of the Software.22
*23
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR24
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,25
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE26
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER27
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,28
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE29
* SOFTWARE.30
*/32
#define _GNU_SOURCE33
#include <stdbool.h>34
#include <errno.h>35
#include <limits.h>36
#include <math.h>37
#include <pthread.h>38
#include <stdatomic.h>39
#include <stdio.h>40
#include <stdlib.h>41
#include <string.h>42
#include <sys/eventfd.h>43
#include <time.h>44
#include <unistd.h>45
#include <sys/stat.h>46
#include <xf86drmMode.h>47
#include <drm_fourcc.h>48
#include <wayland-server-core.h>49
#include <wlr/backend.h>50
#include <wlr/backend/session.h>51
#include <wlr/types/wlr_xdg_activation_v1.h>52
#include <wlr/types/wlr_server_decoration.h>53
#include <wlr/backend/headless.h>54
#include <wlr/backend/drm.h>55
#include <wlr/backend/multi.h>56
#include <wlr/backend/wayland.h>57
#include <wlr/interfaces/wlr_buffer.h>58
#include <wlr/interfaces/wlr_keyboard.h>59
#include <wlr/render/allocator.h>60
#include <wlr/render/wlr_renderer.h>61
#include <wlr/types/wlr_compositor.h>62
#include <wlr/types/wlr_subcompositor.h>63
#include <wlr/types/wlr_cursor.h>64
#include <wlr/types/wlr_data_device.h>65
#include <wlr/types/wlr_fractional_scale_v1.h>66
#include <wlr/types/wlr_input_device.h>67
#include <wlr/types/wlr_pointer_constraints_v1.h>68
#include <wlr/util/region.h>69
#include <wlr/types/wlr_output_power_management_v1.h>70
#include <wlr/types/wlr_output_management_v1.h>71
#include <wlr/types/wlr_output_swapchain_manager.h>72
#include <wlr/types/wlr_gamma_control_v1.h>73
#include <wlr/backend/libinput.h>74
#include <wlr/types/wlr_keyboard.h>75
#include <wlr/types/wlr_layer_shell_v1.h>76
#include <wlr/types/wlr_output.h>77
#include <wlr/types/wlr_output_layout.h>78
#include <wlr/types/wlr_pointer.h>79
#include <wlr/types/wlr_relative_pointer_v1.h>80
#include <wlr/types/wlr_scene.h>81
#include <wlr/types/wlr_seat.h>82
#include <wlr/types/wlr_session_lock_v1.h>83
#include <wlr/types/wlr_security_context_v1.h>84
#include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h>85
#include <wlr/types/wlr_foreign_toplevel_management_v1.h>86
#include <wlr/types/wlr_virtual_keyboard_v1.h>87
#include <wlr/types/wlr_virtual_pointer_v1.h>88
#include <wlr/types/wlr_xcursor_manager.h>89
#include <wlr/types/wlr_xdg_decoration_v1.h>90
#include <wlr/types/wlr_idle_notify_v1.h>91
#include <wlr/types/wlr_idle_inhibit_v1.h>92
#include <wlr/types/wlr_data_control_v1.h>93
#include <wlr/types/wlr_primary_selection.h>94
#include <wlr/types/wlr_primary_selection_v1.h>95
#include <wlr/types/wlr_presentation_time.h>96
#include <wlr/types/wlr_single_pixel_buffer_v1.h>97
#include <wlr/types/wlr_xdg_output_v1.h>98
#include <wlr/types/wlr_screencopy_v1.h>99
#include <wlr/types/wlr_ext_image_copy_capture_v1.h>100
#include <wlr/types/wlr_ext_image_capture_source_v1.h>101
#include <wlr/types/wlr_cursor_shape_v1.h>102
#include <wlr/types/wlr_viewporter.h>103
#include <wlr/types/wlr_xdg_shell.h>104
#include <wlr/util/log.h>105
#include <xkbcommon/xkbcommon.h>106
#include <wlr/xwayland.h>107
#include "api.h"108
#include "trace-records.h"110
#define QUEUE_SIZE 1024111
#define INPUT_DECISION_DEADLINE_MS 32112
#define MAX_PLAN_VIEWS 256113
/* Four header words plus ten per placement fit the 65536-word bridge. */114
#define MAX_PLAN 6553115
#define MAX_KEYS 768116
#define MAX_PIXELS (32u * 1024 * 1024)117
/* All of these objects are confined to the native owner except the bridge. */118
struct queue { struct wm_message entries[QUEUE_SIZE]; atomic_uint read, write; };119
static struct {120
pthread_t thread;121
struct queue events, commands;122
int event_fd, command_fd;123
bool created, joined;124
bool terminal_delivered;125
atomic_int state;126
atomic_uint_fast64_t frames, objects, overflow, rejected, bytes, pixel_nodes, pixel_drops, saved_trees, snapshots;127
atomic_uint_fast64_t input_expired,motion_coalesced,timer_wakes,first_present_ms;128
atomic_bool input_resync;129
char socket[64],requested_socket[64];130
int64_t protocols,request_deadline;131
uint32_t legacy_decoration_default;132
} bridge;133
struct server;134
struct lock_surface;135
struct permission;136
struct active_drag;137
struct constraint;138
struct pending_output_batch;139
struct output {140
struct trace_output trace;141
struct wl_list link;142
struct server *server;143
uint64_t id;144
struct wlr_output *native;145
struct wl_listener frame, destroy, request, readback;146
struct wlr_scene_output *scene;147
char capture[PATH_MAX];148
unsigned sample_frames, sample_index, sample_count;149
int sample_points[128];150
struct wl_listener lock_present,wake_present,gamma_present;151
uint64_t wake_requested,wake_commit_time;152
uint32_t wake_commit;153
bool wake_armed;154
bool pending_power,pending_on;155
struct lock_surface *lock_surface;156
struct wlr_scene_rect *lock_fill;157
uint64_t lock_generation;158
uint32_t lock_commit;159
bool lock_presented, lock_armed;160
bool configured_enabled,gamma_pending,gamma_external,nearest_filter;161
uint16_t *gamma_table;162
size_t gamma_size;163
uint64_t gamma_token,gamma_retry_at,scene_retry_at;164
int x,y;165
uint64_t configuration_generation;166
};167
struct view {168
struct wl_list link;169
struct server *server;170
uint64_t id;171
struct wlr_xdg_toplevel *native;172
struct wlr_layer_surface_v1 *layer;173
struct wlr_xwayland_surface *x11;174
struct wlr_scene_tree *tree, *live, *saved;175
struct wl_listener map, unmap, commit, destroy, title, app_id;176
struct wl_listener move, resize, maximize, fullscreen;177
struct wl_listener role_popup, role_output_destroy;178
struct wl_listener associate, dissociate, request_configure, set_geometry, request_activate;179
int width, height;180
struct wlr_box committed;181
uint32_t committed_serial;182
uint32_t serial, layer_present_commit;183
bool layer_presented, layer_present_armed;184
bool initialized, mapped, clip_enabled;185
int clip_width,clip_height;186
int hints_min_width,hints_min_height,hints_max_width,hints_max_height;187
uint64_t hints_parent;188
struct wlr_ext_foreign_toplevel_handle_v1 *foreign_list;189
struct wlr_foreign_toplevel_handle_v1 *foreign;190
struct wl_listener foreign_activate,foreign_close,foreign_fullscreen;191
struct wl_listener foreign_maximize,foreign_minimize;192
struct wlr_box foreign_box;193
bool foreign_placed,foreign_fullscreen_state;194
};195
static struct wlr_scene_node *view_surface_tree(struct view *view) {196
if(!view->live) return NULL;197
if(!view->native) return &view->live->node;198
if(wl_list_empty(&view->live->children)) return NULL;199
struct wlr_scene_node *surface_tree=wl_container_of(view->live->children.next,surface_tree,link);200
return surface_tree;201
}202
static void apply_view_clip(struct view *view) {203
struct wlr_scene_node *surface_tree=view_surface_tree(view);204
if(!surface_tree) return;205
struct wlr_box clip={view->native ? view->committed.x : 0,206
view->native ? view->committed.y : 0,view->clip_width,view->clip_height};207
wlr_scene_subsurface_tree_set_clip(surface_tree,view->clip_enabled ? &clip : NULL);208
}209
static void discard_saved(struct view *view) {210
if(!view->saved) return;211
wlr_scene_node_destroy(&view->saved->node);212
view->saved=NULL;213
atomic_fetch_sub(&bridge.saved_trees,1);214
}215
struct device {216
struct wl_list link;217
struct server *server;218
struct wlr_input_device *native;219
uint64_t id;220
bool is_virtual;221
struct wl_listener destroy;222
};223
struct keyboard {224
struct wl_list link;225
struct server *server;226
uint64_t id;227
struct wlr_keyboard *native;228
struct wl_listener key, modifiers, destroy;229
int forwarded[MAX_KEYS];230
int forwarded_modifiers[MAX_KEYS];231
bool is_virtual,session_consumed[MAX_KEYS],intercepted[MAX_KEYS],fixed_group;232
uint32_t group;233
};234
struct pixels {235
struct wl_list link;236
uint64_t id;237
struct wlr_scene_buffer *scene;238
bool input;239
struct server *server;240
int64_t scope[5];241
};242
struct pixel_buffer {243
unsigned trace_count;244
uint64_t trace_causes[TRACE_CAUSES];245
struct wlr_buffer base;246
size_t length, stride, order_count;247
bool input;248
int64_t scope[5];249
unsigned char data[];250
};251
struct input_intercept { uint32_t code, modifiers, mask; int scope; int64_t target; };252
struct input {253
uint64_t token, device, generation, completion, deadline,press_token,order_deadline;254
int kind, code, state, origin;255
uint32_t time, modifiers, keysym, base_keysym, first_keysym;256
int result, replacement;257
int replacement_modifiers;258
bool ready,notified,native,completed;259
int64_t target;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,device;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;275
struct wlr_ext_foreign_toplevel_list_v1 *foreign_list;276
struct wlr_foreign_toplevel_manager_v1 *foreign_manager;277
struct wlr_backend *backend;278
struct wlr_session *session;279
struct wlr_xdg_activation_v1 *activation;280
struct wl_listener activation_request;281
struct { uint32_t symbol; unsigned vt; } session_switches[12];282
unsigned session_switch_count;283
struct wlr_renderer *renderer;284
struct wlr_allocator *allocator;285
struct wlr_scene *scene;286
struct wlr_scene_tree *normal;287
struct wlr_scene_tree *lock_scene;288
struct wlr_session_lock_v1 *lock_owner;289
struct wl_listener new_lock, lock_new_surface, lock_unlock, lock_destroy;290
int lock_state;291
uint32_t lock_colour;292
uint64_t lock_generation, lock_deadline;293
struct wlr_output_layout *layout;294
struct wlr_scene_output_layout *scene_layout;295
struct wlr_xdg_shell *shell;296
struct wlr_compositor *compositor;297
struct wlr_xwayland *xwayland;298
struct wl_listener new_x11, x11_destroy, x11_ready;299
struct wlr_seat *seat;300
struct wlr_cursor *cursor;301
struct wlr_relative_pointer_manager_v1 *relative;302
struct wlr_xcursor_manager *cursor_manager;303
struct wl_list data_requests;304
unsigned data_request_count;305
struct wl_listener selection_request,primary_request,drag_request,drag_started,cursor_shape;306
struct active_drag *drag;307
struct wlr_scene_tree *drag_scene;308
bool follow_drag_icon,allow_client_cursor;309
struct wlr_idle_notifier_v1 *idle;310
int activity_classes,activity_origins;311
struct wl_list outputs, views, keyboards, devices, pixels, permissions,decorations,popups,inhibitors;312
struct wl_list constraints,pointer_hints;313
struct wlr_pointer_constraints_v1 *pointer_constraints;314
struct constraint *active_constraint;315
struct wl_listener new_constraint,constraint_pointer_focus,constraint_keyboard_focus;316
unsigned constraint_count,pointer_hint_count;317
bool observing_constraints,relative_route;318
struct wl_list output_controls,output_requests;319
struct wlr_output_manager_v1 *output_manager;320
struct wl_listener output_apply,output_test;321
unsigned output_request_count;322
uint64_t output_revision;323
unsigned output_control_count;324
struct wl_list pending_outputs;325
unsigned pending_output_count;326
struct wl_event_source *output_idle;327
bool output_commit_attempted;328
struct wlr_output_power_manager_v1 *power_manager;329
struct wlr_gamma_control_manager_v1 *gamma_manager;330
struct wl_listener power_request,gamma_request;331
struct wl_listener new_virtual_keyboard,new_virtual_pointer;332
struct wl_listener new_decoration,new_inhibitor,new_legacy_decoration;333
struct wl_list legacy_decorations;334
unsigned decoration_count;335
struct wl_listener new_output, new_view, new_popup, new_input, new_layer;336
uint64_t default_layer_output;337
char pending_output_name[129];338
struct wl_listener motion, absolute, button, axis, cursor_frame, cursor_request;339
struct wl_event_source *commands, *timer, *inventory_idle;340
struct wlr_keyboard synthetic;341
struct xkb_state *emulation_state;342
struct keyboard synthetic_owner;343
uint64_t next_id, next_token, generation, focus;344
struct input inputs[QUEUE_SIZE];345
unsigned input_read, input_write;346
struct { uint64_t device,press_token; int code,target; bool held; } buttons[QUEUE_SIZE];347
unsigned key_refs[MAX_KEYS];348
struct input_intercept key_intercepts[QUEUE_SIZE],button_intercepts[QUEUE_SIZE];349
unsigned key_intercept_count,button_intercept_count;350
bool key_intercept_all,button_intercept_all;351
struct { uint64_t device,press_token; int code; bool held; } intercepted_buttons[QUEUE_SIZE];352
bool motion_hook, route_hit_test, stopping;353
bool observe_pointer, pointer_sampled, pointer_motion_only;354
uint32_t keyboard_modifiers;355
int64_t pointer_target;356
uint64_t pointer_output, pointer_sample_token, pointer_sample_generation;357
uint64_t motion_generation;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;365
int64_t *plan;366
size_t plan_count;367
int64_t plan_token;368
uint64_t plan_deadline, scene_update_deadline, trace_correlation, timer_deadline;369
unsigned plan_trace_count;370
uint64_t plan_trace_causes[TRACE_CAUSES];371
};373
static void scene_move_finish(struct server *s);374
static void scene_move_button(struct server *s,const struct input *input);375
static bool scene_move_motion(struct server *s,uint32_t time,double x,double y,uint64_t completion,int origin);376
static void scene_move_position(struct server *s,int64_t resource,struct wlr_scene_node *node,int x,int y);377
static void scene_move_plan(struct server *s,const int64_t *w);378
#include "security-context.h"379
#include "output-inventory.h"381
static uint64_t now_ms(void) {382
struct timespec ts;383
clock_gettime(CLOCK_MONOTONIC, &ts);384
return (uint64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;385
}386
static void wake(int fd);387
static void latency_trace_at(int stage, uint64_t token, uint64_t object, uint64_t detail,388
const struct timespec *timestamp) {389
if(trace_record_at(stage,token,object,detail,timestamp) && bridge.created && !bridge.joined)390
wake(bridge.event_fd);391
}392
static void latency_trace(int stage, uint64_t token, uint64_t object, uint64_t detail) {393
latency_trace_at(stage, token, object, detail, NULL);394
}395
void wm_latency_mark(int stage, uint64_t token) {396
if (stage < 0 || stage > 31) return;397
latency_trace(stage,token,0,0);398
}399
void wm_trace_enable(int enabled) { atomic_store(&trace.enabled,enabled!=0); }400
uint64_t wm_trace_dropped(void) { return atomic_load(&trace.dropped); }401
static void trace_damage(struct server *s,uint64_t correlation,uint64_t output) {402
if(!atomic_load_explicit(&trace.enabled,memory_order_relaxed)) return;403
struct output *o;404
wl_list_for_each(o,&s->outputs,link)405
if(!output || o->id==output) trace_output_damage(&o->trace,correlation);406
}407
static bool push(struct queue *q, struct wm_message m) {408
unsigned w = atomic_load_explicit(&q->write, memory_order_relaxed);409
unsigned r = atomic_load_explicit(&q->read, memory_order_acquire);410
if (w - r == QUEUE_SIZE) return false;411
q->entries[w % QUEUE_SIZE] = m;412
atomic_store_explicit(&q->write, w + 1, memory_order_release);413
return true;414
}415
static bool pop(struct queue *q, struct wm_message *m) {416
unsigned r = atomic_load_explicit(&q->read, memory_order_relaxed);417
unsigned w = atomic_load_explicit(&q->write, memory_order_acquire);418
if (r == w) return false;419
*m = q->entries[r % QUEUE_SIZE];420
atomic_store_explicit(&q->read, r + 1, memory_order_release);421
return true;422
}423
static void wake(int fd) {424
uint64_t one = 1;425
ssize_t result;426
do { result = write(fd, &one, sizeof(one)); } while (result < 0 && errno == EINTR);427
if (result < 0 && errno != EAGAIN) atomic_fetch_add(&bridge.overflow, 1);428
}429
static void drain(int fd) {430
uint64_t value;431
while (read(fd, &value, sizeof(value)) < 0 && errno == EINTR) {}432
}433
#include "event-stream.h"434
#define EVENT(...) emit((struct wm_message){ .word = {__VA_ARGS__} })435
static void listen(struct wl_signal *signal, struct wl_listener *listener,436
void (*notify)(struct wl_listener *, void *)) {437
listener->notify = notify;438
wl_signal_add(signal, listener);439
}440
static void activity(struct server *s,int kind,int origin) {441
if(s->lock_state && origin!=0) return;442
if(s->idle && (s->activity_classes&kind) && (s->activity_origins&(1<<origin)))443
wlr_idle_notifier_v1_notify_activity(s->idle,s->seat);444
}445
static struct view *find_view(struct server *s, uint64_t id) {446
struct view *v;447
wl_list_for_each(v, &s->views, link) if (v->id == id) return v;448
return NULL;449
}450
static struct output *find_output(struct server *s, uint64_t id) {451
struct output *o;452
wl_list_for_each(o, &s->outputs, link) if (o->id == id) return o;453
return NULL;454
}455
static struct pixels *find_pixels(struct server *s,uint64_t id) {456
struct pixels *p;457
wl_list_for_each(p,&s->pixels,link) if(p->id==id) return p;458
return NULL;459
}460
static struct keyboard *find_keyboard(struct server *s, uint64_t id) {461
if (id == s->synthetic_owner.id) return &s->synthetic_owner;462
struct keyboard *k;463
wl_list_for_each(k, &s->keyboards, link) if (k->id == id) return k;464
return NULL;465
}466
#include "roles.h"467
#include "foreign-toplevel.h"469
static bool visible(struct view *v) {470
int x, y;471
return v && v->tree && v->mapped && wlr_scene_node_coords(&v->tree->node, &x, &y);472
}473
static void deliver_key(struct server *s, uint32_t time, int code, bool pressed, int override) {474
struct wlr_keyboard *key = &s->synthetic;475
if (!key->xkb_state) return;476
if(pressed) { if(s->key_refs[code]++>0) return; }477
else { if(!s->key_refs[code] || --s->key_refs[code]>0) return; }478
/* Publish an explicitly changed group before the next key uses it.479
* Modifier transitions caused by this key still follow the key event. */480
if(override<0) wlr_seat_keyboard_notify_modifiers(s->seat,&key->modifiers);481
xkb_state_update_key(key->xkb_state, code + 8, pressed ? XKB_KEY_DOWN : XKB_KEY_UP);482
struct wlr_keyboard_modifiers mods = {483
.depressed = xkb_state_serialize_mods(key->xkb_state, XKB_STATE_MODS_DEPRESSED),484
.latched = xkb_state_serialize_mods(key->xkb_state, XKB_STATE_MODS_LATCHED),485
.locked = xkb_state_serialize_mods(key->xkb_state, XKB_STATE_MODS_LOCKED),486
.group = xkb_state_serialize_layout(key->xkb_state, XKB_STATE_LAYOUT_EFFECTIVE),487
};488
key->modifiers = mods;489
if(override>=0) {490
struct wlr_keyboard_modifiers replaced=mods;491
replaced.depressed=0;492
const char *names[8]={XKB_MOD_NAME_SHIFT,XKB_MOD_NAME_CAPS,XKB_MOD_NAME_CTRL,493
XKB_MOD_NAME_ALT,"Mod2","Mod3",XKB_MOD_NAME_LOGO,"Mod5"};494
for(int bit=0;bit<8;bit++) {495
xkb_mod_index_t index=xkb_keymap_mod_get_index(key->keymap,names[bit]);496
if((override&(1<<bit)) && index<32) replaced.depressed|=1u<<index;497
}498
wlr_seat_keyboard_notify_modifiers(s->seat,&replaced);499
}500
wlr_seat_keyboard_notify_key(s->seat, time, code, pressed);501
wlr_seat_keyboard_notify_modifiers(s->seat, &mods);502
}503
static void constraints_end(struct server *s);504
static void constraints_observe(struct server *s);505
static void constrained_warp(struct server *s,double x,double y);506
static void clear_held(struct server *s) {507
struct keyboard *k = &s->synthetic_owner;508
for (int pass = 0; ; pass++) {509
for (int i = 0; i < MAX_KEYS; i++) {510
k->intercepted[i]=false;511
if (k->forwarded[i] >= 0) {512
deliver_key(s, now_ms(), k->forwarded[i], false,k->forwarded_modifiers[i]);513
k->forwarded[i] = -1;514
}515
}516
if (!pass) {517
if (wl_list_empty(&s->keyboards)) break;518
k = wl_container_of(s->keyboards.next, k, link);519
} else {520
if (k->link.next == &s->keyboards) break;521
k = wl_container_of(k->link.next, k, link);522
}523
}524
for (int i = 0; i < QUEUE_SIZE; i++) {525
if (s->buttons[i].held) {526
wlr_seat_pointer_notify_button(s->seat, now_ms(), s->buttons[i].target, WL_POINTER_BUTTON_STATE_RELEASED);527
s->buttons[i].held = false;528
}529
}530
}531
static void route_motion(struct server *s, uint32_t time, bool moved);532
static void set_focus(struct server *s, uint64_t id) {533
if (s->lock_state) return;534
struct view *v = id ? find_view(s, id) : NULL;535
if (id && (!visible(v) || (v->layer && v->layer->current.keyboard_interactive ==536
ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE))) return;537
if (s->focus == id) return;538
clear_held(s);539
route_motion(s,now_ms(),false);540
/* XWM selection authorization follows its active X surface, not the541
* Wayland seat alone. Focus can change without a configure transaction. */542
struct view *previous = find_view(s, s->focus);543
if (previous && previous->x11)544
wlr_xwayland_surface_activate(previous->x11, false);545
if (v && v->x11)546
wlr_xwayland_surface_activate(v->x11, true);547
s->focus = id;548
s->generation++;549
if (!v) wlr_seat_keyboard_notify_clear_focus(s->seat);550
else {551
struct wlr_keyboard *k = &s->synthetic;552
wlr_seat_set_keyboard(s->seat, k);553
wlr_seat_keyboard_notify_enter(s->seat, view_surface(v), NULL, 0, &k->modifiers);554
}555
latency_trace(11, s->trace_correlation, id, s->generation);556
EVENT(25, id, s->generation, s->trace_correlation);557
foreign_refresh(s);558
constraints_observe(s);559
}560
static struct wlr_surface *surface_at(struct server *s, double x, double y, double *sx, double *sy) {561
struct wlr_scene_node *node = wlr_scene_node_at(562
s->lock_state ? &s->lock_scene->node : &s->normal->node, x, y, sx, sy);563
if (!node || node->type != WLR_SCENE_NODE_BUFFER) return NULL;564
struct wlr_scene_surface *surface = wlr_scene_surface_try_from_buffer(wlr_scene_buffer_from_node(node));565
if(surface) return surface->surface;566
while(node && node->parent!=s->normal)567
node=node->parent ? &node->parent->node : NULL;568
if(!node || node->type!=WLR_SCENE_NODE_TREE || !node->data) return NULL;569
struct view *view=node->data;570
if(!view->saved || !view->mapped) return NULL;571
int vx,vy;572
if(!wlr_scene_node_coords(&view->tree->node,&vx,&vy)) return NULL;573
double local_x=x-vx+view->committed.x, local_y=y-vy+view->committed.y;574
if(view->native) return wlr_xdg_surface_surface_at(view->native->base,local_x,local_y,sx,sy);575
if(view->layer) return wlr_layer_surface_v1_surface_at(view->layer,local_x,local_y,sx,sy);576
return wlr_surface_surface_at(view_surface(view),local_x,local_y,sx,sy);577
}578
static int64_t target_at(struct server *s, double x, double y);579
static void update_drag_icon(struct server *s);580
struct pointer_surface_position {581
struct wlr_surface *surface;582
int x,y;583
bool found;584
};585
static void pointer_surface_position(struct wlr_scene_buffer *buffer,int x,int y,void *data) {586
struct pointer_surface_position *position=data;587
struct wlr_scene_surface *scene=wlr_scene_surface_try_from_buffer(buffer);588
if(scene && scene->surface==position->surface) {589
/* Subsurface trees move the buffer to its clip origin. Implicit590
* grabs need the unclipped surface origin retained by its parent. */591
position->x=x-buffer->node.x;592
position->y=y-buffer->node.y;593
position->found=true;594
}595
}596
static void pointer_native_position(struct wlr_surface *surface,int x,int y,void *data) {597
struct pointer_surface_position *position=data;598
if(surface==position->surface) {599
position->x+=x; position->y+=y; position->found=true;600
}601
}602
static void route_motion(struct server *s, uint32_t time, bool moved) {603
update_drag_icon(s);604
if(s->route_hit_test) {605
double sx,sy;606
struct wlr_surface *surface=surface_at(s,s->cursor->x,s->cursor->y,&sx,&sy);607
if(s->seat->pointer_state.button_count && !wlr_seat_pointer_has_grab(s->seat)) {608
struct pointer_surface_position position={.surface=s->seat->pointer_state.focused_surface};609
wlr_scene_node_for_each_buffer(s->lock_state ? &s->lock_scene->node : &s->normal->node,610
pointer_surface_position,&position);611
if(!position.found) {612
struct view *view;613
wl_list_for_each(view,&s->views,link) {614
if(!view->saved || !visible(view)) continue;615
int vx,vy;616
wlr_scene_node_coords(&view->tree->node,&vx,&vy);617
position.x=vx-view->committed.x; position.y=vy-view->committed.y;618
view_each_surface(view,pointer_native_position,&position);619
if(position.found) break;620
}621
}622
surface=position.found ? position.surface : NULL;623
sx=s->cursor->x-position.x; sy=s->cursor->y-position.y;624
}625
if(surface) {626
wlr_seat_pointer_notify_enter(s->seat,surface,sx,sy);627
wlr_seat_pointer_notify_motion(s->seat,time,sx,sy);628
} else wlr_seat_pointer_notify_clear_focus(s->seat);629
wlr_seat_pointer_notify_frame(s->seat);630
}631
if(s->observe_pointer && !(s->pointer_motion_only && !s->pointer_sampled && !moved)) {632
int64_t target=target_at(s,s->cursor->x,s->cursor->y);633
struct wlr_output *native=wlr_output_layout_output_at(s->layout,s->cursor->x,s->cursor->y);634
struct output *output=native ? native->data : NULL;635
uint64_t id=output ? output->id : 0;636
if(!s->pointer_sampled || target!=s->pointer_target || id!=s->pointer_output) {637
s->pointer_sampled=true;638
s->pointer_target=target; s->pointer_output=id;639
s->pointer_sample_token=++s->next_token;640
s->pointer_sample_generation=s->generation;641
EVENT(37,target,id,(int64_t)(s->cursor->x*1000),(int64_t)(s->cursor->y*1000),moved,s->generation,s->pointer_sample_token);642
}643
}644
constraints_observe(s);645
}646
static bool motion_matches(struct server *s,const struct input *in) {647
return s->motion_hook && (!s->motion_device || in->device==(uint64_t)s->motion_device) &&648
(!s->motion_region.width || wlr_box_contains_point(&s->motion_region,in->x,in->y));649
}650
static struct device *find_device(struct server *s,uint64_t id);651
static void notify_input(struct server *s,struct input *in);652
static void deliver_ready_input(struct server *s,struct input *in) {653
bool valid = (in->generation == s->generation || (in->native && in->kind==30)) &&654
!(s->lock_state && in->origin != 0) &&655
(in->origin==1 || find_device(s,in->device));656
if(in->kind==31 && in->state)657
valid=valid && target_at(s,in->x,in->y)==in->target;658
if (in->kind == 30) {659
struct keyboard *k = find_keyboard(s, in->device);660
if (k && in->code >= 0 && in->code < MAX_KEYS) {661
int code = k->forwarded[in->code];662
if (!in->state) {663
if (code >= 0) {664
deliver_key(s, in->time, code, false,665
k->forwarded_modifiers[in->code]<0 ? (int)in->modifiers : k->forwarded_modifiers[in->code]);666
k->forwarded[in->code] = -1;667
}668
} else if (valid && in->result && code<0 && in->replacement >= 0 && in->replacement < MAX_KEYS) {669
code = in->result == 2 ? in->replacement : in->code;670
k->forwarded[in->code] = code;671
k->forwarded_modifiers[in->code]=in->result==2 ? in->replacement_modifiers : -1;672
deliver_key(s, in->time, code, true,673
k->forwarded_modifiers[in->code]<0 ? (int)in->modifiers : k->forwarded_modifiers[in->code]);674
}675
}676
} else if (in->kind == 31 && in->code >= 0 && in->code < MAX_KEYS) {677
int found=-1,empty=-1;678
for(int i=0;i<QUEUE_SIZE;i++) {679
if(s->buttons[i].held && s->buttons[i].device==in->device && s->buttons[i].code==in->code) found=i;680
if(!s->buttons[i].held && empty<0) empty=i;681
}682
if(!in->state) {683
uint64_t press_token=in->press_token;684
if(found>=0) {685
press_token=s->buttons[found].press_token;686
wlr_seat_pointer_notify_button(s->seat,in->time,s->buttons[found].target,0);687
s->buttons[found].held=false;688
}689
/* Motion can precede the eventual focus decision. Publish a fresh690
* observation at release in the current epoch, so managers can691
* reconcile hover after their button/grab state clears. */692
bool moved=press_token && s->motion_delivered_token>press_token;693
if(moved) s->pointer_sampled=false;694
route_motion(s,in->time,moved);695
} else if(in->state && valid && in->result && found<0 && empty>=0) {696
int target=in->result==2 ? in->replacement : in->code;697
/* Cursor transport may have advanced while this press waited.698
* Revalidate its copied scene hit and deliver to that surface,699
* without moving the cursor back or stealing an existing grab. */700
if(!s->seat->pointer_state.button_count && !wlr_seat_pointer_has_grab(s->seat)) {701
double sx,sy;702
struct wlr_surface *surface=surface_at(s,in->x,in->y,&sx,&sy);703
if(surface) wlr_seat_pointer_notify_enter(s->seat,surface,sx,sy);704
else wlr_seat_pointer_notify_clear_focus(s->seat);705
}706
s->buttons[empty].device=in->device; s->buttons[empty].code=in->code;707
s->buttons[empty].press_token=in->token;708
s->buttons[empty].target=target; s->buttons[empty].held=true;709
wlr_seat_pointer_notify_button(s->seat,in->time,target,1);710
route_motion(s,in->time,false);711
}712
wlr_seat_pointer_notify_frame(s->seat);713
} else if (in->kind == 32 && valid && in->result && in->token>s->motion_delivered_token &&714
(in->native || (uint64_t)in->code == s->motion_generation)) {715
s->motion_delivered_token=in->token;716
constrained_warp(s,in->x,in->y);717
route_motion(s, in->time,true);718
}719
else if(in->kind==33 && valid && in->result &&720
target_at(s,s->cursor->x,s->cursor->y)==in->target) {721
wlr_seat_pointer_notify_axis(s->seat,in->time,in->state,in->x,(int32_t)in->y,in->code,in->modifiers);722
wlr_seat_pointer_notify_frame(s->seat);723
}724
if(!in->native || in->completion)725
EVENT(35, in->completion ? in->completion : in->token, valid, in->origin, in->token);726
}727
static void cancel_intercepted_input(struct server *s) {728
/* Cancel queued interception, leaving ordinary native streams untouched. */729
while(s->input_read!=s->input_write) {730
struct input *input=&s->inputs[s->input_read++%QUEUE_SIZE];731
if(input->completed) continue;732
input->ready=true;733
input->result=input->native || input->kind==32;734
if(input->kind==32) input->native=true;735
deliver_ready_input(s,input);736
}737
}738
static void deliver_input(struct server *s) {739
if(atomic_exchange(&bridge.input_resync,false)) {740
/* Transport resync invalidates discarded decision tokens. */741
cancel_intercepted_input(s);742
EVENT(99,0,3);743
}744
/* A waiting bound chord cannot hold cursor transport behind it. Expire745
* hooked motion independently, then retire its queue slot in seat order.746
* The transport token prevents an older delayed sample undoing new motion. */747
uint64_t now=now_ms();748
/* Ordinary keys preserve seat order behind a chord only for its original749
* budget. Release them in order even while the bound decision still waits.750
* They remain native and never need a policy disposition. */751
for(unsigned index=s->input_read;index!=s->input_write;index++) {752
struct input *key=&s->inputs[index%QUEUE_SIZE];753
if(key->kind!=30 || !key->native || key->completed) continue;754
if(now<key->order_deadline) break;755
deliver_ready_input(s,key);756
key->completed=true;757
}758
for(unsigned index=s->input_read;index!=s->input_write;index++) {759
struct input *motion=&s->inputs[index%QUEUE_SIZE];760
if(motion->kind!=32 || motion->completed || motion->ready || now<motion->deadline) continue;761
motion->ready=true; motion->native=true; motion->result=1;762
atomic_fetch_add(&bridge.input_expired,1);763
deliver_ready_input(s,motion);764
motion->completed=true;765
}766
while (s->input_read != s->input_write) {767
struct input *in = &s->inputs[s->input_read % QUEUE_SIZE];768
if(in->completed) { s->input_read++; continue; }769
if(in->native && in->kind==32 && in->generation==s->generation && motion_matches(s,in)) {770
if(now_ms()-(uint64_t)s->motion_last < (uint64_t)s->motion_period) {771
if(s->input_read+1!=s->input_write) {772
struct input *next=&s->inputs[(s->input_read+1)%QUEUE_SIZE];773
if(next->native && next->kind==32 && next->device==in->device &&774
next->origin==in->origin && next->generation==in->generation && motion_matches(s,next)) {775
if(in->completion) EVENT(35,in->completion,0,in->origin,in->token);776
s->input_read++;777
continue;778
}779
}780
break;781
}782
in->native=false; in->ready=false; in->code=s->motion_generation;783
s->motion_last=now_ms();784
}785
if(!in->ready && now_ms()>=in->deadline) {786
atomic_fetch_add(&bridge.input_expired,1);787
/* Bound decisions stay intercepted across scheduler stalls.788
* Ordinary keys leave independently after their ordering budget. */789
if(in->kind==30 || in->kind==31) {790
wlr_log(WLR_INFO,"INPUT_BOUND_TIMEOUT kind=%d device=%llu code=%d token=%llu outcome=wait",791
in->kind,(unsigned long long)in->device,in->code,(unsigned long long)in->token);792
in->deadline=UINT64_MAX;793
} else {794
in->ready=true;795
in->result=in->kind==32;796
if(in->kind==32) in->native=true;797
}798
}799
if(!in->ready && !in->notified) {800
bool stale=in->generation!=s->generation;801
bool semantic_key=in->kind==30 && !s->lock_state;802
if((stale && !semantic_key) || (s->lock_state && in->origin!=0) ||803
(in->origin!=1 && !find_device(s,in->device))) {804
in->ready=true; in->result=0;805
} else {806
in->notified=true;807
notify_input(s,in);808
}809
}810
if (!in->ready) break;811
deliver_ready_input(s,in);812
s->input_read++;813
}814
if(s->input_read==s->input_write && !s->has_pending_motion) {815
s->motion_x=s->cursor->x; s->motion_y=s->cursor->y;816
}817
}818
static int64_t target_at(struct server *s, double x, double y) {819
update_drag_icon(s);820
if(s->lock_state) return 0;821
double sx,sy;822
struct wlr_scene_node *node=wlr_scene_node_at(&s->normal->node,x,y,&sx,&sy);823
while(node && node->parent!=s->normal)824
node=node->parent ? &node->parent->node : NULL;825
if(node && node->type==WLR_SCENE_NODE_BUFFER) {826
struct pixels *p;827
wl_list_for_each(p,&s->pixels,link)828
if(&p->scene->node==node) return -(int64_t)p->id;829
return 0;830
}831
if(!node || node->type!=WLR_SCENE_NODE_TREE || !node->data) return 0;832
return ((struct view *)node->data)->id;833
}834
static void notify_input(struct server *s,struct input *in) {835
struct input input=*in;836
int64_t detail=input.kind==30 ? input.base_keysym : input.target;837
int64_t extra=input.kind==30 ? input.first_keysym : (int64_t)input.output;838
EVENT(input.kind,input.token,input.device,input.time,input.code,input.state,839
input.modifiers,s->focus,input.generation,input.origin,840
(int64_t)(input.x*1000),(int64_t)(input.y*1000),841
input.kind==32 ? (uint64_t)input.code : s->motion_generation,input.keysym,detail,extra);842
}843
static uint64_t window_for_surface(struct server *s,struct wlr_surface *surface);844
static bool intercept_matches(struct server *s,const struct input *input) {845
bool key=input->kind==30;846
if(key ? (s->key_intercept_all && !s->lock_state) : s->button_intercept_all) return true;847
struct input_intercept *rules=key ? s->key_intercepts : s->button_intercepts;848
unsigned count=key ? s->key_intercept_count : s->button_intercept_count;849
for(unsigned i=0;i<count;i++) {850
struct input_intercept *rule=&rules[i];851
if((input->modifiers & rule->mask)!=rule->modifiers) continue;852
if(key) {853
if((rule->scope==1 && s->lock_state) || (rule->scope==2 && !s->lock_state)) continue;854
if(rule->code==input->keysym || rule->code==input->base_keysym || rule->code==input->first_keysym)855
return true;856
} else if((!rule->code || rule->code==(uint32_t)input->code) &&857
(rule->scope==0 || (rule->scope==1 && input->target>0) ||858
(rule->scope==2 && input->target<0) ||859
(rule->scope==3 && rule->target==input->target) ||860
(rule->scope==4 && input->target>0 &&861
(uint64_t)input->target!=window_for_surface(s,s->seat->keyboard_state.focused_surface))))862
return true;863
}864
return false;865
}866
static bool configure_input_intercepts(struct server *s,bool key,int64_t *words,int64_t count) {867
if(count<2 || words[0]<0 || words[0]>1 || words[1]<0 || words[1]>QUEUE_SIZE ||868
count!=2+words[1]*5) return false;869
for(int i=0;i<words[1];i++) {870
int64_t *rule=words+2+i*5;871
if(rule[0]<0 || rule[0]>UINT32_MAX || rule[1]<0 || rule[1]>255 ||872
rule[2]<0 || rule[2]>255 || (rule[1] & ~rule[2]) ||873
rule[3]<0 || rule[3]>(key ? 2 : 4)) return false;874
}875
struct input_intercept *rules=key ? s->key_intercepts : s->button_intercepts;876
for(int i=0;i<words[1];i++) {877
int64_t *rule=words+2+i*5;878
rules[i]=(struct input_intercept){rule[0],rule[1],rule[2],rule[3],rule[4]};879
}880
if(key) { s->key_intercept_all=words[0]; s->key_intercept_count=words[1]; }881
else { s->button_intercept_all=words[0]; s->button_intercept_count=words[1]; }882
return true;883
}884
static void queue_input(struct server *s,struct input input) {885
scene_move_button(s,&input);886
if(input.kind!=32 && s->has_pending_motion) {887
struct input pending=s->pending_motion;888
s->has_pending_motion=false;889
queue_input(s,pending);890
}891
if(input.kind==30 || input.kind==31) activity(s,input.kind==30 ? 1 : 4,input.origin);892
input.token=++s->next_token;893
if(!input.generation) input.generation=s->generation;894
input.deadline=now_ms()+INPUT_DECISION_DEADLINE_MS;895
input.order_deadline=input.deadline;896
if(input.kind==31 || input.kind==32) {897
input.target=target_at(s,input.x,input.y);898
struct wlr_output *native=wlr_output_layout_output_at(s->layout,input.x,input.y);899
struct output *output=native ? native->data : NULL;900
input.output=output ? output->id : 0;901
}902
bool intercepted=true;903
if(input.kind==30) {904
struct keyboard *keyboard=find_keyboard(s,input.device);905
if(!keyboard || input.code<0 || input.code>=MAX_KEYS) return;906
intercepted=input.state ? intercept_matches(s,&input) : keyboard->intercepted[input.code];907
if(getenv("SIGIL_WM_DEBUG_INPUT") && input.state)908
wlr_log(WLR_ERROR,"INPUT_ELIGIBILITY device=%llu code=%d modifiers=%u symbol=%u base=%u first=%u rules=%u all=%d intercept=%d",909
(unsigned long long)input.device,input.code,input.modifiers,input.keysym,input.base_keysym,910
input.first_keysym,s->key_intercept_count,s->key_intercept_all,intercepted);911
keyboard->intercepted[input.code]=input.state && intercepted;912
} else if(input.kind==31) {913
int found=-1,empty=-1;914
for(int i=0;i<QUEUE_SIZE;i++) {915
if(s->intercepted_buttons[i].held && s->intercepted_buttons[i].device==input.device &&916
s->intercepted_buttons[i].code==input.code) found=i;917
if(!s->intercepted_buttons[i].held && empty<0) empty=i;918
}919
intercepted=input.state ? intercept_matches(s,&input) : found>=0;920
if(input.state && intercepted && found<0 && empty>=0) {921
s->intercepted_buttons[empty].device=input.device;922
s->intercepted_buttons[empty].press_token=input.token;923
s->intercepted_buttons[empty].code=input.code;924
s->intercepted_buttons[empty].held=true;925
} else if(!input.state && found>=0) {926
input.press_token=s->intercepted_buttons[found].press_token;927
s->intercepted_buttons[found].held=false;928
}929
}930
if ((input.kind == 30 || input.kind == 31) && input.state)931
latency_trace(0, input.token, input.code, 0);932
if(!intercepted) {933
input.native=true; input.ready=true; input.result=1; input.replacement=input.code;934
if(input.kind==31) {935
/* Observation precedes client delivery, so a subsequent client936
* move/resize request sees the press without blocking that press. */937
struct input observed=input;938
observed.kind=126;939
notify_input(s,&observed);940
}941
uint64_t order_deadline=0;942
if(input.kind==30) {943
for(unsigned index=s->input_read;index!=s->input_write;index++) {944
struct input *pending=&s->inputs[index%QUEUE_SIZE];945
if(pending->kind==30 && !pending->completed) {946
order_deadline=pending->order_deadline;947
break;948
}949
}950
}951
if(!order_deadline || now_ms()>=order_deadline) {952
deliver_input(s);953
deliver_ready_input(s,&input);954
return;955
}956
input.order_deadline=order_deadline;957
}958
if(input.kind==32 && s->input_read!=s->input_write) {959
struct input *last=&s->inputs[(s->input_write-1)%QUEUE_SIZE];960
if(last->kind==32 && !last->notified && !last->completed && last->device==input.device &&961
last->origin==input.origin && last->generation==input.generation) {962
if(last->completion) EVENT(35,last->completion,0,last->origin,last->token);963
input.deadline=last->deadline;964
atomic_fetch_add(&bridge.motion_coalesced,1);965
*last=input;966
deliver_input(s);967
return;968
}969
}970
if(s->input_write-s->input_read==QUEUE_SIZE) {971
cancel_intercepted_input(s);972
input.ready=true; input.result=input.native || input.kind==32;973
if(input.kind==32) input.native=true;974
deliver_ready_input(s,&input);975
EVENT(99,0,2); return;976
}977
s->inputs[s->input_write++%QUEUE_SIZE]=input;978
deliver_input(s);979
}980
#include "keyboard-settings.h"981
static uint64_t window_for_surface(struct server *s,struct wlr_surface *surface);982
static void lock_keyboard_input(struct keyboard *keyboard,struct wlr_keyboard_key_event *event);983
#include "session-control.h"985
static void key_event(struct wl_listener *listener, void *data) {986
struct keyboard *k = wl_container_of(listener, k, key);987
struct wlr_keyboard_key_event *e = data;988
if(session_key(k,e)) return;989
struct input input={.kind=30, .device=k->id, .time=e->time_msec,990
.code=e->keycode, .state=e->state, .origin=k->is_virtual ? 2 : 0,991
.keysym=k->native->xkb_state ? xkb_state_key_get_one_sym(k->native->xkb_state,e->keycode+8) : 0,992
.modifiers=wlr_keyboard_get_modifiers(k->native),993
.base_keysym=base_keysym(k->native,e->keycode,false),994
.first_keysym=base_keysym(k->native,e->keycode,true)};995
if(k->server->lock_state) {996
if(k->is_virtual) return;997
bool intercepted=e->state ? intercept_matches(k->server,&input) :998
(e->keycode<MAX_KEYS && k->intercepted[e->keycode]);999
if(!intercepted) { lock_keyboard_input(k,e); return; }1000
}1001
queue_input(k->server,input);1002
}1003
static void modifier_event(struct wl_listener *listener, void *data) {1004
(void)data;1005
struct keyboard *keyboard=wl_container_of(listener,keyboard,modifiers);1006
if(keyboard->fixed_group && keyboard->native->modifiers.group!=keyboard->group) {1007
struct wlr_keyboard_modifiers *mods=&keyboard->native->modifiers;1008
wlr_keyboard_notify_modifiers(keyboard->native,mods->depressed,mods->latched,mods->locked,keyboard->group);1009
return;1010
}1011
keyboard->server->keyboard_modifiers=wlr_keyboard_get_modifiers(keyboard->native);1012
if(keyboard->server->lock_state && !keyboard->is_virtual)1013
wlr_seat_keyboard_notify_modifiers(keyboard->server->seat,&keyboard->native->modifiers);1014
EVENT(43,keyboard->id,keyboard->server->keyboard_modifiers,keyboard->native->modifiers.group);1015
}1016
static void keyboard_destroy(struct wl_listener *listener, void *data) {1017
(void)data;1018
struct keyboard *k = wl_container_of(listener, k, destroy);1019
for(int code=0;code<MAX_KEYS;code++) {1020
if(k->forwarded[code]>=0)1021
deliver_key(k->server,now_ms(),k->forwarded[code],false,k->forwarded_modifiers[code]);1022
}1023
wl_list_remove(&k->key.link);1024
wl_list_remove(&k->modifiers.link);1025
wl_list_remove(&k->destroy.link);1026
wl_list_remove(&k->link);1027
free(k);1028
}1029
#include "devices.h"1030
static void attach_input(struct server *s,struct wlr_input_device *native,bool is_virtual) {1031
struct device *device=calloc(1,sizeof(*device));1032
if(!device) { atomic_fetch_add(&bridge.overflow,1); return; }1033
device->server=s; device->native=native; device->id=++s->next_id;1034
device->is_virtual=is_virtual;1035
native->data=(void *)(uintptr_t)((device->id<<1)|(is_virtual ? 1 : 0));1036
wl_list_insert(&s->devices,&device->link);1037
listen(&native->events.destroy,&device->destroy,device_destroy);1038
atomic_fetch_add(&bridge.objects,1);1039
bool libinput=wlr_input_device_is_libinput(native);1040
struct libinput_device *handle=libinput ? wlr_libinput_get_device_handle(native) : NULL;1041
EVENT(40,device->id,native->type,is_virtual,libinput,1042
handle ? libinput_device_config_tap_get_finger_count(handle) : 0,wlr_input_device_is_wl(native));1043
object_text(device->id,28,native->name);1044
if(native->type==WLR_INPUT_DEVICE_KEYBOARD) {1045
struct keyboard *keyboard=calloc(1,sizeof(*keyboard));1046
if(!keyboard) { atomic_fetch_add(&bridge.overflow,1); return; }1047
keyboard->id=device->id; keyboard->server=s; keyboard->is_virtual=is_virtual;1048
keyboard->native=wlr_keyboard_from_input_device(native);1049
for(int code=0;code<MAX_KEYS;code++) keyboard->forwarded[code]=-1;1050
listen(&keyboard->native->events.key,&keyboard->key,key_event);1051
listen(&keyboard->native->events.modifiers,&keyboard->modifiers,modifier_event);1052
listen(&native->events.destroy,&keyboard->destroy,keyboard_destroy);1053
wl_list_insert(&s->keyboards,&keyboard->link);1054
} else if(native->type==WLR_INPUT_DEVICE_POINTER)1055
wlr_cursor_attach_input_device(s->cursor,native);1056
}1057
static void new_input(struct wl_listener *listener,void *data) {1058
struct server *s=wl_container_of(listener,s,new_input);1059
attach_input(s,data,false);1060
}1061
static void motion_input(struct server *s, uint32_t time, double x, double y, uint64_t device, int origin, uint64_t completion) {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) {1069
struct input in={.kind=32, .device=device, .code=s->motion_generation,1070
.time=time, .x=x, .y=y, .origin=origin, .completion=completion,1071
.generation=s->generation, .modifiers=s->keyboard_modifiers};1072
if (now_ms()-(uint64_t)s->motion_last < (uint64_t)s->motion_period) {1073
if(s->has_pending_motion && s->pending_motion.completion)1074
EVENT(35,s->pending_motion.completion,0,s->pending_motion.origin);1075
s->pending_motion=in; s->has_pending_motion=true;1076
return;1077
}1078
s->motion_last=now_ms();1079
queue_input(s,in);1080
} else {1081
s->motion_delivered_token=++s->next_token;1082
constrained_warp(s,x,y);1083
s->motion_x=s->cursor->x; s->motion_y=s->cursor->y;1084
route_motion(s, time,true);1085
if (completion) EVENT(35, completion, 1, origin);1086
}1087
}1088
static void cursor_motion(struct wl_listener *listener, void *data) {1089
struct server *s = wl_container_of(listener, s, motion);1090
struct wlr_pointer_motion_event *e = data;1091
uintptr_t tag=(uintptr_t)e->pointer->base.data;1092
if(s->lock_state && (tag&1)) return;1093
if(s->relative_route && !s->lock_state)1094
wlr_relative_pointer_manager_v1_send_relative_motion(s->relative,s->seat,1095
(uint64_t)e->time_msec*1000,e->delta_x,e->delta_y,e->unaccel_dx,e->unaccel_dy);1096
s->motion_x+=e->delta_x; s->motion_y+=e->delta_y;1097
motion_input(s, e->time_msec, s->motion_x, s->motion_y, tag>>1, tag&1 ? 2 : 0, 0);1098
}1099
static void cursor_absolute(struct wl_listener *listener, void *data) {1100
struct server *s = wl_container_of(listener, s, absolute);1101
struct wlr_pointer_motion_absolute_event *e = data;1102
double x, y;1103
wlr_cursor_absolute_to_layout_coords(s->cursor, &e->pointer->base, e->x, e->y, &x, &y);1104
s->motion_x=x; s->motion_y=y;1105
uintptr_t tag=(uintptr_t)e->pointer->base.data;1106
motion_input(s, e->time_msec, x, y, tag>>1, tag&1 ? 2 : 0, 0);1107
}1108
static double pointer_x(struct server *s) {1109
return s->input_read!=s->input_write || s->has_pending_motion ? s->motion_x : s->cursor->x;1110
}1111
static double pointer_y(struct server *s) {1112
return s->input_read!=s->input_write || s->has_pending_motion ? s->motion_y : s->cursor->y;1113
}1114
static void axis_input(struct server *s,uint64_t device,int origin,uint32_t time,1115
int orientation,double delta,int32_t discrete,int source,int direction,uint64_t completion) {1116
activity(s,8,origin);1117
(void)device;1118
if(s->lock_state && origin!=0) return;1119
{1120
wlr_seat_pointer_notify_axis(s->seat,time,orientation,delta,discrete,source,direction);1121
wlr_seat_pointer_notify_frame(s->seat);1122
if(completion) EVENT(35,completion,1,origin);1123
}1124
}1125
static void cursor_button(struct wl_listener *listener, void *data) {1126
struct server *s = wl_container_of(listener, s, button);1127
struct wlr_pointer_button_event *e = data;1128
uintptr_t tag=(uintptr_t)e->pointer->base.data;1129
queue_input(s, (struct input){.kind=31, .device=tag>>1, .origin=tag&1 ? 2 : 0, .time=e->time_msec, .code=e->button,1130
.state=e->state, .x=pointer_x(s), .y=pointer_y(s), .modifiers=s->keyboard_modifiers});1131
}1132
static void cursor_axis(struct wl_listener *listener, void *data) {1133
struct server *s = wl_container_of(listener, s, axis);1134
struct wlr_pointer_axis_event *e = data;1135
if(s->lock_state && ((uintptr_t)e->pointer->base.data&1)) return;1136
uintptr_t tag=(uintptr_t)e->pointer->base.data;1137
axis_input(s,tag>>1,tag&1 ? 2 : 0,e->time_msec,e->orientation,e->delta,1138
e->delta_discrete,e->source,e->relative_direction,0);1139
}1140
static void cursor_frame(struct wl_listener *listener, void *data) {1141
(void)data;1142
struct server *s = wl_container_of(listener, s, cursor_frame);1143
wlr_seat_pointer_notify_frame(s->seat);1144
}1145
static void cursor_request(struct wl_listener *listener, void *data) {1146
struct server *s = wl_container_of(listener, s, cursor_request);1147
struct wlr_seat_pointer_request_set_cursor_event *e = data;1148
if (s->allow_client_cursor && e->seat_client == s->seat->pointer_state.focused_client)1149
wlr_cursor_set_surface(s->cursor, e->surface, e->hotspot_x, e->hotspot_y);1150
}1152
static void layer_presentation(struct view *view, bool presented, uint32_t sequence);1153
static void layer_output_present(struct output *output, struct wlr_output_event_present *event);1154
#include "lock.h"1155
#include "permissions.h"1156
static uint64_t window_for_surface(struct server *s,struct wlr_surface *surface);1157
#include "extensions.h"1158
#include "legacy-decorations.h"1159
#include "desktop.h"1160
#include "constraints.h"1162
static void check_plan(struct server *s);1163
static void view_map(struct wl_listener *listener, void *data) {1164
(void)data;1165
struct view *v=wl_container_of(listener,v,map);1166
v->mapped=true;1167
foreign_map(v);1168
wlr_scene_node_set_enabled(&v->tree->node, false);1169
struct wlr_box box=v->native->base->geometry;1170
if(box.width<=0 || box.height<=0) {1171
box.width=v->native->base->surface->current.width;1172
box.height=v->native->base->surface->current.height;1173
}1174
v->committed=box;1175
v->committed_serial=v->native->base->current.configure_serial;1176
EVENT(18,v->id,box.x,box.y,box.width,box.height,v->committed_serial);1177
EVENT(11,v->id);1178
object_text(v->id,15,v->native->title);1179
object_text(v->id,16,v->native->app_id);1180
}1181
static void view_unmap(struct wl_listener *listener, void *data) {1182
(void)data;1183
struct view *v=wl_container_of(listener,v,unmap);1184
v->mapped=false;1185
if (v->layer) {1186
v->layer_present_commit = 0;1187
v->layer_present_armed = false;1188
layer_presentation(v, false, 0);1189
}1190
foreign_unmap(v);1191
if(v->tree) wlr_scene_node_set_enabled(&v->tree->node,false);1192
if(v->server->focus==v->id) set_focus(v->server,0);1193
EVENT(12,v->id);1194
check_plan(v->server);1195
}1196
static void view_commit(struct wl_listener *listener, void *data) {1197
(void)data;1198
struct view *v=wl_container_of(listener,v,commit);1199
struct wlr_xdg_toplevel_state *h=&v->native->current;1200
uint64_t parent=v->native->parent ? ((struct view *)v->native->parent->base->data)->id : 0;1201
if(v->native->base->initial_commit || v->hints_min_width!=h->min_width ||1202
v->hints_min_height!=h->min_height || v->hints_max_width!=h->max_width ||1203
v->hints_max_height!=h->max_height || v->hints_parent!=parent) {1204
int kind=v->native->base->initial_commit ? 14 : 123;1205
EVENT(kind,v->id,h->min_width,h->min_height,h->max_width,h->max_height,parent);1206
v->hints_min_width=h->min_width;1207
v->hints_min_height=h->min_height;1208
v->hints_max_width=h->max_width;1209
v->hints_max_height=h->max_height;1210
v->hints_parent=parent;1211
}1212
struct wlr_box box=v->native->base->geometry;1213
if(box.x!=v->committed.x || box.y!=v->committed.y || box.width!=v->committed.width || box.height!=v->committed.height ||1214
v->committed_serial!=v->native->base->current.configure_serial) {1215
v->committed=box;1216
v->committed_serial=v->native->base->current.configure_serial;1217
EVENT(18,v->id,box.x,box.y,box.width,box.height,v->native->base->current.configure_serial);1218
}1219
apply_view_clip(v);1220
check_plan(v->server);1221
}1222
static void view_destroy(struct wl_listener *listener, void *data) {1223
(void)data;1224
struct view *v=wl_container_of(listener,v,destroy);1225
struct server *s=v->server;1226
foreign_unmap(v);1227
if(s->focus==v->id) set_focus(s,0);1228
wl_list_remove(&v->map.link); wl_list_remove(&v->unmap.link);1229
wl_list_remove(&v->commit.link); wl_list_remove(&v->destroy.link);1230
wl_list_remove(&v->title.link); wl_list_remove(&v->app_id.link);1231
wl_list_remove(&v->move.link); wl_list_remove(&v->resize.link);1232
wl_list_remove(&v->maximize.link); wl_list_remove(&v->fullscreen.link);1233
wl_list_remove(&v->link);1234
v->native->base->data=NULL;1235
discard_saved(v); wlr_scene_node_destroy(&v->tree->node);1236
EVENT(13,v->id);1237
atomic_fetch_sub(&bridge.objects,1);1238
free(v);1239
check_plan(s);1240
}1241
static void view_title(struct wl_listener *listener, void *data) {1242
(void)data;1243
struct view *v=wl_container_of(listener,v,title);1244
object_text(v->id,15,v->native->title);1245
foreign_metadata(v);1246
}1247
static void view_app_id(struct wl_listener *listener, void *data) {1248
(void)data;1249
struct view *v=wl_container_of(listener,v,app_id);1250
object_text(v->id,16,v->native->app_id);1251
foreign_metadata(v);1252
}1253
static void view_move(struct wl_listener *listener, void *data) {1254
struct view *v=wl_container_of(listener,v,move);1255
struct wlr_xdg_toplevel_move_event *e=data;1256
if(wlr_seat_validate_pointer_grab_serial(v->server->seat,v->native->base->surface,e->serial))1257
EVENT(17,v->id,1,e->serial);1258
}1259
static void view_resize(struct wl_listener *listener, void *data) {1260
struct view *v=wl_container_of(listener,v,resize);1261
struct wlr_xdg_toplevel_resize_event *e=data;1262
if(wlr_seat_validate_pointer_grab_serial(v->server->seat,v->native->base->surface,e->serial))1263
EVENT(17,v->id,2,e->serial,e->edges);1264
}1265
static void view_maximize(struct wl_listener *listener, void *data) {1266
(void)data;1267
struct view *v=wl_container_of(listener,v,maximize);1268
EVENT(17,v->id,3,v->native->requested.maximized);1269
}1270
static void view_fullscreen(struct wl_listener *listener, void *data) {1271
(void)data;1272
struct view *v=wl_container_of(listener,v,fullscreen);1273
EVENT(17,v->id,4,v->native->requested.fullscreen);1274
}1275
static void new_view(struct wl_listener *listener, void *data) {1276
struct server *s=wl_container_of(listener,s,new_view);1277
struct wlr_xdg_toplevel *native=data;1278
struct view *v=calloc(1,sizeof(*v));1279
if(!v) { wl_client_post_no_memory(wl_resource_get_client(native->resource)); return; }1280
v->server=s; v->native=native; v->id=++s->next_id;1281
v->tree=wlr_scene_tree_create(s->normal);1282
if(!v->tree) { free(v); return; }1283
v->live=wlr_scene_xdg_surface_create(v->tree,native->base);1284
if(!v->live) { discard_saved(v); wlr_scene_node_destroy(&v->tree->node); free(v); return; }1285
native->base->data=v;1286
v->tree->node.data=v;1287
wlr_scene_node_set_enabled(&v->tree->node,false);1288
wl_list_insert(&s->views,&v->link);1289
listen(&native->base->surface->events.map,&v->map,view_map);1290
listen(&native->base->surface->events.unmap,&v->unmap,view_unmap);1291
listen(&native->base->surface->events.commit,&v->commit,view_commit);1292
listen(&native->events.destroy,&v->destroy,view_destroy);1293
listen(&native->events.set_title,&v->title,view_title);1294
listen(&native->events.set_app_id,&v->app_id,view_app_id);1295
listen(&native->events.request_move,&v->move,view_move);1296
listen(&native->events.request_resize,&v->resize,view_resize);1297
listen(&native->events.request_maximize,&v->maximize,view_maximize);1298
listen(&native->events.request_fullscreen,&v->fullscreen,view_fullscreen);1299
atomic_fetch_add(&bridge.objects,1);1300
pid_t pid=0;1301
uid_t uid=0;1302
gid_t gid=0;1303
wl_client_get_credentials(wl_resource_get_client(native->resource),&pid,&uid,&gid);1304
EVENT(10,v->id,0,pid,uid,gid);1305
refresh_inhibitors(s);1306
refresh_decorations(s);1307
}1308
struct popup {1309
struct wl_list link;1310
struct server *server;1311
uint64_t id,parent,deadline;1312
struct wlr_xdg_popup *native;1313
struct wlr_scene_tree *tree;1314
struct wl_listener commit,destroy,reposition;1315
};1316
static void constrain_popup(struct popup *p) {1317
struct server *s=p->server;1318
struct view *root=find_view(s,window_for_surface(s,p->native->parent));1319
if(!root) return;1320
int x,y;1321
wlr_scene_node_coords(&root->tree->node,&x,&y);1322
struct wlr_output *output=root->layer ? root->layer->output :1323
wlr_output_layout_output_at(s->layout,x+root->committed.width/2.0,y+root->committed.height/2.0);1324
if(!output) return;1325
struct wlr_box box;1326
wlr_output_layout_get_box(s->layout,output,&box);1327
/* wlroots resolves nested popup offsets from the root surface origin. */1328
box.x-=x-root->committed.x;1329
box.y-=y-root->committed.y;1330
wlr_xdg_popup_unconstrain_from_box(p->native,&box);1331
}1332
static void popup_request(struct popup *p) {1333
constrain_popup(p);1334
struct wlr_box box=p->native->scheduled.geometry;1335
p->deadline=now_ms()+bridge.request_deadline;1336
EVENT(80,p->id,p->parent,box.x,box.y,box.width,box.height);1337
}1338
static void popup_commit(struct wl_listener *listener,void *data) {1339
(void)data;1340
struct popup *p=wl_container_of(listener,p,commit);1341
if(p->native->base->initial_commit) popup_request(p);1342
}1343
static void popup_reposition(struct wl_listener *listener,void *data) {1344
(void)data;1345
struct popup *p=wl_container_of(listener,p,reposition);1346
popup_request(p);1347
}1348
static void popup_destroy(struct wl_listener *listener,void *data) {1349
(void)data;1350
struct popup *p=wl_container_of(listener,p,destroy);1351
p->native->base->data=NULL;1352
EVENT(81,p->id);1353
wl_list_remove(&p->commit.link); wl_list_remove(&p->destroy.link);1354
wl_list_remove(&p->reposition.link); wl_list_remove(&p->link); free(p);1355
}1356
static void new_popup(struct wl_listener *listener,void *data) {1357
struct server *s=wl_container_of(listener,s,new_popup);1358
struct wlr_xdg_popup *native=data;1359
if(!native->parent || native->base->data) return;1360
struct wlr_xdg_surface *parent=wlr_xdg_surface_try_from_wlr_surface(native->parent);1361
struct wlr_scene_tree *parent_tree=NULL;1362
uint64_t parent_id=0;1363
struct wlr_layer_surface_v1 *layer=wlr_layer_surface_v1_try_from_wlr_surface(native->parent);1364
if(layer && layer->data) {1365
struct view *v=layer->data; parent_tree=v->tree; parent_id=v->id;1366
} else if(!parent || !parent->data) return;1367
else if(parent->role==WLR_XDG_SURFACE_ROLE_TOPLEVEL) {1368
struct view *v=parent->data; parent_tree=v->tree; parent_id=v->id;1369
} else if(parent->role==WLR_XDG_SURFACE_ROLE_POPUP) {1370
struct popup *p=parent->data; parent_tree=p->tree; parent_id=p->id;1371
}1372
if(!parent_tree) return;1373
struct popup *p=calloc(1,sizeof(*p));1374
if(!p) return;1375
p->native=native; p->server=s; p->parent=parent_id; p->id=++s->next_id;1376
p->tree=wlr_scene_xdg_surface_create(parent_tree,native->base);1377
if(!p->tree) { free(p); return; }1378
native->base->data=p;1379
wl_list_insert(&s->popups,&p->link);1380
listen(&native->base->surface->events.commit,&p->commit,popup_commit);1381
listen(&native->events.destroy,&p->destroy,popup_destroy);1382
listen(&native->events.reposition,&p->reposition,popup_reposition);1383
}1384
static bool configure_popup(struct server *s,int64_t *w) {1385
if(w[2]<-32768 || w[2]>32768 || w[3]<-32768 || w[3]>32768 ||1386
w[4]<1 || w[4]>16384 || w[5]<1 || w[5]>16384) return false;1387
struct popup *p;1388
wl_list_for_each(p,&s->popups,link) {1389
if(p->id!=(uint64_t)w[1] || !p->native->base->initialized) continue;1390
p->native->scheduled.geometry=(struct wlr_box){w[2],w[3],w[4],w[5]};1391
constrain_popup(p);1392
wlr_xdg_surface_schedule_configure(p->native->base);1393
p->deadline=0;1394
return true;1395
}1396
return false;1397
}1399
#include "layers.h"1400
#include "x11.h"1402
/* Plan header: version, count, keyboard focus, deadline-ms; records (10):1403
* view, x,y,w,h, shown, activated, fullscreen, tiled-edges, clip.1404
* Record order is explicit bottom-to-top stacking. */1405
static bool valid_plan(struct server *s,const int64_t *w,size_t n) {1406
if(n<4 || w[0]!=1 || w[1]<0 || w[1]>MAX_PLAN || n!=4+(size_t)w[1]*10 ||1407
w[3]<0 || w[3]>10000) return false;1408
bool focus_valid=w[2]==0;1409
unsigned views=0;1410
for(int i=0;i<w[1];i++) {1411
const int64_t *p=w+4+10*i;1412
if(p[0]==INT64_MIN) return false;1413
struct view *v=p[0]>0 ? find_view(s,p[0]) : NULL;1414
if(p[0]>0 && ++views>MAX_PLAN_VIEWS) return false;1415
struct pixels *image=p[0]<0 ? find_pixels(s,-p[0]) : NULL;1416
if((!v && !image) || (v && !view_ready(v)) || p[1]<-32768 || p[1]>32768 ||1417
p[2]<-32768 || p[2]>32768 || p[3]<1 || p[3]>16384 || p[4]<1 ||1418
p[4]>16384 || p[5]<0 || p[5]>1 || p[6]<0 || p[6]>3 ||1419
p[7]<0 || p[7]>1 || p[8]<0 || p[8]>15 || p[9]<0 || p[9]>3) return false;1420
if(image && (p[6] || p[7] || p[8])) return false;1421
for(int j=0;j<i;j++) if(w[4+10*j]==p[0]) return false;1422
if(v && p[0]==w[2] && p[5] && v->mapped) focus_valid=true;1423
}1424
return focus_valid;1425
}1426
static void save_buffer(struct wlr_scene_buffer *buffer, int x, int y, void *data) {1427
struct view *v=data;1428
if(!buffer->buffer) return;1429
struct wlr_scene_buffer *copy=wlr_scene_buffer_create(v->saved,buffer->buffer);1430
if(!copy) return;1431
wlr_scene_buffer_set_dest_size(copy,buffer->dst_width,buffer->dst_height);1432
wlr_scene_buffer_set_source_box(copy,&buffer->src_box);1433
wlr_scene_buffer_set_transform(copy,buffer->transform);1434
wlr_scene_buffer_set_opaque_region(copy,&buffer->opaque_region);1435
wlr_scene_node_set_position(©->node,x,y);1436
}1437
static void release_saved(struct server *s) {1438
struct view *v;1439
wl_list_for_each(v,&s->views,link) {1440
if(!v->saved) continue;1441
discard_saved(v);1442
wlr_scene_node_set_enabled(&v->live->node,true);1443
}1444
}1445
static void reject_plan_object(struct server *s,int reason,int64_t object) {1446
if(s->plan) {1447
EVENT(23,s->plan_token,reason,object,0);1448
free(s->plan); s->plan=NULL;1449
release_saved(s);1450
}1451
}1452
static void reject_plan(struct server *s,int reason) { reject_plan_object(s,reason,0); }1453
static bool plan_waits_for_view(struct server *s,struct view *view) {1454
if(!s->plan || !view->mapped || view_configured(view)) return false;1455
for(int i=0;i<s->plan[1];i++) {1456
const int64_t *placement=s->plan+4+10*i;1457
if(placement[0]==(int64_t)view->id) return placement[5]!=0;1458
}1459
return false;1460
}1461
static bool pixel_scope_valid(struct server *s,const int64_t *scope);1462
static void check_plan(struct server *s) {1463
if(!s->plan) return;1464
const int64_t *w=s->plan;1465
bool ready=true;1466
for(int i=0;i<w[1];i++) {1467
int64_t id=w[4+10*i];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(plan_waits_for_view(s,v)) ready=false;1472
}1473
if(!ready && now_ms()<s->plan_deadline) return;1474
release_saved(s);1475
scene_move_plan(s,w);1476
for(int i=0;i<w[1];i++) {1477
const int64_t *p=w+4+10*i;1478
if(p[0]<0) {1479
struct pixels *image=find_pixels(s,-p[0]);1480
scene_move_position(s,p[0],&image->scene->node,p[1],p[2]);1481
wlr_scene_buffer_set_dest_size(image->scene,p[3],p[4]);1482
wlr_scene_node_set_enabled(&image->scene->node,1483
p[5] && pixel_scope_valid(s,image->scope));1484
wlr_scene_node_raise_to_top(&image->scene->node);1485
continue;1486
}1487
struct view *v=find_view(s,p[0]);1488
scene_move_position(s,p[0],&v->tree->node,p[1],p[2]);1489
wlr_scene_node_set_enabled(&v->tree->node,p[5] && v->mapped);1490
if (v->layer && p[5] && v->mapped && !v->layer_presented && !v->layer_present_armed) {1491
v->layer_present_commit = v->layer->output->commit_seq + 1;1492
v->layer_present_armed = true;1493
}1494
wlr_scene_node_raise_to_top(&v->tree->node);1495
v->clip_enabled=(p[9]&1)!=0;1496
v->clip_width=p[3]; v->clip_height=p[4];1497
apply_view_clip(v);1498
foreign_placement(v,p);1499
}1500
uint64_t previous_correlation=s->trace_correlation;1501
s->trace_correlation=s->plan_trace_count ? s->plan_trace_causes[0] : 0;1502
set_focus(s,w[2]);1503
s->trace_correlation=previous_correlation;1504
foreign_refresh(s);1505
route_motion(s,now_ms(),false);1506
if (s->scene_update_deadline) {1507
s->scene_update_deadline = 0;1508
struct output *output;1509
wl_list_for_each(output, &s->outputs, link) {1510
wlr_damage_ring_add_whole(&output->scene->damage_ring);1511
wlr_output_schedule_frame(output->native);1512
}1513
}1514
for(unsigned i=0;i<s->plan_trace_count;i++) {1515
trace_damage(s,s->plan_trace_causes[i],0);1516
latency_trace(10,s->plan_trace_causes[i],s->plan_token,0);1517
}1518
EVENT(22,s->plan_token,ready ? 0 : 1);1519
free(s->plan); s->plan=NULL;1520
}1521
static int64_t missing_plan_object(struct server *s,const int64_t *w,size_t n) {1522
if(n<4 || w[0]!=1 || w[1]<0 || w[1]>MAX_PLAN || n!=4+(size_t)w[1]*10) return 0;1523
for(int i=0;i<w[1];i++) {1524
int64_t id=w[4+10*i];1525
if(id==INT64_MIN) return 0;1526
if((id>0 && !find_view(s,id)) || (id<0 && !find_pixels(s,-id))) return id;1527
}1528
return 0;1529
}1530
static bool submit_plan(struct server *s,int64_t token,int64_t *w,size_t n,unsigned trace_count) {1531
int64_t missing=missing_plan_object(s,w,n);1532
if(missing) { EVENT(23,token,2,missing,1); free(w); return false; }1533
if(!valid_plan(s,w,n)) { EVENT(23,token,1); free(w); return false; }1534
reject_plan(s,3);1535
s->plan=w; s->plan_count=n; s->plan_token=token;1536
s->plan_trace_count=trace_count;1537
if(trace_count) memcpy(s->plan_trace_causes,w+n,trace_count*sizeof(uint64_t));1538
s->plan_deadline=now_ms()+w[3];1539
for(int i=0;i<w[1];i++) {1540
const int64_t *p=w+4+10*i;1541
if(p[0]<0) continue;1542
struct view *v=find_view(s,p[0]);1543
if(v->mapped && visible(v) && !v->saved) {1544
v->saved=wlr_scene_tree_create(v->tree);1545
if(v->saved) {1546
atomic_fetch_add(&bridge.saved_trees,1);1547
atomic_fetch_add(&bridge.snapshots,1);1548
wlr_scene_node_for_each_buffer(&v->live->node,save_buffer,v);1549
wlr_scene_node_set_enabled(&v->live->node,false);1550
}1551
}1552
bool resize=(p[9]&2)==0;1553
int width=resize || v->width<=0 ? p[3] : v->width;1554
int height=resize || v->height<=0 ? p[4] : v->height;1555
v->serial=configure_view(v,p[1],p[2],width,height,p[6]&1,p[7],p[8],(p[6]&2)!=0);1556
v->width=width; v->height=height;1557
}1558
EVENT(21,token);1559
struct output *output;1560
wl_list_for_each(output,&s->outputs,link)1561
if(output->native->enabled) wlr_output_schedule_frame(output->native);1562
check_plan(s);1563
return true;1564
}1565
static void frame_done(struct wlr_surface *surface,int x,int y,void *data) {1566
(void)x; (void)y;1567
wlr_surface_send_frame_done(surface,data);1568
}1569
static void publish_output_management(struct server *s);1570
static void queue_output_flush(struct server *s);1571
#include "output-atomic.h"1572
#include "output-controls.h"1573
#include "output-gamma.h"1574
static void output_buffer_filter(struct wlr_scene_buffer *buffer,int x,int y,void *data) {1575
(void)x; (void)y;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);1589
if(!o->native->enabled) return;1590
if (!o->server->lock_state && o->server->scene_update_deadline &&1591
now_ms() < o->server->scene_update_deadline) {1592
/* Keep configure/animation clients making progress while copied1593
* decorations and their complete scene are being prepared. */1594
struct timespec now;1595
clock_gettime(CLOCK_MONOTONIC, &now);1596
output_frame_done(o,&now);1597
return;1598
}1599
wlr_scene_output_for_each_buffer(o->scene,output_buffer_filter,o);1600
lock_arm(o);1601
bool committed=o->gamma_pending ? output_commit_gamma(o) : wlr_scene_output_commit(o->scene,NULL);1602
if(!committed) {1603
/* Retain damage and callbacks until a replacement frame is accepted. */1604
wlr_damage_ring_add_whole(&o->scene->damage_ring);1605
if(!o->gamma_pending) o->scene_retry_at=now_ms()+16;1606
return;1607
}1608
o->scene_retry_at=0;1609
struct timespec now;1610
clock_gettime(CLOCK_MONOTONIC,&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) {1615
struct output *o=wl_container_of(listener,o,readback);1616
struct wlr_output_event_commit *e=data;1617
if(e->state->committed & (WLR_OUTPUT_STATE_ENABLED|WLR_OUTPUT_STATE_MODE|1618
WLR_OUTPUT_STATE_SCALE|WLR_OUTPUT_STATE_TRANSFORM)) inventory_schedule(o->server);1619
if((e->state->committed&WLR_OUTPUT_STATE_ENABLED) && o->native->enabled) {1620
o->gamma_pending=true;1621
wlr_output_schedule_frame(o->native);1622
}1623
if(e->state->committed&(WLR_OUTPUT_STATE_ENABLED|WLR_OUTPUT_STATE_MODE))1624
EVENT(130,o->id,wlr_output_get_gamma_size(o->native),output_gamma_control(o)!=NULL);1625
if (e->state->committed & WLR_OUTPUT_STATE_BUFFER) {1626
if(trace_output_commit(&o->trace,o->native->commit_seq,o->id,e->when)) wake(bridge.event_fd);1627
}1628
if(!o->capture[0] || !(e->state->committed&WLR_OUTPUT_STATE_BUFFER) || !e->state->buffer) return;1629
void *data_ptr;1630
uint32_t format;1631
size_t stride;1632
struct wlr_buffer *b=e->state->buffer;1633
if(!wlr_buffer_begin_data_ptr_access(b,WLR_BUFFER_DATA_PTR_ACCESS_READ,&data_ptr,&format,&stride)) {1634
if (o->sample_frames) {1635
FILE *file = fopen(o->capture, "a");1636
if (file) { fprintf(file, "FAIL buffer is not CPU-readable\n"); fclose(file); }1637
o->sample_frames = 0;1638
o->capture[0] = 0;1639
wlr_output_lock_attach_render(o->native, false);1640
EVENT(51, o->id, false, b->width, b->height);1641
}1642
return;1643
}1644
bool ok=false;1645
if (o->sample_frames) {1646
FILE *file = fopen(o->capture, "a");1647
ok = file && (format == DRM_FORMAT_ARGB8888 || format == DRM_FORMAT_XRGB8888);1648
if (file) {1649
fprintf(file, "FRAME %u %u %d %d", ++o->sample_index,1650
o->native->commit_seq, b->width, b->height);1651
for (unsigned index = 0; index < o->sample_count; index++) {1652
int x = o->sample_points[index * 2], y = o->sample_points[index * 2 + 1];1653
uint32_t colour = 0;1654
if (ok && x < b->width && y < b->height)1655
colour = *(uint32_t *)((char *)data_ptr + y * stride + x * 4) & 0xffffff;1656
else ok = false;1657
fprintf(file, " %06x", colour);1658
}1659
fprintf(file, " %s\n", ok ? "PASS" : "FAIL");1660
if (fclose(file)) ok = false;1661
}1662
o->sample_frames--;1663
} else if(format==DRM_FORMAT_ARGB8888 || format==DRM_FORMAT_XRGB8888) {1664
FILE *file=fopen(o->capture,"wb");1665
if(file) {1666
fprintf(file,"P6\n%d %d\n255\n",b->width,b->height);1667
ok=true;1668
for(int y=0;y<b->height;y++) {1669
uint32_t *row=(uint32_t *)((char *)data_ptr+y*stride);1670
for(int x=0;x<b->width;x++) {1671
unsigned char rgb[3]={row[x]>>16,row[x]>>8,row[x]};1672
if(fwrite(rgb,1,3,file)!=3) ok=false;1673
}1674
}1675
if(fclose(file)) ok=false;1676
}1677
}1678
wlr_buffer_end_data_ptr_access(b);1679
if (!o->sample_frames) {1680
o->capture[0]=0;1681
wlr_output_lock_attach_render(o->native,false);1682
}1683
EVENT(51,o->id,ok,b->width,b->height);1684
}1685
static bool pixel_scope_valid(struct server *s,const int64_t *scope) {1686
if(!scope[0]) return true;1687
struct output *o=find_output(s,scope[0]);1688
if(!o || !o->configured_enabled) return false;1689
struct wlr_box box;1690
wlr_output_layout_get_box(s->layout,o->native,&box);1691
return box.x==scope[1] && box.y==scope[2] && box.width==scope[3] && box.height==scope[4];1692
}1693
static void destroy_pixels(struct pixels *p) {1694
wlr_scene_node_destroy(&p->scene->node);1695
wl_list_remove(&p->link);1696
free(p);1697
atomic_fetch_sub(&bridge.pixel_nodes,1);1698
}1699
static void invalidate_scoped_pixels(struct server *s,uint64_t removed) {1700
struct pixels *p,*next;1701
wl_list_for_each_safe(p,next,&s->pixels,link)1702
if(removed && p->scope[0]==(int64_t)removed)1703
destroy_pixels(p);1704
else if(p->scope[0] && !pixel_scope_valid(s,p->scope))1705
wlr_scene_node_set_enabled(&p->scene->node,false);1706
route_motion(s,0,false);1707
}1708
static void output_destroy(struct wl_listener *listener,void *data) {1709
(void)data;1710
struct output *o=wl_container_of(listener,o,destroy);1711
if(!o->server->stopping) invalidate_scoped_pixels(o->server,o->id);1712
EVENT(3,o->id);1713
o->server->output_revision++;1714
if(o->capture[0]) wlr_output_lock_attach_render(o->native,false);1715
wl_list_remove(&o->frame.link); wl_list_remove(&o->destroy.link);1716
wl_list_remove(&o->request.link); wl_list_remove(&o->readback.link);1717
wl_list_remove(&o->lock_present.link);1718
wl_list_remove(&o->wake_present.link);1719
wl_list_remove(&o->gamma_present.link);1720
if(o->lock_surface) o->lock_surface->output=NULL;1721
if(o->lock_fill && !o->server->stopping) wlr_scene_node_destroy(&o->lock_fill->node);1722
wl_list_remove(&o->link);1723
inventory_schedule(o->server);1724
publish_output_management(o->server);1725
o->native->data=NULL;1726
free(o->gamma_table);1727
free(o);1728
}1729
static void output_request(struct wl_listener *listener,void *data) {1730
struct output *o=wl_container_of(listener,o,request);1731
struct wlr_output_event_request_state *e=data;1732
EVENT(4,o->id,e->state->custom_mode.width,e->state->custom_mode.height);1733
}1734
static void new_output(struct wl_listener *listener,void *data) {1735
struct server *s=wl_container_of(listener,s,new_output);1736
struct wlr_output *native=data;1737
if(s->pending_output_name[0]) wlr_output_set_name(native,s->pending_output_name);1738
if(!wlr_output_init_render(native,s->allocator,s->renderer)) return;1739
struct output *o=calloc(1,sizeof(*o));1740
if(!o) return;1741
o->server=s; o->native=native; o->id=++s->next_id;1742
o->wake_requested=now_ms();1743
o->gamma_pending=true;1744
native->data=o;1745
o->scene=wlr_scene_output_create(s->scene,native);1746
if(!o->scene) { free(o); return; }1747
wl_list_insert(&s->outputs,&o->link);1748
listen(&native->events.frame,&o->frame,output_frame);1749
listen(&native->events.destroy,&o->destroy,output_destroy);1750
listen(&native->events.request_state,&o->request,output_request);1751
listen(&native->events.commit,&o->readback,output_readback);1752
listen(&native->events.present,&o->lock_present,lock_present);1753
listen(&native->events.present,&o->wake_present,output_wake_present);1754
listen(&native->events.present,&o->gamma_present,output_gamma_present);1755
if(s->lock_state) lock_configure(o);1756
struct wlr_output_mode *mode;1757
int index=0,preferred=-1;1758
wl_list_for_each(mode,&native->modes,link) {1759
if(mode->preferred) preferred=index;1760
EVENT(5,o->id,index++,mode->width,mode->height,mode->refresh,mode->preferred);1761
}1762
EVENT(2,o->id,native->width,native->height,preferred,wlr_output_is_wl(native));1763
object_text(o->id,8,native->name);1764
EVENT(130,o->id,wlr_output_get_gamma_size(native),false);1765
s->output_revision++;1766
inventory_schedule(s);1767
publish_output_management(s);1768
}1769
#include "output-batch.h"1770
#include "output-pending.h"1771
#include "output-management.h"1772
static void pixel_destroy(struct wlr_buffer *base) {1773
struct pixel_buffer *b=wl_container_of(base,b,base);1774
atomic_fetch_sub(&bridge.bytes,b->length+sizeof(*b));1775
free(b);1776
}1777
static bool pixel_access(struct wlr_buffer *base,uint32_t flags,void **data,uint32_t *format,size_t *stride) {1778
struct pixel_buffer *b=wl_container_of(base,b,base);1779
if(flags&WLR_BUFFER_DATA_PTR_ACCESS_WRITE) return false;1780
*data=b->data; *format=DRM_FORMAT_ARGB8888; *stride=b->stride; return true;1781
}1782
static void pixel_end(struct wlr_buffer *base) { (void)base; }1783
static const struct wlr_buffer_impl pixel_impl={.destroy=pixel_destroy,.begin_data_ptr_access=pixel_access,.end_data_ptr_access=pixel_end};1784
static bool pixels_accept_input(struct wlr_scene_buffer *buffer,double *sx,double *sy) {1785
(void)sx; (void)sy;1786
struct pixels *p=buffer->node.data;1787
return p && p->input && pixel_scope_valid(p->server,p->scope);1788
}1789
static bool scene_order_valid(const int64_t *words,size_t count) {1790
if(count<1 || words[0]<0 || words[0]>65535 || count!=(size_t)words[0]+1) return false;1791
for(size_t i=1;i<count;i++) {1792
if(words[i]==INT64_MIN) return false;1793
for(size_t j=1;j<i;j++) if(words[i]==words[j]) return false;1794
}1795
return true;1796
}1797
static struct wlr_scene_node *scene_resource(struct server *s,int64_t resource) {1798
if(!resource) return &s->drag_scene->node;1799
if(resource>0) {1800
struct view *view=find_view(s,resource);1801
return view ? &view->tree->node : NULL;1802
}1803
struct pixels *pixels=find_pixels(s,-resource);1804
return pixels ? &pixels->scene->node : NULL;1805
}1806
#include "scene-move.h"1807
/* Position only this resource. Copied retired neighbours are harmless; no1808
* frame or input dispatch can observe creation before its final insertion. */1809
static void position_pixels(struct server *s,struct pixels *p,const int64_t *order,size_t count) {1810
struct wlr_scene_node *lower=NULL,*upper=NULL;1811
bool passed=false;1812
for(size_t i=1;i<count;i++) {1813
if(order[i]==-(int64_t)p->id) { passed=true; continue; }1814
struct wlr_scene_node *node=scene_resource(s,order[i]);1815
if(!node || node->parent!=p->scene->node.parent) continue;1816
if(passed) { upper=node; break; }1817
lower=node;1818
}1819
if(upper) wlr_scene_node_place_below(&p->scene->node,upper);1820
else if(lower) wlr_scene_node_place_above(&p->scene->node,lower);1821
else wlr_scene_node_lower_to_bottom(&p->scene->node);1822
}1823
static bool apply_pixels(struct server *s,uint64_t id,int64_t *w,struct pixel_buffer *b) {1824
struct pixels *p=NULL,*it;1825
wl_list_for_each(it,&s->pixels,link) if(it->id==id) { p=it; break; }1826
if(!b) {1827
if(p) destroy_pixels(p);1828
route_motion(s,0,false);1829
return true;1830
}1831
if(!pixel_scope_valid(s,b->scope)) {1832
wlr_buffer_drop(&b->base);1833
atomic_fetch_add(&bridge.pixel_drops,1);1834
return true;1835
}1836
if(!p) {1837
p=calloc(1,sizeof(*p));1838
if(!p) { wlr_buffer_drop(&b->base); return false; }1839
p->id=id; p->scene=wlr_scene_buffer_create(s->normal,&b->base);1840
if(!p->scene) { free(p); wlr_buffer_drop(&b->base); return false; }1841
p->scene->node.data=p;1842
p->scene->point_accepts_input=pixels_accept_input;1843
wl_list_insert(&s->pixels,&p->link);1844
atomic_fetch_add(&bridge.pixel_nodes,1);1845
} else wlr_scene_buffer_set_buffer(p->scene,&b->base);1846
p->input=b->input;1847
p->server=s;1848
memcpy(p->scope,b->scope,sizeof(p->scope));1849
wlr_scene_node_set_enabled(&p->scene->node,true);1850
wlr_scene_buffer_set_dest_size(p->scene,w[2],w[3]);1851
scene_move_position(s,-(int64_t)id,&p->scene->node,w[0],w[1]);1852
if(b->order_count) {1853
size_t offset=(b->stride*(size_t)b->base.height+7)&~(size_t)7;1854
position_pixels(s,p,(const int64_t *)(b->data+offset),b->order_count);1855
} else {1856
if(w[7]>0) wlr_scene_node_raise_to_top(&p->scene->node);1857
if(w[7]<0) wlr_scene_node_lower_to_bottom(&p->scene->node);1858
}1859
for(unsigned i=0;i<b->trace_count;i++) {1860
trace_damage(s,b->trace_causes[i],p->scope[0]);1861
latency_trace(12,b->trace_causes[i],id,0);1862
}1863
wlr_buffer_drop(&b->base);1864
route_motion(s,0,false);1865
return true;1866
}1868
#include "headless.h"1870
static bool configure_device(struct server *s,const int64_t *w,size_t n) {1871
if(n<5 || w[1]<0 || w[1]>200 || w[2]<0 || w[2]>5000 || w[3]<1 || w[3]>128 || n!=4+(size_t)w[3]) return false;1872
struct keyboard *k=find_keyboard(s,w[0]);1873
if(!k) return false;1874
char layout[129]={0};1875
for(int i=0;i<w[3];i++) {1876
if(w[4+i]<32 || w[4+i]>126) return false;1877
layout[i]=w[4+i];1878
}1879
struct xkb_context *context=xkb_context_new(XKB_CONTEXT_NO_FLAGS);1880
if(!context) return false;1881
struct xkb_rule_names names={.layout=layout};1882
struct xkb_keymap *map=xkb_keymap_new_from_names(context,&names,XKB_KEYMAP_COMPILE_NO_FLAGS);1883
xkb_context_unref(context);1884
if(!map) return false;1885
bool ok=wlr_keyboard_set_keymap(k->native,map);1886
if(ok && k==&s->synthetic_owner) {1887
if(s->emulation_state) xkb_state_unref(s->emulation_state);1888
s->emulation_state=xkb_state_new(map);1889
if(!s->emulation_state) ok=false;1890
}1891
xkb_keymap_unref(map);1892
if(ok) wlr_keyboard_set_repeat_info(k->native,w[1],w[2]);1893
return ok;1894
}1895
/* Ordering is explicit. Retired resources are harmless omissions, since a1896
* copied order can cross an independently delivered destruction fact. */1897
static bool restack_scene(struct server *s,const int64_t *words,size_t count) {1898
if(!scene_order_valid(words,count)) return false;1899
for(size_t i=1;i<count;i++) {1900
struct wlr_scene_node *node=NULL;1901
if(words[i]==0) node=&s->drag_scene->node;1902
else if(words[i]>0) {1903
struct view *view=find_view(s,words[i]);1904
if(view) node=&view->tree->node;1905
} else {1906
struct pixels *pixels=find_pixels(s,-words[i]);1907
if(pixels) node=&pixels->scene->node;1908
}1909
if(node) wlr_scene_node_raise_to_top(node);1910
}1911
route_motion(s,now_ms(),false);1912
return true;1913
}1914
static bool apply_command(struct server *s,struct wm_message m) {1915
int64_t *w=m.word;1916
switch(w[0]) {1917
case 1: s->stopping=true; wl_display_terminate(s->display); return true;1918
case 2: {1919
struct view *v=find_view(s,w[1]);1920
if(!v || !view_ready(v) || w[2]<0 || w[2]>16384 || w[3]<0 || w[3]>16384) return false;1921
v->width=w[2]; v->height=w[3];1922
if(v->x11) {1923
if(!w[2] || !w[3]) return false;1924
wlr_xwayland_surface_configure(v->x11,v->x11->x,v->x11->y,w[2],w[3]);1925
} else if(v->layer) {1926
if(!w[2] || !w[3]) return false;1927
v->serial=wlr_layer_surface_v1_configure(v->layer,w[2],w[3]);1928
} else wlr_xdg_toplevel_set_size(v->native,w[2],w[3]);1929
return true;1930
}1931
case 3:1932
s->route_hit_test=w[1]!=0;1933
if(s->route_hit_test) route_motion(s,now_ms(),false);1934
return true;1935
case 4:1936
if(w[2]<1 || w[3]<0 || w[4]<0 || w[4]>1000 || w[7]<0 || w[8]<0) return false;1937
if(s->has_pending_motion && s->pending_motion.completion)1938
EVENT(35,s->pending_motion.completion,0,s->pending_motion.origin);1939
s->has_pending_motion=false;1940
if(s->input_read==s->input_write) {1941
s->motion_x=s->cursor->x; s->motion_y=s->cursor->y;1942
}1943
s->motion_hook=w[1]!=0; s->motion_generation=w[2]; s->motion_device=w[3];1944
s->motion_period=w[4]; s->motion_region=(struct wlr_box){w[5],w[6],w[7],w[8]};1945
EVENT(36,w[2],w[1]); return true;1946
case 5: {1947
struct view *v=find_view(s,w[1]);1948
if(w[1]<0 || w[2]<0 || w[3]<0 || w[4]<0 || w[5]<0 || (w[2] && w[3])) return false;1949
uint64_t generation=s->generation, authorization=w[2] ? w[2] : w[3];1950
uint64_t operation=w[5] ? (uint64_t)w[5] : authorization;1951
int64_t target=w[4] ? w[4] : w[1];1952
bool valid=(!w[1] || visible(v));1953
if(w[2]) {1954
valid=valid && !s->lock_state && s->input_read!=s->input_write;1955
if(valid) {1956
struct input *in=&s->inputs[s->input_read%QUEUE_SIZE];1957
valid=in->token==(uint64_t)w[2] && in->notified && !in->ready && in->kind==31 &&1958
in->state && in->generation==generation && in->target==target &&1959
target_at(s,in->x,in->y)==target;1960
}1961
} else if(w[3]) {1962
valid=valid && !s->lock_state && s->observe_pointer && s->pointer_sampled &&1963
s->pointer_sample_token==(uint64_t)w[3] && s->pointer_sample_generation==generation &&1964
s->pointer_target==target && target_at(s,s->cursor->x,s->cursor->y)==target;1965
}1966
if(!valid) {1967
if(operation) { EVENT(113,operation,w[1],0,authorization); return true; }1968
return false;1969
}1970
set_focus(s,w[1]);1971
bool accepted=s->focus==(uint64_t)w[1];1972
if(authorization && accepted) {1973
for(unsigned index=s->input_read;index!=s->input_write;index++) {1974
struct input *in=&s->inputs[index%QUEUE_SIZE];1975
if(in->generation==generation) in->generation=s->generation;1976
}1977
if(s->has_pending_motion && s->pending_motion.generation==generation)1978
s->pending_motion.generation=s->generation;1979
}1980
if(operation) { EVENT(113,operation,w[1],accepted,authorization); return true; }1981
return accepted;1982
}1983
case 6: {1984
if(s->lock_state || w[1]!=1 || w[2]<0 || w[2]>=MAX_KEYS || (w[3]!=0 && w[3]!=1) || !s->synthetic.keymap) return false;1985
if(!s->emulation_state) return false;1986
uint32_t symbol=xkb_state_key_get_one_sym(s->emulation_state,w[2]+8),modifiers=0;1987
const char *names[8]={XKB_MOD_NAME_SHIFT,XKB_MOD_NAME_CAPS,XKB_MOD_NAME_CTRL,1988
XKB_MOD_NAME_ALT,"Mod2","Mod3",XKB_MOD_NAME_LOGO,"Mod5"};1989
for(int bit=0;bit<8;bit++)1990
if(xkb_state_mod_name_is_active(s->emulation_state,names[bit],XKB_STATE_MODS_EFFECTIVE)>0) modifiers|=1u<<bit;1991
xkb_state_update_key(s->emulation_state,w[2]+8,w[3] ? XKB_KEY_DOWN : XKB_KEY_UP);1992
queue_input(s,(struct input){.kind=30,.device=s->synthetic_owner.id,.time=now_ms(),.code=w[2],.state=w[3],.origin=1,.completion=w[4],1993
.keysym=symbol,.modifiers=modifiers,1994
.base_keysym=base_keysym(&s->synthetic,w[2],false),1995
.first_keysym=base_keysym(&s->synthetic,w[2],true)});1996
uint32_t current=0;1997
for(int bit=0;bit<8;bit++)1998
if(xkb_state_mod_name_is_active(s->emulation_state,names[bit],XKB_STATE_MODS_EFFECTIVE)>0) current|=1u<<bit;1999
s->keyboard_modifiers=current;2000
EVENT(43,s->synthetic_owner.id,current,xkb_state_serialize_layout(s->emulation_state,XKB_STATE_LAYOUT_EFFECTIVE));2001
return true;2002
}2003
case 7:2004
if(s->lock_state || w[1]!=1 || w[2]<0 || w[2]>=MAX_KEYS || (w[3]!=0 && w[3]!=1)) return false;2005
queue_input(s,(struct input){.kind=31,.device=s->synthetic_owner.id,.time=now_ms(),.code=w[2],.state=w[3],.origin=1,.completion=w[4],.x=pointer_x(s),.y=pointer_y(s),.modifiers=s->keyboard_modifiers});2006
return true;2007
case 8:2008
if(s->lock_state || w[1]!=1) return false;2009
motion_input(s,now_ms(),w[2]/1000.0,w[3]/1000.0,1,1,w[4]); return true;2010
case 9:2011
if(s->lock_state || w[1]!=1 || w[2]<0 || w[2]>1 || w[4]<-32767 || w[4]>32767) return false;2012
axis_input(s,1,1,now_ms(),w[2],w[3]/1000.0,w[4]*WLR_POINTER_AXIS_DISCRETE_STEP,2013
WL_POINTER_AXIS_SOURCE_WHEEL,WL_POINTER_AXIS_RELATIVE_DIRECTION_IDENTICAL,w[5]);2014
return true;2015
case 11:2016
if(w[2]<0 || w[2]>2 || w[3]<0 || w[3]>=MAX_KEYS || w[6]<0 || w[6]>255) return false;2017
for(unsigned i=s->input_read;i!=s->input_write;i++) {2018
struct input *in=&s->inputs[i%QUEUE_SIZE];2019
if(in->token!=(uint64_t)w[1]) continue;2020
if(in->completed) return true;2021
if(in->ready || !in->notified) return false;2022
in->ready=true; in->result=w[2]; in->replacement=w[3];2023
in->replacement_modifiers=w[6];2024
if(in->kind==32 && w[2]==2) { in->x=w[4]/1000.0; in->y=w[5]/1000.0; }2025
deliver_input(s); return true;2026
}2027
/* The native decision deadline may already have retired this token. */2028
return true;2029
case 13: {2030
struct view *v=find_view(s,w[1]);2031
if(!v) return false;2032
close_view(v); return true;2033
}2034
case 14:2035
if(w[1]<0 || w[1]>7) return false;2036
wlr_seat_set_capabilities(s->seat,w[1]); return true;2037
case 40: {2038
if(w[1]<1 || w[2]<1) return false;2039
struct view *view=find_view(s,w[1]);2040
if(!view || !view_ready(view)) {2041
EVENT(120,w[2],w[1],0);2042
return true;2043
}2044
struct wlr_xdg_toplevel_state *state=view->native ? &view->native->current : NULL;2045
uint32_t version=view->native ? wl_resource_get_version(view->native->resource) : 0;2046
EVENT(120,w[2],w[1],1,view->tree->node.x,view->tree->node.y,2047
view->committed.width,view->committed.height,view->mapped,visible(view),s->focus==view->id,2048
state ? state->activated : 0,state ? state->suspended : 0,2049
state ? state->fullscreen : 0,state ? state->tiled : 0,state!=NULL);2050
struct wlr_scene_node *surface_tree=view_surface_tree(view);2051
EVENT(121,w[2],w[1],view->committed.x,view->committed.y,2052
surface_tree ? surface_tree->x : 0,surface_tree ? surface_tree->y : 0,2053
version,view_serial(view),2054
view->native ? view->native->scheduled.suspended : 0,2055
view->native ? view->native->base->pending.configure_serial : 0,2056
view->native ? view->native->base->scheduled_serial : 0);2057
return true;2058
}2059
case 39: {2060
struct view *view=find_view(s,w[1]);2061
if(!view || !view->native) return false;2062
if(view->native->base->initialized) wlr_xdg_surface_schedule_configure(view->native->base);2063
return true;2064
}2065
case 35:2066
if(w[1]<0 || w[2]<0) return false;2067
return set_active_constraint(s,w[1],w[2]);2068
case 36: return pointer_hint_reply(s,w[1],w[2]!=0);2069
case 37: {2070
struct wlr_output *native=wlr_output_layout_output_at(s->layout,s->cursor->x,s->cursor->y);2071
struct output *output=native ? native->data : NULL;2072
EVENT(112,w[1],(int64_t)(s->cursor->x*1000),(int64_t)(s->cursor->y*1000),2073
target_at(s,s->cursor->x,s->cursor->y),output ? output->id : 0,s->generation);2074
return true;2075
}2076
case 38:2077
if(w[1]!=0 && w[1]!=1) return false;2078
s->relative_route=w[1]!=0; return true;2079
case 34: {2080
struct output *output=find_output(s,w[1]);2081
if(!output || (w[2]!=0 && w[2]!=1)) return false;2082
output->nearest_filter=w[2]!=0;2083
wlr_damage_ring_add_whole(&output->scene->damage_ring);2084
wlr_output_schedule_frame(output->native);2085
EVENT(46,output->id,w[2]);2086
return true;2087
}2088
case 32: return output_control_reply(s,w[1],w[2]!=0);2089
case 33:2090
if(w[2]!=0 && w[2]!=1) return false;2091
return output_set_power(s,w[1],w[2]!=0);2092
case 31:2093
if(w[2]<0 || w[2]>UINT32_MAX || (w[2]&255)!=255) return false;2094
if(!s->lock_state || w[1]!=(int64_t)s->lock_generation) return true;2095
s->lock_colour=w[2];2096
{2097
float colour[4]={(s->lock_colour>>24)/255.0f,2098
((s->lock_colour>>16)&255)/255.0f,((s->lock_colour>>8)&255)/255.0f,1};2099
struct output *output;2100
wl_list_for_each(output,&s->outputs,link) {2101
if(output->lock_fill) wlr_scene_rect_set_color(output->lock_fill,colour);2102
lock_damage(output);2103
}2104
}2105
return true;2106
case 15: return lock_focus(s,w[1],w[2]);2107
case 16: return permission_reply(s,w[1],w[2]!=0);2108
case 18: return set_decoration_mode(s,w[1],w[2]);2109
case 19:2110
if(!s->idle || w[2]<0 || w[2]>15 || w[3]<0 || w[3]>7) return false;2111
s->activity_classes=w[2]; s->activity_origins=w[3];2112
wlr_idle_notifier_v1_set_inhibited(s->idle,w[1]!=0);2113
EVENT(45,w[1]!=0,w[2],w[3]); return true;2114
case 20: return configure_popup(s,w);2115
case 21: {2116
struct pixels *p=find_pixels(s,w[1]);2117
if(!p || w[2]<-32768 || w[2]>32768 || w[3]<-32768 || w[3]>32768 ||2118
w[4]<1 || w[4]>16384 || w[5]<1 || w[5]>16384 || w[6]<-1 || w[6]>1) return false;2119
wlr_scene_node_set_position(&p->scene->node,w[2],w[3]);2120
wlr_scene_buffer_set_dest_size(p->scene,w[4],w[5]);2121
if(w[6]>0) wlr_scene_node_raise_to_top(&p->scene->node);2122
if(w[6]<0) wlr_scene_node_lower_to_bottom(&p->scene->node);2123
return true;2124
}2125
case 29: return data_reply(s,w[1],w[2]!=0,w[3]!=0);2126
case 30: s->allow_client_cursor=w[1]!=0; return true;2127
case 28: {2128
struct output *output=find_output(s,w[1]);2129
if(!output || !wlr_output_is_headless(output->native)) return false;2130
wlr_output_destroy(output->native); return true;2131
}2132
case 27:2133
if((w[1]!=0 && w[1]!=1) || (w[2]!=0 && w[2]!=1)) return false;2134
s->observe_pointer=w[1]!=0; s->pointer_sampled=false; s->pointer_motion_only=w[2]!=0;2135
if(!s->pointer_motion_only) route_motion(s,now_ms(),false);2136
return true;2137
case 26: {2138
struct view *v=find_view(s,w[1]);2139
if(!v || !v->x11 || w[2]<-32768 || w[2]>32767 || w[3]<-32768 || w[3]>32767 ||2140
w[4]<1 || w[4]>16384 || w[5]<1 || w[5]>16384) return false;2141
wlr_xwayland_surface_configure(v->x11,w[2],w[3],w[4],w[5]); return true;2142
}2143
case 24: return configure_layer(s,w);2144
case 25: {2145
if(w[1]<0 || w[2]<0) return false;2146
bool accepted=!w[1] || find_output(s,w[1]);2147
if(accepted) s->default_layer_output=w[1];2148
if(w[2]) { EVENT(122,w[2],w[1],accepted); return true; }2149
return accepted;2150
}2151
case 22:2152
if(w[3]!=0 && w[3]!=1) return false;2153
return set_keyboard_group(s,w[1],w[2],w[3]!=0);2154
case 23:2155
if(s->lock_state || w[1]<-32768000 || w[1]>32768000 || w[2]<-32768000 || w[2]>32768000) return false;2156
constrained_warp(s,w[1]/1000.0,w[2]/1000.0);2157
s->motion_x=s->cursor->x; s->motion_y=s->cursor->y;2158
route_motion(s,now_ms(),false);2159
return true;2160
case 90: {2161
int64_t *data=(void *)(intptr_t)w[3];2162
bool ok=false;2163
switch(w[1]) {2164
case 1: return submit_plan(s,w[2],data,w[4],w[5]);2165
case 2:2166
ok=queue_output_batch(s,2,w[2],data,w[4]);2167
free(data);2168
if(!ok) EVENT(24,w[2],2,0);2169
return true;2170
case 3: ok=configure_device(s,data,w[4]); break;2171
case 4: ok=configure_cursor(s,data,w[4]); break;2172
case 6: ok=configure_keyboard(s,data,w[4]); break;2173
case 7: ok=add_headless_output(s,data,w[4]); break;2174
case 15: ok=record_output_samples(s,w[2],data,w[4]); break;2175
case 16:2176
ok=output_set_gamma(s,w[2],data,w[4]);2177
if(!ok && w[4]>=1) EVENT(131,data[0],w[2],false);2178
break;2179
case 17: ok=scene_move_start(s,w[2],data,w[4]); break;2180
case 8: ok=restack_scene(s,data,w[4]); break;2181
case 9: ok=configure_pointer(s,w[2],data,w[4]); break;2182
case 10: output_batch(s,w[2],data,w[4]); free(data); return true;2183
case 12: ok=configure_session_switches(s,data,w[4]); break;2184
case 13: ok=configure_input_intercepts(s,true,data,w[4]); break;2185
case 14: ok=configure_input_intercepts(s,false,data,w[4]); break;2186
case 11: output_configuration_reply(s,w[2],data,w[4]); free(data); return true;2187
case 5: {2188
struct output *o=find_output(s,w[2]);2189
if(o && !o->capture[0] && w[4]>0 && w[4]<PATH_MAX) {2190
ok=true;2191
for(int i=0;i<w[4];i++) {2192
if(data[i]<1 || data[i]>127) { ok=false; break; }2193
o->capture[i]=data[i];2194
}2195
o->capture[w[4]]=0;2196
if(ok) wlr_output_lock_attach_render(o->native,true);2197
else o->capture[0]=0;2198
wlr_damage_ring_add_whole(&o->scene->damage_ring);2199
wlr_output_schedule_frame(o->native);2200
}2201
break;2202
}2203
}2204
free(data); EVENT(24,w[2],w[1],ok); return ok || w[1]==6;2205
}2206
case 41:2207
if (w[1] < 1 || w[1] > 200) return false;2208
s->scene_update_deadline = now_ms() + w[1];2209
return true;2210
case 43:2211
if(s->scene_move.token==(uint64_t)w[1]) scene_move_finish(s);2212
return true;2213
case 42: {2214
struct output *o = find_output(s, w[1]);2215
if (!o) return false;2216
if (o->sample_frames) {2217
o->sample_frames = 0;2218
o->capture[0] = 0;2219
wlr_output_lock_attach_render(o->native, false);2220
}2221
return true;2222
}2223
case 91: {2224
struct pixel_buffer *b=(void *)(intptr_t)w[12];2225
return apply_pixels(s,w[1],w+2,b);2226
}2227
default: return false;2228
}2229
}2230
static int commands_ready(int fd,uint32_t mask,void *data) {2231
(void)mask;2232
struct server *s=data;2233
drain(fd);2234
struct wm_message command;2235
unsigned count=0;2236
while(count++<64 && pop(&bridge.commands,&command)) {2237
s->trace_correlation=command.word[15];2238
if(!apply_command(s,command)) {2239
atomic_fetch_add(&bridge.rejected,1);2240
EVENT(98,command.word[0],command.word[1]);2241
}2242
s->trace_correlation=0;2243
}2244
if(atomic_load(&bridge.commands.read)!=atomic_load(&bridge.commands.write)) wake(fd);2245
return 0;2246
}2247
static int timer_tick(void *data) {2248
struct server *s=data;2249
s->timer_deadline=0;2250
atomic_fetch_add(&bridge.timer_wakes,1);2251
check_plan(s);2252
if (s->scene_update_deadline && now_ms() >= s->scene_update_deadline) {2253
s->scene_update_deadline = 0;2254
struct output *output;2255
wl_list_for_each(output, &s->outputs, link) {2256
wlr_damage_ring_add_whole(&output->scene->damage_ring);2257
wlr_output_schedule_frame(output->native);2258
}2259
}2260
if(s->lock_state) {2261
lock_ack(s);2262
if(s->lock_deadline && now_ms()>=s->lock_deadline) s->lock_deadline=0;2263
}2264
permissions_expire(s);2265
data_expire(s);2266
output_controls_expire(s);2267
output_gamma_expire(s);2268
struct output *output;2269
wl_list_for_each(output,&s->outputs,link) {2270
if(!output->scene_retry_at || now_ms()<output->scene_retry_at) continue;2271
output->scene_retry_at=0;2272
if(output->native->enabled) wlr_output_schedule_frame(output->native);2273
}2274
output_requests_expire(s);2275
pointer_hints_expire(s);2276
struct popup *p,*next;2277
wl_list_for_each_safe(p,next,&s->popups,link)2278
if(p->deadline && now_ms()>=p->deadline) wlr_xdg_popup_destroy(p->native);2279
if(s->has_pending_motion && now_ms()-(uint64_t)s->motion_last >= (uint64_t)s->motion_period) {2280
s->has_pending_motion=false; s->motion_last=now_ms();2281
queue_input(s,s->pending_motion);2282
}2283
deliver_input(s);2284
return 0;2285
}2286
#include "deadlines.h"2287
static const struct wlr_keyboard_impl synthetic_impl={0};2288
static int run_host(void) {2289
struct server s={0};2290
wl_list_init(&s.outputs); wl_list_init(&s.views);2291
wl_list_init(&s.keyboards); wl_list_init(&s.devices); wl_list_init(&s.pixels);2292
wl_list_init(&s.permissions);2293
wl_list_init(&s.data_requests);2294
wl_list_init(&s.output_controls);2295
wl_list_init(&s.pending_outputs);2296
wl_list_init(&s.output_requests);2297
wl_list_init(&s.constraints); wl_list_init(&s.pointer_hints);2298
wl_list_init(&s.decorations);2299
wl_list_init(&s.legacy_decorations);2300
wl_list_init(&s.inhibitors);2301
wl_list_init(&s.popups);2302
s.next_id=1; s.generation=1; s.lock_colour=255; s.relative_route=true;2303
s.display=wl_display_create();2304
if(!s.display) return 1;2305
int result=1;2306
s.security_context=wlr_security_context_manager_v1_create(s.display);2307
if(!s.security_context) goto cleanup;2308
wl_display_set_global_filter(s.display,client_global_filter,&s);2309
struct wl_event_loop *loop=wl_display_get_event_loop(s.display);2310
s.backend=wlr_backend_autocreate(loop,&s.session);2311
if(!s.backend) goto cleanup;2312
s.renderer=wlr_renderer_autocreate(s.backend);2313
if(!s.renderer || !wlr_renderer_init_wl_display(s.renderer,s.display)) goto cleanup;2314
s.allocator=wlr_allocator_autocreate(s.backend,s.renderer);2315
if(!s.allocator) goto cleanup;2316
s.compositor=wlr_compositor_create(s.display,6,s.renderer);2317
if(!s.compositor || !wlr_subcompositor_create(s.display) ||2318
!wlr_viewporter_create(s.display) || !wlr_fractional_scale_manager_v1_create(s.display,1)) goto cleanup;2319
wlr_data_device_manager_create(s.display);2320
if(!wlr_presentation_create(s.display,s.backend,1)) goto cleanup;2321
s.layout=wlr_output_layout_create(s.display);2322
s.scene=wlr_scene_create();2323
if(!s.layout || !s.scene) goto cleanup;2324
s.scene_layout=wlr_scene_attach_output_layout(s.scene,s.layout);2325
s.normal=wlr_scene_tree_create(&s.scene->tree);2326
s.drag_scene=wlr_scene_tree_create(s.normal);2327
s.lock_scene=wlr_scene_tree_create(&s.scene->tree);2328
if(!s.scene_layout || !s.normal || !s.drag_scene || !s.lock_scene) goto cleanup;2329
wlr_scene_node_set_enabled(&s.lock_scene->node,false);2330
s.seat=wlr_seat_create(s.display,"seat0");2331
s.cursor=wlr_cursor_create();2332
if(!s.seat || !s.cursor) goto cleanup;2333
wlr_cursor_attach_output_layout(s.cursor,s.layout);2334
s.relative=wlr_relative_pointer_manager_v1_create(s.display);2335
wlr_keyboard_init(&s.synthetic,&synthetic_impl,"sigil-wm-emulation");2336
s.synthetic_owner=(struct keyboard){.server=&s,.id=1,.native=&s.synthetic};2337
for(int i=0;i<MAX_KEYS;i++) s.synthetic_owner.forwarded[i]=-1;2338
wlr_seat_set_keyboard(s.seat,&s.synthetic);2339
s.shell=wlr_xdg_shell_create(s.display,6);2340
if(!s.shell || !s.relative) goto cleanup;2341
if(bridge.protocols&1024) {2342
s.foreign_list=wlr_ext_foreign_toplevel_list_v1_create(s.display,1);2343
if(!s.foreign_list) goto cleanup;2344
}2345
if(bridge.protocols&2048) {2346
s.foreign_manager=wlr_foreign_toplevel_manager_v1_create(s.display);2347
if(!s.foreign_manager) goto cleanup;2348
}2349
if(bridge.protocols&1) {2350
struct wlr_session_lock_manager_v1 *manager=wlr_session_lock_manager_v1_create(s.display);2351
if(!manager) goto cleanup;2352
listen(&manager->events.new_lock,&s.new_lock,lock_new);2353
}2354
if(bridge.protocols&2) {2355
struct wlr_virtual_keyboard_manager_v1 *keys=wlr_virtual_keyboard_manager_v1_create(s.display);2356
struct wlr_virtual_pointer_manager_v1 *pointers=wlr_virtual_pointer_manager_v1_create(s.display);2357
if(!keys || !pointers) goto cleanup;2358
listen(&keys->events.new_virtual_keyboard,&s.new_virtual_keyboard,virtual_keyboard);2359
listen(&pointers->events.new_virtual_pointer,&s.new_virtual_pointer,virtual_pointer);2360
}2361
if(bridge.protocols&8) {2362
struct wlr_xdg_decoration_manager_v1 *decor=wlr_xdg_decoration_manager_v1_create(s.display);2363
struct wlr_server_decoration_manager *legacy=wlr_server_decoration_manager_create(s.display);2364
if(!decor || !legacy) goto cleanup;2365
wlr_server_decoration_manager_set_default_mode(legacy,bridge.legacy_decoration_default);2366
listen(&legacy->events.new_decoration,&s.new_legacy_decoration,new_legacy_decoration);2367
listen(&decor->events.new_toplevel_decoration,&s.new_decoration,new_decoration);2368
}2369
if(bridge.protocols&16) {2370
s.idle=wlr_idle_notifier_v1_create(s.display);2371
struct wlr_idle_inhibit_manager_v1 *inhibit=wlr_idle_inhibit_v1_create(s.display);2372
if(!s.idle || !inhibit) goto cleanup;2373
listen(&inhibit->events.new_inhibitor,&s.new_inhibitor,new_inhibitor);2374
}2375
if(bridge.protocols&4) {2376
s.xwayland=wlr_xwayland_create(s.display,s.compositor,true);2377
if(!s.xwayland) goto cleanup;2378
wlr_xwayland_set_seat(s.xwayland,s.seat);2379
listen(&s.xwayland->events.new_surface,&s.new_x11,new_x11);2380
listen(&s.xwayland->events.destroy,&s.x11_destroy,x11_manager_destroy);2381
listen(&s.xwayland->events.ready,&s.x11_ready,x11_manager_ready);2382
object_text(0,9,s.xwayland->display_name);2383
}2384
if((bridge.protocols&64) && !desktop_init(&s)) goto cleanup;2385
if((bridge.protocols&128) && (!output_controls_init(&s) || !output_management_init(&s))) goto cleanup;2386
if(bridge.protocols&256) {2387
s.activation=wlr_xdg_activation_v1_create(s.display);2388
if(!s.activation) goto cleanup;2389
listen(&s.activation->events.request_activate,&s.activation_request,activation_requested);2390
}2391
if((bridge.protocols&512) && !constraints_init(&s)) goto cleanup;2392
if(bridge.protocols&32) {2393
struct wlr_layer_shell_v1 *layers=wlr_layer_shell_v1_create(s.display,4);2394
if(!layers) goto cleanup;2395
listen(&layers->events.new_surface,&s.new_layer,new_layer);2396
}2397
listen(&s.backend->events.new_output,&s.new_output,new_output);2398
listen(&s.backend->events.new_input,&s.new_input,new_input);2399
listen(&s.shell->events.new_toplevel,&s.new_view,new_view);2400
listen(&s.shell->events.new_popup,&s.new_popup,new_popup);2401
listen(&s.cursor->events.motion,&s.motion,cursor_motion);2402
listen(&s.cursor->events.motion_absolute,&s.absolute,cursor_absolute);2403
listen(&s.cursor->events.button,&s.button,cursor_button);2404
listen(&s.cursor->events.axis,&s.axis,cursor_axis);2405
listen(&s.cursor->events.frame,&s.cursor_frame,cursor_frame);2406
listen(&s.seat->events.request_set_cursor,&s.cursor_request,cursor_request);2407
s.commands=wl_event_loop_add_fd(loop,bridge.command_fd,WL_EVENT_READABLE,commands_ready,&s);2408
s.timer=wl_event_loop_add_timer(loop,timer_tick,&s);2409
if(!s.commands || !s.timer) goto cleanup;2410
const char *socket=bridge.requested_socket[0] ? bridge.requested_socket : wl_display_add_socket_auto(s.display);2411
if(bridge.requested_socket[0] && wl_display_add_socket(s.display,socket)!=0) goto cleanup;2412
if(!socket) goto cleanup;2413
snprintf(bridge.socket,sizeof(bridge.socket),"%s",socket);2414
atomic_store(&bridge.state,2);2415
if(!wlr_backend_start(s.backend)) goto cleanup;2416
inventory_schedule(&s);2417
EVENT(1,1,s.session!=NULL);2418
while(!s.stopping)2419
if(dispatch_native_once(&s,-1)<0 && errno!=EINTR) goto cleanup;2420
result=0;2421
cleanup:2422
atomic_store(&bridge.state,4);2423
s.stopping=true;2424
if(s.plan) free(s.plan);2425
s.plan=NULL;2426
if(s.commands) wl_event_source_remove(s.commands);2427
if(s.timer) wl_event_source_remove(s.timer);2428
if(s.inventory_idle) wl_event_source_remove(s.inventory_idle);2429
if(s.motion.notify) {2430
wl_list_remove(&s.motion.link); wl_list_remove(&s.absolute.link);2431
wl_list_remove(&s.button.link); wl_list_remove(&s.axis.link);2432
wl_list_remove(&s.cursor_frame.link); wl_list_remove(&s.cursor_request.link);2433
wl_list_remove(&s.new_input.link); wl_list_remove(&s.new_output.link);2434
wl_list_remove(&s.new_view.link); wl_list_remove(&s.new_popup.link);2435
}2436
if(s.activation_request.notify) wl_list_remove(&s.activation_request.link);2437
constraints_finish(&s);2438
output_pending_finish(&s);2439
output_management_finish(&s);2440
output_gamma_finish(&s);2441
output_controls_finish(&s);2442
desktop_finish(&s);2443
if(s.new_layer.notify) wl_list_remove(&s.new_layer.link);2444
if(s.new_lock.notify) wl_list_remove(&s.new_lock.link);2445
if(s.new_virtual_keyboard.notify) {2446
wl_list_remove(&s.new_virtual_keyboard.link); wl_list_remove(&s.new_virtual_pointer.link);2447
}2448
if(s.new_decoration.notify) wl_list_remove(&s.new_decoration.link);2449
if(s.new_legacy_decoration.notify) wl_list_remove(&s.new_legacy_decoration.link);2450
if(s.new_inhibitor.notify) wl_list_remove(&s.new_inhibitor.link);2451
if(s.xwayland) wlr_xwayland_destroy(s.xwayland);2452
wl_display_destroy_clients(s.display);2453
struct pixels *p,*next;2454
wl_list_for_each_safe(p,next,&s.pixels,link) {2455
wl_list_remove(&p->link); free(p);2456
atomic_fetch_sub(&bridge.pixel_nodes,1);2457
}2458
if(s.scene) wlr_scene_node_destroy(&s.scene->tree.node);2459
if(s.cursor) wlr_cursor_destroy(s.cursor);2460
if(s.cursor_manager) wlr_xcursor_manager_destroy(s.cursor_manager);2461
if(s.allocator) wlr_allocator_destroy(s.allocator);2462
if(s.renderer) wlr_renderer_destroy(s.renderer);2463
if(s.backend) wlr_backend_destroy(s.backend);2464
if(s.emulation_state) xkb_state_unref(s.emulation_state);2465
if(s.synthetic_owner.id) wlr_keyboard_finish(&s.synthetic);2466
wl_display_destroy(s.display);2467
return result;2468
}2469
static void free_command(struct wm_message m) {2470
if(m.word[0]==90) free((void *)(intptr_t)m.word[3]);2471
if(m.word[0]==91 && m.word[12]) wlr_buffer_drop((void *)(intptr_t)m.word[12]);2472
}2473
static void *host_thread(void *unused) {2474
(void)unused;2475
wlr_log_init(WLR_ERROR,NULL);2476
int result=run_host();2477
struct wm_message m;2478
while(pop(&bridge.commands,&m)) free_command(m);2479
atomic_store(&bridge.state,result ? -1 : 3);2480
EVENT(7,0,result);2481
return NULL;2482
}2483
int wm_start(void) {2484
if(bridge.created || bridge.request_deadline<1) return 0;2485
bridge.created=true;2486
if(getenv("SIGIL_WM_TRACE_LATENCY") && !strcmp(getenv("SIGIL_WM_TRACE_LATENCY"),"1"))2487
wm_trace_enable(1);2488
bridge.event_fd=eventfd(0,EFD_NONBLOCK|EFD_CLOEXEC);2489
bridge.command_fd=eventfd(0,EFD_NONBLOCK|EFD_CLOEXEC);2490
if(bridge.event_fd<0 || bridge.command_fd<0) goto fail;2491
atomic_store(&bridge.state,1);2492
if(!pthread_create(&bridge.thread,NULL,host_thread,NULL)) return 1;2493
fail:2494
if(bridge.event_fd>=0) close(bridge.event_fd);2495
if(bridge.command_fd>=0) close(bridge.command_fd);2496
bridge.joined=true;2497
atomic_store(&bridge.state,-1);2498
return 0;2499
}2500
int wm_options(int64_t protocols,int64_t request_deadline) {2501
if(bridge.created || protocols<0 || (protocols&~4095) || request_deadline<1 || request_deadline>60000) return 0;2502
bridge.protocols=protocols; bridge.request_deadline=request_deadline;2503
return 1;2504
}2505
int wm_decoration_default(int mode) {2506
if(bridge.created || mode<0 || mode>2) return 0;2507
bridge.legacy_decoration_default=mode;2508
return 1;2509
}2510
int wm_socket_name(const char *name) {2511
if(bridge.created || !name || strlen(name)>=sizeof(bridge.requested_socket)) return 0;2512
if(!strcmp(name,".") || !strcmp(name,"..")) return 0;2513
for(const unsigned char *character=(const unsigned char *)name;*character;character++)2514
if(*character<33 || *character>126 || *character=='/') return 0;2515
snprintf(bridge.requested_socket,sizeof(bridge.requested_socket),"%s",name);2516
return 1;2517
}2518
int wm_state(void) { return atomic_load(&bridge.state); }2519
int wm_event_fd(void) { return bridge.created && !bridge.joined ? bridge.event_fd : -1; }2520
int wm_next(struct wm_message *event) {2521
if(!event || !bridge.created || bridge.joined) return 0;2522
drain(bridge.event_fd);2523
if(next_copied_event(event)) {2524
if(event->word[0]==7) bridge.terminal_delivered=true;2525
return 1;2526
}2527
struct trace_record record;2528
if(trace_next(&record)) {2529
*event=(struct wm_message){.word={129,record.correlation,record.stage,record.ns,2530
record.object,record.sequence,record.ns/1000000000ULL,record.ns%1000000000ULL}};2531
return 1;2532
}2533
int state=wm_state();2534
if(!bridge.terminal_delivered && (state==3 || state==-1)) {2535
*event=(struct wm_message){.word={7,0,state==-1}};2536
bridge.terminal_delivered=true;2537
return 1;2538
}2539
return 0;2540
}2541
static int send_owned_result(struct wm_message m,unsigned *depth,int *state) {2542
*state=wm_state();2543
if(*state!=2 || bridge.joined) {2544
*depth=atomic_load(&bridge.commands.write)-atomic_load(&bridge.commands.read);2545
return *state==1 && !bridge.joined ? WM_SEND_STARTING : WM_SEND_STOPPED;2546
}2547
if(!push(&bridge.commands,m)) { *depth=QUEUE_SIZE; return WM_SEND_FULL; }2548
wake(bridge.command_fd); return WM_SEND_ACCEPTED;2549
}2550
static int send_owned(struct wm_message m) {2551
unsigned depth;2552
int state;2553
return send_owned_result(m,&depth,&state)==WM_SEND_ACCEPTED;2554
}2555
int wm_send(const struct wm_message *m) {2556
if(!m || m->word[0]>=90 || m->word[0]<=0) return 0;2557
return send_owned(*m);2558
}2559
int wm_input_reply(const struct wm_message *m,int kind) {2560
/* Repeated retries must not turn backpressure into logging pressure. */2561
static struct { uint64_t next,suppressed; } reports[3][3];2562
if(!m || m->word[0]!=11 || kind<30 || kind>32) return WM_SEND_INVALID;2563
unsigned depth;2564
int state,result=send_owned_result(*m,&depth,&state);2565
if(result==WM_SEND_ACCEPTED) return result;2566
unsigned reason=result==WM_SEND_FULL ? 0 : result==WM_SEND_STARTING ? 1 : 2;2567
uint64_t now=now_ms();2568
if(now>=reports[kind-30][reason].next) {2569
fprintf(stderr,"INPUT_REPLY case=%s action=%s depth=%u kind=%d state=%d suppressed=%llu\n",2570
result==WM_SEND_FULL ? "queue-full" : result==WM_SEND_STARTING ? "receiver-starting" : "receiver-stopped",2571
result==WM_SEND_STOPPED ? "drop" : "wait",depth,kind,state,2572
(unsigned long long)reports[kind-30][reason].suppressed);2573
reports[kind-30][reason].next=now+1000;2574
reports[kind-30][reason].suppressed=0;2575
} else reports[kind-30][reason].suppressed++;2576
return result;2577
}2578
int wm_submit_traced(int64_t kind,int64_t token,const int64_t *words,int64_t count,const uint64_t *causes,int trace_count) {2579
if(!words || count<1 || count>65536 || kind<1 || kind>17) return 0;2580
if(trace_count<0 || trace_count>TRACE_CAUSES || (trace_count && !causes)) return 0;2581
int64_t *copy=malloc((count+trace_count)*sizeof(int64_t));2582
if(!copy) return 0;2583
memcpy(copy,words,count*sizeof(int64_t));2584
if(trace_count) memcpy(copy+count,causes,trace_count*sizeof(uint64_t));2585
struct wm_message m={.word={90,kind,token,(int64_t)(intptr_t)copy,count}};2586
m.word[5]=trace_count;2587
m.word[15]=trace_count ? causes[0] : 0;2588
if(send_owned(m)) return 1;2589
free(copy); return 0;2590
}2591
int wm_submit(int64_t kind,int64_t token,const int64_t *words,int64_t count) {2592
return wm_submit_traced(kind,token,words,count,NULL,0);2593
}2594
int wm_pixels_traced(int64_t id,const int64_t *w,const void *data,const int64_t *options,const uint64_t *causes,int trace_count) {2595
if(trace_count<0 || trace_count>TRACE_CAUSES || (trace_count && !causes)) return 0;2596
if(options && (options[0]<0 || options[0]>1 || options[1]<0)) return 0;2597
if(!w || id<=0 || w[0]<-32768 || w[0]>32768 || w[1]<-32768 || w[1]>32768 || w[7]<-1 || w[7]>2) return 0;2598
size_t order_count=0;2599
if(w[7]==2) {2600
if(!data || !options || options[6]<1 || options[6]>65535) return 0;2601
order_count=(size_t)options[6]+1;2602
if(!scene_order_valid(options+6,order_count)) return 0;2603
bool included=false;2604
for(size_t i=7;i<6+order_count;i++) if(options[i]==-id) included=true;2605
if(!included) return 0;2606
}2607
struct pixel_buffer *b=NULL;2608
if(data) {2609
if(w[2]<1 || w[2]>16384 || w[3]<1 || w[3]>16384 || w[4]<1 || w[4]>16384 ||2610
w[5]<1 || w[5]>16384 || w[6]<w[4]*4 || w[6]>65536 || w[6]%4 ||2611
w[5]*w[6]>8*1024*1024) return 0;2612
if(options && options[1]>0 &&2613
(options[2]<-32768 || options[2]>32768 || options[3]<-32768 || options[3]>32768 ||2614
options[4]<1 || options[4]>32768 || options[5]<1 || options[5]>32768 ||2615
w[0]<options[2] || w[1]<options[3] ||2616
w[0]+w[2]>options[2]+options[4] || w[1]+w[3]>options[3]+options[5])) return 0;2617
size_t pixels_length=w[5]*w[6];2618
size_t offset=order_count ? (pixels_length+7)&~(size_t)7 : pixels_length;2619
size_t length=offset+order_count*sizeof(int64_t),size=sizeof(*b)+length;2620
uint_fast64_t retained=atomic_load(&bridge.bytes);2621
do { if(retained+size>MAX_PIXELS) return 0; }2622
while(!atomic_compare_exchange_weak(&bridge.bytes,&retained,retained+size));2623
b=calloc(1,size);2624
if(!b) { atomic_fetch_sub(&bridge.bytes,size); return 0; }2625
b->length=length; b->stride=w[6]; b->input=options && options[0];2626
b->trace_count=trace_count;2627
if(trace_count) memcpy(b->trace_causes,causes,trace_count*sizeof(uint64_t));2628
b->order_count=order_count;2629
if(options) memcpy(b->scope,options+1,sizeof(b->scope));2630
memcpy(b->data,data,pixels_length);2631
if(order_count) memcpy(b->data+offset,options+6,order_count*sizeof(int64_t));2632
wlr_buffer_init(&b->base,&pixel_impl,w[4],w[5]);2633
}2634
struct wm_message m={.word={91,id}};2635
memcpy(m.word+2,w,8*sizeof(int64_t));2636
m.word[12]=(int64_t)(intptr_t)b;2637
m.word[15]=trace_count ? causes[0] : 0;2638
if(send_owned(m)) return 1;2639
if(b) wlr_buffer_drop(&b->base);2640
return 0;2641
}2642
int wm_pixels_with_options(int64_t id,const int64_t *w,const void *data,const int64_t *options) {2643
return wm_pixels_traced(id,w,data,options,NULL,0);2644
}2645
int wm_pixels(int64_t id,const int64_t *w,const void *data) {2646
return wm_pixels_with_options(id,w,data,NULL);2647
}2648
const char *wm_socket(void) { return wm_state()==2 ? bridge.socket : ""; }2649
uint64_t wm_stat(int which) {2650
switch(which) {2651
case 0: return atomic_load(&bridge.frames);2652
case 1: return atomic_load(&bridge.objects);2653
case 2: return atomic_load(&bridge.overflow);2654
case 3: return atomic_load(&bridge.rejected);2655
case 4: return atomic_load(&bridge.bytes);2656
case 5: return atomic_load(&bridge.pixel_nodes);2657
case 6: return atomic_load(&bridge.pixel_drops);2658
case 7: return atomic_load(&bridge.saved_trees);2659
case 8: return atomic_load(&bridge.snapshots);2660
case 9: return event_resync_count;2661
case 10: return atomic_load(&bridge.input_expired);2662
case 11: return atomic_load(&bridge.motion_coalesced);2663
case 12: return atomic_load(&bridge.timer_wakes);2664
case 13: return atomic_load(&bridge.first_present_ms);2665
default: return UINT64_MAX;2666
}2667
}2668
int wm_join(void) {2669
if(!bridge.created || bridge.joined || (wm_state()!=3 && wm_state()!=-1)) return 0;2670
if(pthread_join(bridge.thread,NULL)) return 0;2671
struct wm_message command;2672
while(pop(&bridge.commands,&command)) free_command(command);2673
free_event_stream();2674
close(bridge.event_fd); close(bridge.command_fd); bridge.joined=true; return 1;2675
}