swm.c 246.5 KiB raw
1
/*
2
 * swm.c
3
 * Simple Wayland window manager.
4
 */
5
#include <errno.h>
6
#include <getopt.h>
7
#include <libinput.h>
8
#include <linux/input-event-codes.h>
9
#include <math.h>
10
#include <signal.h>
11
#include <stdbool.h>
12
#include <stdio.h>
13
#include <stdlib.h>
14
#include <string.h>
15
#include <sys/stat.h>
16
#include <sys/wait.h>
17
#include <time.h>
18
#include <unistd.h>
19
#include <wayland-server-core.h>
20
#include <wlr/backend.h>
21
#include <wlr/backend/libinput.h>
22
#include <wlr/render/allocator.h>
23
#include <wlr/render/wlr_renderer.h>
24
#include <wlr/types/wlr_alpha_modifier_v1.h>
25
#include <wlr/types/wlr_compositor.h>
26
#include <wlr/types/wlr_cursor.h>
27
#include <wlr/types/wlr_cursor_shape_v1.h>
28
#include <wlr/types/wlr_data_control_v1.h>
29
#include <wlr/types/wlr_data_device.h>
30
#include <wlr/types/wlr_drm.h>
31
#include <wlr/types/wlr_export_dmabuf_v1.h>
32
#include <wlr/types/wlr_ext_data_control_v1.h>
33
#include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h>
34
#include <wlr/types/wlr_ext_image_capture_source_v1.h>
35
#include <wlr/types/wlr_ext_image_copy_capture_v1.h>
36
#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
37
#include <wlr/types/wlr_fractional_scale_v1.h>
38
#include <wlr/types/wlr_gamma_control_v1.h>
39
#include <wlr/types/wlr_idle_inhibit_v1.h>
40
#include <wlr/types/wlr_idle_notify_v1.h>
41
#include <wlr/types/wlr_input_device.h>
42
#include <wlr/types/wlr_input_method_v2.h>
43
#include <wlr/types/wlr_keyboard.h>
44
#include <wlr/types/wlr_keyboard_group.h>
45
#include <wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h>
46
#include <wlr/types/wlr_layer_shell_v1.h>
47
#include <wlr/types/wlr_linux_dmabuf_v1.h>
48
#include <wlr/types/wlr_linux_drm_syncobj_v1.h>
49
#include <wlr/types/wlr_output.h>
50
#include <wlr/types/wlr_output_layout.h>
51
#include <wlr/types/wlr_output_management_v1.h>
52
#include <wlr/types/wlr_output_power_management_v1.h>
53
#include <wlr/types/wlr_pointer.h>
54
#include <wlr/types/wlr_pointer_constraints_v1.h>
55
#include <wlr/types/wlr_pointer_gestures_v1.h>
56
#include <wlr/types/wlr_presentation_time.h>
57
#include <wlr/types/wlr_primary_selection.h>
58
#include <wlr/types/wlr_primary_selection_v1.h>
59
#include <wlr/types/wlr_relative_pointer_v1.h>
60
#include <wlr/types/wlr_scene.h>
61
#include <wlr/types/wlr_screencopy_v1.h>
62
#include <wlr/types/wlr_seat.h>
63
#include <wlr/types/wlr_server_decoration.h>
64
#include <wlr/types/wlr_session_lock_v1.h>
65
#include <wlr/types/wlr_single_pixel_buffer_v1.h>
66
#include <wlr/types/wlr_subcompositor.h>
67
#include <wlr/types/wlr_text_input_v3.h>
68
#include <wlr/types/wlr_viewporter.h>
69
#include <wlr/types/wlr_virtual_keyboard_v1.h>
70
#include <wlr/types/wlr_virtual_pointer_v1.h>
71
#include <wlr/types/wlr_xcursor_manager.h>
72
#include <wlr/types/wlr_xdg_activation_v1.h>
73
#include <wlr/types/wlr_xdg_decoration_v1.h>
74
#include <wlr/types/wlr_xdg_dialog_v1.h>
75
#include <wlr/types/wlr_xdg_output_v1.h>
76
#include <wlr/types/wlr_xdg_shell.h>
77
#include <wlr/types/wlr_xdg_system_bell_v1.h>
78
#include <wlr/util/log.h>
79
#include <wlr/util/region.h>
80
#include <wlr/xwayland.h>
81
#include <xcb/xcb.h>
82
#include <xcb/xcb_icccm.h>
83
#include <xkbcommon/xkbcommon.h>
84
85
#include "ext-workspace-v1-protocol.h"
86
#include "swm-toplevel-v1-protocol.h"
87
#include "swm-workspace-v1-protocol.h"
88
#include "swm.h"
89
#include "util.h"
90
#include "xdg-shell-protocol.h"
91
92
/* Constants and helper macros. */
93
#define MIN(a, b)               ((a) < (b) ? (a) : (b))
94
#define MAX(A, B)               ((A) > (B) ? (A) : (B))
95
#define CLEANMASK(mask)         (mask & ~WLR_MODIFIER_CAPS)
96
#define VISIBLEON(C, M)         ((M) && (C)->ws && (C)->ws->mon == (M))
97
#define SLICE                   (32) /* Number of steps across the master area. */
98
#define LENGTH(X)               (sizeof X / sizeof X[0])
99
#define END(A)                  ((A) + LENGTH(A))
100
#define LISTEN(E, L, H)         wl_signal_add((E), ((L)->notify = (H), (L)))
101
#define MAX_CLIENTS             256
102
#define MAX_MONITORS            16
103
#define MAX_LAYER_SURFACES      256
104
#define MAX_KEYBOARD_GROUPS     32
105
#define MAX_POINTER_CONSTRAINTS 256
106
#define MAX_TEXT_INPUTS         128
107
#define MAX_INPUT_POPUPS        64
108
#define MAX_POPUPS              256
109
#define MAX_SESSION_LOCKS       4
110
#define MAX_PENDING_SPAWNS      256
111
#define MAX_WINDOW_STATES       512
112
#define MAX_WINDOW_STATE_FIELD  256
113
#define MAX_STATUS_FIELD        1024
114
#define MAX_WINDOW_STATE_LINE   1024
115
#define MAX_STATE_PATH          4096
116
#define MAX_WORKSPACE_TITLE     256
117
#define MAX_WS_ID               16
118
#define MAX_STATIC_LISTENERS    128
119
#define MAX_GLOBAL_LISTENERS    64
120
#define MAX_WS_MANAGERS         64
121
#define MAX_WS_HANDLES          (MAX_WS_MANAGERS * WSCOUNT)
122
#define MAX_AUTOSTART           64
123
#define MAX_COMMAND_SIZE        1024
124
#define MAX_COMMAND_ARGS        64
125
126
/* Cursor interaction state. */
127
enum { CURSOR_NORMAL, CURSOR_PRESSED, CURSOR_MOVE, CURSOR_RESIZE, CURSOR_TILE_RESIZE };
128
129
/* Master-stack configuration commands. */
130
enum {
131
    MASTER_SHRINK,
132
    MASTER_GROW,
133
    MASTER_ADD,
134
    MASTER_DELETE,
135
    STACK_INCREMENT,
136
    STACK_DECREMENT,
137
    STACK_RESET,
138
    LAYOUT_FLIP
139
};
140
141
/* Workspace cycling operations. */
142
enum {
143
    WORKSPACE_NEXT,
144
    WORKSPACE_PREVIOUS,
145
    WORKSPACE_NEXT_ALL,
146
    WORKSPACE_PREVIOUS_ALL,
147
    WORKSPACE_NEXT_MOVE,
148
    WORKSPACE_PREVIOUS_MOVE
149
};
150
151
/* Client surface implementations. */
152
enum { XDG_SHELL, LAYER_SHELL, X11 };
153
154
/* Window layers, ordered from back to front. */
155
enum {
156
    LAYER_BACKGROUND,
157
    LAYER_BOTTOM,
158
    LAYER_TILE,
159
    LAYER_FLOAT,
160
    LAYER_TOP,
161
    LAYER_FULLSCREEN,
162
    LAYER_OVERLAY,
163
    LAYER_BLOCK,
164
    NUM_LAYERS
165
};
166
167
/* Command argument value. */
168
typedef union {
169
    int         i;
170
    uint32_t    u;
171
    float       f;
172
    const void *v;
173
} arg_t;
174
175
/* Pointer button binding. */
176
typedef struct {
177
    unsigned int mod;
178
    unsigned int button;
179
    void (*func)(const arg_t *);
180
    const arg_t arg;
181
} button_t;
182
183
/* Display and its window-manager state. */
184
typedef struct monitor_t monitor_t;
185
/* Global workspace state. */
186
typedef struct workspace_t workspace_t;
187
188
/* Managed XDG or Xwayland client. */
189
typedef struct {
190
    /* This must remain first so generic surface code can read it. */
191
    unsigned int type; /* XDG_SHELL or X11. */
192
193
    monitor_t             *mon;
194
    workspace_t           *ws;
195
    struct wlr_scene_tree *scene;
196
    struct wlr_scene_rect *border[4]; /* top, bottom, left, right */
197
    struct wlr_scene_tree *scene_surface;
198
    struct wl_list         link;
199
    struct wl_list         flink;
200
    struct wlr_box         geom;          /* Position within the layout, including borders. */
201
    struct wlr_box         prev;          /* Previous position and size, including borders. */
202
    struct wlr_box         maxstack_prev; /* Floating position and size before max layout. */
203
    struct wlr_box         pending_geom;  /* Size and position requested by the latest resize. */
204
    struct wlr_box         bounds;        /* Only the width and height are used. */
205
    union {
206
        struct wlr_xdg_surface      *xdg;
207
        struct wlr_xwayland_surface *xwayland;
208
    } surface;
209
    struct wlr_xdg_toplevel_decoration_v1     *decoration;
210
    struct wl_listener                         commit;
211
    struct wl_listener                         map;
212
    struct wl_listener                         maximize;
213
    struct wl_listener                         unmap;
214
    struct wl_listener                         destroy;
215
    struct wl_listener                         set_title;
216
    struct wl_listener                         set_appid;
217
    struct wl_listener                         fullscreen;
218
    struct wl_listener                         set_decoration_mode;
219
    struct wl_listener                         destroy_decoration;
220
    struct wlr_xdg_dialog_v1                  *dialog;
221
    struct wl_listener                         dialog_destroy;
222
    struct wl_listener                         dialog_modal;
223
    struct wl_listener                         set_parent;
224
    struct wl_listener                         activate;
225
    struct wl_listener                         associate;
226
    struct wl_listener                         dissociate;
227
    struct wl_listener                         configure;
228
    struct wl_listener                         set_hints;
229
    struct wlr_foreign_toplevel_handle_v1     *ftl;
230
    struct wlr_ext_foreign_toplevel_handle_v1 *extftl;
231
    monitor_t                                 *ftl_monitor;
232
    struct wl_listener                         ftl_activate;
233
    struct wl_listener                         ftl_close;
234
    struct wl_listener                         ftl_fullscreen;
235
    unsigned int                               bw;
236
    bool                                       is_floating, is_urgent, is_fullscreen, is_borderless;
237
    bool                                       is_max_stacked;
238
    int                                        persist_float;
239
    int                                        pending_map;
240
    uint32_t                                   resize_edges;
241
    uint32_t                                   resize; /* ID of a resize awaiting confirmation. */
242
} client_t;
243
244
/* Keyboard binding. */
245
typedef struct {
246
    uint32_t     mod;
247
    xkb_keysym_t keysym;
248
    void (*func)(const arg_t *);
249
    const arg_t arg;
250
} key_t;
251
252
/* Physical or virtual keyboard group. */
253
typedef struct {
254
    struct wlr_keyboard_group *wlr_group;
255
    bool                       is_virtual;
256
    int                        nsyms;
257
    const xkb_keysym_t        *keysyms; /* Valid only when nsyms is nonzero. */
258
    uint32_t                   mods;    /* Valid only when nsyms is nonzero. */
259
    struct wl_event_source    *key_repeat_source;
260
    struct wl_listener         modifiers;
261
    struct wl_listener         key;
262
    struct wl_listener         destroy;
263
} keyboard_group_t;
264
265
/* Layer-shell surface. */
266
typedef struct {
267
    /* This must remain first so generic surface code can read it. */
268
    unsigned int type; /* Always LAYER_SHELL. */
269
270
    monitor_t                         *mon;
271
    struct wlr_scene_tree             *scene;
272
    struct wlr_scene_rect             *dim;
273
    struct wlr_scene_tree             *popups;
274
    struct wlr_scene_layer_surface_v1 *scene_layer;
275
    struct wl_list                     link;
276
    int                                mapped;
277
    struct wlr_layer_surface_v1       *layer_surface;
278
    struct wl_listener                 destroy;
279
    struct wl_listener                 unmap;
280
    struct wl_listener                 surface_commit;
281
} layer_surface_t;
282
283
/* Layer-shell matching rule. */
284
typedef struct {
285
    const char  *namespace;
286
    const float *dim;
287
} layer_rule_t;
288
289
/* Workspace layout implementation. */
290
typedef struct {
291
    void (*arrange)(monitor_t *);
292
    bool cycle;
293
} layout_t;
294
295
/* Geometry of a master-stack layout. */
296
typedef struct {
297
    int  side;   /* One of the SWM_MASTER_* sides. */
298
    bool rotate; /* The master area runs along the top or bottom edge. */
299
    bool flip;   /* The master area sits on the far edge of its axis. */
300
} master_layout_t;
301
302
struct monitor_t {
303
    struct wl_list           link;
304
    struct wlr_output       *wlr_output;
305
    struct wlr_scene_output *scene_output;
306
    struct wlr_scene_rect   *fullscreen_bg; /* Hides other layers behind fullscreen windows. */
307
    struct wl_listener       frame;
308
    struct wl_listener       destroy;
309
    struct wl_listener       request_state;
310
    struct wl_listener       output_bind;
311
    struct wl_listener       destroy_lock_surface;
312
    struct wlr_session_lock_surface_v1 *lock_surface;
313
    struct wlr_box                      m;         /* Full display area within the layout. */
314
    struct wlr_box                      w;         /* Area available for regular windows. */
315
    struct wl_list                      layers[4]; /* Layer surfaces on this display. */
316
    workspace_t                        *ws;        /* Workspace shown on this display. */
317
    workspace_t *previous_workspace;               /* Workspace shown before the current one. */
318
    int          gamma_lut_changed;
319
    int          asleep;
320
};
321
322
/* Each workspace keeps separate settings for vertical and horizontal layouts. */
323
/* Master-stack layout parameters. */
324
typedef struct {
325
    int msize;  /* Master area width or height, in 1/SLICE steps. */
326
    int mwin;   /* Maximum number of windows in the master area. */
327
    int stacks; /* Stack columns, or rows in a horizontal layout. */
328
} stack_state_t;
329
330
/* A workspace owns its windows and layout settings, and can appear on only one display. */
331
struct workspace_t {
332
    char            name[4];
333
    char            title[MAX_WORKSPACE_TITLE]; /* Ephemeral human-readable label. */
334
    uint32_t        color; /* Ephemeral color encoded as 0xRRGGBBAA; zero means unset. */
335
    int             idx;
336
    monitor_t      *mon; /* Display showing this workspace, or nullptr if hidden. */
337
    const layout_t *lt;
338
    const layout_t *prevlt;
339
    stack_state_t   v, h;
340
    struct wl_list  handles; /* Protocol handles that publish this workspace. */
341
};
342
343
/* Each connected workspace client gets one manager and one group for all displays. */
344
/* Bound ext-workspace manager. */
345
typedef struct {
346
    struct wl_resource *resource; /* Client's workspace manager. */
347
    struct wl_resource *group;    /* Client's group, or nullptr after it disconnects. */
348
    workspace_t        *pending;  /* Workspace to activate when the client commits. */
349
    struct wl_list      link;     /* Entry in ws_managers. */
350
} workspace_manager_t;
351
352
/* Exported ext-workspace handle. */
353
typedef struct {
354
    struct wl_resource  *resource;
355
    workspace_t         *ws;
356
    workspace_manager_t *mgr;  /* nullptr after the manager is destroyed. */
357
    struct wl_list       link; /* Entry in workspace_t.handles. */
358
} workspace_handle_t;
359
360
/* Bound client for swm's ephemeral workspace metadata protocol. */
361
typedef struct {
362
    struct wl_resource *resource;
363
    struct wl_list      link;
364
} metadata_manager_t;
365
366
/* Spawn awaiting workspace assignment. */
367
typedef struct {
368
    pid_t          pid;
369
    workspace_t   *ws;
370
    struct wl_list link;
371
} pending_spawn_t;
372
373
/* Persisted client geometry. */
374
typedef struct {
375
    char           appid[MAX_WINDOW_STATE_FIELD];
376
    char           title[MAX_WINDOW_STATE_FIELD];
377
    struct wlr_box geom;
378
    struct wl_list link;
379
} window_state_t;
380
381
/* Connects applications to an input method such as fcitx5. */
382
/* Text-input relay state. */
383
typedef struct {
384
    struct wlr_text_input_v3 *ti;
385
    struct wl_listener        enable;
386
    struct wl_listener        commit;
387
    struct wl_listener        disable;
388
    struct wl_listener        destroy;
389
} text_input_t;
390
391
/* XDG popup state. */
392
typedef struct {
393
    struct wlr_xdg_popup *popup;
394
    struct wl_listener    commit;
395
    struct wl_listener    destroy;
396
} popup_t;
397
398
/* Input-method popup state. */
399
typedef struct {
400
    struct wlr_input_popup_surface_v2 *popup;
401
    struct wlr_scene_tree             *scene;
402
    struct wl_list                     link;
403
    struct wl_listener                 map;
404
    struct wl_listener                 unmap;
405
    struct wl_listener                 commit;
406
    struct wl_listener                 destroy;
407
} input_popup_t;
408
409
/* Output configuration rule. */
410
typedef struct {
411
    const char              *name;
412
    float                    scale;
413
    enum wl_output_transform rr;
414
    int                      x, y;
415
} monitor_rule_t;
416
417
/* Active pointer constraint. */
418
typedef struct {
419
    struct wlr_pointer_constraint_v1 *constraint;
420
    struct wl_listener                destroy;
421
} pointer_constraint_t;
422
423
/* Client matching rule. */
424
typedef struct {
425
    const char *id;
426
    const char *title;
427
    int         ws; /* Workspace number starting at zero; -1 keeps the current one. */
428
    bool        is_floating;
429
    int         monitor;
430
    bool        borderless;
431
} rule_t;
432
433
/* Session lock state. */
434
typedef struct {
435
    struct wlr_scene_tree      *scene;
436
    struct wlr_session_lock_v1 *lock;
437
    struct wl_listener          new_surface;
438
    struct wl_listener          unlock;
439
    struct wl_listener          destroy;
440
} session_lock_t;
441
442
/* Listener allocated from the static pool. */
443
typedef struct {
444
    struct wl_listener listener;
445
} static_listener_t;
446
447
/* Function declarations. */
448
static void         apply_bounds(client_t *c, struct wlr_box *bbox);
449
static void         client_place(client_t *c);
450
static void         apply_rules(client_t *c, workspace_t *defaultws);
451
static unsigned int client_border_width(client_t *c);
452
static void         arrange(monitor_t *m);
453
static void         arrange_layer(
454
    monitor_t *m, struct wl_list *list, struct wlr_box *usable_area, int exclusive
455
);
456
static void              arrange_layers(monitor_t *m);
457
static void              autostart_exec(void);
458
static void              axis_notify(struct wl_listener *listener, void *data);
459
static void              button_press(struct wl_listener *listener, void *data);
460
static bool              tiled_resize_boundary(monitor_t *m, int x, int y, bool *horizontal);
461
static void              tiled_resize_cursor(bool active, bool horizontal);
462
static int               tiled_resize_value(int offset, int size, bool flip);
463
static void              tiled_resize_update(void);
464
static void              change_vt(const arg_t *arg);
465
static void              check_idle_inhibitor(struct wlr_surface *exclude);
466
static void              cleanup(void);
467
static void              cleanup_monitor(struct wl_listener *listener, void *data);
468
static void              cleanup_listeners(void);
469
static void              close_monitor(monitor_t *m);
470
static void              layer_surface_commit_notify(struct wl_listener *listener, void *data);
471
static void              commit_notify(struct wl_listener *listener, void *data);
472
static void              popup_commit(struct wl_listener *listener, void *data);
473
static void              popup_destroy(struct wl_listener *listener, void *data);
474
static void              create_decoration(struct wl_listener *listener, void *data);
475
static void              create_idle_inhibitor(struct wl_listener *listener, void *data);
476
static void              create_keyboard(struct wlr_keyboard *keyboard);
477
static keyboard_group_t *create_keyboard_group(bool is_virtual);
478
static void              create_layer_surface(struct wl_listener *listener, void *data);
479
static void              create_lock_surface(struct wl_listener *listener, void *data);
480
static void              create_monitor(struct wl_listener *listener, void *data);
481
static void              create_notify(struct wl_listener *listener, void *data);
482
static void              create_pointer(struct wlr_pointer *pointer);
483
static void              create_pointer_constraint(struct wl_listener *listener, void *data);
484
static void              create_popup(struct wl_listener *listener, void *data);
485
static void              cursor_constrain(struct wlr_pointer_constraint_v1 *constraint);
486
static void              cursor_frame(struct wl_listener *listener, void *data);
487
static void              cursor_warp_to_hint(void);
488
static void              destroy_decoration(struct wl_listener *listener, void *data);
489
static void              destroy_drag_icon(struct wl_listener *listener, void *data);
490
static void              destroy_idle_inhibitor(struct wl_listener *listener, void *data);
491
static void              layer_surface_destroy_notify(struct wl_listener *listener, void *data);
492
static void              destroy_lock(session_lock_t *lock, int unlocked);
493
static void              destroy_lock_surface(struct wl_listener *listener, void *data);
494
static void              destroy_notify(struct wl_listener *listener, void *data);
495
static void              destroy_pointer_constraint(struct wl_listener *listener, void *data);
496
static void              destroy_session_lock(struct wl_listener *listener, void *data);
497
static void              destroy_keyboard_group(struct wl_listener *listener, void *data);
498
static monitor_t        *direction_to_monitor(enum wlr_direction dir);
499
static void              cycle_layout(const arg_t *arg);
500
static void              cycle_workspace(const arg_t *arg);
501
static void              focus_client(client_t *c, int lift);
502
static client_t         *focus_close(client_t *c);
503
static void              focus_main(const arg_t *arg);
504
static void              focus_urgent(const arg_t *arg);
505
static void              ftl_activate_notify(struct wl_listener *listener, void *data);
506
static void              ftl_capture_request_notify(struct wl_listener *listener, void *data);
507
static void              ftl_close_notify(struct wl_listener *listener, void *data);
508
static void              ftl_fullscreen_notify(struct wl_listener *listener, void *data);
509
static void              ftl_sync(client_t *c);
510
static void              input_method_commit_notify(struct wl_listener *listener, void *data);
511
static void              input_method_create_notify(struct wl_listener *listener, void *data);
512
static void              input_method_destroy_notify(struct wl_listener *listener, void *data);
513
static struct wlr_text_input_v3 *input_method_focused_text_input(void);
514
static void      input_method_grab_keyboard(struct wl_listener *listener, void *data);
515
static void      input_method_new_popup(struct wl_listener *listener, void *data);
516
static void      input_method_send_state(struct wlr_text_input_v3 *ti);
517
static void      input_method_set_focus(struct wlr_surface *surface);
518
static void      position_input_popups(void);
519
static void      input_popup_commit(struct wl_listener *listener, void *data);
520
static void      input_popup_destroy(struct wl_listener *listener, void *data);
521
static void      input_popup_map(struct wl_listener *listener, void *data);
522
static bool      input_popup_position(input_popup_t *p);
523
static void      input_popup_unmap(struct wl_listener *listener, void *data);
524
static void      text_input_create_notify(struct wl_listener *listener, void *data);
525
static void      text_input_commit_notify(struct wl_listener *listener, void *data);
526
static void      text_input_destroy_notify(struct wl_listener *listener, void *data);
527
static void      text_input_disable_notify(struct wl_listener *listener, void *data);
528
static void      text_input_enable_notify(struct wl_listener *listener, void *data);
529
static void      focus_monitor(const arg_t *arg);
530
static void      focus_stack(const arg_t *arg);
531
static client_t *focus_top(monitor_t *m);
532
static bool      client_is_blocked(client_t *c);
533
static void      fullscreen_notify(struct wl_listener *listener, void *data);
534
static void      gpu_reset(struct wl_listener *listener, void *data);
535
static int       handle_signal(int signo, void *data);
536
static void      master_bottom(monitor_t *m);
537
static void      master_left(monitor_t *m);
538
static void      master_right(monitor_t *m);
539
static void      master_top(monitor_t *m);
540
static void      input_device(struct wl_listener *listener, void *data);
541
static int       key_binding(uint32_t mods, xkb_keysym_t sym, bool repeat);
542
static void      new_shortcuts_inhibitor(struct wl_listener *listener, void *data);
543
static bool      shortcuts_inhibited(void);
544
static void      key_press(struct wl_listener *listener, void *data);
545
static void      key_press_modifiers(struct wl_listener *listener, void *data);
546
static void      load_window_states(void);
547
static int       key_repeat(void *data);
548
static void      kill_client(const arg_t *arg);
549
static void      lock_session(struct wl_listener *listener, void *data);
550
static void      listen_global(struct wl_signal *signal, struct wl_listener *listener);
551
static void      listen_static(struct wl_signal *signal, wl_notify_func_t notify);
552
static void      listener_release(struct wl_listener *listener);
553
static void      map_notify(struct wl_listener *listener, void *data);
554
static void      maximize_notify(struct wl_listener *listener, void *data);
555
static void      max_stack(monitor_t *m);
556
static void      restore_max_stack(monitor_t *m);
557
static void      motion_absolute(struct wl_listener *listener, void *data);
558
static void      motion_notify(
559
    uint32_t                 time,
560
    struct wlr_input_device *device,
561
    double                   sx,
562
    double                   sy,
563
    double                   sx_unaccel,
564
    double                   sy_unaccel,
565
    bool                     refocus
566
);
567
static void motion_relative(struct wl_listener *listener, void *data);
568
static void gesture_swipe_begin(struct wl_listener *listener, void *data);
569
static void gesture_swipe_update(struct wl_listener *listener, void *data);
570
static void gesture_swipe_end(struct wl_listener *listener, void *data);
571
static void gesture_pinch_begin(struct wl_listener *listener, void *data);
572
static void gesture_pinch_update(struct wl_listener *listener, void *data);
573
static void gesture_pinch_end(struct wl_listener *listener, void *data);
574
static void gesture_hold_begin(struct wl_listener *listener, void *data);
575
static void gesture_hold_end(struct wl_listener *listener, void *data);
576
static void move_resize(const arg_t *arg);
577
static void output_manager_apply(struct wl_listener *listener, void *data);
578
static void output_manager_apply_or_test(struct wlr_output_configuration_v1 *config, bool test);
579
static void output_manager_test(struct wl_listener *listener, void *data);
580
static void pointer_focus(
581
    client_t *c, struct wlr_surface *surface, double sx, double sy, uint32_t time, bool refocus
582
);
583
static void         print_status(void);
584
static void         publish_status(void);
585
static void         status_idle_notify(void *data);
586
static void         publish_windows(const char *runtime);
587
static void         execute_command(char *const argv[], const char *context);
588
static void         prepare_child(void);
589
static void         power_manager_set_mode(struct wl_listener *listener, void *data);
590
static void         quit(const arg_t *arg);
591
static void         raise_client(const arg_t *arg);
592
static void         render_monitor(struct wl_listener *listener, void *data);
593
static void         request_decoration_mode(struct wl_listener *listener, void *data);
594
static void         request_start_drag(struct wl_listener *listener, void *data);
595
static void         remember_client(client_t *c);
596
static void         forget_client(client_t *c);
597
static void         save_window_states(void);
598
static void         request_monitor_state(struct wl_listener *listener, void *data);
599
static void         resize(client_t *c, struct wlr_box geo, bool interact);
600
static void         resize_apply(client_t *c);
601
static void         restore_client(client_t *c);
602
static void         run(char *startup_cmd);
603
static void         set_cursor(struct wl_listener *listener, void *data);
604
static void         set_cursor_shape(struct wl_listener *listener, void *data);
605
static void         set_floating(client_t *c, bool floating);
606
static void         set_fullscreen(client_t *c, bool fullscreen);
607
static void         set_monitor(client_t *c, monitor_t *m, workspace_t *ws);
608
static void         set_workspace(client_t *c, workspace_t *ws);
609
static void         update_shortcuts_inhibitors(struct wlr_surface *surface);
610
static client_t    *client_main(client_t *c);
611
static bool         clients_related(client_t *a, client_t *b);
612
static void         assign_workspace(workspace_t *ws, monitor_t *m);
613
static workspace_t *free_workspace(void);
614
static void         view_workspace(workspace_t *ws, monitor_t *m);
615
static void         workspace_broadcast(void);
616
static void         workspace_metadata_bind(
617
    struct wl_client *client, void *data, uint32_t version, uint32_t id
618
);
619
static void toplevel_control_bind(
620
    struct wl_client *client, void *data, uint32_t version, uint32_t id
621
);
622
static void workspace_metadata_broadcast(void);
623
static void workspace_handle_create(workspace_manager_t *mgr, workspace_t *ws);
624
static void workspace_manager_bind(
625
    struct wl_client *client, void *data, uint32_t version, uint32_t id
626
);
627
static void       workspace_output_bind(struct wl_listener *listener, void *data);
628
static uint32_t   workspace_state(workspace_t *ws);
629
static void       set_primary_selection(struct wl_listener *listener, void *data);
630
static void       set_selection(struct wl_listener *listener, void *data);
631
static void       setup(void);
632
static void       spawn(const arg_t *arg);
633
static void       expand_argv(const char *const *argv, char **expanded, char *storage);
634
static void       swap_client(const arg_t *arg);
635
static void       stack_config(const arg_t *arg);
636
static void       stack_master(monitor_t *m, int side);
637
static void       start_drag(struct wl_listener *listener, void *data);
638
static void       tag(const arg_t *arg);
639
static void       tag_monitor(const arg_t *arg);
640
static void       toggle_floating(const arg_t *arg);
641
static void       toggle_fullscreen(const arg_t *arg);
642
static void       toggle_max_stack(const arg_t *arg);
643
static void       unlock_session(struct wl_listener *listener, void *data);
644
static void       layer_surface_unmap_notify(struct wl_listener *listener, void *data);
645
static void       unmap_notify(struct wl_listener *listener, void *data);
646
static void       update_monitors(struct wl_listener *listener, void *data);
647
static void       update_app_id(struct wl_listener *listener, void *data);
648
static void       update_title(struct wl_listener *listener, void *data);
649
static void       urgent(struct wl_listener *listener, void *data);
650
static void       ring_system_bell(struct wl_listener *listener, void *data);
651
static void       mark_urgent(client_t *c);
652
static void       create_dialog(struct wl_listener *listener, void *data);
653
static void       dialog_changed(struct wl_listener *listener, void *data);
654
static void       dialog_destroyed(struct wl_listener *listener, void *data);
655
static void       view(const arg_t *arg);
656
static void       virtual_keyboard(struct wl_listener *listener, void *data);
657
static void       virtual_pointer(struct wl_listener *listener, void *data);
658
static monitor_t *point_to_monitor(double x, double y);
659
static void       point_to_node(
660
    double               x,
661
    double               y,
662
    struct wlr_surface **psurface,
663
    client_t           **pc,
664
    layer_surface_t    **pl,
665
    double              *nx,
666
    double              *ny
667
);
668
static void zoom(const arg_t *arg);
669
670
/* Global state. */
671
static pid_t                   child_pid = -1;
672
static size_t                  autostart_len;
673
static bool                    locked;
674
static void                   *exclusive_focus;
675
static struct wl_display      *dpy;
676
static struct wl_event_loop   *event_loop;
677
static struct wl_event_source *signal_sources[3];
678
static struct wl_event_source *status_idle;
679
static struct wl_listener     *global_listeners[MAX_GLOBAL_LISTENERS];
680
static size_t                  global_listener_count;
681
static sigset_t                original_signal_mask;
682
static struct wlr_backend     *backend;
683
static struct wlr_scene       *scene;
684
static struct wlr_scene_tree  *layers[NUM_LAYERS];
685
static struct wlr_scene_tree  *drag_icon;
686
/* Map from ZWLR_LAYER_SHELL_* constants to Lyr* enum */
687
static const int layermap[] = { LAYER_BACKGROUND, LAYER_BOTTOM, LAYER_TOP, LAYER_OVERLAY };
688
static struct wlr_renderer   *drw;
689
static struct wlr_allocator  *alloc;
690
static struct wlr_compositor *compositor;
691
static struct wlr_session    *session;
692
693
static struct wlr_xdg_shell                             *xdg_shell;
694
static struct wlr_xdg_wm_dialog_v1                      *xdg_dialog_mgr;
695
static struct wlr_xdg_system_bell_v1                    *system_bell;
696
static struct wlr_xdg_activation_v1                     *activation;
697
static struct wlr_xdg_decoration_manager_v1             *xdg_decoration_mgr;
698
static struct wl_list                                    clients; /* tiling order */
699
static struct wl_list                                    fstack;  /* focus order */
700
static struct wlr_idle_notifier_v1                      *idle_notifier;
701
static struct wlr_idle_inhibit_manager_v1               *idle_inhibit_mgr;
702
static struct wlr_layer_shell_v1                        *layer_shell;
703
static struct wlr_output_manager_v1                     *output_mgr;
704
static struct wlr_virtual_keyboard_manager_v1           *virtual_keyboard_mgr;
705
static struct wlr_virtual_pointer_manager_v1            *virtual_pointer_mgr;
706
static struct wlr_cursor_shape_manager_v1               *cursor_shape_mgr;
707
static struct wlr_output_power_manager_v1               *power_mgr;
708
static struct wlr_foreign_toplevel_manager_v1           *ftl_mgr;
709
static struct wlr_keyboard_shortcuts_inhibit_manager_v1 *kb_inhibit_mgr;
710
static struct wlr_input_method_manager_v2               *im_mgr;
711
static struct wlr_text_input_manager_v3                 *ti_mgr;
712
static struct wlr_input_method_v2                       *input_method; /* at most one */
713
static struct wl_listener im_commit    = { .notify = input_method_commit_notify };
714
static struct wl_listener im_destroy   = { .notify = input_method_destroy_notify };
715
static struct wl_listener im_grab_kb   = { .notify = input_method_grab_keyboard };
716
static struct wl_listener im_new_popup = { .notify = input_method_new_popup };
717
static struct wlr_ext_foreign_toplevel_list_v1                         *ext_ftl_list;
718
static struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1 *ext_ftl_capture_mgr;
719
static struct wl_listener ext_ftl_capture_request = { .notify = ftl_capture_request_notify };
720
721
static struct wlr_pointer_constraints_v1      *pointer_constraints;
722
static struct wlr_pointer_gestures_v1         *pointer_gestures;
723
static struct wlr_relative_pointer_manager_v1 *relative_pointer_mgr;
724
static struct wlr_pointer_constraint_v1       *active_constraint;
725
726
static struct wlr_cursor          *cursor;
727
static struct wlr_xcursor_manager *cursor_mgr;
728
729
static struct wlr_scene_rect              *root_bg;
730
static struct wlr_session_lock_manager_v1 *session_lock_mgr;
731
static struct wlr_scene_rect              *locked_bg;
732
static struct wlr_session_lock_v1         *cur_lock;
733
734
static struct wlr_seat  *seat;
735
static keyboard_group_t *kb_group;
736
static unsigned int      cursor_mode;
737
static client_t         *grabc;
738
static int               grabcx, grabcy; /* Pointer position within the grabbed window. */
739
static int               grabx, graby;   /* Pointer position within the display layout. */
740
static uint32_t          grabedges;
741
static struct wlr_box    grabgeom;
742
static workspace_t      *grabws;             /* Workspace changed by a tiled resize. */
743
static bool              grab_horizontal;    /* Tiled resize changes a horizontal boundary. */
744
static bool              tiled_resize_hover; /* Pointer uses a tiled resize cursor. */
745
static bool              tiled_resize_hover_horizontal; /* Hovered boundary orientation. */
746
747
static struct wlr_output_layout *output_layout;
748
static struct wlr_box            sgeom;
749
static struct wl_list            mons;
750
static struct wl_list            ws_managers;       /* workspace_manager_t.link */
751
static struct wl_list            metadata_managers; /* metadata_manager_t.link */
752
static struct wl_list            pending_spawns;    /* pending_spawn_t.link */
753
static struct wl_list            window_states;     /* window_state_t.link */
754
static struct wl_list            input_popups;      /* input_popup_t.link */
755
static int                       virtual_keyboards;
756
static monitor_t                *selmon;
757
static bool                      arranging_pointer_focus;
758
759
/* Event handlers shared for the lifetime of the compositor. */
760
static struct wl_listener cursor_axis            = { .notify = axis_notify };
761
static struct wl_listener cursor_button          = { .notify = button_press };
762
static struct wl_listener cursor_frame_listener  = { .notify = cursor_frame };
763
static struct wl_listener cursor_motion          = { .notify = motion_relative };
764
static struct wl_listener cursor_motion_absolute = { .notify = motion_absolute };
765
static struct wl_listener cursor_swipe_begin     = { .notify = gesture_swipe_begin };
766
static struct wl_listener cursor_swipe_update    = { .notify = gesture_swipe_update };
767
static struct wl_listener cursor_swipe_end       = { .notify = gesture_swipe_end };
768
static struct wl_listener cursor_pinch_begin     = { .notify = gesture_pinch_begin };
769
static struct wl_listener cursor_pinch_update    = { .notify = gesture_pinch_update };
770
static struct wl_listener cursor_pinch_end       = { .notify = gesture_pinch_end };
771
static struct wl_listener cursor_hold_begin      = { .notify = gesture_hold_begin };
772
static struct wl_listener cursor_hold_end        = { .notify = gesture_hold_end };
773
static struct wl_listener gpu_reset_listener     = { .notify = gpu_reset };
774
static struct wl_listener layout_change          = { .notify = update_monitors };
775
static struct wl_listener new_idle_inhibitor     = { .notify = create_idle_inhibitor };
776
static struct wl_listener new_input_device       = { .notify = input_device };
777
static struct wl_listener new_input_method       = { .notify = input_method_create_notify };
778
static struct wl_listener new_virtual_keyboard   = { .notify = virtual_keyboard };
779
static struct wl_listener new_virtual_pointer    = { .notify = virtual_pointer };
780
static struct wl_listener new_pointer_constraint = { .notify = create_pointer_constraint };
781
static struct wl_listener new_shortcuts_inhibitor_listener = { .notify = new_shortcuts_inhibitor };
782
static struct wl_listener new_output                       = { .notify = create_monitor };
783
static struct wl_listener new_xdg_toplevel                 = { .notify = create_notify };
784
static struct wl_listener new_text_input                   = { .notify = text_input_create_notify };
785
static struct wl_listener new_xdg_popup                    = { .notify = create_popup };
786
static struct wl_listener new_xdg_decoration               = { .notify = create_decoration };
787
static struct wl_listener new_xdg_dialog                   = { .notify = create_dialog };
788
static struct wl_listener new_layer_surface                = { .notify = create_layer_surface };
789
static struct wl_listener output_mgr_apply                 = { .notify = output_manager_apply };
790
static struct wl_listener output_mgr_test                  = { .notify = output_manager_test };
791
static struct wl_listener output_power_mgr_set_mode        = { .notify = power_manager_set_mode };
792
static struct wl_listener request_activate                 = { .notify = urgent };
793
static struct wl_listener system_bell_ring                 = { .notify = ring_system_bell };
794
static struct wl_listener request_cursor                   = { .notify = set_cursor };
795
static struct wl_listener request_set_psel                 = { .notify = set_primary_selection };
796
static struct wl_listener request_set_sel                  = { .notify = set_selection };
797
static struct wl_listener request_set_cursor_shape         = { .notify = set_cursor_shape };
798
static struct wl_listener request_start_drag_listener      = { .notify = request_start_drag };
799
static struct wl_listener start_drag_listener              = { .notify = start_drag };
800
static struct wl_listener new_session_lock                 = { .notify = lock_session };
801
802
static void                 activate_x11(struct wl_listener *listener, void *data);
803
static void                 associate_x11(struct wl_listener *listener, void *data);
804
static void                 configure_x11(struct wl_listener *listener, void *data);
805
static void                 create_notify_x11(struct wl_listener *listener, void *data);
806
static void                 dissociate_x11(struct wl_listener *listener, void *data);
807
static void                 set_hints(struct wl_listener *listener, void *data);
808
static void                 xwayland_ready(struct wl_listener *listener, void *data);
809
static struct wl_listener   new_xwayland_surface    = { .notify = create_notify_x11 };
810
static struct wl_listener   xwayland_ready_listener = { .notify = xwayland_ready };
811
static struct wlr_xwayland *xwayland;
812
813
/* Load configuration after declaring the types and state it may use. */
814
#include "config.h"
815
816
static_assert(WSCOUNT > 0 && WSCOUNT <= 32, "WSCOUNT must be between 1 and 32");
817
818
static workspace_t workspaces[WSCOUNT];
819
820
static pid_t                           autostart_pids[MAX_AUTOSTART];
821
static struct wlr_backend_output_state output_state_pool[MAX_MONITORS];
822
823
/* Fixed-capacity object pools. */
824
typedef struct {
825
    void       *items;
826
    bool       *used;
827
    size_t      capacity;
828
    size_t      item_size;
829
    const char *name;
830
} pool_t;
831
832
static void *pool_take(pool_t *pool) {
833
    size_t i;
834
835
    for (i = 0; i < pool->capacity; i++) {
836
        if (pool->used[i])
837
            continue;
838
        pool->used[i] = true;
839
        return (char *)pool->items + i * pool->item_size;
840
    }
841
    fprintf(stderr, "swm: %s pool exhausted (limit %zu)\n", pool->name, pool->capacity);
842
    return nullptr;
843
}
844
845
/* Define a fixed-capacity pool along with the storage it hands out. */
846
#define POOL(name, type, capacity)                                                                 \
847
    static type   name##_items[capacity];                                                          \
848
    static bool   name##_used[capacity];                                                           \
849
    static pool_t name##_pool = { name##_items, name##_used, (capacity), sizeof(type), #name }
850
851
static void pool_release(pool_t *pool, void *item) {
852
    size_t index = ((char *)item - (char *)pool->items) / pool->item_size;
853
854
    pool->used[index] = false;
855
    memset(item, 0, pool->item_size);
856
}
857
858
POOL(client, client_t, MAX_CLIENTS);
859
POOL(monitor, monitor_t, MAX_MONITORS);
860
POOL(layer_surface, layer_surface_t, MAX_LAYER_SURFACES);
861
POOL(keyboard_group, keyboard_group_t, MAX_KEYBOARD_GROUPS);
862
POOL(pointer_constraint, pointer_constraint_t, MAX_POINTER_CONSTRAINTS);
863
POOL(text_input, text_input_t, MAX_TEXT_INPUTS);
864
POOL(input_popup, input_popup_t, MAX_INPUT_POPUPS);
865
POOL(popup, popup_t, MAX_POPUPS);
866
POOL(session_lock, session_lock_t, MAX_SESSION_LOCKS);
867
POOL(pending_spawn, pending_spawn_t, MAX_PENDING_SPAWNS);
868
POOL(window_state, window_state_t, MAX_WINDOW_STATES);
869
POOL(static_listener, static_listener_t, MAX_STATIC_LISTENERS);
870
POOL(workspace_manager, workspace_manager_t, MAX_WS_MANAGERS);
871
POOL(workspace_handle, workspace_handle_t, MAX_WS_HANDLES);
872
POOL(metadata_manager, metadata_manager_t, MAX_WS_MANAGERS);
873
874
/* Backend-specific client operations. */
875
/* Client helpers shared by the XDG shell and Xwayland implementations. These
876
 * are inline so unused helpers are omitted for builds that disable a backend. */
877
878
/* Return whether a client uses Xwayland. */
879
static inline bool client_is_x11(client_t *c) {
880
    return c->type == X11;
881
}
882
883
/* Return the underlying wlroots surface. */
884
static inline struct wlr_surface *client_surface(client_t *c) {
885
    if (client_is_x11(c))
886
        return c->surface.xwayland->surface;
887
    return c->surface.xdg->surface;
888
}
889
890
/* Resolve a surface to its owning toplevel. */
891
static inline int toplevel_from_wlr_surface(
892
    struct wlr_surface *s, client_t **pc, layer_surface_t **pl
893
) {
894
    struct wlr_xdg_surface      *xdg_surface, *tmp_xdg_surface;
895
    struct wlr_surface          *root_surface;
896
    struct wlr_layer_surface_v1 *layer_surface;
897
    client_t                    *c    = nullptr;
898
    layer_surface_t             *l    = nullptr;
899
    int                          type = -1;
900
    struct wlr_xwayland_surface *xsurface;
901
902
    if (!s)
903
        return -1;
904
    root_surface = wlr_surface_get_root_surface(s);
905
906
    if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(root_surface))) {
907
        c    = xsurface->data;
908
        type = c ? (int)c->type : -1;
909
        goto end;
910
    }
911
    if ((layer_surface = wlr_layer_surface_v1_try_from_wlr_surface(root_surface))) {
912
        l    = layer_surface->data;
913
        type = l ? LAYER_SHELL : -1;
914
        goto end;
915
    }
916
    xdg_surface = wlr_xdg_surface_try_from_wlr_surface(root_surface);
917
918
    while (xdg_surface) {
919
        tmp_xdg_surface = nullptr;
920
921
        switch (xdg_surface->role) {
922
        case WLR_XDG_SURFACE_ROLE_POPUP:
923
924
            if (!xdg_surface->popup || !xdg_surface->popup->parent)
925
                return -1;
926
927
            tmp_xdg_surface = wlr_xdg_surface_try_from_wlr_surface(xdg_surface->popup->parent);
928
929
            if (!tmp_xdg_surface)
930
                return toplevel_from_wlr_surface(xdg_surface->popup->parent, pc, pl);
931
932
            xdg_surface = tmp_xdg_surface;
933
            break;
934
        case WLR_XDG_SURFACE_ROLE_TOPLEVEL:
935
            c    = xdg_surface->data;
936
            type = c ? (int)c->type : -1;
937
            goto end;
938
        case WLR_XDG_SURFACE_ROLE_NONE:
939
            return -1;
940
        }
941
    }
942
end:
943
944
    if (pl)
945
        *pl = l;
946
    if (pc)
947
        *pc = c;
948
    return type;
949
}
950
951
/* Client operations. */
952
static inline void client_activate_surface(struct wlr_surface *s, bool activated) {
953
    struct wlr_xdg_toplevel     *toplevel;
954
    struct wlr_xwayland_surface *xsurface;
955
956
    if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(s))) {
957
        wlr_xwayland_surface_activate(xsurface, activated);
958
        return;
959
    }
960
    if ((toplevel = wlr_xdg_toplevel_try_from_wlr_surface(s)))
961
        wlr_xdg_toplevel_set_activated(toplevel, activated);
962
}
963
964
/* Set the XDG configure bounds for a client. */
965
static inline uint32_t client_set_bounds(client_t *c, int32_t width, int32_t height) {
966
    if (client_is_x11(c))
967
        return 0;
968
969
    if (wl_resource_get_version(c->surface.xdg->toplevel->resource) >=
970
            XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION &&
971
        width >= 0 && height >= 0 && (c->bounds.width != width || c->bounds.height != height)) {
972
        c->bounds.width  = width;
973
        c->bounds.height = height;
974
        return wlr_xdg_toplevel_set_bounds(c->surface.xdg->toplevel, width, height);
975
    }
976
    return 0;
977
}
978
979
/* Return the client application identifier. */
980
static inline const char *client_get_appid(client_t *c) {
981
    if (client_is_x11(c))
982
        return c->surface.xwayland->class ? c->surface.xwayland->class : "broken";
983
    return c->surface.xdg->toplevel->app_id ? c->surface.xdg->toplevel->app_id : "broken";
984
}
985
986
/* Return the client surface clipping rectangle. */
987
static inline void client_get_clip(client_t *c, struct wlr_box *clip) {
988
    *clip = (struct wlr_box){
989
        .x      = 0,
990
        .y      = 0,
991
        .width  = c->geom.width - c->bw,
992
        .height = c->geom.height - c->bw,
993
    };
994
995
    if (client_is_x11(c))
996
        return;
997
998
    clip->x = c->surface.xdg->geometry.x;
999
    clip->y = c->surface.xdg->geometry.y;
1000
}
1001
1002
/* Return the client surface geometry. */
1003
static inline void client_get_geometry(client_t *c, struct wlr_box *geom) {
1004
    if (client_is_x11(c)) {
1005
        geom->x      = c->surface.xwayland->x;
1006
        geom->y      = c->surface.xwayland->y;
1007
        geom->width  = c->surface.xwayland->width;
1008
        geom->height = c->surface.xwayland->height;
1009
        return;
1010
    }
1011
    *geom = c->surface.xdg->geometry;
1012
}
1013
1014
/* Return the parent client, if any. */
1015
static inline client_t *client_get_parent(client_t *c) {
1016
    client_t *p = nullptr;
1017
1018
    if (client_is_x11(c)) {
1019
        if (c->surface.xwayland->parent)
1020
            toplevel_from_wlr_surface(c->surface.xwayland->parent->surface, &p, nullptr);
1021
        return p;
1022
    }
1023
    if (c->surface.xdg->toplevel->parent)
1024
        toplevel_from_wlr_surface(c->surface.xdg->toplevel->parent->base->surface, &p, nullptr);
1025
    return p;
1026
}
1027
1028
/* Return the client title. */
1029
static inline const char *client_get_title(client_t *c) {
1030
    if (client_is_x11(c))
1031
        return c->surface.xwayland->title ? c->surface.xwayland->title : "broken";
1032
    return c->surface.xdg->toplevel->title ? c->surface.xdg->toplevel->title : "broken";
1033
}
1034
1035
/* Return whether the client should float by type. */
1036
static inline bool client_is_float_type(client_t *c) {
1037
    struct wlr_xdg_toplevel      *toplevel;
1038
    struct wlr_xdg_toplevel_state state;
1039
1040
    if (client_is_x11(c)) {
1041
        struct wlr_xwayland_surface *surface    = c->surface.xwayland;
1042
        xcb_size_hints_t            *size_hints = surface->size_hints;
1043
1044
        if (surface->modal)
1045
            return true;
1046
1047
        if (wlr_xwayland_surface_has_window_type(surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DIALOG) ||
1048
            wlr_xwayland_surface_has_window_type(surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_SPLASH) ||
1049
            wlr_xwayland_surface_has_window_type(
1050
                surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_TOOLBAR
1051
            ) ||
1052
            wlr_xwayland_surface_has_window_type(
1053
                surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_UTILITY
1054
            )) {
1055
            return true;
1056
        }
1057
        return size_hints && size_hints->min_width > 0 && size_hints->min_height > 0 &&
1058
               (size_hints->max_width == size_hints->min_width ||
1059
                size_hints->max_height == size_hints->min_height);
1060
    }
1061
    toplevel = c->surface.xdg->toplevel;
1062
    state    = toplevel->current;
1063
    return toplevel->parent ||
1064
           (state.min_width != 0 && state.min_height != 0 &&
1065
            (state.min_width == state.max_width || state.min_height == state.max_height));
1066
}
1067
1068
/* Return whether the client is rendered on an output. */
1069
static inline bool client_is_rendered_on_mon(client_t *c, monitor_t *m) {
1070
    /* This is needed for when you don't want to check formal assignment,
1071
     * but rather actual displaying of the pixels.
1072
     * Usually VISIBLEON suffices and is also faster. */
1073
    struct wlr_surface_output *s;
1074
    int                        unused_lx, unused_ly;
1075
1076
    if (!wlr_scene_node_coords(&c->scene->node, &unused_lx, &unused_ly))
1077
        return false;
1078
    wl_list_for_each(
1079
        s, &client_surface(c)->current_outputs, link
1080
    ) if (s->output == m->wlr_output) return true;
1081
    return false;
1082
}
1083
1084
/* Return whether the client process is stopped. */
1085
static inline bool client_is_stopped(client_t *c) {
1086
    int       pid;
1087
    siginfo_t in = {};
1088
1089
    if (client_is_x11(c))
1090
        return false;
1091
1092
    wl_client_get_credentials(c->surface.xdg->client->client, &pid, nullptr, nullptr);
1093
1094
    if (waitid(P_PID, pid, &in, WNOHANG | WCONTINUED | WSTOPPED | WNOWAIT) < 0) {
1095
        /* This process is not our child process, while is very unlikely that
1096
         * it is stopped, in order to do not skip frames, assume that it is. */
1097
        if (errno == ECHILD)
1098
            return true;
1099
    } else if (in.si_pid) {
1100
        if (in.si_code == CLD_STOPPED || in.si_code == CLD_TRAPPED)
1101
            return true;
1102
1103
        if (in.si_code == CLD_CONTINUED)
1104
            return false;
1105
    }
1106
    return false;
1107
}
1108
1109
/* Return whether the client bypasses window management. */
1110
static inline bool client_is_unmanaged(client_t *c) {
1111
    if (client_is_x11(c))
1112
        return c->surface.xwayland->override_redirect;
1113
    return false;
1114
}
1115
1116
/* Return whether the client supports the requested pointer operation. */
1117
static inline bool client_allows_move_resize(client_t *c, uint32_t mode) {
1118
    return !client_is_unmanaged(c) && (!c->is_fullscreen || mode == CURSOR_RESIZE);
1119
}
1120
1121
/* Send keyboard focus to a client surface. */
1122
static inline void client_notify_enter(struct wlr_surface *s, struct wlr_keyboard *kb) {
1123
    if (kb)
1124
        wlr_seat_keyboard_notify_enter(seat, s, kb->keycodes, kb->num_keycodes, &kb->modifiers);
1125
    else
1126
        wlr_seat_keyboard_notify_enter(seat, s, nullptr, 0, nullptr);
1127
1128
    /* Send text-input enter only after wl_keyboard.enter has been queued. */
1129
    input_method_set_focus(s);
1130
}
1131
1132
/* Request that a client close. */
1133
static inline void client_send_close(client_t *c) {
1134
    if (client_is_x11(c)) {
1135
        wlr_xwayland_surface_close(c->surface.xwayland);
1136
        return;
1137
    }
1138
    wlr_xdg_toplevel_send_close(c->surface.xdg->toplevel);
1139
}
1140
1141
/* Set the color of every managed client border while its scene exists. */
1142
static inline void client_set_border_color(client_t *c, const float color[static 4]) {
1143
    int i;
1144
1145
    if (!c->scene || client_is_unmanaged(c))
1146
        return;
1147
1148
    for (i = 0; i < 4; i++)
1149
        wlr_scene_rect_set_color(c->border[i], color);
1150
}
1151
1152
/* Notify a client of its fullscreen state. */
1153
static inline void client_set_fullscreen(client_t *c, bool fullscreen) {
1154
    if (client_is_x11(c)) {
1155
        wlr_xwayland_surface_set_fullscreen(c->surface.xwayland, fullscreen);
1156
        return;
1157
    }
1158
    wlr_xdg_toplevel_set_fullscreen(c->surface.xdg->toplevel, fullscreen);
1159
}
1160
1161
/* Set the preferred scale for a client surface. */
1162
static inline void client_set_scale(struct wlr_surface *s, float scale) {
1163
    wlr_fractional_scale_v1_notify_scale(s, scale);
1164
    wlr_surface_set_preferred_buffer_scale(s, (int32_t)ceilf(scale));
1165
}
1166
1167
/* Configure the client surface size. */
1168
static inline uint32_t client_set_size(client_t *c, uint32_t width, uint32_t height) {
1169
    if (client_is_x11(c)) {
1170
        wlr_xwayland_surface_configure(
1171
            c->surface.xwayland, c->geom.x + c->bw, c->geom.y + c->bw, width, height
1172
        );
1173
        return 0;
1174
    }
1175
    if ((int32_t)width == c->surface.xdg->toplevel->current.width &&
1176
        (int32_t)height == c->surface.xdg->toplevel->current.height)
1177
        return 0;
1178
    return wlr_xdg_toplevel_set_size(c->surface.xdg->toplevel, (int32_t)width, (int32_t)height);
1179
}
1180
1181
/* Notify a client of an interactive resize. */
1182
static inline void client_set_resizing(client_t *c, bool resizing) {
1183
    if (client_is_x11(c))
1184
        return;
1185
    wlr_xdg_toplevel_set_resizing(c->surface.xdg->toplevel, resizing);
1186
}
1187
1188
/* Notify a client of its tiled edges. */
1189
static inline void client_set_tiled(client_t *c, uint32_t edges) {
1190
    if (client_is_x11(c)) {
1191
        wlr_xwayland_surface_set_maximized(
1192
            c->surface.xwayland, edges != WLR_EDGE_NONE, edges != WLR_EDGE_NONE
1193
        );
1194
        return;
1195
    }
1196
    if (wl_resource_get_version(c->surface.xdg->toplevel->resource) >=
1197
        XDG_TOPLEVEL_STATE_TILED_RIGHT_SINCE_VERSION) {
1198
        wlr_xdg_toplevel_set_tiled(c->surface.xdg->toplevel, edges);
1199
    } else {
1200
        wlr_xdg_toplevel_set_maximized(c->surface.xdg->toplevel, edges != WLR_EDGE_NONE);
1201
    }
1202
}
1203
1204
/* Notify a client of its suspended state. */
1205
static inline void client_set_suspended(client_t *c, bool suspended) {
1206
    if (client_is_x11(c))
1207
        return;
1208
1209
    wlr_xdg_toplevel_set_suspended(c->surface.xdg->toplevel, suspended);
1210
}
1211
1212
/* Return whether an unmanaged client requests focus. */
1213
static inline bool client_wants_focus(client_t *c) {
1214
    return client_is_unmanaged(c) &&
1215
           wlr_xwayland_surface_override_redirect_wants_focus(c->surface.xwayland) &&
1216
           wlr_xwayland_surface_icccm_input_model(c->surface.xwayland) !=
1217
               WLR_ICCCM_INPUT_MODEL_NONE;
1218
}
1219
1220
/* Return whether a client requests fullscreen. */
1221
static inline bool client_wants_fullscreen(client_t *c) {
1222
    if (client_is_x11(c))
1223
        return c->surface.xwayland->fullscreen;
1224
    return c->surface.xdg->toplevel->requested.fullscreen;
1225
}
1226
1227
/* Keep these values independent of wlroots so this module has no I/O or
1228
 * compositor dependencies. They are the stable values from wayland-util.h. */
1229
#define EDGE_TOP  1
1230
#define EDGE_LEFT 4
1231
1232
/* Clamp a box to the minimum size and output bounds. */
1233
void swm_box_apply_bounds(struct swm_box *box, const struct swm_box *bounds, unsigned int border) {
1234
    int minimum = 1 + 2 * (int)border;
1235
1236
    if (box->width < minimum)
1237
        box->width = minimum;
1238
1239
    if (box->height < minimum)
1240
        box->height = minimum;
1241
1242
    if (box->x >= bounds->x + bounds->width)
1243
        box->x = bounds->x + bounds->width - box->width;
1244
1245
    if (box->y >= bounds->y + bounds->height)
1246
        box->y = bounds->y + bounds->height - box->height;
1247
1248
    if (box->x + box->width <= bounds->x)
1249
        box->x = bounds->x;
1250
1251
    if (box->y + box->height <= bounds->y)
1252
        box->y = bounds->y;
1253
}
1254
1255
/* Resize a box from the selected edges. */
1256
struct swm_box swm_box_resize(
1257
    const struct swm_box *box, int dx, int dy, unsigned int edges, unsigned int border
1258
) {
1259
    int            minimum = 1 + 2 * (int)border;
1260
    struct swm_box result  = *box;
1261
    int            width   = box->width + ((edges & EDGE_LEFT) ? -dx : dx);
1262
    int            height  = box->height + ((edges & EDGE_TOP) ? -dy : dy);
1263
1264
    result.width  = width < minimum ? minimum : width;
1265
    result.height = height < minimum ? minimum : height;
1266
1267
    if (edges & EDGE_LEFT)
1268
        result.x = box->x + box->width - result.width;
1269
1270
    if (edges & EDGE_TOP)
1271
        result.y = box->y + box->height - result.height;
1272
1273
    return result;
1274
}
1275
1276
/* Reconcile pending geometry with a committed size. */
1277
void swm_box_reconcile_commit(
1278
    struct swm_box *box, int width, int height, unsigned int border, unsigned int edges
1279
) {
1280
    int outer_width, outer_height;
1281
1282
    if (width <= 0 || height <= 0)
1283
        return;
1284
1285
    outer_width  = width + 2 * (int)border;
1286
    outer_height = height + 2 * (int)border;
1287
1288
    if (edges & EDGE_LEFT)
1289
        box->x += box->width - outer_width;
1290
1291
    if (edges & EDGE_TOP)
1292
        box->y += box->height - outer_height;
1293
1294
    box->width  = outer_width;
1295
    box->height = outer_height;
1296
}
1297
1298
/* Position an input popup inside an output. */
1299
struct swm_box swm_popup_position(
1300
    const struct swm_box *client,
1301
    unsigned int          border,
1302
    const struct swm_box *cursor_rect,
1303
    int                   width,
1304
    int                   height,
1305
    const struct swm_box *output
1306
) {
1307
    struct swm_box popup = {
1308
        .x      = client->x + (int)border + cursor_rect->x,
1309
        .y      = client->y + (int)border + cursor_rect->y + cursor_rect->height,
1310
        .width  = width,
1311
        .height = height,
1312
    };
1313
    if (!output)
1314
        return popup;
1315
1316
    if (popup.x + width > output->x + output->width)
1317
        popup.x = output->x + output->width - width;
1318
1319
    if (popup.x < output->x)
1320
        popup.x = output->x;
1321
1322
    if (popup.y + height > output->y + output->height)
1323
        popup.y = client->y + (int)border + cursor_rect->y - height;
1324
1325
    if (popup.y < output->y)
1326
        popup.y = output->y;
1327
1328
    return popup;
1329
}
1330
1331
/* Compute exported workspace state flags. */
1332
unsigned int swm_workspace_state(bool active, bool occupied, bool urgent) {
1333
    unsigned int state = 0;
1334
1335
    if (active)
1336
        state |= SWM_WORKSPACE_ACTIVE;
1337
    if (urgent)
1338
        state |= SWM_WORKSPACE_URGENT;
1339
    if (!active && !occupied)
1340
        state |= SWM_WORKSPACE_HIDDEN;
1341
1342
    return state;
1343
}
1344
1345
/* Find the next eligible workspace. */
1346
int swm_workspace_next(
1347
    int         current,
1348
    int         count,
1349
    int         direction,
1350
    int         allow_empty,
1351
    const bool *visible_elsewhere,
1352
    const bool *occupied
1353
) {
1354
    int step, index;
1355
1356
    if (count <= 1 || (direction != -1 && direction != 1) || current < 0 || current >= count)
1357
        return -1;
1358
1359
    for (step = 1; step < count; step++) {
1360
        index = (current + direction * step + count) % count;
1361
1362
        if (visible_elsewhere[index])
1363
            continue;
1364
1365
        if (!allow_empty && !occupied[index])
1366
            continue;
1367
        return index;
1368
    }
1369
    return -1;
1370
}
1371
1372
/* Apply a master-stack configuration command. */
1373
bool swm_stack_configure(struct swm_stack_state *state, int command, int side, int *new_side) {
1374
    if (!state || !new_side || side < SWM_MASTER_LEFT || side > SWM_MASTER_BOTTOM)
1375
        return false;
1376
1377
    *new_side = side;
1378
1379
    switch (command) {
1380
    case SWM_MASTER_SHRINK:
1381
        if (state->msize > 1)
1382
            state->msize--;
1383
        break;
1384
    case SWM_MASTER_GROW:
1385
        if (state->msize < SWM_SLICE - 1)
1386
            state->msize++;
1387
        break;
1388
    case SWM_MASTER_ADD:
1389
        state->mwin++;
1390
        break;
1391
    case SWM_MASTER_DEL:
1392
        if (state->mwin > 0)
1393
            state->mwin--;
1394
        break;
1395
    case SWM_STACK_INC:
1396
        state->stacks++;
1397
        break;
1398
    case SWM_STACK_DEC:
1399
        if (state->stacks > 1)
1400
            state->stacks--;
1401
        break;
1402
    case SWM_STACK_RESET:
1403
        state->msize  = SWM_SLICE / 2;
1404
        state->mwin   = 1;
1405
        state->stacks = 1;
1406
1407
        if (side == SWM_MASTER_RIGHT)
1408
            *new_side = SWM_MASTER_LEFT;
1409
        else if (side == SWM_MASTER_BOTTOM)
1410
            *new_side = SWM_MASTER_TOP;
1411
        break;
1412
    case SWM_FLIP_LAYOUT:
1413
        *new_side = side == SWM_MASTER_LEFT    ? SWM_MASTER_RIGHT
1414
                    : side == SWM_MASTER_RIGHT ? SWM_MASTER_LEFT
1415
                    : side == SWM_MASTER_TOP   ? SWM_MASTER_BOTTOM
1416
                                               : SWM_MASTER_TOP;
1417
        break;
1418
    default:
1419
        return false;
1420
    }
1421
    return true;
1422
}
1423
1424
/* Return whether a rule matches a client identity. */
1425
bool swm_rule_matches(
1426
    const char *rule_id, const char *rule_title, const char *appid, const char *title
1427
) {
1428
    appid = appid ? appid : "";
1429
    title = title ? title : "";
1430
    if (!rule_id && !rule_title)
1431
        return false;
1432
1433
    return (!rule_id || !strcmp(rule_id, "*") || strstr(appid, rule_id)) &&
1434
           (!rule_title || strstr(title, rule_title));
1435
}
1436
1437
/* Replace state-file delimiters in a field. */
1438
void swm_sanitize_field(char *dst, size_t size, const char *src, const char *fallback) {
1439
    size_t i;
1440
1441
    if (!size)
1442
        return;
1443
1444
    if (!src || !*src)
1445
        src = fallback ? fallback : "";
1446
1447
    for (i = 0; src[i] && i + 1 < size; i++)
1448
        dst[i] = src[i] == '\t' || src[i] == '\n' || src[i] == '\r' ? ' ' : src[i];
1449
1450
    dst[i] = '\0';
1451
}
1452
1453
/* Parse one persisted window-state record. */
1454
bool swm_parse_window_state(
1455
    const char     *line,
1456
    char           *appid,
1457
    size_t          appid_size,
1458
    char           *title,
1459
    size_t          title_size,
1460
    struct swm_box *geometry
1461
) {
1462
    char parsed_appid[256], parsed_title[256];
1463
    int  fields;
1464
1465
    if (!line || !appid || !appid_size || !title || !title_size || !geometry)
1466
        return false;
1467
1468
    fields = sscanf(
1469
        line,
1470
        "%255[^\t]\t%255[^\t]\t%d\t%d\t%d\t%d",
1471
        parsed_appid,
1472
        parsed_title,
1473
        &geometry->x,
1474
        &geometry->y,
1475
        &geometry->width,
1476
        &geometry->height
1477
    );
1478
1479
    if (fields != 6) {
1480
        fields = sscanf(
1481
            line,
1482
            "%255[^\t]\t%d\t%d\t%d\t%d",
1483
            parsed_appid,
1484
            &geometry->x,
1485
            &geometry->y,
1486
            &geometry->width,
1487
            &geometry->height
1488
        );
1489
        if (fields != 5)
1490
            return false;
1491
1492
        parsed_title[0] = '\0';
1493
    }
1494
    swm_sanitize_field(appid, appid_size, parsed_appid, "broken");
1495
    swm_sanitize_field(title, title_size, parsed_title, nullptr);
1496
1497
    return true;
1498
}
1499
1500
/* Compute the effective client border width. */
1501
unsigned int swm_border_width(
1502
    unsigned int configured,
1503
    bool         fullscreen,
1504
    bool         borderless,
1505
    bool         x11,
1506
    bool         unmanaged,
1507
    bool         client_side
1508
) {
1509
    if (fullscreen || borderless || (x11 && unmanaged) || client_side)
1510
        return 0;
1511
    return configured;
1512
}
1513
1514
/* Find the next enabled layout. */
1515
int swm_next_layout(int current, int count, const bool *cycle) {
1516
    int step, index;
1517
1518
    if (!cycle || count <= 0 || current < 0 || current >= count)
1519
        return -1;
1520
1521
    for (step = 1; step <= count; step++) {
1522
        index = (current + step) % count;
1523
1524
        if (cycle[index])
1525
            return index;
1526
    }
1527
    return -1;
1528
}
1529
1530
/* Arrange clients into master and stack areas. */
1531
size_t swm_stack_layout(
1532
    const struct swm_box         *area,
1533
    const struct swm_stack_state *state,
1534
    int                           rotate,
1535
    int                           flip,
1536
    size_t                        count,
1537
    struct swm_box               *boxes
1538
) {
1539
    struct swm_box root = *area, master, stack, column, cell;
1540
    int            n    = (int)count, mwin, sn, stacks, slice;
1541
    int            j, ci, cw, extra, rows, base, remainder, offset;
1542
    size_t         i;
1543
1544
    if (!count)
1545
        return 0;
1546
1547
    if (rotate) {
1548
        int t       = root.x;
1549
        root.x      = root.y;
1550
        root.y      = t;
1551
        t           = root.width;
1552
        root.width  = root.height;
1553
        root.height = t;
1554
    }
1555
    slice = root.width / SWM_SLICE;
1556
    mwin  = MIN(state->mwin, n);
1557
1558
    if (mwin < 0)
1559
        mwin = 0;
1560
    sn     = n - mwin;
1561
    stacks = MIN(state->stacks, sn);
1562
1563
    if (stacks < 0)
1564
        stacks = 0;
1565
1566
    master = stack = root;
1567
1568
    if (stacks && mwin) {
1569
        master.width = slice * state->msize;
1570
1571
        if (master.width < 0)
1572
            master.width = 0;
1573
1574
        if (master.width > root.width)
1575
            master.width = root.width;
1576
        stack.width -= master.width;
1577
1578
        if (flip)
1579
            master.x += stack.width;
1580
        else
1581
            stack.x += master.width;
1582
    }
1583
    j      = 0;
1584
    ci     = -1;
1585
    rows   = mwin;
1586
    column = master;
1587
    cw     = stacks ? stack.width / stacks : 0;
1588
    extra  = stacks ? stack.width % stacks : 0;
1589
1590
    for (i = 0; i < count; i++) {
1591
        while (j >= rows) {
1592
            if (stacks <= 0)
1593
                return i;
1594
            j = 0;
1595
            ci++;
1596
            rows         = sn / stacks + (stacks - ci <= sn % stacks ? 1 : 0);
1597
            column       = stack;
1598
            column.width = cw + (ci == stacks - 1 ? extra : 0);
1599
1600
            if (flip)
1601
                column.x = stack.x + stack.width - (ci + 1) * cw - (ci == stacks - 1 ? extra : 0);
1602
            else
1603
                column.x = stack.x + ci * cw;
1604
        }
1605
        base        = column.height / rows;
1606
        remainder   = column.height % rows;
1607
        offset      = j * base + MIN(j, remainder);
1608
        cell.x      = column.x;
1609
        cell.y      = column.y + offset;
1610
        cell.width  = column.width;
1611
        cell.height = base + (j < remainder ? 1 : 0);
1612
1613
        if (rotate) {
1614
            int t       = cell.x;
1615
            cell.x      = cell.y;
1616
            cell.y      = t;
1617
            t           = cell.width;
1618
            cell.width  = cell.height;
1619
            cell.height = t;
1620
        }
1621
        boxes[i] = cell;
1622
        j++;
1623
    }
1624
    return count;
1625
}
1626
1627
/* Return whether a master side runs along the top or bottom edge. */
1628
static bool master_rotates(int side) {
1629
    return side == SWM_MASTER_TOP || side == SWM_MASTER_BOTTOM;
1630
}
1631
1632
/* Return whether a master side sits on the far edge of its axis. */
1633
static bool master_flips(int side) {
1634
    return side == SWM_MASTER_RIGHT || side == SWM_MASTER_BOTTOM;
1635
}
1636
1637
/* Describe a master-stack layout, or report that it is not one. */
1638
static bool layout_master(const layout_t *lt, master_layout_t *ml) {
1639
    if (!lt || !lt->arrange)
1640
        return false;
1641
1642
    if (lt->arrange == master_left)
1643
        ml->side = SWM_MASTER_LEFT;
1644
    else if (lt->arrange == master_top)
1645
        ml->side = SWM_MASTER_TOP;
1646
    else if (lt->arrange == master_right)
1647
        ml->side = SWM_MASTER_RIGHT;
1648
    else if (lt->arrange == master_bottom)
1649
        ml->side = SWM_MASTER_BOTTOM;
1650
    else
1651
        return false;
1652
1653
    ml->rotate = master_rotates(ml->side);
1654
    ml->flip   = master_flips(ml->side);
1655
    return true;
1656
}
1657
1658
/* Return the enabled layout with its master area on a side, if any. */
1659
static const layout_t *layout_from_side(int side) {
1660
    master_layout_t ml;
1661
    size_t          i;
1662
1663
    for (i = 0; i < LENGTH(layouts); i++) {
1664
        if (layout_master(&layouts[i], &ml) && ml.side == side)
1665
            return &layouts[i];
1666
    }
1667
    return nullptr;
1668
}
1669
1670
/* Return the active master boundary for a tiled workspace. */
1671
static bool tiled_resize_boundary(monitor_t *m, int x, int y, bool *horizontal) {
1672
    client_t       *c;
1673
    workspace_t    *ws;
1674
    stack_state_t  *st;
1675
    master_layout_t ml;
1676
    int             boundary, count = 0, masters;
1677
    bool            rotate, flip;
1678
1679
    if (!m || !(ws = m->ws) || !layout_master(ws->lt, &ml) || x < m->w.x ||
1680
        x >= m->w.x + m->w.width || y < m->w.y || y >= m->w.y + m->w.height)
1681
        return false;
1682
1683
    rotate = ml.rotate;
1684
    flip   = ml.flip;
1685
1686
    wl_list_for_each(c, &clients, link) if (VISIBLEON(c, m) && !c->is_floating && !c->is_fullscreen)
1687
        count++;
1688
1689
    st      = rotate ? &ws->h : &ws->v;
1690
    masters = MIN(MAX(st->mwin, 0), count);
1691
1692
    if (!masters || masters == count)
1693
        return false;
1694
1695
    if (rotate) {
1696
        boundary = flip ? m->w.y + m->w.height - m->w.height / SLICE * st->msize
1697
                        : m->w.y + m->w.height / SLICE * st->msize;
1698
        if ((unsigned int)abs(y - boundary) > tiled_resize_margin)
1699
            return false;
1700
    } else {
1701
        boundary = flip ? m->w.x + m->w.width - m->w.width / SLICE * st->msize
1702
                        : m->w.x + m->w.width / SLICE * st->msize;
1703
        if ((unsigned int)abs(x - boundary) > tiled_resize_margin)
1704
            return false;
1705
    }
1706
    *horizontal = rotate;
1707
    return true;
1708
}
1709
1710
/* Convert a pointer offset to a tiled master size. */
1711
static int tiled_resize_value(int offset, int size, bool flip) {
1712
    int step, value;
1713
1714
    if (size < SLICE || (step = size / SLICE) == 0)
1715
        return -1;
1716
1717
    value = (offset + step / 2) / step;
1718
1719
    if (flip)
1720
        value = SLICE - value;
1721
    return MIN(MAX(value, 1), SLICE - 1);
1722
}
1723
1724
/* Function implementations. */
1725
/* Clamp client geometry to the supplied bounds. */
1726
void apply_bounds(client_t *c, struct wlr_box *bbox) {
1727
    swm_box_apply_bounds((struct swm_box *)&c->geom, (const struct swm_box *)bbox, c->bw);
1728
}
1729
1730
/* Return the border width appropriate for a window's type and state. */
1731
unsigned int client_border_width(client_t *c) {
1732
    bool x11 = false, unmanaged = false;
1733
    bool client_side = c->decoration && c->decoration->requested_mode ==
1734
                                            WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
1735
    x11              = client_is_x11(c);
1736
    unmanaged        = x11 && client_is_unmanaged(c);
1737
    return swm_border_width(
1738
        borderwidth, c->is_fullscreen, c->is_borderless, x11, unmanaged, client_side
1739
    );
1740
}
1741
1742
/* Apply matching rules to choose a window's workspace, display, and style. */
1743
void apply_rules(client_t *c, workspace_t *defaultws) {
1744
    /* Later matching rules override earlier ones. */
1745
    const char   *appid, *title;
1746
    workspace_t  *ws = defaultws;
1747
    int           i;
1748
    const rule_t *r;
1749
    monitor_t    *mon = defaultws && defaultws->mon ? defaultws->mon : selmon, *m;
1750
1751
    appid = client_get_appid(c);
1752
    title = client_get_title(c);
1753
1754
    for (r = rules; r < END(rules); r++) {
1755
        if (swm_rule_matches(r->id, r->title, appid, title)) {
1756
            c->is_floating   = r->is_floating;
1757
            c->is_borderless = r->borderless;
1758
1759
            if (r->ws >= 0 && r->ws < WSCOUNT)
1760
                ws = &workspaces[r->ws];
1761
            i = 0;
1762
            wl_list_for_each(m, &mons, link) {
1763
                if (r->monitor == i++)
1764
                    mon = m;
1765
            }
1766
        }
1767
    }
1768
    c->is_floating |= client_is_float_type(c);
1769
    set_monitor(c, mon, ws);
1770
}
1771
1772
/* Update the visibility, layer, size, and focus of windows on a display. */
1773
void arrange(monitor_t *m) {
1774
    client_t *c;
1775
    client_t *focus;
1776
    int       max;
1777
1778
    if (!m->wlr_output->enabled)
1779
        return;
1780
1781
    focus = focus_top(m);
1782
    max   = m->ws && m->ws->lt->arrange == max_stack;
1783
    wl_list_for_each(c, &clients, link) {
1784
        if (c->mon == m) {
1785
            int visible;
1786
1787
            if (c->is_max_stacked && (!c->ws || c->ws->lt->arrange != max_stack)) {
1788
                c->is_max_stacked = false;
1789
                resize(c, c->maxstack_prev, 0);
1790
            }
1791
            visible = !c->pending_map && VISIBLEON(c, m) &&
1792
                      (!max || c->is_fullscreen || clients_related(c, focus));
1793
            wlr_scene_node_set_enabled(&c->scene->node, visible);
1794
            client_set_suspended(c, !visible);
1795
        }
1796
    }
1797
    wlr_scene_node_set_enabled(&m->fullscreen_bg->node, focus && focus->is_fullscreen);
1798
1799
    /* In a floating layout, keep tiled and floating windows in one layer so
1800
     * their normal stacking order is preserved. */
1801
    wl_list_for_each(c, &clients, link) {
1802
        if (c->mon != m || !m->ws || c->scene->node.parent == layers[LAYER_FULLSCREEN])
1803
            continue;
1804
1805
        wlr_scene_node_reparent(
1806
            &c->scene->node,
1807
            (!m->ws->lt->arrange && c->is_floating)  ? layers[LAYER_TILE]
1808
            : (m->ws->lt->arrange && c->is_floating) ? layers[LAYER_FLOAT]
1809
                                                     : c->scene->node.parent
1810
        );
1811
    }
1812
    if (m->ws && m->ws->lt->arrange)
1813
        m->ws->lt->arrange(m);
1814
    /* Recheck pointer focus after windows move. Avoid repeating this if the
1815
     * focus change itself rearranges the max layout. */
1816
    if (!arranging_pointer_focus) {
1817
        arranging_pointer_focus = true;
1818
        motion_notify(0, nullptr, 0, 0, 0, 0, 1);
1819
        arranging_pointer_focus = false;
1820
    }
1821
    check_idle_inhibitor(nullptr);
1822
}
1823
1824
/* Position the surfaces in one desktop layer and reserve any requested space. */
1825
void arrange_layer(monitor_t *m, struct wl_list *list, struct wlr_box *usable_area, int exclusive) {
1826
    layer_surface_t *l;
1827
    struct wlr_box   full_area = m->m;
1828
1829
    wl_list_for_each(l, list, link) {
1830
        struct wlr_layer_surface_v1 *layer_surface = l->layer_surface;
1831
1832
        if (!layer_surface->initialized)
1833
            continue;
1834
1835
        if (exclusive != (layer_surface->current.exclusive_zone > 0))
1836
            continue;
1837
1838
        wlr_scene_layer_surface_v1_configure(l->scene_layer, &full_area, usable_area);
1839
        wlr_scene_node_set_position(&l->popups->node, l->scene->node.x, l->scene->node.y);
1840
1841
        if (l->dim) {
1842
            wlr_scene_node_set_position(&l->dim->node, full_area.x, full_area.y);
1843
            wlr_scene_rect_set_size(l->dim, full_area.width, full_area.height);
1844
        }
1845
    }
1846
}
1847
1848
/* Arrange desktop layers and give keyboard focus to the highest eligible surface. */
1849
void arrange_layers(monitor_t *m) {
1850
    int              i;
1851
    struct wlr_box   usable_area = m->m;
1852
    layer_surface_t *l;
1853
    uint32_t         layers_above_shell[] = {
1854
        ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
1855
        ZWLR_LAYER_SHELL_V1_LAYER_TOP,
1856
    };
1857
1858
    if (!m->wlr_output->enabled)
1859
        return;
1860
1861
    /* Reserve space for panels from the top layer down. */
1862
    for (i = 3; i >= 0; i--)
1863
        arrange_layer(m, &m->layers[i], &usable_area, 1);
1864
1865
    if (!wlr_box_equal(&usable_area, &m->w)) {
1866
        m->w = usable_area;
1867
        arrange(m);
1868
    }
1869
    /* Place overlays that do not reserve space, from the top layer down. */
1870
    for (i = 3; i >= 0; i--)
1871
        arrange_layer(m, &m->layers[i], &usable_area, 0);
1872
1873
    /* Give keyboard focus to the highest layer that requests it. */
1874
    for (i = 0; i < (int)LENGTH(layers_above_shell); i++) {
1875
        wl_list_for_each_reverse(l, &m->layers[layers_above_shell[i]], link) {
1876
            if (locked ||
1877
                l->layer_surface->current.keyboard_interactive !=
1878
                    ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE ||
1879
                !l->mapped)
1880
                continue;
1881
            /* Deactivate the focused client. */
1882
            focus_client(nullptr, 0);
1883
            exclusive_focus = l;
1884
            client_notify_enter(l->layer_surface->surface, wlr_seat_get_keyboard(seat));
1885
            return;
1886
        }
1887
    }
1888
}
1889
1890
/* Expand $NAME variables into caller-owned stack storage. */
1891
void expand_argv(const char *const *argv, char **expanded, char *storage) {
1892
    size_t i, remaining = MAX_COMMAND_SIZE, size;
1893
1894
    for (i = 0; argv[i]; i++) {
1895
        if (i + 1 >= MAX_COMMAND_ARGS)
1896
            die("command has too many arguments");
1897
        expanded[i] = storage;
1898
        if (!(size = env_expand(storage, remaining, argv[i])))
1899
            die("expanded command exceeds %d bytes", MAX_COMMAND_SIZE);
1900
        storage   += size;
1901
        remaining -= size;
1902
    }
1903
    expanded[i] = nullptr;
1904
}
1905
1906
/* Start every command in the configured autostart list. */
1907
void autostart_exec(void) {
1908
    /* Run each configured startup command. A nullptr separates commands, and
1909
     * a second nullptr ends the list. */
1910
    const char *const *p;
1911
    size_t             i = 0;
1912
1913
    for (p = autostart; *p && autostart_len < MAX_AUTOSTART; autostart_len++, p++)
1914
1915
        while (*++p)
1916
            ;
1917
1918
    if (*p)
1919
        fprintf(stderr, "swm: autostart limit reached (%d)\n", MAX_AUTOSTART);
1920
1921
    for (p = autostart; *p && i < autostart_len; i++, p++) {
1922
        if ((autostart_pids[i] = fork()) == 0) {
1923
            char *argv[MAX_COMMAND_ARGS], storage[MAX_COMMAND_SIZE];
1924
1925
            expand_argv(p, argv, storage);
1926
1927
            prepare_child();
1928
            setsid();
1929
            execute_command(argv, "autostart");
1930
        }
1931
        while (*++p)
1932
            ;
1933
    }
1934
}
1935
1936
/* Forward a scroll event to the application under the pointer. */
1937
void axis_notify(struct wl_listener *listener, void *data) {
1938
    /* A mouse wheel or touchpad produced a scrolling event. */
1939
    struct wlr_pointer_axis_event *event = data;
1940
1941
    wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
1942
    /* TODO: Allow scroll events to trigger compositor bindings. */
1943
    /* Forward the scroll event to the application under the pointer. */
1944
    wlr_seat_pointer_notify_axis(
1945
        seat,
1946
        event->time_msec,
1947
        event->orientation,
1948
        event->delta,
1949
        event->delta_discrete,
1950
        event->source,
1951
        event->relative_direction
1952
    );
1953
}
1954
1955
/* Focus, move, or resize a window in response to a pointer button. */
1956
void button_press(struct wl_listener *listener, void *data) {
1957
    struct wlr_pointer_button_event *event = data;
1958
    struct wlr_keyboard             *keyboard;
1959
    uint32_t                         mods;
1960
    client_t                        *c;
1961
    layer_surface_t                 *l;
1962
    const button_t                  *b;
1963
1964
    wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
1965
1966
    switch (event->state) {
1967
    case WL_POINTER_BUTTON_STATE_PRESSED:
1968
        cursor_mode = CURSOR_PRESSED;
1969
        selmon      = point_to_monitor(cursor->x, cursor->y);
1970
1971
        if (locked)
1972
            break;
1973
1974
        /* Focus a window when a button is pressed over it. */
1975
        point_to_node(cursor->x, cursor->y, nullptr, &c, &l, nullptr, nullptr);
1976
1977
        if (c && (!client_is_unmanaged(c) || client_wants_focus(c)))
1978
            focus_client(c, 1);
1979
        else if (
1980
            l && l->layer_surface->current.keyboard_interactive ==
1981
                     ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND
1982
        ) {
1983
            focus_client(nullptr, 0);
1984
            client_notify_enter(l->layer_surface->surface, wlr_seat_get_keyboard(seat));
1985
        }
1986
1987
        keyboard = wlr_seat_get_keyboard(seat);
1988
        mods     = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
1989
1990
        if (event->button == BTN_LEFT && CLEANMASK(mods) == CLEANMASK(MOD) &&
1991
            tiled_resize_boundary(
1992
                selmon, (int)round(cursor->x), (int)round(cursor->y), &grab_horizontal
1993
            )) {
1994
            grabws      = selmon->ws;
1995
            cursor_mode = CURSOR_TILE_RESIZE;
1996
            tiled_resize_cursor(true, grab_horizontal);
1997
            return;
1998
        }
1999
        tiled_resize_cursor(false, false);
2000
2001
        for (b = buttons; b < END(buttons); b++) {
2002
            if (CLEANMASK(mods) == CLEANMASK(b->mod) && event->button == b->button && b->func) {
2003
                b->func(&b->arg);
2004
                return;
2005
            }
2006
        }
2007
        break;
2008
    case WL_POINTER_BUTTON_STATE_RELEASED:
2009
        /* Releasing any button ends an interactive move or resize. */
2010
        /* TODO: Restore the cursor requested by the window under the pointer. */
2011
        if (!locked && cursor_mode != CURSOR_NORMAL && cursor_mode != CURSOR_PRESSED) {
2012
            if (cursor_mode == CURSOR_TILE_RESIZE) {
2013
                cursor_mode = CURSOR_NORMAL;
2014
                grabws      = nullptr;
2015
                tiled_resize_update();
2016
                return;
2017
            }
2018
            if (cursor_mode == CURSOR_RESIZE)
2019
                client_set_resizing(grabc, 0);
2020
            wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
2021
            cursor_mode = CURSOR_NORMAL;
2022
            /* Move the window to the workspace on its new display. The
2023
             * cursor can sit outside every output (on a layout edge or in a
2024
             * gap); keep the window on its current display in that case. */
2025
            selmon = point_to_monitor(cursor->x, cursor->y);
2026
            set_monitor(grabc, selmon ? selmon : grabc->mon, 0);
2027
            remember_client(grabc);
2028
            grabc = nullptr;
2029
            return;
2030
        }
2031
        cursor_mode = CURSOR_NORMAL;
2032
        tiled_resize_update();
2033
        break;
2034
    }
2035
    /* Forward buttons that were not handled here to the application under the pointer. */
2036
    wlr_seat_pointer_notify_button(seat, event->time_msec, event->button, event->state);
2037
}
2038
2039
/* Switch to the requested virtual terminal. */
2040
void change_vt(const arg_t *arg) {
2041
    wlr_session_change_vt(session, arg->u);
2042
}
2043
2044
/* Prevent idling while a visible application requests it. */
2045
void check_idle_inhibitor(struct wlr_surface *exclude) {
2046
    int                           inhibited = 0, unused_lx, unused_ly;
2047
    struct wlr_idle_inhibitor_v1 *inhibitor;
2048
2049
    wl_list_for_each(inhibitor, &idle_inhibit_mgr->inhibitors, link) {
2050
        struct wlr_surface    *surface = wlr_surface_get_root_surface(inhibitor->surface);
2051
        struct wlr_scene_tree *tree    = surface->data;
2052
2053
        if (exclude != surface && tree &&
2054
            wlr_scene_node_coords(&tree->node, &unused_lx, &unused_ly)) {
2055
            inhibited = 1;
2056
            break;
2057
        }
2058
    }
2059
    wlr_idle_notifier_v1_set_inhibited(idle_notifier, inhibited);
2060
}
2061
2062
/* Release compositor resources in dependency order before exiting. */
2063
void cleanup(void) {
2064
    size_t i;
2065
2066
    cleanup_listeners();
2067
    /* Destroy windows before the protocol objects that listen to their
2068
     * surfaces, so popup cleanup cannot call an expired listener. */
2069
    wl_display_destroy_clients(dpy);
2070
2071
    if (xwayland) {
2072
        wlr_xwayland_destroy(xwayland);
2073
        xwayland = nullptr;
2074
    }
2075
    if (child_pid > 0) {
2076
        kill(-child_pid, SIGTERM);
2077
        waitpid(child_pid, nullptr, WNOHANG);
2078
    }
2079
    for (i = 0; i < autostart_len; i++) {
2080
        if (autostart_pids[i] > 0) {
2081
            kill(-autostart_pids[i], SIGTERM);
2082
            waitpid(autostart_pids[i], nullptr, WNOHANG);
2083
        }
2084
    }
2085
    for (i = 0; i < LENGTH(signal_sources); i++)
2086
        wl_event_source_remove(signal_sources[i]);
2087
    wlr_xcursor_manager_destroy(cursor_mgr);
2088
2089
    destroy_keyboard_group(&kb_group->destroy, nullptr);
2090
2091
    /* Destroy this explicitly because wlroots may otherwise retain a pointer
2092
     * to the seat after the seat is gone. */
2093
    wlr_backend_destroy(backend);
2094
2095
    wl_display_destroy(dpy);
2096
    /* Destroy the scene after the display, once every display is gone. */
2097
    wlr_scene_node_destroy(&scene->tree.node);
2098
}
2099
2100
/* Remove a disconnected display and release everything attached to it. */
2101
void cleanup_monitor(struct wl_listener *listener, void *data) {
2102
    monitor_t       *m = wl_container_of(listener, m, destroy);
2103
    layer_surface_t *l, *tmp;
2104
    size_t           i;
2105
2106
    /* The layer lists belong to the display and disappear with it. */
2107
    for (i = 0; i < LENGTH(m->layers); i++) {
2108
        wl_list_for_each_safe(l, tmp, &m->layers[i], link)
2109
            wlr_layer_surface_v1_destroy(l->layer_surface);
2110
    }
2111
    wl_list_remove(&m->destroy.link);
2112
    wl_list_remove(&m->frame.link);
2113
    wl_list_remove(&m->link);
2114
    wl_list_remove(&m->request_state.link);
2115
    wl_list_remove(&m->output_bind.link);
2116
2117
    if (m->lock_surface)
2118
        destroy_lock_surface(&m->destroy_lock_surface, nullptr);
2119
    m->wlr_output->data = nullptr;
2120
    wlr_output_layout_remove(output_layout, m->wlr_output);
2121
    wlr_scene_output_destroy(m->scene_output);
2122
2123
    close_monitor(m);
2124
    wlr_scene_node_destroy(&m->fullscreen_bg->node);
2125
    pool_release(&monitor_pool, m);
2126
}
2127
2128
/* Attach a listener that stays connected for the compositor's lifetime. */
2129
void listen_global(struct wl_signal *signal, struct wl_listener *listener) {
2130
    if (global_listener_count >= LENGTH(global_listeners))
2131
        die("too many global listeners");
2132
    global_listeners[global_listener_count++] = listener;
2133
    wl_signal_add(signal, listener);
2134
}
2135
2136
/* Detach global event listeners before their event sources are destroyed. */
2137
void cleanup_listeners(void) {
2138
    while (global_listener_count)
2139
        wl_list_remove(&global_listeners[--global_listener_count]->link);
2140
}
2141
2142
/* Hide a display's workspace and move its windows to a valid display. */
2143
void close_monitor(monitor_t *m) {
2144
    /* If the selected display closes, select another one. Its workspace
2145
     * becomes hidden, but its windows remain there. */
2146
    client_t      *c;
2147
    monitor_t     *candidate;
2148
    workspace_t   *ws;
2149
    struct wlr_box geom;
2150
2151
    if (m == selmon || (selmon && !selmon->wlr_output->enabled)) {
2152
        selmon = nullptr;
2153
        wl_list_for_each(candidate, &mons, link) {
2154
            if (candidate != m && candidate->wlr_output->enabled) {
2155
                selmon = candidate;
2156
                break;
2157
            }
2158
        }
2159
    }
2160
    if (m->ws)
2161
        m->ws->mon = nullptr;
2162
    m->ws = m->previous_workspace = nullptr;
2163
2164
    for (ws = workspaces; ws < END(workspaces); ws++) {
2165
        if (ws->mon == m)
2166
            ws->mon = nullptr;
2167
    }
2168
    /* Keep each window tied to a valid display; its workspace decides visibility. */
2169
    wl_list_for_each(c, &clients, link) {
2170
        if (c->mon == m) {
2171
            geom = c->geom;
2172
2173
            if (c->is_floating && selmon) {
2174
                geom.x = selmon->m.x + c->geom.x - m->m.x;
2175
                geom.y = selmon->m.y + c->geom.y - m->m.y;
2176
            }
2177
            c->mon = selmon;
2178
2179
            if (selmon)
2180
                resize(c, c->is_fullscreen ? selmon->m : geom, 0);
2181
        }
2182
        if (c->ftl_monitor == m) {
2183
            if (c->ftl && !m->wlr_output->enabled)
2184
                wlr_foreign_toplevel_handle_v1_output_leave(c->ftl, m->wlr_output);
2185
            c->ftl_monitor = nullptr;
2186
        }
2187
    }
2188
    focus_client(focus_top(selmon), 1);
2189
    print_status();
2190
}
2191
2192
/* Apply a panel or background surface's newly committed state. */
2193
void layer_surface_commit_notify(struct wl_listener *listener, void *data) {
2194
    layer_surface_t                  *l             = wl_container_of(listener, l, surface_commit);
2195
    struct wlr_layer_surface_v1      *layer_surface = l->layer_surface;
2196
    struct wlr_scene_tree            *scene_layer = layers[layermap[layer_surface->current.layer]];
2197
    struct wlr_layer_surface_v1_state old_state;
2198
2199
    if (l->layer_surface->initial_commit) {
2200
        client_set_scale(layer_surface->surface, l->mon->wlr_output->scale);
2201
2202
        /* Arrange the surface using the state it is about to commit. */
2203
        old_state                 = l->layer_surface->current;
2204
        l->layer_surface->current = l->layer_surface->pending;
2205
        arrange_layers(l->mon);
2206
        l->layer_surface->current = old_state;
2207
        return;
2208
    }
2209
    if (layer_surface->current.committed == 0 && l->mapped == layer_surface->surface->mapped)
2210
        return;
2211
    l->mapped = layer_surface->surface->mapped;
2212
2213
    if (l->dim)
2214
        wlr_scene_node_set_enabled(&l->dim->node, l->mapped);
2215
2216
    if (scene_layer != l->scene->node.parent) {
2217
        wlr_scene_node_reparent(&l->scene->node, scene_layer);
2218
2219
        if (l->dim) {
2220
            wlr_scene_node_reparent(&l->dim->node, scene_layer);
2221
            wlr_scene_node_place_below(&l->dim->node, &l->scene->node);
2222
        }
2223
        wl_list_remove(&l->link);
2224
        wl_list_insert(&l->mon->layers[layer_surface->current.layer], &l->link);
2225
        wlr_scene_node_reparent(
2226
            &l->popups->node,
2227
            (layer_surface->current.layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP ? layers[LAYER_TOP]
2228
                                                                          : scene_layer)
2229
        );
2230
    }
2231
    arrange_layers(l->mon);
2232
}
2233
2234
/* Apply a Wayland window's initial state or confirmed resize. */
2235
void commit_notify(struct wl_listener *listener, void *data) {
2236
    client_t      *c = wl_container_of(listener, c, commit);
2237
    struct wlr_box geom;
2238
2239
    if (c->surface.xdg->initial_commit) {
2240
        /* Pick a display early so the application receives the right scale.
2241
         * A title-based rule may move it once its title is available. */
2242
        apply_rules(c, nullptr);
2243
2244
        if (c->mon) {
2245
            client_set_scale(client_surface(c), c->mon->wlr_output->scale);
2246
        }
2247
        set_monitor(c, nullptr, 0); /* Reapply matching rules when the window maps. */
2248
2249
        wlr_xdg_toplevel_set_wm_capabilities(
2250
            c->surface.xdg->toplevel, WLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN
2251
        );
2252
2253
        if (c->decoration)
2254
            request_decoration_mode(&c->set_decoration_mode, c->decoration);
2255
        wlr_xdg_toplevel_set_size(c->surface.xdg->toplevel, 0, 0);
2256
        return;
2257
    }
2258
    /* Some applications, especially terminals, round requested sizes. Once
2259
     * they confirm the resize, use the size they actually chose. */
2260
    if (!c->is_fullscreen && c->resize) {
2261
        client_get_geometry(c, &geom);
2262
        swm_box_reconcile_commit(
2263
            (struct swm_box *)&c->geom, geom.width, geom.height, c->bw, c->resize_edges
2264
        );
2265
    }
2266
    if (c->resize && c->mon) {
2267
        uint32_t resize_serial = c->resize;
2268
        int      latest        = resize_serial <= c->surface.xdg->current.configure_serial;
2269
2270
        apply_bounds(c, &c->mon->w);
2271
        resize_apply(c);
2272
2273
        if (latest)
2274
            c->resize = 0;
2275
        else
2276
            c->geom = c->pending_geom;
2277
    } else if (c->resize) {
2278
        /* The client lost its display with a resize in flight; settle the
2279
         * resize once a display is available again. */
2280
    } else {
2281
        resize(c, c->geom, c->is_floating && !c->is_fullscreen);
2282
    }
2283
    if (!c->resize)
2284
        c->resize_edges = WLR_EDGE_NONE;
2285
}
2286
2287
/* Create and constrain a popup after its initial state is committed. */
2288
void popup_commit(struct wl_listener *listener, void *data) {
2289
    popup_t              *p     = wl_container_of(listener, p, commit);
2290
    struct wlr_xdg_popup *popup = p->popup;
2291
    layer_surface_t      *l     = nullptr;
2292
    client_t             *c     = nullptr;
2293
    struct wlr_box        box;
2294
    int                   type = -1;
2295
2296
    if (!popup->base->initial_commit)
2297
        return;
2298
2299
    type = toplevel_from_wlr_surface(popup->base->surface, &c, &l);
2300
2301
    if ((type == LAYER_SHELL && (!l || !l->mon)) || (type != LAYER_SHELL && (!c || !c->mon)) ||
2302
        !popup->parent || !popup->parent->data) {
2303
        wlr_xdg_popup_destroy(popup);
2304
        return;
2305
    }
2306
    popup->base->surface->data = wlr_scene_xdg_surface_create(popup->parent->data, popup->base);
2307
2308
    if (!popup->base->surface->data) {
2309
        wlr_xdg_popup_destroy(popup);
2310
        return;
2311
    }
2312
    box    = type == LAYER_SHELL ? l->mon->m : c->mon->w;
2313
    box.x -= (type == LAYER_SHELL ? l->scene->node.x : c->geom.x);
2314
    box.y -= (type == LAYER_SHELL ? l->scene->node.y : c->geom.y);
2315
    wlr_xdg_popup_unconstrain_from_box(popup, &box);
2316
}
2317
2318
/* Unpublish a destroyed popup's scene tree and release its state. */
2319
void popup_destroy(struct wl_listener *listener, void *data) {
2320
    popup_t *p = wl_container_of(listener, p, destroy);
2321
2322
    /* The scene tree dies with the popup, but its wl_surface can outlive both. */
2323
    p->popup->base->surface->data = nullptr;
2324
    wl_list_remove(&p->commit.link);
2325
    wl_list_remove(&p->destroy.link);
2326
    pool_release(&popup_pool, p);
2327
}
2328
2329
/* Attach an event listener from the fixed-capacity listener pool. */
2330
void listen_static(struct wl_signal *signal, wl_notify_func_t notify) {
2331
    static_listener_t *slot = pool_take(&static_listener_pool);
2332
2333
    if (!slot)
2334
        return;
2335
    slot->listener.notify = notify;
2336
    wl_signal_add(signal, &slot->listener);
2337
}
2338
2339
/* Release a listener allocated from the static pool. */
2340
void listener_release(struct wl_listener *listener) {
2341
    static_listener_t *slot = wl_container_of(listener, slot, listener);
2342
    pool_release(&static_listener_pool, slot);
2343
}
2344
2345
/* Track a window's requested border-decoration mode. */
2346
void create_decoration(struct wl_listener *listener, void *data) {
2347
    struct wlr_xdg_toplevel_decoration_v1 *deco = data;
2348
    client_t                              *c    = deco->toplevel->base->data;
2349
2350
    if (!c)
2351
        return;
2352
    c->decoration = deco;
2353
2354
    LISTEN(&deco->events.request_mode, &c->set_decoration_mode, request_decoration_mode);
2355
    LISTEN(&deco->events.destroy, &c->destroy_decoration, destroy_decoration);
2356
2357
    request_decoration_mode(&c->set_decoration_mode, deco);
2358
}
2359
2360
/* Track a new request to keep the session awake. */
2361
void create_idle_inhibitor(struct wl_listener *listener, void *data) {
2362
    struct wlr_idle_inhibitor_v1 *idle_inhibitor = data;
2363
2364
    listen_static(&idle_inhibitor->events.destroy, destroy_idle_inhibitor);
2365
2366
    check_idle_inhibitor(nullptr);
2367
}
2368
2369
/* Add a physical keyboard to the shared keyboard group. */
2370
void create_keyboard(struct wlr_keyboard *keyboard) {
2371
    /* Use the keyboard group's key mapping. */
2372
    wlr_keyboard_set_keymap(keyboard, kb_group->wlr_group->keyboard.keymap);
2373
2374
    /* Add the keyboard to the shared group. */
2375
    wlr_keyboard_group_add_keyboard(kb_group->wlr_group, keyboard);
2376
}
2377
2378
/* Create a keyboard group with the configured key map and repeat settings. */
2379
keyboard_group_t *create_keyboard_group(bool is_virtual) {
2380
    keyboard_group_t   *group = pool_take(&keyboard_group_pool);
2381
    struct xkb_context *context;
2382
    struct xkb_keymap  *keymap;
2383
2384
    if (!group)
2385
        return nullptr;
2386
    group->wlr_group = wlr_keyboard_group_create();
2387
2388
    if (!group->wlr_group) {
2389
        pool_release(&keyboard_group_pool, group);
2390
        return nullptr;
2391
    }
2392
    group->wlr_group->data = group;
2393
    group->is_virtual      = is_virtual;
2394
2395
    /* Prepare an XKB keymap and assign it to the keyboard group. */
2396
    context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
2397
2398
    if (!(keymap = xkb_keymap_new_from_names(context, &xkb_rules, XKB_KEYMAP_COMPILE_NO_FLAGS)))
2399
        die("failed to compile keymap");
2400
2401
    wlr_keyboard_set_keymap(&group->wlr_group->keyboard, keymap);
2402
    xkb_keymap_unref(keymap);
2403
    xkb_context_unref(context);
2404
2405
    wlr_keyboard_set_repeat_info(&group->wlr_group->keyboard, repeat_rate, repeat_delay);
2406
2407
    /* Listen for key and modifier changes. */
2408
    LISTEN(&group->wlr_group->keyboard.events.key, &group->key, key_press);
2409
    LISTEN(&group->wlr_group->keyboard.events.modifiers, &group->modifiers, key_press_modifiers);
2410
2411
    group->key_repeat_source = wl_event_loop_add_timer(event_loop, key_repeat, group);
2412
2413
    /* Wayland exposes one active keyboard per seat, so combine physical
2414
     * keyboards into one group. */
2415
    if (!is_virtual)
2416
        wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard);
2417
    return group;
2418
}
2419
2420
/* Add a new panel, background, or overlay surface to its display. */
2421
void create_layer_surface(struct wl_listener *listener, void *data) {
2422
    struct wlr_layer_surface_v1 *layer_surface = data;
2423
    layer_surface_t             *l;
2424
    const layer_rule_t          *r;
2425
    monitor_t                   *m;
2426
    struct wlr_surface          *surface     = layer_surface->surface;
2427
    struct wlr_scene_tree       *scene_layer = layers[layermap[layer_surface->pending.layer]];
2428
2429
    if (!layer_surface->output &&
2430
        !(layer_surface->output = selmon ? selmon->wlr_output : nullptr)) {
2431
        wlr_layer_surface_v1_destroy(layer_surface);
2432
        return;
2433
    }
2434
    if (!(m = layer_surface->output->data)) {
2435
        wlr_layer_surface_v1_destroy(layer_surface);
2436
        return;
2437
    }
2438
    if (!(l = pool_take(&layer_surface_pool))) {
2439
        wlr_layer_surface_v1_destroy(layer_surface);
2440
        return;
2441
    }
2442
    l->type          = LAYER_SHELL;
2443
    l->layer_surface = layer_surface;
2444
    l->mon           = m;
2445
    l->scene_layer   = wlr_scene_layer_surface_v1_create(scene_layer, layer_surface);
2446
2447
    if (!l->scene_layer) {
2448
        pool_release(&layer_surface_pool, l);
2449
        wlr_layer_surface_v1_destroy(layer_surface);
2450
        return;
2451
    }
2452
    l->scene = l->scene_layer->tree;
2453
2454
    for (r = layerrules; r < END(layerrules); r++) {
2455
        if (strcmp(layer_surface->namespace, r->namespace))
2456
            continue;
2457
        l->dim = wlr_scene_rect_create(scene_layer, 0, 0, r->dim);
2458
2459
        if (!l->dim) {
2460
            wlr_scene_node_destroy(&l->scene->node);
2461
            pool_release(&layer_surface_pool, l);
2462
            wlr_layer_surface_v1_destroy(layer_surface);
2463
            return;
2464
        }
2465
        wlr_scene_node_place_below(&l->dim->node, &l->scene->node);
2466
        wlr_scene_node_set_enabled(&l->dim->node, 0);
2467
        break;
2468
    }
2469
    l->popups = wlr_scene_tree_create(
2470
        layer_surface->current.layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP ? layers[LAYER_TOP]
2471
                                                                     : scene_layer
2472
    );
2473
2474
    if (!l->popups) {
2475
        if (l->dim)
2476
            wlr_scene_node_destroy(&l->dim->node);
2477
        wlr_scene_node_destroy(&l->scene->node);
2478
        pool_release(&layer_surface_pool, l);
2479
        wlr_layer_surface_v1_destroy(layer_surface);
2480
        return;
2481
    }
2482
    layer_surface->data = l;
2483
    surface->data       = l->popups;
2484
    l->scene->node.data = l->popups->node.data = l;
2485
    LISTEN(&surface->events.commit, &l->surface_commit, layer_surface_commit_notify);
2486
    LISTEN(&surface->events.unmap, &l->unmap, layer_surface_unmap_notify);
2487
    LISTEN(&layer_surface->events.destroy, &l->destroy, layer_surface_destroy_notify);
2488
2489
    wl_list_insert(&l->mon->layers[layer_surface->pending.layer], &l->link);
2490
    wlr_surface_send_enter(surface, layer_surface->output);
2491
}
2492
2493
/* Cover a display with a new session-lock surface. */
2494
void create_lock_surface(struct wl_listener *listener, void *data) {
2495
    session_lock_t                     *lock         = wl_container_of(listener, lock, new_surface);
2496
    struct wlr_session_lock_surface_v1 *lock_surface = data;
2497
    monitor_t             *m = lock_surface->output ? lock_surface->output->data : nullptr;
2498
    struct wlr_scene_tree *scene_tree;
2499
2500
    if (!m) {
2501
        wlr_session_lock_v1_destroy(lock->lock);
2502
        return;
2503
    }
2504
    scene_tree = lock_surface->surface->data =
2505
        wlr_scene_subsurface_tree_create(lock->scene, lock_surface->surface);
2506
2507
    if (!scene_tree) {
2508
        wlr_session_lock_v1_destroy(lock->lock);
2509
        return;
2510
    }
2511
    m->lock_surface = lock_surface;
2512
2513
    wlr_scene_node_set_position(&scene_tree->node, m->m.x, m->m.y);
2514
    wlr_session_lock_surface_v1_configure(lock_surface, m->m.width, m->m.height);
2515
2516
    LISTEN(&lock_surface->events.destroy, &m->destroy_lock_surface, destroy_lock_surface);
2517
2518
    if (m == selmon)
2519
        client_notify_enter(lock_surface->surface, wlr_seat_get_keyboard(seat));
2520
}
2521
2522
/* Configure and publish a newly connected display. */
2523
void create_monitor(struct wl_listener *listener, void *data) {
2524
    /* The backend found a new display. */
2525
    struct wlr_output      *wlr_output = data;
2526
    const monitor_rule_t   *r;
2527
    size_t                  i;
2528
    struct wlr_output_state state;
2529
    monitor_t              *m;
2530
2531
    if (!wlr_output_init_render(wlr_output, alloc, drw))
2532
        return;
2533
2534
    if (!(m = pool_take(&monitor_pool))) {
2535
        fprintf(stderr, "swm: ignoring output %s: monitor limit reached\n", wlr_output->name);
2536
        return;
2537
    }
2538
    wlr_output->data = m;
2539
    m->wlr_output    = wlr_output;
2540
2541
    for (i = 0; i < LENGTH(m->layers); i++)
2542
        wl_list_init(&m->layers[i]);
2543
2544
    wlr_output_state_init(&state);
2545
    /* Initialize the display from its matching configuration rule. */
2546
    for (r = monrules; r < END(monrules); r++) {
2547
        if (!r->name || strstr(wlr_output->name, r->name)) {
2548
            m->m.x = r->x;
2549
            m->m.y = r->y;
2550
            wlr_output_state_set_scale(&state, r->scale);
2551
            wlr_output_state_set_transform(&state, r->rr);
2552
            break;
2553
        }
2554
    }
2555
    /* Show the lowest-numbered hidden workspace on this output; client
2556
     * monitor pointers are synced in update_monitors() once the output has its
2557
     * final geometry. */
2558
    m->ws = m->previous_workspace = free_workspace();
2559
2560
    if (m->ws)
2561
        m->ws->mon = m;
2562
2563
    /* The mode is a tuple of (width, height, refresh rate), and each
2564
     * monitor supports only a specific set of modes. We just pick the
2565
     * monitor's preferred mode; a more sophisticated compositor would let
2566
     * the user configure it. */
2567
    wlr_output_state_set_mode(&state, wlr_output_preferred_mode(wlr_output));
2568
2569
    /* Listen for display events. */
2570
    LISTEN(&wlr_output->events.frame, &m->frame, render_monitor);
2571
    LISTEN(&wlr_output->events.destroy, &m->destroy, cleanup_monitor);
2572
    LISTEN(&wlr_output->events.request_state, &m->request_state, request_monitor_state);
2573
    LISTEN(&wlr_output->events.bind, &m->output_bind, workspace_output_bind);
2574
2575
    wlr_output_state_set_enabled(&state, 1);
2576
    wlr_output_commit_state(wlr_output, &state);
2577
    wlr_output_state_finish(&state);
2578
2579
    wl_list_insert(&mons, &m->link);
2580
    print_status();
2581
2582
    /* Hide unrelated content behind a transparent fullscreen window, as the
2583
     * XDG shell protocol requires. */
2584
    /* update_monitors() will set the final size and position. */
2585
    m->fullscreen_bg = wlr_scene_rect_create(layers[LAYER_FULLSCREEN], 0, 0, fullscreen_bg);
2586
2587
    if (!m->fullscreen_bg)
2588
        die("failed to create monitor fullscreen background");
2589
    wlr_scene_node_set_enabled(&m->fullscreen_bg->node, 0);
2590
2591
    /* Add the display at its configured position and publish its properties
2592
     * to applications. */
2593
    m->scene_output = wlr_scene_output_create(scene, wlr_output);
2594
2595
    if (!m->scene_output)
2596
        die("failed to create scene output");
2597
2598
    if (m->m.x == -1 && m->m.y == -1)
2599
        wlr_output_layout_add_auto(output_layout, wlr_output);
2600
    else
2601
        wlr_output_layout_add(output_layout, wlr_output, m->m.x, m->m.y);
2602
}
2603
2604
/* Allocate state and listeners for a new Wayland window. */
2605
void create_notify(struct wl_listener *listener, void *data) {
2606
    /* This event is raised when a client creates a new toplevel (application
2607
     * window). */
2608
    struct wlr_xdg_toplevel *toplevel = data;
2609
    client_t                *c        = nullptr;
2610
2611
    /* Allocate window state for this surface. */
2612
    if (!(c = pool_take(&client_pool))) {
2613
        wlr_xdg_toplevel_send_close(toplevel);
2614
        return;
2615
    }
2616
    toplevel->base->data = c;
2617
    c->surface.xdg       = toplevel->base;
2618
    c->bw                = borderwidth;
2619
2620
    LISTEN(&toplevel->base->surface->events.commit, &c->commit, commit_notify);
2621
    LISTEN(&toplevel->base->surface->events.map, &c->map, map_notify);
2622
    LISTEN(&toplevel->base->surface->events.unmap, &c->unmap, unmap_notify);
2623
    LISTEN(&toplevel->events.destroy, &c->destroy, destroy_notify);
2624
    LISTEN(&toplevel->events.request_fullscreen, &c->fullscreen, fullscreen_notify);
2625
    LISTEN(&toplevel->events.request_maximize, &c->maximize, maximize_notify);
2626
    LISTEN(&toplevel->events.set_title, &c->set_title, update_title);
2627
    LISTEN(&toplevel->events.set_app_id, &c->set_appid, update_app_id);
2628
    LISTEN(&toplevel->events.set_parent, &c->set_parent, dialog_changed);
2629
}
2630
2631
/* Track modal state for a newly created XDG dialog. */
2632
void create_dialog(struct wl_listener *listener, void *data) {
2633
    struct wlr_xdg_dialog_v1 *dialog = data;
2634
    client_t                 *c      = dialog->xdg_toplevel->base->data;
2635
2636
    if (!c)
2637
        return;
2638
    c->dialog = dialog;
2639
    LISTEN(&dialog->events.set_modal, &c->dialog_modal, dialog_changed);
2640
    LISTEN(&dialog->events.destroy, &c->dialog_destroy, dialog_destroyed);
2641
    dialog_changed(&c->dialog_modal, nullptr);
2642
}
2643
2644
/* Re-evaluate focus and pointer targets after dialog relationship changes. */
2645
void dialog_changed(struct wl_listener *listener, void *data) {
2646
    client_t *focused = nullptr;
2647
2648
    toplevel_from_wlr_surface(seat->keyboard_state.focused_surface, &focused, nullptr);
2649
    if (focused && client_is_blocked(focused))
2650
        focus_client(focus_top(focused->mon), 1);
2651
    motion_notify(0, nullptr, 0, 0, 0, 0, 1);
2652
}
2653
2654
/* Stop tracking a destroyed dialog and immediately restore parent interaction. */
2655
void dialog_destroyed(struct wl_listener *listener, void *data) {
2656
    client_t *c = wl_container_of(listener, c, dialog_destroy);
2657
2658
    wl_list_remove(&c->dialog_modal.link);
2659
    wl_list_remove(&c->dialog_destroy.link);
2660
    c->dialog = nullptr;
2661
    dialog_changed(nullptr, nullptr);
2662
}
2663
2664
/* Attach a pointer device and apply its configured input settings. */
2665
void create_pointer(struct wlr_pointer *pointer) {
2666
    struct libinput_device *device;
2667
2668
    if (wlr_input_device_is_libinput(&pointer->base) &&
2669
        (device = wlr_libinput_get_device_handle(&pointer->base))) {
2670
2671
        if (libinput_device_config_tap_get_finger_count(device)) {
2672
            libinput_device_config_tap_set_enabled(device, tap_to_click);
2673
            libinput_device_config_tap_set_drag_enabled(device, tap_and_drag);
2674
            libinput_device_config_tap_set_drag_lock_enabled(device, drag_lock);
2675
            libinput_device_config_tap_set_button_map(device, button_map);
2676
        }
2677
        if (libinput_device_config_scroll_has_natural_scroll(device))
2678
            libinput_device_config_scroll_set_natural_scroll_enabled(device, natural_scrolling);
2679
2680
        if (libinput_device_config_dwt_is_available(device))
2681
            libinput_device_config_dwt_set_enabled(device, disable_while_typing);
2682
2683
        if (libinput_device_config_left_handed_is_available(device))
2684
            libinput_device_config_left_handed_set(device, left_handed);
2685
2686
        if (libinput_device_config_middle_emulation_is_available(device))
2687
            libinput_device_config_middle_emulation_set_enabled(device, middle_button_emulation);
2688
2689
        if (libinput_device_config_scroll_get_methods(device) != LIBINPUT_CONFIG_SCROLL_NO_SCROLL)
2690
            libinput_device_config_scroll_set_method(device, scroll_method);
2691
2692
        if (libinput_device_config_click_get_methods(device) != LIBINPUT_CONFIG_CLICK_METHOD_NONE)
2693
            libinput_device_config_click_set_method(device, click_method);
2694
2695
        if (libinput_device_config_send_events_get_modes(device))
2696
            libinput_device_config_send_events_set_mode(device, send_events_mode);
2697
2698
        if (libinput_device_config_accel_is_available(device)) {
2699
            libinput_device_config_accel_set_profile(device, accel_profile);
2700
            libinput_device_config_accel_set_speed(device, accel_speed);
2701
        }
2702
    }
2703
    wlr_cursor_attach_input_device(cursor, &pointer->base);
2704
}
2705
2706
/* Track an application's request to confine or lock the pointer. */
2707
void create_pointer_constraint(struct wl_listener *listener, void *data) {
2708
    pointer_constraint_t *pointer_constraint = pool_take(&pointer_constraint_pool);
2709
2710
    if (!pointer_constraint)
2711
        return;
2712
    pointer_constraint->constraint = data;
2713
    LISTEN(
2714
        &pointer_constraint->constraint->events.destroy,
2715
        &pointer_constraint->destroy,
2716
        destroy_pointer_constraint
2717
    );
2718
2719
    if (pointer_constraint->constraint->surface == seat->pointer_state.focused_surface)
2720
        cursor_constrain(pointer_constraint->constraint);
2721
}
2722
2723
/* Wait for a new popup's initial commit before adding it to the scene. */
2724
void create_popup(struct wl_listener *listener, void *data) {
2725
    /* This event is raised when a client (either xdg-shell or layer-shell)
2726
     * creates a new popup. */
2727
    struct wlr_xdg_popup *popup = data;
2728
    popup_t              *p     = pool_take(&popup_pool);
2729
2730
    if (!p)
2731
        return;
2732
    p->popup = popup;
2733
2734
    LISTEN(&popup->base->surface->events.commit, &p->commit, popup_commit);
2735
    LISTEN(&popup->events.destroy, &p->destroy, popup_destroy);
2736
}
2737
2738
/* Activate the pointer constraint belonging to the focused surface. */
2739
void cursor_constrain(struct wlr_pointer_constraint_v1 *constraint) {
2740
    if (active_constraint == constraint)
2741
        return;
2742
2743
    if (active_constraint)
2744
        wlr_pointer_constraint_v1_send_deactivated(active_constraint);
2745
2746
    active_constraint = constraint;
2747
2748
    if (constraint)
2749
        wlr_pointer_constraint_v1_send_activated(constraint);
2750
}
2751
2752
/* Finish a group of pointer events for the focused application. */
2753
void cursor_frame(struct wl_listener *listener, void *data) {
2754
    /* This event is forwarded by the cursor when a pointer emits a frame
2755
     * event. Frame events are sent after regular pointer events to group
2756
     * multiple events together. For instance, two axis events may happen at the
2757
     * same time, in which case a frame event won't be sent in between. */
2758
    /* Notify the client with pointer focus of the frame event. */
2759
    wlr_seat_pointer_notify_frame(seat);
2760
}
2761
2762
/* Move the pointer to an application's requested position when a constraint ends. */
2763
void cursor_warp_to_hint(void) {
2764
    client_t *c  = nullptr;
2765
    double    sx = active_constraint->current.cursor_hint.x;
2766
    double    sy = active_constraint->current.cursor_hint.y;
2767
2768
    toplevel_from_wlr_surface(active_constraint->surface, &c, nullptr);
2769
2770
    if (c && active_constraint->current.cursor_hint.enabled) {
2771
        wlr_cursor_warp(cursor, nullptr, sx + c->geom.x + c->bw, sy + c->geom.y + c->bw);
2772
        wlr_seat_pointer_warp(active_constraint->seat, sx, sy);
2773
    }
2774
}
2775
2776
/* Stop tracking a window's decoration request after it is destroyed. */
2777
void destroy_decoration(struct wl_listener *listener, void *data) {
2778
    client_t    *c     = wl_container_of(listener, c, destroy_decoration);
2779
    unsigned int oldbw = c->bw;
2780
2781
    wl_list_remove(&c->destroy_decoration.link);
2782
    wl_list_remove(&c->set_decoration_mode.link);
2783
    c->decoration = nullptr;
2784
    c->bw         = client_border_width(c);
2785
2786
    if (oldbw != c->bw && client_surface(c)->mapped && c->scene)
2787
        resize(c, c->geom, 0);
2788
}
2789
2790
/* Remove a drag icon and restore pointer focus beneath it. */
2791
void destroy_drag_icon(struct wl_listener *listener, void *data) {
2792
    /* Focus enter isn't sent during drag, so refocus the focused node. */
2793
    focus_client(focus_top(selmon), 1);
2794
    motion_notify(0, nullptr, 0, 0, 0, 0, 1);
2795
    wl_list_remove(&listener->link);
2796
    listener_release(listener);
2797
}
2798
2799
/* Remove an idle request and recalculate whether the session may sleep. */
2800
void destroy_idle_inhibitor(struct wl_listener *listener, void *data) {
2801
    /* `data` is the wlr_surface of the idle inhibitor being destroyed,
2802
     * and is still in the manager's list at this point. */
2803
    check_idle_inhibitor(wlr_surface_get_root_surface(data));
2804
    wl_list_remove(&listener->link);
2805
    listener_release(listener);
2806
}
2807
2808
/* Remove a destroyed panel, background, or overlay from its display. */
2809
void layer_surface_destroy_notify(struct wl_listener *listener, void *data) {
2810
    layer_surface_t *l = wl_container_of(listener, l, destroy);
2811
2812
    wl_list_remove(&l->link);
2813
    wl_list_remove(&l->destroy.link);
2814
    wl_list_remove(&l->unmap.link);
2815
    wl_list_remove(&l->surface_commit.link);
2816
    /* A wl_surface outlives the role object built on it. Drop the scene tree
2817
     * published in its user data before destroying that tree. */
2818
    l->layer_surface->surface->data = nullptr;
2819
    wlr_scene_node_destroy(&l->popups->node);
2820
2821
    if (l->dim)
2822
        wlr_scene_node_destroy(&l->dim->node);
2823
    pool_release(&layer_surface_pool, l);
2824
}
2825
2826
/* Handle a session locker that exits without unlocking cleanly. */
2827
void destroy_lock(session_lock_t *lock, int unlock) {
2828
    monitor_t *m;
2829
2830
    wlr_seat_keyboard_notify_clear_focus(seat);
2831
    input_method_set_focus(nullptr);
2832
    locked = !unlock;
2833
2834
    if (unlock) {
2835
        wlr_scene_node_set_enabled(&locked_bg->node, 0);
2836
        focus_client(focus_top(selmon), 0);
2837
        motion_notify(0, nullptr, 0, 0, 0, 0, 1);
2838
    }
2839
    wl_list_remove(&lock->new_surface.link);
2840
    wl_list_remove(&lock->unlock.link);
2841
    wl_list_remove(&lock->destroy.link);
2842
2843
    /* Detach the lock surfaces still attached to a display, since the scene
2844
     * destroyed below owns their trees. Keyboard focus was cleared above, so
2845
     * this only unpublishes those trees and drops their destroy listeners. */
2846
    wl_list_for_each(m, &mons, link) {
2847
        if (m->lock_surface)
2848
            destroy_lock_surface(&m->destroy_lock_surface, nullptr);
2849
    }
2850
    wlr_scene_node_destroy(&lock->scene->node);
2851
    cur_lock = nullptr;
2852
    pool_release(&session_lock_pool, lock);
2853
}
2854
2855
/* Remove a display's session-lock surface and restore its background. */
2856
void destroy_lock_surface(struct wl_listener *listener, void *data) {
2857
    monitor_t                          *m = wl_container_of(listener, m, destroy_lock_surface);
2858
    struct wlr_session_lock_surface_v1 *surface, *lock_surface = m->lock_surface;
2859
2860
    m->lock_surface = nullptr;
2861
    wl_list_remove(&m->destroy_lock_surface.link);
2862
    /* The wl_surface can outlive the lock surface built on it, while its scene
2863
     * tree cannot. */
2864
    lock_surface->surface->data = nullptr;
2865
2866
    if (lock_surface->surface != seat->keyboard_state.focused_surface)
2867
        return;
2868
2869
    if (locked && cur_lock && !wl_list_empty(&cur_lock->surfaces)) {
2870
        surface = wl_container_of(cur_lock->surfaces.next, surface, link);
2871
        client_notify_enter(surface->surface, wlr_seat_get_keyboard(seat));
2872
    } else if (!locked) {
2873
        focus_client(focus_top(selmon), 1);
2874
    } else {
2875
        wlr_seat_keyboard_clear_focus(seat);
2876
        input_method_set_focus(nullptr);
2877
    }
2878
}
2879
2880
/* Release all state associated with a destroyed Wayland window. */
2881
void destroy_notify(struct wl_listener *listener, void *data) {
2882
    /* Called when the xdg_toplevel is destroyed. */
2883
    client_t *c = wl_container_of(listener, c, destroy);
2884
    wl_list_remove(&c->destroy.link);
2885
    wl_list_remove(&c->set_title.link);
2886
    wl_list_remove(&c->set_appid.link);
2887
    wl_list_remove(&c->fullscreen.link);
2888
2889
    if (c->type != XDG_SHELL) {
2890
        wl_list_remove(&c->activate.link);
2891
        wl_list_remove(&c->associate.link);
2892
        wl_list_remove(&c->configure.link);
2893
        wl_list_remove(&c->dissociate.link);
2894
        wl_list_remove(&c->set_hints.link);
2895
    } else {
2896
        wl_list_remove(&c->commit.link);
2897
        wl_list_remove(&c->map.link);
2898
        wl_list_remove(&c->unmap.link);
2899
        wl_list_remove(&c->maximize.link);
2900
        wl_list_remove(&c->set_parent.link);
2901
2902
        if (c->dialog) {
2903
            wl_list_remove(&c->dialog_modal.link);
2904
            wl_list_remove(&c->dialog_destroy.link);
2905
            c->dialog = nullptr;
2906
        }
2907
2908
        if (c->decoration) {
2909
            wl_list_remove(&c->destroy_decoration.link);
2910
            wl_list_remove(&c->set_decoration_mode.link);
2911
            c->decoration = nullptr;
2912
        }
2913
    }
2914
    pool_release(&client_pool, c);
2915
}
2916
2917
/* Remove a pointer constraint and restore unrestricted movement. */
2918
void destroy_pointer_constraint(struct wl_listener *listener, void *data) {
2919
    pointer_constraint_t *pointer_constraint =
2920
        wl_container_of(listener, pointer_constraint, destroy);
2921
2922
    if (active_constraint == pointer_constraint->constraint) {
2923
        cursor_warp_to_hint();
2924
        active_constraint = nullptr;
2925
    }
2926
    wl_list_remove(&pointer_constraint->destroy.link);
2927
    pool_release(&pointer_constraint_pool, pointer_constraint);
2928
}
2929
2930
/* Release a completed session lock and its remaining surfaces. */
2931
void destroy_session_lock(struct wl_listener *listener, void *data) {
2932
    session_lock_t *lock = wl_container_of(listener, lock, destroy);
2933
    destroy_lock(lock, 0);
2934
}
2935
2936
/* Remove a keyboard group, its timer, and its event listeners. */
2937
void destroy_keyboard_group(struct wl_listener *listener, void *data) {
2938
    keyboard_group_t *group       = wl_container_of(listener, group, destroy);
2939
    int               was_current = wlr_seat_get_keyboard(seat) == &group->wlr_group->keyboard;
2940
    bool              is_main     = group == kb_group;
2941
2942
    if (group->is_virtual)
2943
        virtual_keyboards--;
2944
    wl_event_source_remove(group->key_repeat_source);
2945
    wl_list_remove(&group->key.link);
2946
    wl_list_remove(&group->modifiers.link);
2947
    wl_list_remove(&group->destroy.link);
2948
    wlr_keyboard_group_destroy(group->wlr_group);
2949
    pool_release(&keyboard_group_pool, group);
2950
2951
    if (is_main) {
2952
        kb_group = nullptr;
2953
        return;
2954
    }
2955
    if (was_current)
2956
        wlr_seat_set_keyboard(seat, &kb_group->wlr_group->keyboard);
2957
    wlr_seat_set_capabilities(
2958
        seat,
2959
        WL_SEAT_CAPABILITY_POINTER |
2960
            (!wl_list_empty(&kb_group->wlr_group->devices) || virtual_keyboards
2961
                 ? WL_SEAT_CAPABILITY_KEYBOARD
2962
                 : 0)
2963
    );
2964
}
2965
2966
/* Return the nearest enabled display in the requested direction. */
2967
monitor_t *direction_to_monitor(enum wlr_direction dir) {
2968
    struct wlr_output *next;
2969
2970
    if (!selmon)
2971
        return nullptr;
2972
2973
    if (!wlr_output_layout_get(output_layout, selmon->wlr_output))
2974
        return selmon;
2975
2976
    if ((next = wlr_output_layout_adjacent_output(
2977
             output_layout, dir, selmon->wlr_output, selmon->m.x, selmon->m.y
2978
         )))
2979
        return next->data;
2980
2981
    if ((next = wlr_output_layout_farthest_output(
2982
             output_layout,
2983
             dir ^ (WLR_DIRECTION_LEFT | WLR_DIRECTION_RIGHT),
2984
             selmon->wlr_output,
2985
             selmon->m.x,
2986
             selmon->m.y
2987
         )))
2988
        return next->data;
2989
2990
    return selmon;
2991
}
2992
2993
/* Transfer keyboard focus to a window and update stacking and border state. */
2994
void focus_client(client_t *c, int lift) {
2995
    struct wlr_surface *old = seat->keyboard_state.focused_surface;
2996
    int                 unused_lx, unused_ly, old_client_type;
2997
    client_t           *old_c = nullptr;
2998
    layer_surface_t    *old_l = nullptr;
2999
3000
    if (locked)
3001
        return;
3002
3003
    if (c && client_is_blocked(c))
3004
        c = focus_top(c->mon);
3005
3006
    /* Raise the window when requested. An unmapped window has no scene. */
3007
    if (c && lift && c->scene)
3008
        wlr_scene_node_raise_to_top(&c->scene->node);
3009
3010
    if (c && client_surface(c) == old)
3011
        return;
3012
3013
    if ((old_client_type = toplevel_from_wlr_surface(old, &old_c, &old_l)) == XDG_SHELL) {
3014
        struct wlr_xdg_popup *popup, *tmp;
3015
        wl_list_for_each_safe(popup, tmp, &old_c->surface.xdg->popups, link)
3016
            wlr_xdg_popup_destroy(popup);
3017
    }
3018
    /* Put the new window first in the focus history and select its display. */
3019
    if (c && !client_is_unmanaged(c)) {
3020
        wl_list_remove(&c->flink);
3021
        wl_list_insert(&fstack, &c->flink);
3022
        selmon       = c->mon;
3023
        c->is_urgent = false;
3024
3025
        if (c->mon && c->mon->ws && c->mon->ws->lt->arrange == max_stack)
3026
            arrange(c->mon);
3027
3028
        /* Don't change border color if there is an exclusive focus or we are
3029
         * handling a drag operation. */
3030
        if (!exclusive_focus && !seat->drag)
3031
            client_set_border_color(c, focuscolor);
3032
    }
3033
    /* Deactivate the old window when focus changes. */
3034
    if (old && (!c || client_surface(c) != old)) {
3035
        /* If an overlay is focused, don't focus or activate the client,
3036
         * but only update its position in fstack to render its border with
3037
         * focuscolor and focus it after the overlay is closed. */
3038
        if (old_client_type == LAYER_SHELL && old_l == exclusive_focus &&
3039
            wlr_scene_node_coords(&old_l->scene->node, &unused_lx, &unused_ly) &&
3040
            old_l->layer_surface->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) {
3041
            return;
3042
        } else if (old_c && old_c == exclusive_focus && client_wants_focus(old_c)) {
3043
            return;
3044
            /* Don't deactivate old client if the new one wants focus, as this
3045
             * avoids breaking applications such as winecfg. */
3046
        } else if (
3047
            old_c && old_c->scene && !client_is_unmanaged(old_c) && (!c || !client_wants_focus(c))
3048
        ) {
3049
            client_set_border_color(old_c, bordercolor);
3050
            client_activate_surface(old, 0);
3051
        }
3052
    }
3053
    print_status();
3054
3055
    if (!c) {
3056
        /* Clear keyboard focus when no window replaces the old one. */
3057
        wlr_seat_keyboard_notify_clear_focus(seat);
3058
        input_method_set_focus(nullptr);
3059
        return;
3060
    }
3061
    /* Update the cursor image for the focused surface. */
3062
    motion_notify(0, nullptr, 0, 0, 0, 0, 0);
3063
    /* Give keyboard focus to the window's main surface. */
3064
    client_notify_enter(client_surface(c), wlr_seat_get_keyboard(seat));
3065
    /* Mark the new window as active. */
3066
    client_activate_surface(client_surface(c), 1);
3067
}
3068
3069
/* Return the main client in a related client group. */
3070
client_t *client_main(client_t *c) {
3071
    client_t *p;
3072
    int       steps = MAX_CLIENTS;
3073
3074
    /* Windows can form a parent cycle: X11 clients may point WM_TRANSIENT_FOR
3075
     * at each other, and nothing validates the chain. Take at most one step per
3076
     * window so a cycle stops the walk instead of hanging the compositor. */
3077
    while (c && steps-- > 0 && (p = client_get_parent(c)) && p != c)
3078
        c = p;
3079
    return c;
3080
}
3081
3082
/* Return whether a mapped modal XDG child blocks this direct parent. */
3083
bool client_is_blocked(client_t *c) {
3084
    client_t *child;
3085
3086
    if (!c || client_is_x11(c))
3087
        return false;
3088
3089
    wl_list_for_each(child, &clients, link) {
3090
        if (!client_is_x11(child) && child->dialog && child->dialog->modal &&
3091
            client_surface(child)->mapped &&
3092
            child->surface.xdg->toplevel->parent == c->surface.xdg->toplevel)
3093
            return true;
3094
    }
3095
    return false;
3096
}
3097
3098
/* Return whether two clients belong to one group. */
3099
bool clients_related(client_t *a, client_t *b) {
3100
    return a && b && client_main(a) == client_main(b);
3101
}
3102
3103
/* Return the next window in list order, wrapping past the list head. */
3104
static client_t *client_step(client_t *c, int dir) {
3105
    struct wl_list *node = dir > 0 ? c->link.next : c->link.prev;
3106
3107
    if (node == &clients)
3108
        node = dir > 0 ? node->next : node->prev;
3109
    return wl_container_of(node, c, link);
3110
}
3111
3112
/* Focus the nearest related window when the current one closes. */
3113
client_t *focus_close(client_t *c) {
3114
    client_t *p;
3115
3116
    if (!c || !c->mon)
3117
        return nullptr;
3118
3119
    if ((p = client_get_parent(c)) && VISIBLEON(p, c->mon) && !client_is_blocked(p))
3120
        return p;
3121
3122
    for (p = client_step(c, -1); p != c; p = client_step(p, -1)) {
3123
        if (VISIBLEON(p, c->mon) && !client_is_blocked(p) && !clients_related(p, c))
3124
            return p;
3125
    }
3126
    return nullptr;
3127
}
3128
3129
/* Select the next enabled display and focus its top window. */
3130
void focus_monitor(const arg_t *arg) {
3131
    int i = 0, nmons = wl_list_length(&mons);
3132
3133
    if (selmon && nmons) {
3134
        do /* Skip disabled displays. */
3135
            selmon = direction_to_monitor(arg->i);
3136
        while (!selmon->wlr_output->enabled && i++ < nmons);
3137
    }
3138
    focus_client(focus_top(selmon), 1);
3139
}
3140
3141
/* Move focus forward or backward through tiled windows. */
3142
void focus_stack(const arg_t *arg) {
3143
    /* Focus the next or previous tiled window on the selected display. */
3144
    client_t *c, *sel = focus_top(selmon);
3145
3146
    if (!sel)
3147
        return;
3148
3149
    for (c = client_step(sel, arg->i); c != sel; c = client_step(c, arg->i)) {
3150
        if (VISIBLEON(c, selmon) && !client_is_blocked(c) && !client_get_parent(c)) {
3151
            bool fullscreen = sel->is_fullscreen;
3152
3153
            if (fullscreen)
3154
                set_fullscreen(sel, 0);
3155
            focus_client(c, 1);
3156
3157
            if (fullscreen)
3158
                set_fullscreen(c, 1);
3159
            return;
3160
        }
3161
    }
3162
}
3163
3164
/* Toggle focus between the master window and the previous window. */
3165
void focus_main(const arg_t *arg) {
3166
    /* focus the main (first tiled) window; if it
3167
     * already has focus, return focus to the previously focused window. */
3168
    client_t *c, *sel = focus_top(selmon);
3169
3170
    wl_list_for_each(c, &clients, link) {
3171
        if (VISIBLEON(c, selmon) && !client_is_blocked(c) && !c->is_floating) {
3172
            if (c != sel) {
3173
                focus_client(c, 1);
3174
            } else {
3175
                client_t *p;
3176
                wl_list_for_each(p, &fstack, flink) {
3177
                    if (p != sel && VISIBLEON(p, selmon) && !client_is_blocked(p)) {
3178
                        focus_client(p, 1);
3179
                        break;
3180
                    }
3181
                }
3182
            }
3183
            return;
3184
        }
3185
    }
3186
}
3187
3188
/* Show and focus the most recently urgent window. */
3189
void focus_urgent(const arg_t *arg) {
3190
    /* focus the most recently urgent window, switching to its
3191
     * workspace if necessary. */
3192
    client_t *c;
3193
3194
    wl_list_for_each(c, &fstack, flink) {
3195
        if (c->is_urgent && c->ws) {
3196
            if (c->ws->mon != selmon)
3197
                view_workspace(c->ws, selmon);
3198
            focus_client(c, 1);
3199
            return;
3200
        }
3201
    }
3202
}
3203
3204
/* Return the most recently focused visible window. */
3205
client_t *focus_top(monitor_t *m) {
3206
    client_t *c;
3207
    wl_list_for_each(c, &fstack, flink) {
3208
        if (VISIBLEON(c, m) && !client_is_blocked(c))
3209
            return c;
3210
    }
3211
    return nullptr;
3212
}
3213
3214
/* Show and focus a window requested by a taskbar or switcher. */
3215
void ftl_activate_notify(struct wl_listener *listener, void *data) {
3216
    /* A taskbar requested this window; show its workspace and focus it. */
3217
    client_t *c = wl_container_of(listener, c, ftl_activate);
3218
3219
    if (c->ws && c->ws->mon != selmon)
3220
        view_workspace(c->ws, selmon);
3221
    focus_client(c, 1);
3222
}
3223
3224
/* Create a scene-backed capture source for a published window. */
3225
void ftl_capture_request_notify(struct wl_listener *listener, void *data) {
3226
    struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request *request = data;
3227
    struct wlr_ext_image_capture_source_v1                                  *source;
3228
    client_t *c = request->toplevel_handle->data;
3229
3230
    if (!c || !c->scene || !client_surface(c)->mapped)
3231
        return;
3232
    source = wlr_ext_image_capture_source_v1_create_with_scene_node(
3233
        &c->scene->node, event_loop, alloc, drw
3234
    );
3235
3236
    if (source)
3237
        wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request_accept(request, source);
3238
}
3239
3240
/* Forward a taskbar's close request to the selected window. */
3241
void ftl_close_notify(struct wl_listener *listener, void *data) {
3242
    client_t *c = wl_container_of(listener, c, ftl_close);
3243
3244
    client_send_close(c);
3245
}
3246
3247
/* Apply a taskbar's fullscreen request to a window. */
3248
void ftl_fullscreen_notify(struct wl_listener *listener, void *data) {
3249
    struct wlr_foreign_toplevel_handle_v1_fullscreen_event *event = data;
3250
    client_t *c = wl_container_of(listener, c, ftl_fullscreen);
3251
3252
    set_fullscreen(c, event->fullscreen);
3253
}
3254
3255
/* Publish a window's title, application ID, display, and state. */
3256
void ftl_sync(client_t *c) {
3257
    /* Publish this window's current state to taskbars and switchers. */
3258
    const char *title, *appid;
3259
3260
    if (!c->ftl)
3261
        return;
3262
    title = client_get_title(c);
3263
    appid = client_get_appid(c);
3264
    wlr_foreign_toplevel_handle_v1_set_title(c->ftl, title ? title : "");
3265
    wlr_foreign_toplevel_handle_v1_set_app_id(c->ftl, appid ? appid : "");
3266
3267
    if (c->ftl_monitor != c->mon) {
3268
        if (c->ftl_monitor)
3269
            wlr_foreign_toplevel_handle_v1_output_leave(c->ftl, c->ftl_monitor->wlr_output);
3270
3271
        if (c->mon)
3272
            wlr_foreign_toplevel_handle_v1_output_enter(c->ftl, c->mon->wlr_output);
3273
        c->ftl_monitor = c->mon;
3274
    }
3275
    wlr_foreign_toplevel_handle_v1_set_activated(c->ftl, c == focus_top(selmon));
3276
    wlr_foreign_toplevel_handle_v1_set_fullscreen(c->ftl, c->is_fullscreen);
3277
3278
    if (c->extftl) {
3279
        struct wlr_ext_foreign_toplevel_handle_v1_state state = {
3280
            .app_id = appid,
3281
            .title  = title,
3282
        };
3283
        wlr_ext_foreign_toplevel_handle_v1_update_state(c->extftl, &state);
3284
    }
3285
}
3286
3287
/* Apply an application's requested fullscreen state. */
3288
void fullscreen_notify(struct wl_listener *listener, void *data) {
3289
    client_t *c = wl_container_of(listener, c, fullscreen);
3290
    set_fullscreen(c, client_wants_fullscreen(c));
3291
}
3292
3293
/* Return the enabled text input belonging to the focused application. */
3294
struct wlr_text_input_v3 *input_method_focused_text_input(void) {
3295
    /* Find the enabled text input for the focused application. */
3296
    struct wlr_text_input_v3 *ti;
3297
    struct wlr_surface       *surface = seat->keyboard_state.focused_surface;
3298
3299
    if (!surface)
3300
        return nullptr;
3301
    wl_list_for_each(ti, &ti_mgr->text_inputs, link) {
3302
        if (ti->focused_surface == surface && ti->current_enabled)
3303
            return ti;
3304
    }
3305
    return nullptr;
3306
}
3307
3308
/* Send the focused application's text-input state to the input method. */
3309
void input_method_send_state(struct wlr_text_input_v3 *ti) {
3310
    /* Send the application's text-input state to the input method. */
3311
    if (!input_method)
3312
        return;
3313
3314
    if (ti->active_features & WLR_TEXT_INPUT_V3_FEATURE_SURROUNDING_TEXT) {
3315
        wlr_input_method_v2_send_surrounding_text(
3316
            input_method,
3317
            ti->current.surrounding.text,
3318
            ti->current.surrounding.cursor,
3319
            ti->current.surrounding.anchor
3320
        );
3321
        wlr_input_method_v2_send_text_change_cause(input_method, ti->current.text_change_cause);
3322
    }
3323
    if (ti->active_features & WLR_TEXT_INPUT_V3_FEATURE_CONTENT_TYPE)
3324
        wlr_input_method_v2_send_content_type(
3325
            input_method, ti->current.content_type.hint, ti->current.content_type.purpose
3326
        );
3327
    wlr_input_method_v2_send_done(input_method);
3328
}
3329
3330
/* Update text-input and input-method state after keyboard focus changes. */
3331
void input_method_set_focus(struct wlr_surface *surface) {
3332
    /* Tell text-input clients when keyboard focus moves, and deactivate the
3333
     * input method if its application lost focus. */
3334
    struct wlr_text_input_v3 *ti;
3335
3336
    wl_list_for_each(ti, &ti_mgr->text_inputs, link) {
3337
        if (ti->focused_surface) {
3338
            if (ti->focused_surface == surface)
3339
                continue;
3340
3341
            if (input_method && ti->current_enabled) {
3342
                wlr_input_method_v2_send_deactivate(input_method);
3343
                wlr_input_method_v2_send_done(input_method);
3344
            }
3345
            wlr_text_input_v3_send_leave(ti);
3346
        }
3347
        if (surface &&
3348
            wl_resource_get_client(ti->resource) == wl_resource_get_client(surface->resource))
3349
            wlr_text_input_v3_send_enter(ti, surface);
3350
    }
3351
    update_shortcuts_inhibitors(surface);
3352
    position_input_popups();
3353
}
3354
3355
/* Forward text and edits committed by the input method to the application. */
3356
void input_method_commit_notify(struct wl_listener *listener, void *data) {
3357
    /* Send completed input-method text and edits to the application. */
3358
    struct wlr_input_method_v2 *im = input_method;
3359
    struct wlr_text_input_v3   *ti = input_method_focused_text_input();
3360
3361
    if (!im || !ti)
3362
        return;
3363
3364
    if (im->current.preedit.text)
3365
        wlr_text_input_v3_send_preedit_string(
3366
            ti,
3367
            im->current.preedit.text,
3368
            im->current.preedit.cursor_begin,
3369
            im->current.preedit.cursor_end
3370
        );
3371
3372
    if (im->current.commit_text)
3373
        wlr_text_input_v3_send_commit_string(ti, im->current.commit_text);
3374
3375
    if (im->current.delete.before_length || im->current.delete.after_length)
3376
        wlr_text_input_v3_send_delete_surrounding_text(
3377
            ti, im->current.delete.before_length, im->current.delete.after_length
3378
        );
3379
    wlr_text_input_v3_send_done(ti);
3380
}
3381
3382
/* Connect a newly available input method to the focused text input. */
3383
void input_method_create_notify(struct wl_listener *listener, void *data) {
3384
    struct wlr_input_method_v2 *im = data;
3385
    struct wlr_text_input_v3   *ti;
3386
3387
    if (input_method) {
3388
        /* Only one input method can serve a seat. */
3389
        wlr_input_method_v2_send_unavailable(im);
3390
        return;
3391
    }
3392
    input_method = im;
3393
    wl_signal_add(&im->events.commit, &im_commit);
3394
    wl_signal_add(&im->events.destroy, &im_destroy);
3395
    wl_signal_add(&im->events.grab_keyboard, &im_grab_kb);
3396
    wl_signal_add(&im->events.new_popup_surface, &im_new_popup);
3397
3398
    if ((ti = input_method_focused_text_input())) {
3399
        wlr_input_method_v2_send_activate(input_method);
3400
        input_method_send_state(ti);
3401
    }
3402
}
3403
3404
/* Disconnect the active input method and remove its listeners. */
3405
void input_method_destroy_notify(struct wl_listener *listener, void *data) {
3406
    wl_list_remove(&im_commit.link);
3407
    wl_list_remove(&im_destroy.link);
3408
    wl_list_remove(&im_grab_kb.link);
3409
    wl_list_remove(&im_new_popup.link);
3410
    input_method = nullptr;
3411
}
3412
3413
/* Give the input method access to the active keyboard. */
3414
void input_method_grab_keyboard(struct wl_listener *listener, void *data) {
3415
    struct wlr_input_method_keyboard_grab_v2 *grab = data;
3416
3417
    wlr_input_method_keyboard_grab_v2_set_keyboard(grab, &kb_group->wlr_group->keyboard);
3418
}
3419
3420
/* Add a new input-method popup above regular windows. */
3421
void input_method_new_popup(struct wl_listener *listener, void *data) {
3422
    struct wlr_input_popup_surface_v2 *popup = data;
3423
    input_popup_t                     *p     = pool_take(&input_popup_pool);
3424
3425
    if (!p)
3426
        return;
3427
3428
    p->popup = popup;
3429
    p->scene = wlr_scene_subsurface_tree_create(layers[LAYER_OVERLAY], popup->surface);
3430
3431
    if (!p->scene) {
3432
        pool_release(&input_popup_pool, p);
3433
        return;
3434
    }
3435
    wlr_scene_node_set_enabled(&p->scene->node, 0);
3436
    wl_list_insert(&input_popups, &p->link);
3437
3438
    LISTEN(&popup->surface->events.map, &p->map, input_popup_map);
3439
    LISTEN(&popup->surface->events.unmap, &p->unmap, input_popup_unmap);
3440
    LISTEN(&popup->surface->events.commit, &p->commit, input_popup_commit);
3441
    LISTEN(&popup->events.destroy, &p->destroy, input_popup_destroy);
3442
}
3443
3444
/* Position an input-method popup beside the focused text cursor. */
3445
bool input_popup_position(input_popup_t *p) {
3446
    /* Place the input-method popup below the application's text cursor. */
3447
    struct wlr_text_input_v3 *ti = input_method_focused_text_input();
3448
    struct wlr_box            rect, output;
3449
    client_t                 *c = nullptr;
3450
    struct swm_box            position;
3451
    int                       width, height;
3452
3453
    if (!ti)
3454
        return false;
3455
    toplevel_from_wlr_surface(ti->focused_surface, &c, nullptr);
3456
3457
    if (!c)
3458
        return false;
3459
    rect = ti->current.cursor_rectangle;
3460
3461
    if (!(ti->current.features & WLR_TEXT_INPUT_V3_FEATURE_CURSOR_RECTANGLE))
3462
        rect = (struct wlr_box){};
3463
    width  = p->popup->surface->current.width;
3464
    height = p->popup->surface->current.height;
3465
3466
    if (c->mon)
3467
        output = c->mon->m;
3468
    position = swm_popup_position(
3469
        (const struct swm_box *)&c->geom,
3470
        c->bw,
3471
        (const struct swm_box *)&rect,
3472
        width,
3473
        height,
3474
        c->mon ? (const struct swm_box *)&output : nullptr
3475
    );
3476
    wlr_scene_node_set_position(&p->scene->node, position.x, position.y);
3477
    wlr_input_popup_surface_v2_send_text_input_rectangle(
3478
        p->popup, &(struct wlr_box){ .y = -rect.height, .width = rect.width, .height = rect.height }
3479
    );
3480
    return true;
3481
}
3482
3483
/* Reposition and show all mapped input-method popups. */
3484
void position_input_popups(void) {
3485
    input_popup_t *p;
3486
3487
    wl_list_for_each(p, &input_popups, link) wlr_scene_node_set_enabled(
3488
        &p->scene->node, p->popup->surface->mapped && input_popup_position(p)
3489
    );
3490
}
3491
3492
/* Position and show a newly mapped input-method popup. */
3493
void input_popup_map(struct wl_listener *listener, void *data) {
3494
    input_popup_t *p = wl_container_of(listener, p, map);
3495
3496
    wlr_scene_node_set_enabled(&p->scene->node, input_popup_position(p));
3497
}
3498
3499
/* Hide an input-method popup when it is unmapped. */
3500
void input_popup_unmap(struct wl_listener *listener, void *data) {
3501
    input_popup_t *p = wl_container_of(listener, p, unmap);
3502
3503
    wlr_scene_node_set_enabled(&p->scene->node, 0);
3504
}
3505
3506
/* Reposition an input-method popup after its content changes. */
3507
void input_popup_commit(struct wl_listener *listener, void *data) {
3508
    input_popup_t *p = wl_container_of(listener, p, commit);
3509
3510
    wlr_scene_node_set_enabled(
3511
        &p->scene->node, p->popup->surface->mapped && input_popup_position(p)
3512
    );
3513
}
3514
3515
/* Remove a destroyed input-method popup and release its state. */
3516
void input_popup_destroy(struct wl_listener *listener, void *data) {
3517
    input_popup_t *p = wl_container_of(listener, p, destroy);
3518
3519
    wl_list_remove(&p->link);
3520
    wl_list_remove(&p->map.link);
3521
    wl_list_remove(&p->unmap.link);
3522
    wl_list_remove(&p->commit.link);
3523
    wl_list_remove(&p->destroy.link);
3524
    wlr_scene_node_destroy(&p->scene->node);
3525
    pool_release(&input_popup_pool, p);
3526
}
3527
3528
/* Track a new application's text-input connection. */
3529
void text_input_create_notify(struct wl_listener *listener, void *data) {
3530
    struct wlr_text_input_v3 *wlr_ti  = data;
3531
    struct wlr_surface       *surface = seat->keyboard_state.focused_surface;
3532
    text_input_t             *ti      = pool_take(&text_input_pool);
3533
3534
    if (!ti)
3535
        return;
3536
3537
    ti->ti = wlr_ti;
3538
    LISTEN(&wlr_ti->events.enable, &ti->enable, text_input_enable_notify);
3539
    LISTEN(&wlr_ti->events.commit, &ti->commit, text_input_commit_notify);
3540
    LISTEN(&wlr_ti->events.disable, &ti->disable, text_input_disable_notify);
3541
    LISTEN(&wlr_ti->events.destroy, &ti->destroy, text_input_destroy_notify);
3542
3543
    if (surface &&
3544
        wl_resource_get_client(wlr_ti->resource) == wl_resource_get_client(surface->resource))
3545
        wlr_text_input_v3_send_enter(wlr_ti, surface);
3546
}
3547
3548
/* Activate the input method for an enabled text field. */
3549
void text_input_enable_notify(struct wl_listener *listener, void *data) {
3550
    text_input_t *ti = wl_container_of(listener, ti, enable);
3551
3552
    if (!input_method || ti->ti != input_method_focused_text_input())
3553
        return;
3554
    wlr_input_method_v2_send_activate(input_method);
3555
    input_method_send_state(ti->ti);
3556
}
3557
3558
/* Forward an application's updated text-field state to the input method. */
3559
void text_input_commit_notify(struct wl_listener *listener, void *data) {
3560
    text_input_t *ti = wl_container_of(listener, ti, commit);
3561
3562
    if (!input_method || ti->ti != input_method_focused_text_input())
3563
        return;
3564
    input_method_send_state(ti->ti);
3565
    position_input_popups();
3566
}
3567
3568
/* Deactivate the input method when an application leaves its text field. */
3569
void text_input_disable_notify(struct wl_listener *listener, void *data) {
3570
    text_input_t *ti = wl_container_of(listener, ti, disable);
3571
3572
    if (!input_method || ti->ti->focused_surface != seat->keyboard_state.focused_surface)
3573
        return;
3574
    wlr_input_method_v2_send_deactivate(input_method);
3575
    wlr_input_method_v2_send_done(input_method);
3576
}
3577
3578
/* Disconnect and release a destroyed text-input connection. */
3579
void text_input_destroy_notify(struct wl_listener *listener, void *data) {
3580
    text_input_t *ti = wl_container_of(listener, ti, destroy);
3581
3582
    if (input_method && ti->ti->current_enabled &&
3583
        ti->ti->focused_surface == seat->keyboard_state.focused_surface) {
3584
        wlr_input_method_v2_send_deactivate(input_method);
3585
        wlr_input_method_v2_send_done(input_method);
3586
    }
3587
    wl_list_remove(&ti->enable.link);
3588
    wl_list_remove(&ti->commit.link);
3589
    wl_list_remove(&ti->disable.link);
3590
    wl_list_remove(&ti->destroy.link);
3591
    pool_release(&text_input_pool, ti);
3592
}
3593
3594
/* Recover from a GPU reset. */
3595
void gpu_reset(struct wl_listener *listener, void *data) {
3596
    struct wlr_renderer  *old_drw   = drw;
3597
    struct wlr_allocator *old_alloc = alloc;
3598
    struct monitor_t     *m;
3599
3600
    if (!(drw = wlr_renderer_autocreate(backend)))
3601
        die("couldn't recreate renderer");
3602
3603
    if (!(alloc = wlr_allocator_autocreate(backend, drw)))
3604
        die("couldn't recreate allocator");
3605
3606
    wl_list_remove(&gpu_reset_listener.link);
3607
    wl_signal_add(&drw->events.lost, &gpu_reset_listener);
3608
3609
    wlr_compositor_set_renderer(compositor, drw);
3610
3611
    wl_list_for_each(m, &mons, link) {
3612
        wlr_output_init_render(m->wlr_output, alloc, drw);
3613
    }
3614
    wlr_allocator_destroy(old_alloc);
3615
    wlr_renderer_destroy(old_drw);
3616
}
3617
3618
/* Reap child processes or begin shutdown when a queued signal arrives. */
3619
int handle_signal(int signo, void *data) {
3620
    if (signo == SIGCHLD) {
3621
        pid_t            pid;
3622
        pending_spawn_t *ps;
3623
        struct wl_list  *node, *next;
3624
3625
        while ((pid = waitpid(-1, nullptr, WNOHANG)) > 0) {
3626
            size_t i;
3627
3628
            if (pid == child_pid)
3629
                child_pid = -1;
3630
3631
            for (i = 0; i < autostart_len; i++)
3632
3633
                if (autostart_pids[i] == pid)
3634
                    autostart_pids[i] = -1;
3635
3636
            for (node = pending_spawns.next; node != &pending_spawns; node = next) {
3637
                next = node->next;
3638
                ps   = wl_container_of(node, ps, link);
3639
3640
                if (ps->pid != pid)
3641
                    continue;
3642
                wl_list_remove(&ps->link);
3643
                pool_release(&pending_spawn_pool, ps);
3644
            }
3645
        }
3646
    } else if (signo == SIGINT || signo == SIGTERM)
3647
        quit(nullptr);
3648
    return 0;
3649
}
3650
3651
/* Add a newly connected keyboard or pointer and update seat capabilities. */
3652
void input_device(struct wl_listener *listener, void *data) {
3653
    /* This event is raised by the backend when a new input device becomes
3654
     * available. */
3655
    struct wlr_input_device *device = data;
3656
    uint32_t                 caps;
3657
3658
    switch (device->type) {
3659
    case WLR_INPUT_DEVICE_KEYBOARD:
3660
        create_keyboard(wlr_keyboard_from_input_device(device));
3661
        break;
3662
    case WLR_INPUT_DEVICE_POINTER:
3663
        create_pointer(wlr_pointer_from_input_device(device));
3664
        break;
3665
    default:
3666
        /* TODO: Handle other input device types. */
3667
        break;
3668
    }
3669
    /* Advertise the input devices available to applications. swm always has
3670
     * a cursor, even when no physical pointing device is connected. */
3671
    /* TODO: Check whether swm truly needs to always advertise a pointer. */
3672
    caps = WL_SEAT_CAPABILITY_POINTER;
3673
3674
    if (!wl_list_empty(&kb_group->wlr_group->devices) || virtual_keyboards)
3675
        caps |= WL_SEAT_CAPABILITY_KEYBOARD;
3676
    wlr_seat_set_capabilities(seat, caps);
3677
}
3678
3679
/* Run the matching window-manager shortcut and report whether it may repeat. */
3680
int key_binding(uint32_t mods, xkb_keysym_t sym, bool repeat) {
3681
    /* Handle window-manager shortcuts before applications receive the key. */
3682
    const key_t *k;
3683
    int          inhibited = shortcuts_inhibited();
3684
3685
    for (k = keys; k < END(keys); k++) {
3686
        if (CLEANMASK(mods) == CLEANMASK(k->mod) &&
3687
            xkb_keysym_to_lower(sym) == xkb_keysym_to_lower(k->keysym) && k->func) {
3688
            int repeatable = k->func == focus_stack || k->func == swap_client ||
3689
                             k->func == stack_config || k->func == cycle_workspace;
3690
3691
            if (repeat && !repeatable)
3692
                continue;
3693
            /* Keep core window-management controls available even when a
3694
             * fullscreen client inhibits application-facing shortcuts. */
3695
            if (inhibited && k->func != change_vt && k->func != quit && k->func != focus_stack &&
3696
                k->func != toggle_max_stack && k->func != toggle_fullscreen)
3697
                continue;
3698
            k->func(&k->arg);
3699
            return repeatable ? 2 : 1;
3700
        }
3701
    }
3702
    return 0;
3703
}
3704
3705
/* Handle shortcuts and forward unhandled key events to the focused application. */
3706
void key_press(struct wl_listener *listener, void *data) {
3707
    int i;
3708
3709
    /* This event is raised when a key is pressed or released. */
3710
    keyboard_group_t              *group = wl_container_of(listener, group, key);
3711
    struct wlr_keyboard_key_event *event = data;
3712
3713
    /* Convert the input key code to the numbering used by XKB. */
3714
    uint32_t keycode = event->keycode + 8;
3715
3716
    /* Resolve the key to one or more symbols using the active key map. */
3717
    const xkb_keysym_t *syms;
3718
    int      nsyms   = xkb_state_key_get_syms(group->wlr_group->keyboard.xkb_state, keycode, &syms);
3719
    int      handled = 0;
3720
    uint32_t mods    = wlr_keyboard_get_modifiers(&group->wlr_group->keyboard);
3721
3722
    wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
3723
3724
    /* Handle shortcuts only on key press and while the session is unlocked. */
3725
    if (!locked && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
3726
        for (i = 0; i < nsyms; i++)
3727
            handled |= key_binding(mods, syms[i], 0);
3728
    }
3729
    if ((handled & 2) && group->wlr_group->keyboard.repeat_info.delay > 0) {
3730
        group->mods    = mods;
3731
        group->keysyms = syms;
3732
        group->nsyms   = nsyms;
3733
        wl_event_source_timer_update(
3734
            group->key_repeat_source, group->wlr_group->keyboard.repeat_info.delay
3735
        );
3736
    } else {
3737
        group->nsyms = 0;
3738
        wl_event_source_timer_update(group->key_repeat_source, 0);
3739
    }
3740
    if (handled)
3741
        return;
3742
3743
    wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard);
3744
    /* Forward unhandled keys to the focused application. */
3745
    wlr_seat_keyboard_notify_key(seat, event->time_msec, event->keycode, event->state);
3746
}
3747
3748
/* Forward a keyboard's updated modifier state to the focused application. */
3749
void key_press_modifiers(struct wl_listener *listener, void *data) {
3750
    /* A modifier such as Shift or Alt changed. */
3751
    keyboard_group_t *group = wl_container_of(listener, group, modifiers);
3752
3753
    wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard);
3754
    /* Forward the new modifier state to the focused application. */
3755
    wlr_seat_keyboard_notify_modifiers(seat, &group->wlr_group->keyboard.modifiers);
3756
    tiled_resize_update();
3757
}
3758
3759
/* Repeat the active shortcut at the keyboard's configured rate. */
3760
int key_repeat(void *data) {
3761
    keyboard_group_t *group = data;
3762
    int               i;
3763
3764
    if (!group->nsyms || group->wlr_group->keyboard.repeat_info.rate <= 0)
3765
        return 0;
3766
3767
    wl_event_source_timer_update(
3768
        group->key_repeat_source, 1000 / group->wlr_group->keyboard.repeat_info.rate
3769
    );
3770
3771
    for (i = 0; i < group->nsyms; i++)
3772
        key_binding(group->mods, group->keysyms[i], 1);
3773
3774
    return 0;
3775
}
3776
3777
/* Ask the focused application to close its window. */
3778
void kill_client(const arg_t *arg) {
3779
    client_t *sel = focus_top(selmon);
3780
3781
    if (sel)
3782
        client_send_close(sel);
3783
}
3784
3785
/* Accept a session lock and prevent applications from receiving input. */
3786
void lock_session(struct wl_listener *listener, void *data) {
3787
    struct wlr_session_lock_v1 *session_lock = data;
3788
    session_lock_t             *lock;
3789
3790
    if (cur_lock) {
3791
        wlr_session_lock_v1_destroy(session_lock);
3792
        return;
3793
    }
3794
    if (!(lock = pool_take(&session_lock_pool))) {
3795
        wlr_session_lock_v1_destroy(session_lock);
3796
        return;
3797
    }
3798
    lock->scene = wlr_scene_tree_create(layers[LAYER_BLOCK]);
3799
3800
    if (!lock->scene) {
3801
        pool_release(&session_lock_pool, lock);
3802
        wlr_session_lock_v1_destroy(session_lock);
3803
        return;
3804
    }
3805
    session_lock->data = lock;
3806
    wlr_scene_node_set_enabled(&locked_bg->node, 1);
3807
    focus_client(nullptr, 0);
3808
3809
    cur_lock = lock->lock = session_lock;
3810
    locked                = true;
3811
3812
    LISTEN(&session_lock->events.new_surface, &lock->new_surface, create_lock_surface);
3813
    LISTEN(&session_lock->events.destroy, &lock->destroy, destroy_session_lock);
3814
    LISTEN(&session_lock->events.unlock, &lock->unlock, unlock_session);
3815
3816
    wlr_session_lock_v1_send_locked(session_lock);
3817
}
3818
3819
/* Open a temporary file to be renamed over path once it is fully written. */
3820
static FILE *file_replace_begin(const char *path, char *tmppath, size_t size) {
3821
    if (snprintf(tmppath, size, "%s.tmp.%ld", path, (long)getpid()) >= (int)size)
3822
        return nullptr;
3823
    return fopen(tmppath, "w");
3824
}
3825
3826
/* Put a fully written temporary file in place, or discard it. */
3827
static bool file_replace_commit(FILE *f, const char *tmppath, const char *path) {
3828
    bool ok = !ferror(f) && fflush(f) == 0;
3829
3830
    if (fclose(f) < 0)
3831
        ok = false;
3832
3833
    if (ok && rename(tmppath, path) == 0)
3834
        return true;
3835
    unlink(tmppath);
3836
    return false;
3837
}
3838
3839
/* Build the path used to persist floating window geometry. */
3840
static const char *window_state_path(void) {
3841
    static char path[MAX_STATE_PATH];
3842
    const char *base = getenv("XDG_STATE_HOME");
3843
    const char *home = getenv("HOME");
3844
3845
    if (base && *base)
3846
        snprintf(path, sizeof(path), "%s/swm/windows", base);
3847
    else
3848
        snprintf(path, sizeof(path), "%s/.local/state/swm/windows", home ? home : "/tmp");
3849
    return path;
3850
}
3851
3852
/* Sanitize a field for the state file, naming an unknown value "broken". */
3853
static void state_field(char dst[MAX_WINDOW_STATE_FIELD], const char *src) {
3854
    swm_sanitize_field(dst, MAX_WINDOW_STATE_FIELD, src, "broken");
3855
}
3856
3857
/* Find persisted state matching a client. */
3858
static window_state_t *find_window_state(client_t *c) {
3859
    window_state_t *state;
3860
    char            appid[MAX_WINDOW_STATE_FIELD], title[MAX_WINDOW_STATE_FIELD];
3861
3862
    state_field(appid, client_get_appid(c));
3863
    state_field(title, client_get_title(c));
3864
3865
    wl_list_for_each(state, &window_states, link) {
3866
        if (!strcmp(state->appid, appid) && (!state->title[0] || !strcmp(state->title, title))) {
3867
            return state;
3868
        }
3869
    }
3870
    return nullptr;
3871
}
3872
3873
/* Write all remembered floating-window geometries to disk. */
3874
void save_window_states(void) {
3875
    window_state_t *state;
3876
    FILE           *f;
3877
    const char     *path = window_state_path();
3878
    char            dir[MAX_STATE_PATH], tmppath[MAX_STATE_PATH], *slash, *p;
3879
3880
    snprintf(dir, sizeof(dir), "%s", path);
3881
3882
    if ((slash = strrchr(dir, '/'))) {
3883
        *slash = '\0';
3884
        for (p = dir + 1; *p; p++) {
3885
            if (*p != '/')
3886
                continue;
3887
            *p = '\0';
3888
            if (mkdir(dir, 0700) < 0 && errno != EEXIST)
3889
                return;
3890
            *p = '/';
3891
        }
3892
        if (mkdir(dir, 0700) < 0 && errno != EEXIST)
3893
            return;
3894
    }
3895
    if (!(f = file_replace_begin(path, tmppath, sizeof(tmppath)))) {
3896
        fprintf(stderr, "swm: cannot save floating window state: %s\n", strerror(errno));
3897
        return;
3898
    }
3899
    wl_list_for_each(state, &window_states, link) fprintf(
3900
        f,
3901
        "%s\t%s\t%d\t%d\t%d\t%d\n",
3902
        state->appid,
3903
        state->title,
3904
        state->geom.x,
3905
        state->geom.y,
3906
        state->geom.width,
3907
        state->geom.height
3908
    );
3909
    if (!file_replace_commit(f, tmppath, path))
3910
        fprintf(stderr, "swm: cannot save floating window state: %s\n", strerror(errno));
3911
}
3912
3913
/* Load remembered floating-window geometries from disk. */
3914
void load_window_states(void) {
3915
    FILE           *f;
3916
    window_state_t *state;
3917
    char            appid[MAX_WINDOW_STATE_FIELD], title[MAX_WINDOW_STATE_FIELD];
3918
    char            line[MAX_WINDOW_STATE_LINE];
3919
    struct swm_box  geometry;
3920
3921
    if (!(f = fopen(window_state_path(), "r")))
3922
        return;
3923
3924
    while (fgets(line, sizeof(line), f)) {
3925
        if (!swm_parse_window_state(line, appid, sizeof(appid), title, sizeof(title), &geometry))
3926
            continue;
3927
3928
        if (!(state = pool_take(&window_state_pool))) {
3929
            fprintf(stderr, "swm: ignoring remaining saved window states\n");
3930
            break;
3931
        }
3932
        state_field(state->appid, appid);
3933
3934
        if (title[0])
3935
            state_field(state->title, title);
3936
        else
3937
            state->title[0] = '\0';
3938
        state->geom = (struct wlr_box){ geometry.x, geometry.y, geometry.width, geometry.height };
3939
        wl_list_insert(window_states.prev, &state->link);
3940
    }
3941
    fclose(f);
3942
}
3943
3944
/* Save a floating window's current geometry for a later session. */
3945
void remember_client(client_t *c) {
3946
    window_state_t *state;
3947
3948
    if (!c || !c->persist_float || !c->is_floating || client_get_parent(c))
3949
        return;
3950
3951
    if (!(state = find_window_state(c))) {
3952
        if (!(state = pool_take(&window_state_pool)))
3953
            return;
3954
        state_field(state->appid, client_get_appid(c));
3955
        state_field(state->title, client_get_title(c));
3956
        wl_list_insert(window_states.prev, &state->link);
3957
    } else if (!state->title[0]) {
3958
        state_field(state->title, client_get_title(c));
3959
    }
3960
    state->geom = c->geom;
3961
    save_window_states();
3962
}
3963
3964
/* Remove a window's saved geometry. */
3965
void forget_client(client_t *c) {
3966
    window_state_t *state;
3967
3968
    if (!c || !(state = find_window_state(c)))
3969
        return;
3970
    wl_list_remove(&state->link);
3971
    pool_release(&window_state_pool, state);
3972
    save_window_states();
3973
}
3974
3975
/* Restore a floating window to its saved geometry when available. */
3976
void restore_client(client_t *c) {
3977
    window_state_t *state;
3978
3979
    if (!c->persist_float || client_get_parent(c))
3980
        return;
3981
3982
    if ((state = find_window_state(c))) {
3983
        resize(c, state->geom, 0);
3984
        set_floating(c, 1);
3985
    }
3986
}
3987
3988
/* Make a new window visible, place it, publish it, and focus it. */
3989
void map_notify(struct wl_listener *listener, void *data) {
3990
    /* Called when the surface is mapped, or ready to display on-screen. */
3991
    client_t        *p = nullptr;
3992
    client_t        *w, *c = wl_container_of(listener, c, map);
3993
    monitor_t       *m;
3994
    pending_spawn_t *ps, *pstmp;
3995
    workspace_t     *spawnws = nullptr;
3996
    pid_t            pid     = -1;
3997
    unsigned int     oldbw;
3998
    int              i, inherit_fullscreen = 0;
3999
4000
    /* Create drawing nodes for the window and its border. */
4001
    c->scene = client_surface(c)->data = wlr_scene_tree_create(layers[LAYER_TILE]);
4002
4003
    if (!c->scene) {
4004
        client_send_close(c);
4005
        return;
4006
    }
4007
    /* arrange() makes the window visible later. */
4008
    wlr_scene_node_set_enabled(&c->scene->node, client_is_unmanaged(c));
4009
    c->scene_surface = c->type == XDG_SHELL
4010
                           ? wlr_scene_xdg_surface_create(c->scene, c->surface.xdg)
4011
                           : wlr_scene_subsurface_tree_create(c->scene, client_surface(c));
4012
4013
    if (!c->scene_surface)
4014
        goto fail;
4015
    c->scene->node.data = c->scene_surface->node.data = c;
4016
4017
    client_get_geometry(c, &c->geom);
4018
4019
    /* Handle unmanaged windows before creating borders. */
4020
    if (client_is_unmanaged(c)) {
4021
        /* Unmanaged X11 windows always float. */
4022
        wlr_scene_node_reparent(&c->scene->node, layers[LAYER_FLOAT]);
4023
        wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y);
4024
        client_set_size(c, c->geom.width, c->geom.height);
4025
4026
        if (client_wants_focus(c)) {
4027
            focus_client(c, 1);
4028
            exclusive_focus = c;
4029
        }
4030
        return;
4031
    }
4032
    for (i = 0; i < 4; i++) {
4033
        c->border[i] =
4034
            wlr_scene_rect_create(c->scene, 0, 0, c->is_urgent ? urgentcolor : bordercolor);
4035
4036
        if (!c->border[i])
4037
            goto fail;
4038
        c->border[i]->node.data = c;
4039
    }
4040
    /* Include the border in the window's initial size. */
4041
    client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT);
4042
    c->geom.width  += 2 * c->bw;
4043
    c->geom.height += 2 * c->bw;
4044
4045
    /* Add new windows after existing tiled windows so they do not replace the
4046
     * master. Keep the newest window first in the focus history. */
4047
    c->pending_map = 1;
4048
    wl_list_insert(clients.prev, &c->link);
4049
    wl_list_insert(&fstack, &c->flink);
4050
4051
    if (!client_is_x11(c)) {
4052
        wl_client_get_credentials(c->surface.xdg->client->client, &pid, nullptr, nullptr);
4053
        wl_list_for_each_safe(ps, pstmp, &pending_spawns, link) {
4054
            if (ps->pid != pid)
4055
                continue;
4056
            spawnws = ps->ws;
4057
            wl_list_remove(&ps->link);
4058
            pool_release(&pending_spawn_pool, ps);
4059
            break;
4060
        }
4061
    }
4062
    /* Child windows float on their parent's workspace and display. Apply the
4063
     * configured matching rules to other windows. */
4064
    if ((p = client_get_parent(c))) {
4065
        c->is_floating = true;
4066
        set_monitor(c, p->mon, p->ws);
4067
    } else {
4068
        apply_rules(c, spawnws);
4069
        c->persist_float  = c->is_floating;
4070
        oldbw             = c->bw;
4071
        c->bw             = client_border_width(c);
4072
        c->geom.width    += 2 * ((int)c->bw - (int)oldbw);
4073
        c->geom.height   += 2 * ((int)c->bw - (int)oldbw);
4074
        restore_client(c);
4075
    }
4076
    /* Publish the window to taskbars and switchers. */
4077
    c->ftl = wlr_foreign_toplevel_handle_v1_create(ftl_mgr);
4078
4079
    if (c->ftl) {
4080
        LISTEN(&c->ftl->events.request_activate, &c->ftl_activate, ftl_activate_notify);
4081
        LISTEN(&c->ftl->events.request_close, &c->ftl_close, ftl_close_notify);
4082
        LISTEN(&c->ftl->events.request_fullscreen, &c->ftl_fullscreen, ftl_fullscreen_notify);
4083
    }
4084
    c->extftl = wlr_ext_foreign_toplevel_handle_v1_create(
4085
        ext_ftl_list,
4086
        &(struct wlr_ext_foreign_toplevel_handle_v1_state){
4087
            .app_id = client_get_appid(c),
4088
            .title  = client_get_title(c),
4089
        }
4090
    );
4091
    if (c->extftl)
4092
        c->extftl->data = c;
4093
    ftl_sync(c);
4094
    /* A managed window can receive keyboard focus only after it is visible. */
4095
    if (c->ws && c->ws->mon)
4096
        focus_client(c, 1);
4097
    c->pending_map = 0;
4098
4099
    if (c->mon)
4100
        arrange(c->mon);
4101
    print_status();
4102
4103
    /* A new parentless window takes over a fullscreen window's display. */
4104
    if (p)
4105
        return;
4106
    m = c->mon ? c->mon : point_to_monitor(c->geom.x, c->geom.y);
4107
    wl_list_for_each(w, &clients, link) {
4108
        if (w != c && w->is_fullscreen && m == w->mon && w->ws == c->ws) {
4109
            inherit_fullscreen = 1;
4110
            set_fullscreen(w, 0);
4111
        }
4112
    }
4113
    if (inherit_fullscreen)
4114
        set_fullscreen(c, 1);
4115
    return;
4116
4117
fail:
4118
    /* Undo the partially built scene and let the window try again. */
4119
    wlr_scene_node_destroy(&c->scene->node);
4120
    memset(c->border, 0, sizeof(c->border));
4121
    c->scene = client_surface(c)->data = nullptr;
4122
    c->scene_surface                   = nullptr;
4123
    client_send_close(c);
4124
}
4125
4126
/* Acknowledge unsupported maximize requests from older applications. */
4127
void maximize_notify(struct wl_listener *listener, void *data) {
4128
    /* swm does not support maximization, but older applications may still ask
4129
     * for it. Send an unchanged configuration to acknowledge the request. */
4130
    client_t *c = wl_container_of(listener, c, maximize);
4131
4132
    if (c->surface.xdg->initialized && wl_resource_get_version(c->surface.xdg->toplevel->resource) <
4133
                                           XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION)
4134
        wlr_xdg_surface_schedule_configure(c->surface.xdg);
4135
}
4136
4137
/* Fill the usable display area with every tiled window in the workspace. */
4138
void max_stack(monitor_t *m) {
4139
    client_t *c;
4140
4141
    wl_list_for_each(c, &clients, link) {
4142
        if (!VISIBLEON(c, m) || c->is_fullscreen)
4143
            continue;
4144
4145
        if (c->is_floating && !c->is_max_stacked) {
4146
            c->maxstack_prev  = c->geom;
4147
            c->is_max_stacked = true;
4148
        }
4149
        resize(c, m->w, 0);
4150
    }
4151
    if ((c = focus_top(m)))
4152
        wlr_scene_node_raise_to_top(&c->scene->node);
4153
}
4154
4155
/* Convert absolute pointer input into movement across the display layout. */
4156
void motion_absolute(struct wl_listener *listener, void *data) {
4157
    /* Convert absolute pointer coordinates, such as those from a tablet or a
4158
     * nested compositor, into display-layout coordinates. */
4159
    struct wlr_pointer_motion_absolute_event *event = data;
4160
    double                                    lx, ly, dx, dy;
4161
4162
    if (!event->time_msec) /* Virtual pointers use a zero timestamp. */
4163
        wlr_cursor_warp_absolute(cursor, &event->pointer->base, event->x, event->y);
4164
4165
    wlr_cursor_absolute_to_layout_coords(
4166
        cursor, &event->pointer->base, event->x, event->y, &lx, &ly
4167
    );
4168
    dx = lx - cursor->x;
4169
    dy = ly - cursor->y;
4170
    motion_notify(event->time_msec, &event->pointer->base, dx, dy, dx, dy, 1);
4171
}
4172
4173
/* Move the pointer or grabbed window and update the surface under it. */
4174
void motion_notify(
4175
    uint32_t                 time,
4176
    struct wlr_input_device *device,
4177
    double                   dx,
4178
    double                   dy,
4179
    double                   dx_unaccel,
4180
    double                   dy_unaccel,
4181
    bool                     refocus
4182
) {
4183
    double                            sx = 0, sy = 0, sx_confined, sy_confined;
4184
    client_t                         *c = nullptr, *w = nullptr;
4185
    layer_surface_t                  *l          = nullptr;
4186
    struct wlr_surface               *surface    = nullptr;
4187
    struct wlr_pointer_constraint_v1 *constraint = active_constraint;
4188
4189
    /* A zero timestamp marks an internal pointer-focus update. */
4190
    if (time) {
4191
        wlr_relative_pointer_manager_v1_send_relative_motion(
4192
            relative_pointer_mgr, seat, (uint64_t)time * 1000, dx, dy, dx_unaccel, dy_unaccel
4193
        );
4194
4195
        if (constraint && constraint->surface != seat->pointer_state.focused_surface)
4196
            constraint = nullptr;
4197
4198
        if (constraint && cursor_mode != CURSOR_RESIZE && cursor_mode != CURSOR_MOVE &&
4199
            cursor_mode != CURSOR_TILE_RESIZE) {
4200
            toplevel_from_wlr_surface(constraint->surface, &c, nullptr);
4201
4202
            if (c) {
4203
                sx = cursor->x - c->geom.x - c->bw;
4204
                sy = cursor->y - c->geom.y - c->bw;
4205
4206
                if (wlr_region_confine(
4207
                        &constraint->region, sx, sy, sx + dx, sy + dy, &sx_confined, &sy_confined
4208
                    )) {
4209
                    dx = sx_confined - sx;
4210
                    dy = sy_confined - sy;
4211
                }
4212
                if (constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED) {
4213
                    wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
4214
                    return;
4215
                }
4216
            }
4217
        }
4218
        wlr_cursor_move(cursor, device, dx, dy);
4219
        wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
4220
4221
        /* Update the selected display even while dragging a window. */
4222
        if (sloppyfocus)
4223
            selmon = point_to_monitor(cursor->x, cursor->y);
4224
    }
4225
    /* Hit-test at the updated cursor position. */
4226
    point_to_node(cursor->x, cursor->y, &surface, &c, nullptr, &sx, &sy);
4227
4228
    if (cursor_mode == CURSOR_PRESSED && !seat->drag &&
4229
        surface != seat->pointer_state.focused_surface &&
4230
        toplevel_from_wlr_surface(seat->pointer_state.focused_surface, &w, &l) >= 0) {
4231
        c       = w;
4232
        surface = seat->pointer_state.focused_surface;
4233
        sx      = cursor->x - (l ? l->scene->node.x : w->geom.x);
4234
        sy      = cursor->y - (l ? l->scene->node.y : w->geom.y);
4235
    }
4236
    /* Keep the drag icon under the pointer. */
4237
    wlr_scene_node_set_position(&drag_icon->node, (int)round(cursor->x), (int)round(cursor->y));
4238
4239
    /* Move or resize the grabbed window instead of changing pointer focus. */
4240
    if (cursor_mode == CURSOR_MOVE) {
4241
        /* Move the grabbed client to the new position. */
4242
        resize(
4243
            grabc,
4244
            (struct wlr_box){ .x      = (int)round(cursor->x) - grabcx,
4245
                              .y      = (int)round(cursor->y) - grabcy,
4246
                              .width  = grabc->geom.width,
4247
                              .height = grabc->geom.height },
4248
            1
4249
        );
4250
        return;
4251
    } else if (cursor_mode == CURSOR_RESIZE) {
4252
        int            movedx = (int)round(cursor->x) - grabx;
4253
        int            movedy = (int)round(cursor->y) - graby;
4254
        struct swm_box pure =
4255
            swm_box_resize((const struct swm_box *)&grabgeom, movedx, movedy, grabedges, grabc->bw);
4256
        struct wlr_box geo  = { pure.x, pure.y, pure.width, pure.height };
4257
        grabc->resize_edges = grabedges;
4258
        resize(grabc, geo, 1);
4259
        return;
4260
    } else if (cursor_mode == CURSOR_TILE_RESIZE) {
4261
        tiled_resize_update();
4262
        return;
4263
    }
4264
    tiled_resize_update();
4265
    /* Show the default cursor over the background and window borders. */
4266
    if (!surface && !seat->drag && !tiled_resize_hover)
4267
        wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
4268
4269
    pointer_focus(c, surface, sx, sy, time, refocus);
4270
}
4271
4272
/* Apply pointer movement reported relative to the previous position. */
4273
void motion_relative(struct wl_listener *listener, void *data) {
4274
    /* A pointer reported movement relative to its previous position. */
4275
    struct wlr_pointer_motion_event *event = data;
4276
4277
    /* Move the cursor by the reported distance. wlroots applies display bounds
4278
     * and any settings specific to the input device. */
4279
    motion_notify(
4280
        event->time_msec,
4281
        &event->pointer->base,
4282
        event->delta_x,
4283
        event->delta_y,
4284
        event->unaccel_dx,
4285
        event->unaccel_dy,
4286
        1
4287
    );
4288
}
4289
4290
/* Set or clear the cursor for a tiled master boundary. */
4291
static void tiled_resize_cursor(bool active, bool horizontal) {
4292
    if (active) {
4293
        if (!tiled_resize_hover || tiled_resize_hover_horizontal != horizontal)
4294
            wlr_cursor_set_xcursor(cursor, cursor_mgr, horizontal ? "ns-resize" : "ew-resize");
4295
        tiled_resize_hover_horizontal = horizontal;
4296
    } else if (tiled_resize_hover) {
4297
        wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
4298
    }
4299
    tiled_resize_hover = active;
4300
}
4301
4302
/* Update the tiled master size or its hover cursor. */
4303
static void tiled_resize_update(void) {
4304
    monitor_t           *m = point_to_monitor(cursor->x, cursor->y);
4305
    struct wlr_keyboard *keyboard;
4306
    workspace_t         *ws;
4307
    stack_state_t       *st;
4308
    master_layout_t      ml;
4309
    int                  size, offset, value;
4310
    bool                 horizontal, flip;
4311
4312
    if (cursor_mode != CURSOR_TILE_RESIZE) {
4313
        if (cursor_mode == CURSOR_PRESSED)
4314
            return;
4315
        keyboard = wlr_seat_get_keyboard(seat);
4316
        if (!keyboard || CLEANMASK(wlr_keyboard_get_modifiers(keyboard)) != CLEANMASK(MOD)) {
4317
            tiled_resize_cursor(false, false);
4318
            return;
4319
        }
4320
        if (tiled_resize_boundary(m, (int)round(cursor->x), (int)round(cursor->y), &horizontal))
4321
            tiled_resize_cursor(true, horizontal);
4322
        else
4323
            tiled_resize_cursor(false, false);
4324
        return;
4325
    }
4326
4327
    if (!grabws || !grabws->mon)
4328
        return;
4329
4330
    ws = grabws;
4331
4332
    if (!layout_master(ws->lt, &ml))
4333
        return;
4334
    m    = ws->mon;
4335
    st   = grab_horizontal ? &ws->h : &ws->v;
4336
    flip = ml.flip;
4337
    size = grab_horizontal ? m->w.height : m->w.width;
4338
4339
    offset = grab_horizontal ? (int)round(cursor->y) - m->w.y : (int)round(cursor->x) - m->w.x;
4340
    value  = tiled_resize_value(offset, size, flip);
4341
4342
    if (value >= 0 && st->msize != value) {
4343
        st->msize = value;
4344
        arrange(m);
4345
    }
4346
}
4347
4348
/* Forward touchpad swipe gestures to clients of the focused surface. */
4349
void gesture_swipe_begin(struct wl_listener *listener, void *data) {
4350
    struct wlr_pointer_swipe_begin_event *event = data;
4351
4352
    wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
4353
    wlr_pointer_gestures_v1_send_swipe_begin(
4354
        pointer_gestures, seat, event->time_msec, event->fingers
4355
    );
4356
}
4357
4358
void gesture_swipe_update(struct wl_listener *listener, void *data) {
4359
    struct wlr_pointer_swipe_update_event *event = data;
4360
4361
    wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
4362
    wlr_pointer_gestures_v1_send_swipe_update(
4363
        pointer_gestures, seat, event->time_msec, event->dx, event->dy
4364
    );
4365
}
4366
4367
void gesture_swipe_end(struct wl_listener *listener, void *data) {
4368
    struct wlr_pointer_swipe_end_event *event = data;
4369
4370
    wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
4371
    wlr_pointer_gestures_v1_send_swipe_end(
4372
        pointer_gestures, seat, event->time_msec, event->cancelled
4373
    );
4374
}
4375
4376
/* Forward touchpad pinch gestures, including scale and rotation. */
4377
void gesture_pinch_begin(struct wl_listener *listener, void *data) {
4378
    struct wlr_pointer_pinch_begin_event *event = data;
4379
4380
    wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
4381
    wlr_pointer_gestures_v1_send_pinch_begin(
4382
        pointer_gestures, seat, event->time_msec, event->fingers
4383
    );
4384
}
4385
4386
void gesture_pinch_update(struct wl_listener *listener, void *data) {
4387
    struct wlr_pointer_pinch_update_event *event = data;
4388
4389
    wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
4390
    wlr_pointer_gestures_v1_send_pinch_update(
4391
        pointer_gestures,
4392
        seat,
4393
        event->time_msec,
4394
        event->dx,
4395
        event->dy,
4396
        event->scale,
4397
        event->rotation
4398
    );
4399
}
4400
4401
void gesture_pinch_end(struct wl_listener *listener, void *data) {
4402
    struct wlr_pointer_pinch_end_event *event = data;
4403
4404
    wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
4405
    wlr_pointer_gestures_v1_send_pinch_end(
4406
        pointer_gestures, seat, event->time_msec, event->cancelled
4407
    );
4408
}
4409
4410
/* Forward touchpad hold gestures to clients of the focused surface. */
4411
void gesture_hold_begin(struct wl_listener *listener, void *data) {
4412
    struct wlr_pointer_hold_begin_event *event = data;
4413
4414
    wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
4415
    wlr_pointer_gestures_v1_send_hold_begin(
4416
        pointer_gestures, seat, event->time_msec, event->fingers
4417
    );
4418
}
4419
4420
void gesture_hold_end(struct wl_listener *listener, void *data) {
4421
    struct wlr_pointer_hold_end_event *event = data;
4422
4423
    wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
4424
    wlr_pointer_gestures_v1_send_hold_end(
4425
        pointer_gestures, seat, event->time_msec, event->cancelled
4426
    );
4427
}
4428
4429
/* Restore the normal workspace layout before direct window manipulation. */
4430
static void restore_max_stack(monitor_t *m) {
4431
    workspace_t *ws;
4432
4433
    if (!m || !(ws = m->ws) || !ws->lt || ws->lt->arrange != max_stack)
4434
        return;
4435
4436
    ws->lt = ws->prevlt && ws->prevlt->arrange != max_stack ? ws->prevlt : &layouts[0];
4437
    arrange(m);
4438
    print_status();
4439
}
4440
4441
/* Begin moving or resizing the window under the pointer. */
4442
void move_resize(const arg_t *arg) {
4443
    if (cursor_mode != CURSOR_NORMAL && cursor_mode != CURSOR_PRESSED)
4444
        return;
4445
    point_to_node(cursor->x, cursor->y, nullptr, &grabc, nullptr, nullptr, nullptr);
4446
4447
    if (!grabc || !client_allows_move_resize(grabc, arg->u))
4448
        return;
4449
4450
    if (grabc->is_fullscreen)
4451
        set_fullscreen(grabc, 0);
4452
4453
    restore_max_stack(grabc->mon);
4454
4455
    /* Float the window and let pointer motion move or resize it. */
4456
    grabc->persist_float = 1;
4457
    set_floating(grabc, 1);
4458
4459
    switch (cursor_mode = arg->u) {
4460
    case CURSOR_MOVE:
4461
        grabcx = (int)round(cursor->x) - grabc->geom.x;
4462
        grabcy = (int)round(cursor->y) - grabc->geom.y;
4463
        wlr_cursor_set_xcursor(cursor, cursor_mgr, "all-scroll");
4464
        break;
4465
    case CURSOR_RESIZE:
4466
        grabx     = (int)round(cursor->x);
4467
        graby     = (int)round(cursor->y);
4468
        grabgeom  = grabc->geom;
4469
        grabedges = (grabx < grabgeom.x + grabgeom.width / 2 ? WLR_EDGE_LEFT : WLR_EDGE_RIGHT) |
4470
                    (graby < grabgeom.y + grabgeom.height / 2 ? WLR_EDGE_TOP : WLR_EDGE_BOTTOM);
4471
        client_set_resizing(grabc, 1);
4472
        wlr_cursor_set_xcursor(
4473
            cursor,
4474
            cursor_mgr,
4475
            grabedges == (WLR_EDGE_TOP | WLR_EDGE_LEFT)      ? "nw-resize"
4476
            : grabedges == (WLR_EDGE_TOP | WLR_EDGE_RIGHT)   ? "ne-resize"
4477
            : grabedges == (WLR_EDGE_BOTTOM | WLR_EDGE_LEFT) ? "sw-resize"
4478
                                                             : "se-resize"
4479
        );
4480
        break;
4481
    }
4482
}
4483
4484
/* Track a new request to capture window-manager shortcuts. */
4485
void new_shortcuts_inhibitor(struct wl_listener *listener, void *data) {
4486
    update_shortcuts_inhibitors(seat->keyboard_state.focused_surface);
4487
}
4488
4489
/* Activate only the shortcut inhibitor belonging to the focused surface. */
4490
void update_shortcuts_inhibitors(struct wlr_surface *surface) {
4491
    struct wlr_keyboard_shortcuts_inhibitor_v1 *inhibitor;
4492
4493
    wl_list_for_each(inhibitor, &kb_inhibit_mgr->inhibitors, link) {
4494
        if (inhibitor->surface == surface) {
4495
            if (!inhibitor->active)
4496
                wlr_keyboard_shortcuts_inhibitor_v1_activate(inhibitor);
4497
        } else if (inhibitor->active) {
4498
            wlr_keyboard_shortcuts_inhibitor_v1_deactivate(inhibitor);
4499
        }
4500
    }
4501
}
4502
4503
/* Return whether the focused application currently captures shortcuts. */
4504
bool shortcuts_inhibited(void) {
4505
    struct wlr_surface                         *surface = seat->keyboard_state.focused_surface;
4506
    struct wlr_keyboard_shortcuts_inhibitor_v1 *inhibitor;
4507
4508
    if (!surface)
4509
        return false;
4510
    wl_list_for_each(inhibitor, &kb_inhibit_mgr->inhibitors, link) {
4511
        if (inhibitor->surface == surface && inhibitor->active)
4512
            return true;
4513
    }
4514
    return false;
4515
}
4516
4517
/* Apply a display configuration requested by an external client. */
4518
void output_manager_apply(struct wl_listener *listener, void *data) {
4519
    struct wlr_output_configuration_v1 *config = data;
4520
4521
    output_manager_apply_or_test(config, 0);
4522
}
4523
4524
/* Validate a requested display configuration and optionally apply it. */
4525
void output_manager_apply_or_test(struct wlr_output_configuration_v1 *config, bool test) {
4526
    /* Test or apply a display change requested by a tool such as wlr-randr.
4527
     * update_monitors() updates local state after the layout reports the change. */
4528
    struct wlr_output_configuration_head_v1 *config_head;
4529
    size_t                                   states_len = wl_list_length(&config->heads), i = 0;
4530
    int                                      ok;
4531
4532
    if (!states_len || states_len > MAX_MONITORS) {
4533
        wlr_output_configuration_v1_send_failed(config);
4534
        wlr_output_configuration_v1_destroy(config);
4535
        return;
4536
    }
4537
    wl_list_for_each(config_head, &config->heads, link) {
4538
        output_state_pool[i].output = config_head->state.output;
4539
        wlr_output_state_init(&output_state_pool[i].base);
4540
        wlr_output_head_v1_state_apply(&config_head->state, &output_state_pool[i].base);
4541
        i++;
4542
    }
4543
    ok = wlr_backend_test(backend, output_state_pool, states_len);
4544
4545
    if (ok && !test)
4546
        ok = wlr_backend_commit(backend, output_state_pool, states_len);
4547
4548
    for (i = 0; i < states_len; i++)
4549
        wlr_output_state_finish(&output_state_pool[i].base);
4550
4551
    if (ok && !test) {
4552
        wl_list_for_each(config_head, &config->heads, link) {
4553
            struct wlr_output *wlr_output = config_head->state.output;
4554
            monitor_t         *m          = wlr_output->data;
4555
4556
            /* An applied output-management configuration supersedes DPMS. */
4557
            m->asleep = 0;
4558
            /* Don't move disabled outputs or positions which did not change. */
4559
            if (wlr_output->enabled &&
4560
                (m->m.x != config_head->state.x || m->m.y != config_head->state.y))
4561
                wlr_output_layout_add(
4562
                    output_layout, wlr_output, config_head->state.x, config_head->state.y
4563
                );
4564
        }
4565
    }
4566
    if (ok)
4567
        wlr_output_configuration_v1_send_succeeded(config);
4568
    else
4569
        wlr_output_configuration_v1_send_failed(config);
4570
4571
    wlr_output_configuration_v1_destroy(config);
4572
4573
    if (!test && ok)
4574
        update_monitors(nullptr, nullptr);
4575
}
4576
4577
/* Validate a requested display configuration without applying it. */
4578
void output_manager_test(struct wl_listener *listener, void *data) {
4579
    struct wlr_output_configuration_v1 *config = data;
4580
4581
    output_manager_apply_or_test(config, 1);
4582
}
4583
4584
/* Notify applications when the pointer enters, moves within, or leaves a surface. */
4585
void pointer_focus(
4586
    client_t *c, struct wlr_surface *surface, double sx, double sy, uint32_t time, bool refocus
4587
) {
4588
    struct wlr_pointer_constraint_v1 *constraint, *focused = nullptr;
4589
    struct timespec                   now;
4590
4591
    if (surface != seat->pointer_state.focused_surface && sloppyfocus && refocus && c &&
4592
        !client_is_unmanaged(c))
4593
        focus_client(c, 0);
4594
4595
    /* Clear pointer focus when there is no surface. */
4596
    if (!surface) {
4597
        cursor_constrain(nullptr);
4598
        wlr_seat_pointer_notify_clear_focus(seat);
4599
        return;
4600
    }
4601
    if (!time) {
4602
        clock_gettime(CLOCK_MONOTONIC, &now);
4603
        time = now.tv_sec * 1000 + now.tv_nsec / 1000000;
4604
    }
4605
    /* Let the client know that the mouse cursor has entered one
4606
     * of its surfaces, and make keyboard focus follow if desired.
4607
     * wlroots does nothing if the surface is already focused. */
4608
    wlr_seat_pointer_notify_enter(seat, surface, sx, sy);
4609
    wlr_seat_pointer_notify_motion(seat, time, sx, sy);
4610
    wl_list_for_each(constraint, &pointer_constraints->constraints, link) {
4611
        if (constraint->surface == surface) {
4612
            focused = constraint;
4613
            break;
4614
        }
4615
    }
4616
    cursor_constrain(focused);
4617
}
4618
4619
/* Sanitize a field for status output, leaving an unknown value empty. */
4620
static void status_field(char *dst, size_t size, const char *src) {
4621
    swm_sanitize_field(dst, size, src, nullptr);
4622
}
4623
4624
/* Publish visible window rectangles in slurp's predefined-region format. */
4625
void publish_windows(const char *runtime) {
4626
    client_t *c;
4627
    bool      floating;
4628
    FILE     *file;
4629
    char      path[MAX_STATE_PATH], tmppath[MAX_STATE_PATH], title[MAX_WINDOW_STATE_FIELD];
4630
4631
    if (!runtime || snprintf(path, sizeof(path), "%s/swm-windows", runtime) >= (int)sizeof(path) ||
4632
        !(file = file_replace_begin(path, tmppath, sizeof(tmppath))))
4633
        return;
4634
4635
    /* slurp resolves equal overlapping regions in input order. Publish the
4636
     * floating layer last so its windows remain selectable above tiled windows. */
4637
    for (floating = false;; floating = true) {
4638
        wl_list_for_each(c, &clients, link) {
4639
            if (c->is_floating != floating || !c->mon || !client_surface(c)->mapped ||
4640
                !c->scene->node.enabled)
4641
                continue;
4642
            status_field(title, sizeof(title), client_get_title(c));
4643
            fprintf(
4644
                file, "%d,%d %dx%d %s\n", c->geom.x, c->geom.y, c->geom.width, c->geom.height, title
4645
            );
4646
        }
4647
        if (floating)
4648
            break;
4649
    }
4650
    file_replace_commit(file, tmppath, path);
4651
}
4652
4653
/* Publish the status once the current batch of changes has settled. */
4654
void status_idle_notify(void *data) {
4655
    status_idle = nullptr;
4656
    publish_status();
4657
}
4658
4659
/* Request a status publish. A single change often touches focus, layout and
4660
 * workspaces in turn, and each step would otherwise rewrite the state files and
4661
 * resend every window's state to taskbars. Coalesce them into one publish at
4662
 * the end of the event loop iteration. */
4663
void print_status(void) {
4664
    if (status_idle)
4665
        return;
4666
4667
    /* Without an event loop to defer to, publish now. */
4668
    if (!event_loop ||
4669
        !(status_idle = wl_event_loop_add_idle(event_loop, status_idle_notify, nullptr)))
4670
        publish_status();
4671
}
4672
4673
/* Publish current display, workspace, and focus state on standard output. */
4674
void publish_status(void) {
4675
    monitor_t  *m = nullptr;
4676
    client_t   *c;
4677
    FILE       *titlefile;
4678
    char        titlepath[MAX_STATE_PATH], tmppath[MAX_STATE_PATH];
4679
    char        title[MAX_STATUS_FIELD], appid[MAX_STATUS_FIELD];
4680
    const char *runtime, *src;
4681
    uint32_t    occ, urg;
4682
    int         i;
4683
4684
    occ = urg = 0;
4685
    wl_list_for_each(c, &clients, link) {
4686
        ftl_sync(c);
4687
4688
        if (!c->ws)
4689
            continue;
4690
        occ |= 1u << c->ws->idx;
4691
4692
        if (c->is_urgent)
4693
            urg |= 1u << c->ws->idx;
4694
    }
4695
    wl_list_for_each(m, &mons, link) {
4696
        if ((c = focus_top(m))) {
4697
            status_field(title, sizeof(title), client_get_title(c));
4698
            status_field(appid, sizeof(appid), client_get_appid(c));
4699
            printf("%s title %s\n", m->wlr_output->name, title);
4700
            printf("%s appid %s\n", m->wlr_output->name, appid);
4701
            printf("%s fullscreen %d\n", m->wlr_output->name, c->is_fullscreen);
4702
            printf("%s floating %d\n", m->wlr_output->name, c->is_floating);
4703
        } else {
4704
            printf("%s title \n", m->wlr_output->name);
4705
            printf("%s appid \n", m->wlr_output->name);
4706
            printf("%s fullscreen \n", m->wlr_output->name);
4707
            printf("%s floating \n", m->wlr_output->name);
4708
        }
4709
        printf("%s selmon %u\n", m->wlr_output->name, m == selmon);
4710
        printf("%s workspace %s\n", m->wlr_output->name, m->ws ? m->ws->name : "");
4711
        printf(
4712
            "%s tags %" PRIu32 " %" PRIu32 " %" PRIu32 " %" PRIu32 "\n",
4713
            m->wlr_output->name,
4714
            occ,
4715
            m->ws ? 1u << m->ws->idx : 0,
4716
            m->ws ? 1u << m->ws->idx : 0,
4717
            urg
4718
        );
4719
    }
4720
    for (i = 0; i < WSCOUNT; i++) {
4721
        status_field(title, sizeof(title), workspaces[i].title);
4722
        printf("workspace %d title %s\n", i + 1, title);
4723
        if (workspaces[i].color)
4724
            printf("workspace %d color #%08" PRIx32 "\n", i + 1, workspaces[i].color);
4725
        else
4726
            printf("workspace %d color \n", i + 1);
4727
    }
4728
    runtime = getenv("XDG_RUNTIME_DIR");
4729
    publish_windows(runtime);
4730
4731
    if (runtime && selmon) {
4732
        src = (c = focus_top(selmon)) ? client_get_title(c) : "";
4733
        status_field(title, sizeof(title), src);
4734
        snprintf(titlepath, sizeof(titlepath), "%s/swm-title", runtime);
4735
4736
        if ((titlefile = file_replace_begin(titlepath, tmppath, sizeof(tmppath)))) {
4737
            fprintf(titlefile, "%s\n", title);
4738
            file_replace_commit(titlefile, tmppath, titlepath);
4739
        }
4740
    }
4741
    fflush(stdout);
4742
    workspace_broadcast();
4743
}
4744
4745
/* Replace a command child with its program or an error notification. */
4746
[[noreturn]] void execute_command(char *const argv[], const char *context) {
4747
    char message[MAX_COMMAND_SIZE];
4748
    int  error;
4749
4750
    execvp(argv[0], argv);
4751
    error = errno;
4752
    fprintf(stderr, "swm: %s: cannot execute %s: %s\n", context, argv[0], strerror(error));
4753
    snprintf(message, sizeof(message), "Cannot execute %s: %s", argv[0], strerror(error));
4754
    execlp(
4755
        "notify-send",
4756
        "notify-send",
4757
        "--app-name=swm",
4758
        "--urgency=critical",
4759
        "Command failed",
4760
        message,
4761
        nullptr
4762
    );
4763
    _exit(127);
4764
}
4765
4766
/* Restore process state that child commands should not inherit from swm. */
4767
void prepare_child(void) {
4768
    if (sigprocmask(SIG_SETMASK, &original_signal_mask, nullptr) < 0)
4769
        die("sigprocmask:");
4770
    signal(SIGPIPE, SIG_DFL);
4771
}
4772
4773
/* Turn a display on or off at an external client's request. */
4774
void power_manager_set_mode(struct wl_listener *listener, void *data) {
4775
    struct wlr_output_power_v1_set_mode_event *event = data;
4776
    struct wlr_output_state                    state = {};
4777
    monitor_t                                 *m     = event->output->data;
4778
4779
    if (!m)
4780
        return;
4781
4782
    m->gamma_lut_changed = 1; /* Reapply gamma LUT when re-enabling the output */
4783
    wlr_output_state_set_enabled(&state, event->mode);
4784
    wlr_output_commit_state(m->wlr_output, &state);
4785
4786
    m->asleep = !event->mode;
4787
    update_monitors(nullptr, nullptr);
4788
}
4789
4790
/* Stop the Wayland event loop and begin shutdown. */
4791
void quit(const arg_t *arg) {
4792
    wl_display_terminate(dpy);
4793
}
4794
4795
/* Draw a display when its next frame is due. */
4796
void render_monitor(struct wl_listener *listener, void *data) {
4797
    /* This function is called every time an output is ready to display a frame,
4798
     * generally at the output's refresh rate (e.g. 60Hz). */
4799
    monitor_t              *m = wl_container_of(listener, m, frame);
4800
    client_t               *c;
4801
    struct wlr_output_state pending = {};
4802
    struct timespec         now;
4803
4804
    /* Render if no XDG clients have an outstanding resize and are visible on
4805
     * this monitor. */
4806
    wl_list_for_each(c, &clients, link) {
4807
        if (c->resize && !c->is_floating && client_is_rendered_on_mon(c, m) &&
4808
            !client_is_stopped(c))
4809
            goto skip;
4810
    }
4811
    wlr_scene_output_commit(m->scene_output, nullptr);
4812
4813
skip:
4814
    /* Tell applications that the frame has been drawn. */
4815
    clock_gettime(CLOCK_MONOTONIC, &now);
4816
    wlr_scene_output_send_frame_done(m->scene_output, &now);
4817
    wlr_output_state_finish(&pending);
4818
}
4819
4820
/* Choose and send the border-decoration mode for a Wayland window. */
4821
void request_decoration_mode(struct wl_listener *listener, void *data) {
4822
    client_t                                *c = wl_container_of(listener, c, set_decoration_mode);
4823
    unsigned int                             oldbw = c->bw;
4824
    enum wlr_xdg_toplevel_decoration_v1_mode mode =
4825
        c->decoration->requested_mode == WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE
4826
            ? WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE
4827
            : WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;
4828
4829
    c->bw = client_border_width(c);
4830
    /* Keep the requested border mode until the surface is initialized and
4831
     * wlroots can send it to the application. */
4832
    if (c->surface.xdg->initialized) {
4833
        wlr_xdg_toplevel_decoration_v1_set_mode(c->decoration, mode);
4834
4835
        if (oldbw != c->bw && client_surface(c)->mapped && c->scene)
4836
            resize(c, c->geom, 0);
4837
    }
4838
}
4839
4840
/* Accept a drag only when its button press has a valid serial number. */
4841
void request_start_drag(struct wl_listener *listener, void *data) {
4842
    struct wlr_seat_request_start_drag_event *event = data;
4843
4844
    if (wlr_seat_validate_pointer_grab_serial(seat, event->origin, event->serial))
4845
        wlr_seat_start_pointer_drag(seat, event->drag, event->serial);
4846
    else
4847
        wlr_data_source_destroy(event->drag->source);
4848
}
4849
4850
/* Commit a display-state change requested by the backend. */
4851
void request_monitor_state(struct wl_listener *listener, void *data) {
4852
    struct wlr_output_event_request_state *event = data;
4853
4854
    wlr_output_commit_state(event->output, event->state);
4855
    update_monitors(nullptr, nullptr);
4856
}
4857
4858
/* Request a new window geometry and apply it when the application is ready. */
4859
void resize(client_t *c, struct wlr_box geo, bool interact) {
4860
    struct wlr_box *bbox;
4861
4862
    if (!c->mon || !client_surface(c)->mapped)
4863
        return;
4864
4865
    bbox = interact ? &sgeom : &c->mon->w;
4866
4867
    geo.width  = MAX(1 + 2 * (int)c->bw, geo.width);
4868
    geo.height = MAX(1 + 2 * (int)c->bw, geo.height);
4869
    client_set_bounds(c, geo.width, geo.height);
4870
    c->geom = geo;
4871
    apply_bounds(c, bbox);
4872
4873
    /* During an interactive Wayland resize, keep drawing the old size until
4874
     * the application confirms the new one. XWayland resizes immediately. */
4875
    c->resize = client_set_size(c, c->geom.width - 2 * c->bw, c->geom.height - 2 * c->bw);
4876
4877
    if (c->resize)
4878
        c->pending_geom = c->geom;
4879
4880
    if (interact && c->resize) {
4881
        return;
4882
    }
4883
    resize_apply(c);
4884
4885
    if (c == focus_top(c->mon))
4886
        position_input_popups();
4887
}
4888
4889
/* Commit a window's geometry to its surface, drawing nodes, and borders. */
4890
void resize_apply(client_t *c) {
4891
    struct wlr_box clip;
4892
4893
    /* Update the window's drawing nodes, including its borders. */
4894
    wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y);
4895
    wlr_scene_node_set_position(&c->scene_surface->node, c->bw, c->bw);
4896
    wlr_scene_rect_set_size(c->border[0], c->geom.width, c->bw);
4897
    wlr_scene_rect_set_size(c->border[1], c->geom.width, c->bw);
4898
    wlr_scene_rect_set_size(c->border[2], c->bw, c->geom.height - 2 * c->bw);
4899
    wlr_scene_rect_set_size(c->border[3], c->bw, c->geom.height - 2 * c->bw);
4900
    wlr_scene_node_set_position(&c->border[1]->node, 0, c->geom.height - c->bw);
4901
    wlr_scene_node_set_position(&c->border[2]->node, 0, c->bw);
4902
    wlr_scene_node_set_position(&c->border[3]->node, c->geom.width - c->bw, c->bw);
4903
4904
    client_get_clip(c, &clip);
4905
    wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip);
4906
}
4907
4908
/* Start the backend and child commands, then run the Wayland event loop. */
4909
void run(char *startup_cmd) {
4910
    /* Add a Unix socket to the Wayland display. */
4911
    const char *socket = wl_display_add_socket_auto(dpy);
4912
4913
    if (!socket)
4914
        die("startup: display_add_socket_auto");
4915
    setenv("WAYLAND_DISPLAY", socket, 1);
4916
    setenv("XDG_CURRENT_DESKTOP", "swm", 1);
4917
4918
    /* Start the backend. This will enumerate outputs and inputs, become the DRM
4919
     * master, and control the display devices. */
4920
    if (!wlr_backend_start(backend))
4921
        die("startup: backend_start");
4922
4923
    /* Now that the socket exists and the backend is started, run the
4924
     * autostart commands and the startup command. */
4925
    if (!getenv("SWM_NO_AUTOSTART"))
4926
        autostart_exec();
4927
4928
    if (startup_cmd) {
4929
        int piperw[2];
4930
4931
        if (pipe(piperw) < 0)
4932
            die("startup: pipe:");
4933
4934
        if ((child_pid = fork()) < 0)
4935
            die("startup: fork:");
4936
4937
        if (child_pid == 0) {
4938
            prepare_child();
4939
            setsid();
4940
            dup2(piperw[0], STDIN_FILENO);
4941
            close(piperw[0]);
4942
            close(piperw[1]);
4943
            execl("/bin/sh", "/bin/sh", "-c", startup_cmd, nullptr);
4944
            die("startup: execl:");
4945
        }
4946
        dup2(piperw[1], STDOUT_FILENO);
4947
        close(piperw[1]);
4948
        close(piperw[0]);
4949
    }
4950
    /* Mark stdout as non-blocking to avoid the startup script
4951
     * causing swm to freeze when a user neither closes stdin
4952
     * nor reads standard input in the startup script. */
4953
4954
    if (fd_set_nonblock(STDOUT_FILENO) < 0)
4955
        close(STDOUT_FILENO);
4956
4957
    print_status();
4958
4959
    /* At this point the outputs are initialized, choose initial selmon based on
4960
     * cursor position, and set the default cursor image. */
4961
    selmon = point_to_monitor(cursor->x, cursor->y);
4962
4963
    /* TODO: Avoid the cursor briefly appearing at (0, 0) during startup. */
4964
    wlr_cursor_warp_closest(cursor, nullptr, cursor->x, cursor->y);
4965
    wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
4966
4967
    /* Run the Wayland event loop. This does not return until you exit the
4968
     * compositor. Starting the backend rigged up all of the necessary event
4969
     * loop configuration to listen to libinput events, DRM events, generate
4970
     * frame events at the refresh rate, and so on. */
4971
    wl_display_run(dpy);
4972
}
4973
4974
/* Use the cursor image requested by the application under the pointer. */
4975
void set_cursor(struct wl_listener *listener, void *data) {
4976
    /* An application supplied a new cursor image. */
4977
    struct wlr_seat_pointer_request_set_cursor_event *event = data;
4978
4979
    /* Keep the move or resize cursor until the pointer grab ends. The
4980
     * application will request its cursor again when the pointer re-enters. */
4981
    if ((cursor_mode != CURSOR_NORMAL && cursor_mode != CURSOR_PRESSED) || tiled_resize_hover)
4982
        return;
4983
    /* This can be sent by any client, so we check to make sure this one
4984
     * actually has pointer focus first. If so, we can tell the cursor to
4985
     * use the provided surface as the cursor image. It will set the
4986
     * hardware cursor on the output that it's currently on and continue to
4987
     * do so as the cursor moves between outputs. */
4988
    if (event->seat_client == seat->pointer_state.focused_client)
4989
        wlr_cursor_set_surface(cursor, event->surface, event->hotspot_x, event->hotspot_y);
4990
}
4991
4992
/* Use the named cursor shape requested by the application under the pointer. */
4993
void set_cursor_shape(struct wl_listener *listener, void *data) {
4994
    struct wlr_cursor_shape_manager_v1_request_set_shape_event *event = data;
4995
4996
    if ((cursor_mode != CURSOR_NORMAL && cursor_mode != CURSOR_PRESSED) || tiled_resize_hover)
4997
        return;
4998
    /* This can be sent by any client, so we check to make sure this one
4999
     * actually has pointer focus first. If so, we can tell the cursor to
5000
     * use the provided cursor shape. */
5001
    if (event->seat_client == seat->pointer_state.focused_client)
5002
        wlr_cursor_set_xcursor(cursor, cursor_mgr, wlr_cursor_shape_v1_name(event->shape));
5003
}
5004
5005
/* Move a window between tiled and floating layout behavior. */
5006
void set_floating(client_t *c, bool floating) {
5007
    client_t *p    = client_get_parent(c);
5008
    c->is_floating = floating;
5009
    c->bw          = client_border_width(c);
5010
    client_set_tiled(
5011
        c, floating ? 0 : WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT
5012
    );
5013
    /* Preserve the window's layer when the whole layout is floating. */
5014
    if (!c->mon || !client_surface(c)->mapped || !c->mon->ws || !c->mon->ws->lt->arrange)
5015
        return;
5016
    wlr_scene_node_reparent(
5017
        &c->scene->node,
5018
        layers
5019
            [c->is_fullscreen || (p && p->is_fullscreen) ? LAYER_FULLSCREEN
5020
             : c->is_floating                            ? LAYER_FLOAT
5021
                                                         : LAYER_TILE]
5022
    );
5023
    resize(c, c->geom, 0);
5024
5025
    if (!c->pending_map) {
5026
        arrange(c->mon);
5027
        print_status();
5028
    }
5029
}
5030
5031
/* Enter or leave fullscreen while preserving the window's previous geometry. */
5032
void set_fullscreen(client_t *c, bool fullscreen) {
5033
    if (c->is_fullscreen == fullscreen) {
5034
        if (c->mon && client_surface(c)->mapped)
5035
            client_set_fullscreen(c, fullscreen);
5036
        return;
5037
    }
5038
    c->is_fullscreen = fullscreen;
5039
5040
    if (!c->mon || !client_surface(c)->mapped)
5041
        return;
5042
    c->bw = client_border_width(c);
5043
    client_set_fullscreen(c, fullscreen);
5044
    wlr_scene_node_reparent(
5045
        &c->scene->node,
5046
        layers
5047
            [c->is_fullscreen ? LAYER_FULLSCREEN
5048
             : c->is_floating ? LAYER_FLOAT
5049
                              : LAYER_TILE]
5050
    );
5051
5052
    if (fullscreen) {
5053
        c->prev = c->geom;
5054
        resize(c, c->mon->m, 0);
5055
    } else {
5056
        /* restore previous size instead of arrange for floating windows since
5057
         * their positions were chosen by the user. */
5058
        resize(c, c->prev, 0);
5059
    }
5060
    if (!c->pending_map) {
5061
        arrange(c->mon);
5062
        print_status();
5063
    }
5064
}
5065
5066
/* Select the next or previous layout for the focused workspace. */
5067
void cycle_layout(const arg_t *arg) {
5068
    workspace_t *ws;
5069
    bool         cycle[LENGTH(layouts)];
5070
    int          current = -1, next;
5071
    size_t       i;
5072
5073
    if (!selmon || !(ws = selmon->ws))
5074
        return;
5075
5076
    for (i = 0; i < LENGTH(layouts); i++)
5077
5078
        if (ws->lt == &layouts[i]) {
5079
            current = (int)i;
5080
            break;
5081
        }
5082
    if (current < 0)
5083
        return;
5084
5085
    for (i = 0; i < LENGTH(layouts); i++)
5086
        cycle[i] = layouts[i].cycle;
5087
    next = swm_next_layout(current, (int)LENGTH(layouts), cycle);
5088
5089
    if (next < 0)
5090
        return;
5091
    ws->prevlt = ws->lt;
5092
    ws->lt     = &layouts[next];
5093
    arrange(selmon);
5094
    print_status();
5095
}
5096
5097
/* Select a neighboring workspace, optionally moving the focused window with it. */
5098
void cycle_workspace(const arg_t *arg) {
5099
    /* Handle next/previous workspaces, optionally including empty ones or
5100
     * taking the focused window along. Workspaces visible on other outputs
5101
     * are skipped. */
5102
    int dir =
5103
        (arg->i == WORKSPACE_NEXT || arg->i == WORKSPACE_NEXT_ALL || arg->i == WORKSPACE_NEXT_MOVE)
5104
            ? +1
5105
            : -1;
5106
    int          allowempty = (arg->i != WORKSPACE_NEXT && arg->i != WORKSPACE_PREVIOUS);
5107
    int          mv         = (arg->i == WORKSPACE_NEXT_MOVE || arg->i == WORKSPACE_PREVIOUS_MOVE);
5108
    workspace_t *ws, *cur;
5109
    client_t    *c, *sel;
5110
    bool         visible_elsewhere[WSCOUNT] = {};
5111
    bool         occupied[WSCOUNT]          = {};
5112
    int          i, idx;
5113
5114
    if (!selmon || !(cur = selmon->ws))
5115
        return;
5116
    sel = focus_top(selmon);
5117
5118
    for (i = 0; i < WSCOUNT; i++)
5119
        visible_elsewhere[i] = workspaces[i].mon && workspaces[i].mon != selmon;
5120
    wl_list_for_each(c, &clients, link) if (c->ws) occupied[c->ws->idx] = 1;
5121
    idx = swm_workspace_next(cur->idx, WSCOUNT, dir, allowempty, visible_elsewhere, occupied);
5122
5123
    if (idx < 0)
5124
        return;
5125
    ws = &workspaces[idx];
5126
5127
    if (mv && sel)
5128
        set_workspace(sel, ws);
5129
    view_workspace(ws, selmon);
5130
5131
    if (mv && sel)
5132
        focus_client(sel, 1);
5133
}
5134
5135
/* Fit a window to the display it was just moved to. */
5136
void client_place(client_t *c) {
5137
    /* An initial commit may assign a monitor before map_notify() creates the
5138
     * client's scene tree. Defer all scene operations until the surface maps. */
5139
    if (!c->mon || !client_surface(c)->mapped)
5140
        return;
5141
5142
    /* Keep the window overlapping its display. */
5143
    if (c->is_fullscreen) {
5144
        client_set_fullscreen(c, 1);
5145
        wlr_scene_node_reparent(&c->scene->node, layers[LAYER_FULLSCREEN]);
5146
        resize(c, c->mon->m, 0);
5147
    } else {
5148
        resize(c, c->geom, 0);
5149
        set_floating(c, c->is_floating);
5150
    }
5151
}
5152
5153
/* Move a window to a display and keep its workspace and scale consistent. */
5154
void set_monitor(client_t *c, monitor_t *m, workspace_t *ws) {
5155
    monitor_t *oldmon = c->mon;
5156
5157
    if (!ws)
5158
        ws = m ? m->ws : c->ws;
5159
    c->ws = ws;
5160
    /* If moving to a monitor and the target workspace is already visible,
5161
     * use that monitor. A nullptr monitor explicitly detaches an unmapped client
5162
     * and must not be redirected back onto the workspace's monitor. */
5163
    if (m && ws && ws->mon)
5164
        m = ws->mon;
5165
5166
    if (oldmon == m) {
5167
        if (m)
5168
            arrange(m);
5169
        return;
5170
    }
5171
    c->mon = m;
5172
5173
    if (!c->is_fullscreen)
5174
        c->prev = c->geom;
5175
5176
    /* Moving this node tells applications which displays they overlap. */
5177
    if (oldmon)
5178
        arrange(oldmon);
5179
    client_place(c);
5180
    focus_client(focus_top(selmon), 1);
5181
}
5182
5183
/* Move a window and any related windows to another workspace. */
5184
void set_workspace(client_t *c, workspace_t *ws) {
5185
    /* Move a window to another workspace. */
5186
    client_t  *w;
5187
    monitor_t *oldmon;
5188
    client_t  *main = client_main(c);
5189
5190
    if (!main || !ws || main->ws == ws)
5191
        return;
5192
    oldmon = main->mon;
5193
    wl_list_for_each(w, &clients, link) {
5194
        if (!clients_related(w, main))
5195
            continue;
5196
        w->ws = ws;
5197
5198
        if (ws->mon && ws->mon != w->mon) {
5199
            w->mon = ws->mon;
5200
5201
            if (!w->is_fullscreen)
5202
                w->prev = w->geom;
5203
            client_place(w);
5204
        }
5205
    }
5206
    if (oldmon)
5207
        arrange(oldmon);
5208
5209
    if (ws->mon && ws->mon != oldmon)
5210
        arrange(ws->mon);
5211
    focus_client(focus_top(selmon), 1);
5212
    print_status();
5213
}
5214
5215
/* Show a workspace on a display and synchronize all of its windows. */
5216
void assign_workspace(workspace_t *ws, monitor_t *m) {
5217
    /* Attach a workspace to a display and update all of its windows. This
5218
     * deliberately does less than client_place(): callers arrange the display
5219
     * afterwards, which settles layers and tiled geometry for the whole
5220
     * workspace at once. */
5221
    client_t *c;
5222
5223
    ws->mon = m;
5224
5225
    if (!m)
5226
        return;
5227
    wl_list_for_each(c, &clients, link) {
5228
        if (c->ws != ws)
5229
            continue;
5230
5231
        if (c->mon != m) {
5232
            c->mon = m;
5233
5234
            if (!c->is_fullscreen)
5235
                c->prev = c->geom;
5236
            resize(c, c->geom, 0);
5237
        }
5238
        if (c->is_fullscreen)
5239
            resize(c, m->m, 0);
5240
    }
5241
}
5242
5243
/* Return the first workspace not shown on an output. */
5244
workspace_t *free_workspace(void) {
5245
    workspace_t *ws;
5246
5247
    for (ws = workspaces; ws < END(workspaces); ws++) {
5248
        if (!ws->mon)
5249
            return ws;
5250
    }
5251
    return nullptr;
5252
}
5253
5254
/* Show a workspace, exchanging it with another display when already visible. */
5255
void view_workspace(workspace_t *ws, monitor_t *m) {
5256
    /* show workspace ws on output m; if ws is visible on another output,
5257
     * the two displays exchange workspaces. */
5258
    monitor_t   *other;
5259
    workspace_t *old;
5260
    client_t    *focus;
5261
5262
    if (!m || !ws || ws == m->ws)
5263
        return;
5264
    old = m->ws;
5265
5266
    if ((other = ws->mon)) {
5267
        other->ws                 = old;
5268
        other->previous_workspace = ws;
5269
5270
        if (old)
5271
            assign_workspace(old, other);
5272
    } else if (old) {
5273
        assign_workspace(old, nullptr);
5274
    }
5275
    m->previous_workspace = old;
5276
    m->ws                 = ws;
5277
    assign_workspace(ws, m);
5278
    focus = focus_top(m);
5279
    arrange(m);
5280
5281
    if (other)
5282
        arrange(other);
5283
    /* Pointer focus can change during arrangement. Restore the workspace's
5284
     * most recently focused client after its windows move. */
5285
    focus_client(focus, 1);
5286
    print_status();
5287
}
5288
5289
/* Send one workspace's ephemeral metadata to a control client. */
5290
static void workspace_metadata_send(struct wl_resource *resource, workspace_t *ws) {
5291
    uint32_t flags = 0;
5292
5293
    if (ws->title[0])
5294
        flags |= SWM_WORKSPACE_MANAGER_V1_METADATA_TITLE;
5295
    if (ws->color)
5296
        flags |= SWM_WORKSPACE_MANAGER_V1_METADATA_COLOR;
5297
    if (selmon && selmon->ws == ws)
5298
        flags |= SWM_WORKSPACE_MANAGER_V1_METADATA_SELECTED;
5299
    swm_workspace_manager_v1_send_metadata(
5300
        resource, (uint32_t)ws->idx + 1, flags, ws->title, ws->color
5301
    );
5302
}
5303
5304
/* Send a complete workspace metadata snapshot to one control client. */
5305
static void workspace_metadata_snapshot(struct wl_resource *resource) {
5306
    int i;
5307
5308
    for (i = 0; i < WSCOUNT; i++)
5309
        workspace_metadata_send(resource, &workspaces[i]);
5310
    swm_workspace_manager_v1_send_done(resource);
5311
}
5312
5313
/* Publish all workspace metadata to every connected control client. */
5314
static void workspace_metadata_broadcast(void) {
5315
    metadata_manager_t *mgr;
5316
5317
    wl_list_for_each(mgr, &metadata_managers, link) workspace_metadata_snapshot(mgr->resource);
5318
}
5319
5320
/* Return a requested workspace or report a protocol error. */
5321
static workspace_t *workspace_metadata_get(struct wl_resource *resource, uint32_t number) {
5322
    if (!number || number > WSCOUNT) {
5323
        wl_resource_post_error(
5324
            resource,
5325
            SWM_WORKSPACE_MANAGER_V1_ERROR_INVALID_WORKSPACE,
5326
            "workspace %" PRIu32 " is outside 1..%d",
5327
            number,
5328
            WSCOUNT
5329
        );
5330
        return nullptr;
5331
    }
5332
    return &workspaces[number - 1];
5333
}
5334
5335
/* Change an ephemeral workspace title. */
5336
static void workspace_metadata_set_title(
5337
    struct wl_client *client, struct wl_resource *resource, uint32_t number, const char *title
5338
) {
5339
    workspace_t *ws = workspace_metadata_get(resource, number);
5340
5341
    if (!ws)
5342
        return;
5343
    if (strlen(title) >= sizeof(ws->title)) {
5344
        wl_resource_post_error(
5345
            resource,
5346
            SWM_WORKSPACE_MANAGER_V1_ERROR_TITLE_TOO_LONG,
5347
            "workspace title exceeds %zu bytes",
5348
            sizeof(ws->title) - 1
5349
        );
5350
        return;
5351
    }
5352
    if (!strcmp(ws->title, title))
5353
        return;
5354
    strcpy(ws->title, title);
5355
    print_status();
5356
}
5357
5358
/* Change an ephemeral workspace color. */
5359
static void workspace_metadata_set_color(
5360
    struct wl_client *client, struct wl_resource *resource, uint32_t number, uint32_t color
5361
) {
5362
    workspace_t *ws = workspace_metadata_get(resource, number);
5363
5364
    if (!ws)
5365
        return;
5366
    if (ws->color == color)
5367
        return;
5368
    ws->color = color;
5369
    print_status();
5370
}
5371
5372
/* Release an ephemeral workspace metadata manager. */
5373
static void workspace_metadata_destroy_request(
5374
    struct wl_client *client, struct wl_resource *resource
5375
) {
5376
    wl_resource_destroy(resource);
5377
}
5378
5379
static const struct swm_workspace_manager_v1_interface workspace_metadata_impl = {
5380
    .set_title = workspace_metadata_set_title,
5381
    .set_color = workspace_metadata_set_color,
5382
    .destroy   = workspace_metadata_destroy_request,
5383
};
5384
5385
/* Stop publishing workspace metadata after a client disconnects. */
5386
static void workspace_metadata_destroy(struct wl_resource *resource) {
5387
    metadata_manager_t *mgr = wl_resource_get_user_data(resource);
5388
5389
    if (!mgr)
5390
        return;
5391
    wl_list_remove(&mgr->link);
5392
    pool_release(&metadata_manager_pool, mgr);
5393
}
5394
5395
/* Publish all ephemeral workspace metadata to a newly connected client. */
5396
static void workspace_metadata_bind(
5397
    struct wl_client *client, void *data, uint32_t version, uint32_t id
5398
) {
5399
    metadata_manager_t *mgr = pool_take(&metadata_manager_pool);
5400
5401
    if (!mgr) {
5402
        wl_client_post_no_memory(client);
5403
        return;
5404
    }
5405
    mgr->resource =
5406
        wl_resource_create(client, &swm_workspace_manager_v1_interface, (int)version, id);
5407
5408
    if (!mgr->resource) {
5409
        pool_release(&metadata_manager_pool, mgr);
5410
        wl_client_post_no_memory(client);
5411
        return;
5412
    }
5413
    wl_resource_set_implementation(
5414
        mgr->resource, &workspace_metadata_impl, mgr, workspace_metadata_destroy
5415
    );
5416
    wl_list_insert(&metadata_managers, &mgr->link);
5417
    workspace_metadata_snapshot(mgr->resource);
5418
}
5419
5420
/* Show and focus a toplevel selected by its ext-foreign-toplevel identifier. */
5421
static void toplevel_control_activate(
5422
    struct wl_client *client, struct wl_resource *resource, const char *identifier
5423
) {
5424
    client_t *c;
5425
5426
    wl_list_for_each(c, &clients, link) {
5427
        if (!c->extftl || strcmp(c->extftl->identifier, identifier))
5428
            continue;
5429
        if (c->ws && c->ws->mon != selmon)
5430
            view_workspace(c->ws, selmon);
5431
        focus_client(c, 1);
5432
        return;
5433
    }
5434
    wl_resource_post_error(
5435
        resource,
5436
        SWM_TOPLEVEL_MANAGER_V1_ERROR_INVALID_IDENTIFIER,
5437
        "toplevel identifier '%s' does not exist",
5438
        identifier
5439
    );
5440
}
5441
5442
/* Release a toplevel control manager. */
5443
static void toplevel_control_destroy(struct wl_client *client, struct wl_resource *resource) {
5444
    wl_resource_destroy(resource);
5445
}
5446
5447
static const struct swm_toplevel_manager_v1_interface toplevel_control_impl = {
5448
    .activate = toplevel_control_activate,
5449
    .destroy  = toplevel_control_destroy,
5450
};
5451
5452
/* Publish swm's toplevel control interface to a client. */
5453
static void toplevel_control_bind(
5454
    struct wl_client *client, void *data, uint32_t version, uint32_t id
5455
) {
5456
    struct wl_resource *resource =
5457
        wl_resource_create(client, &swm_toplevel_manager_v1_interface, (int)version, id);
5458
5459
    if (!resource) {
5460
        wl_client_post_no_memory(client);
5461
        return;
5462
    }
5463
    wl_resource_set_implementation(resource, &toplevel_control_impl, nullptr, nullptr);
5464
}
5465
5466
/*
5467
 * ext-workspace-v1 implementation. wlroots 0.19 ships no helper for this
5468
 * protocol, so the resources are managed by hand. swm advertises a single
5469
 * workspace group spanning all outputs, containing every workspace; the
5470
 * only capability is activate, which behaves like a workspace switch.
5471
 */
5472
static void workspace_manager_commit(struct wl_client *client, struct wl_resource *resource) {
5473
    workspace_manager_t *mgr = wl_resource_get_user_data(resource);
5474
    workspace_t         *ws;
5475
5476
    if (!mgr || !(ws = mgr->pending))
5477
        return;
5478
    mgr->pending = nullptr;
5479
    view_workspace(ws, selmon);
5480
}
5481
5482
/* Stop publishing workspaces to a manager. */
5483
static void workspace_manager_stop(struct wl_client *client, struct wl_resource *resource) {
5484
    ext_workspace_manager_v1_send_finished(resource);
5485
    wl_resource_destroy(resource);
5486
}
5487
5488
static const struct ext_workspace_manager_v1_interface ws_manager_impl = {
5489
    .commit = workspace_manager_commit,
5490
    .stop   = workspace_manager_stop,
5491
};
5492
5493
/* Stop publishing workspaces after a manager client disconnects. */
5494
static void workspace_manager_destroy(struct wl_resource *resource) {
5495
    workspace_manager_t *mgr = wl_resource_get_user_data(resource);
5496
    workspace_handle_t  *h;
5497
    int                  i;
5498
5499
    if (!mgr)
5500
        return;
5501
    /* Handles and the group may outlive the manager resource. */
5502
    for (i = 0; i < WSCOUNT; i++) {
5503
        wl_list_for_each(h, &workspaces[i].handles, link) {
5504
            if (h->mgr == mgr)
5505
                h->mgr = nullptr;
5506
        }
5507
    }
5508
    if (mgr->group)
5509
        wl_resource_set_user_data(mgr->group, nullptr);
5510
    wl_list_remove(&mgr->link);
5511
    pool_release(&workspace_manager_pool, mgr);
5512
}
5513
5514
/* Reject a client-created workspace. */
5515
static void workspace_group_create_workspace(
5516
    struct wl_client *client, struct wl_resource *resource, const char *name
5517
) {
5518
    /* Ignore creation because swm has a fixed set of workspaces. */
5519
}
5520
5521
/* Reject destruction of the global workspace group. */
5522
static void workspace_group_destroy_request(
5523
    struct wl_client *client, struct wl_resource *resource
5524
) {
5525
    wl_resource_destroy(resource);
5526
}
5527
5528
static const struct ext_workspace_group_handle_v1_interface ws_group_impl = {
5529
    .create_workspace = workspace_group_create_workspace,
5530
    .destroy          = workspace_group_destroy_request,
5531
};
5532
5533
/* Remove a disconnected client's global workspace group. */
5534
static void workspace_group_destroy(struct wl_resource *resource) {
5535
    workspace_manager_t *mgr = wl_resource_get_user_data(resource);
5536
5537
    if (mgr)
5538
        mgr->group = nullptr;
5539
}
5540
5541
/* Queue workspace activation. */
5542
static void workspace_activate(struct wl_client *client, struct wl_resource *resource) {
5543
    workspace_handle_t *h = wl_resource_get_user_data(resource);
5544
5545
    if (h && h->mgr)
5546
        h->mgr->pending = h->ws;
5547
}
5548
5549
/* Ignore workspace deactivation. */
5550
static void workspace_deactivate(struct wl_client *client, struct wl_resource *resource) {
5551
    /* Ignore deactivation because swm always has an active workspace. */
5552
}
5553
5554
/* Ignore client workspace assignment. */
5555
static void workspace_assign(
5556
    struct wl_client *client, struct wl_resource *resource, struct wl_resource *group
5557
) {
5558
    /* Ignore assignment because swm has only one workspace group. */
5559
}
5560
5561
/* Ignore client workspace removal. */
5562
static void workspace_remove_request(struct wl_client *client, struct wl_resource *resource) {
5563
    /* Ignore removal because swm has a fixed set of workspaces. */
5564
}
5565
5566
/* Ignore client workspace destruction. */
5567
static void workspace_destroy_request(struct wl_client *client, struct wl_resource *resource) {
5568
    wl_resource_destroy(resource);
5569
}
5570
5571
static const struct ext_workspace_handle_v1_interface ws_handle_impl = {
5572
    .destroy    = workspace_destroy_request,
5573
    .activate   = workspace_activate,
5574
    .deactivate = workspace_deactivate,
5575
    .assign     = workspace_assign,
5576
    .remove     = workspace_remove_request,
5577
};
5578
5579
/* Remove one client's handle for a published workspace. */
5580
static void workspace_handle_destroy(struct wl_resource *resource) {
5581
    workspace_handle_t *h = wl_resource_get_user_data(resource);
5582
5583
    if (!h)
5584
        return;
5585
    wl_list_remove(&h->link);
5586
    pool_release(&workspace_handle_pool, h);
5587
}
5588
5589
/* Publish one workspace to a connected workspace manager. */
5590
void workspace_handle_create(workspace_manager_t *mgr, workspace_t *ws) {
5591
    workspace_handle_t *h = pool_take(&workspace_handle_pool);
5592
    uint32_t            coord;
5593
    struct wl_array     coords = { sizeof(coord), sizeof(coord), &coord };
5594
    char                id[MAX_WS_ID];
5595
5596
    if (!h) {
5597
        wl_client_post_no_memory(wl_resource_get_client(mgr->resource));
5598
        return;
5599
    }
5600
    h->ws       = ws;
5601
    h->mgr      = mgr;
5602
    h->resource = wl_resource_create(
5603
        wl_resource_get_client(mgr->resource), &ext_workspace_handle_v1_interface, 1, 0
5604
    );
5605
5606
    if (!h->resource) {
5607
        pool_release(&workspace_handle_pool, h);
5608
        wl_client_post_no_memory(wl_resource_get_client(mgr->resource));
5609
        return;
5610
    }
5611
    wl_resource_set_implementation(h->resource, &ws_handle_impl, h, workspace_handle_destroy);
5612
    wl_list_insert(&ws->handles, &h->link);
5613
5614
    ext_workspace_manager_v1_send_workspace(mgr->resource, h->resource);
5615
    snprintf(id, sizeof(id), "ws%d", ws->idx + 1);
5616
    ext_workspace_handle_v1_send_id(h->resource, id);
5617
    ext_workspace_handle_v1_send_name(h->resource, ws->name);
5618
    coord = (uint32_t)ws->idx;
5619
    ext_workspace_handle_v1_send_coordinates(h->resource, &coords);
5620
    ext_workspace_handle_v1_send_capabilities(
5621
        h->resource, EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_ACTIVATE
5622
    );
5623
    ext_workspace_handle_v1_send_state(h->resource, workspace_state(ws));
5624
5625
    if (mgr->group)
5626
        ext_workspace_group_handle_v1_send_workspace_enter(mgr->group, h->resource);
5627
}
5628
5629
/* Publish swm's workspace group to a newly connected client. */
5630
void workspace_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) {
5631
    workspace_manager_t *mgr = pool_take(&workspace_manager_pool);
5632
    monitor_t           *m;
5633
    struct wl_resource  *out;
5634
    int                  i;
5635
5636
    if (!mgr) {
5637
        wl_client_post_no_memory(client);
5638
        return;
5639
    }
5640
    mgr->resource =
5641
        wl_resource_create(client, &ext_workspace_manager_v1_interface, (int)version, id);
5642
5643
    if (!mgr->resource) {
5644
        pool_release(&workspace_manager_pool, mgr);
5645
        wl_client_post_no_memory(client);
5646
        return;
5647
    }
5648
    wl_resource_set_implementation(mgr->resource, &ws_manager_impl, mgr, workspace_manager_destroy);
5649
    wl_list_insert(&ws_managers, &mgr->link);
5650
5651
    mgr->group = wl_resource_create(client, &ext_workspace_group_handle_v1_interface, 1, 0);
5652
5653
    if (!mgr->group) {
5654
        wl_client_post_no_memory(client);
5655
        return;
5656
    }
5657
    wl_resource_set_implementation(mgr->group, &ws_group_impl, mgr, workspace_group_destroy);
5658
    ext_workspace_manager_v1_send_workspace_group(mgr->resource, mgr->group);
5659
    ext_workspace_group_handle_v1_send_capabilities(mgr->group, 0);
5660
    wl_list_for_each(m, &mons, link) {
5661
        wl_resource_for_each(out, &m->wlr_output->resources) {
5662
            if (wl_resource_get_client(out) == client)
5663
                ext_workspace_group_handle_v1_send_output_enter(mgr->group, out);
5664
        }
5665
    }
5666
    for (i = 0; i < WSCOUNT; i++)
5667
        workspace_handle_create(mgr, &workspaces[i]);
5668
    ext_workspace_manager_v1_send_done(mgr->resource);
5669
}
5670
5671
/* Add a newly bound display to every published workspace group. */
5672
void workspace_output_bind(struct wl_listener *listener, void *data) {
5673
    /* Tell workspace clients about the newly bound display. */
5674
    struct wlr_output_event_bind *event  = data;
5675
    struct wl_client             *client = wl_resource_get_client(event->resource);
5676
    workspace_manager_t          *mgr;
5677
5678
    wl_list_for_each(mgr, &ws_managers, link) {
5679
        if (mgr->group && wl_resource_get_client(mgr->resource) == client) {
5680
            ext_workspace_group_handle_v1_send_output_enter(mgr->group, event->resource);
5681
            ext_workspace_manager_v1_send_done(mgr->resource);
5682
        }
5683
    }
5684
}
5685
5686
/* Compute and publish a workspace's active, hidden, urgent, and focus state. */
5687
uint32_t workspace_state(workspace_t *ws) {
5688
    _Static_assert(
5689
        (unsigned int)SWM_WORKSPACE_ACTIVE == (unsigned int)EXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE,
5690
        "workspace active bit changed"
5691
    );
5692
    _Static_assert(
5693
        (unsigned int)SWM_WORKSPACE_URGENT == (unsigned int)EXT_WORKSPACE_HANDLE_V1_STATE_URGENT,
5694
        "workspace urgent bit changed"
5695
    );
5696
    _Static_assert(
5697
        (unsigned int)SWM_WORKSPACE_HIDDEN == (unsigned int)EXT_WORKSPACE_HANDLE_V1_STATE_HIDDEN,
5698
        "workspace hidden bit changed"
5699
    );
5700
    client_t *c;
5701
    uint32_t  state;
5702
    int       occupied = 0;
5703
    int       urgent   = 0;
5704
5705
    wl_list_for_each(c, &clients, link) {
5706
        if (c->ws != ws)
5707
            continue;
5708
        occupied  = 1;
5709
        urgent   |= c->is_urgent;
5710
    }
5711
    /* ext-workspace-v1 has no occupied state. Mark empty, inactive
5712
     * workspaces hidden so bars can omit them while retaining occupied and
5713
     * currently selected workspaces. */
5714
    state = swm_workspace_state(ws->mon != nullptr, occupied, urgent);
5715
    return state;
5716
}
5717
5718
/* Publish current workspace state to every connected workspace client. */
5719
void workspace_broadcast(void) {
5720
    workspace_manager_t *mgr;
5721
    workspace_handle_t  *h;
5722
    uint32_t             state;
5723
    int                  i;
5724
5725
    if (!wl_list_empty(&ws_managers)) {
5726
        for (i = 0; i < WSCOUNT; i++) {
5727
            state = workspace_state(&workspaces[i]);
5728
            wl_list_for_each(h, &workspaces[i].handles, link)
5729
                ext_workspace_handle_v1_send_state(h->resource, state);
5730
        }
5731
        wl_list_for_each(mgr, &ws_managers, link) ext_workspace_manager_v1_send_done(mgr->resource);
5732
    }
5733
    workspace_metadata_broadcast();
5734
}
5735
5736
/* Accept an application's request to replace the primary selection. */
5737
void set_primary_selection(struct wl_listener *listener, void *data) {
5738
    /* Honor an application's request to set the primary selection. */
5739
    struct wlr_seat_request_set_primary_selection_event *event = data;
5740
5741
    wlr_seat_set_primary_selection(seat, event->source, event->serial);
5742
}
5743
5744
/* Accept an application's request to replace the clipboard contents. */
5745
void set_selection(struct wl_listener *listener, void *data) {
5746
    /* Honor an application's request to set the clipboard selection. */
5747
    struct wlr_seat_request_set_selection_event *event = data;
5748
5749
    wlr_seat_set_selection(seat, event->source, event->serial);
5750
}
5751
5752
/* Create the Wayland server, protocols, input state, and rendering resources. */
5753
void setup(void) {
5754
    static const int signals[] = { SIGCHLD, SIGINT, SIGTERM };
5755
    int              drm_fd, i;
5756
5757
    wlr_log_init(log_level, nullptr);
5758
5759
    /* Create the Wayland server and its event loop. */
5760
    dpy        = wl_display_create();
5761
    event_loop = wl_display_get_event_loop(dpy);
5762
5763
    if (sigprocmask(SIG_BLOCK, nullptr, &original_signal_mask) < 0)
5764
        die("failed to read signal mask:");
5765
5766
    for (i = 0; i < (int)LENGTH(signals); i++) {
5767
        signal_sources[i] =
5768
            wl_event_loop_add_signal(event_loop, signals[i], handle_signal, nullptr);
5769
5770
        if (!signal_sources[i])
5771
            die("failed to register signal handler");
5772
    }
5773
    signal(SIGPIPE, SIG_IGN);
5774
5775
    /* Choose a backend for the current environment and its input and display devices. */
5776
    if (!(backend = wlr_backend_autocreate(event_loop, &session)))
5777
        die("couldn't create backend");
5778
5779
    /* Create the tree that controls window position and drawing order. */
5780
    scene = wlr_scene_create();
5781
5782
    if (!scene)
5783
        die("failed to create scene");
5784
    root_bg = wlr_scene_rect_create(&scene->tree, 0, 0, rootcolor);
5785
5786
    if (!root_bg)
5787
        die("failed to create root background");
5788
5789
    for (i = 0; i < NUM_LAYERS; i++) {
5790
        layers[i] = wlr_scene_tree_create(&scene->tree);
5791
5792
        if (!layers[i])
5793
            die("failed to create scene layer");
5794
    }
5795
    drag_icon = wlr_scene_tree_create(&scene->tree);
5796
5797
    if (!drag_icon)
5798
        die("failed to create drag icon layer");
5799
    wlr_scene_node_place_below(&drag_icon->node, &layers[LAYER_BLOCK]->node);
5800
5801
    /* Choose a renderer. WLR_RENDERER can override the automatic choice. */
5802
    if (!(drw = wlr_renderer_autocreate(backend)))
5803
        die("couldn't create renderer");
5804
    listen_global(&drw->events.lost, &gpu_reset_listener);
5805
5806
    /* Advertise the buffer types supported by both the renderer and scene code. */
5807
    wlr_renderer_init_wl_shm(drw, dpy);
5808
5809
    if (wlr_renderer_get_texture_formats(drw, WLR_BUFFER_CAP_DMABUF)) {
5810
        wlr_drm_create(dpy, drw);
5811
        wlr_scene_set_linux_dmabuf_v1(scene, wlr_linux_dmabuf_v1_create_with_renderer(dpy, 5, drw));
5812
    }
5813
    if ((drm_fd = wlr_renderer_get_drm_fd(drw)) >= 0 && drw->features.timeline &&
5814
        backend->features.timeline)
5815
        wlr_linux_drm_syncobj_manager_v1_create(dpy, 1, drm_fd);
5816
5817
    /* Create the buffers that connect the renderer to the display backend. */
5818
    if (!(alloc = wlr_allocator_autocreate(backend, drw)))
5819
        die("couldn't create allocator");
5820
5821
    /* Advertise the core protocols for windows, buffers, capture, and clipboard data. */
5822
    compositor = wlr_compositor_create(dpy, 6, drw);
5823
    wlr_subcompositor_create(dpy);
5824
    wlr_data_device_manager_create(dpy);
5825
    wlr_export_dmabuf_manager_v1_create(dpy);
5826
    wlr_screencopy_manager_v1_create(dpy);
5827
    wlr_data_control_manager_v1_create(dpy);
5828
    wlr_ext_data_control_manager_v1_create(dpy, 1);
5829
    wlr_primary_selection_v1_device_manager_create(dpy);
5830
    wlr_viewporter_create(dpy);
5831
    wlr_single_pixel_buffer_manager_v1_create(dpy);
5832
    wlr_fractional_scale_manager_v1_create(dpy, 1);
5833
    wlr_presentation_create(dpy, backend, 2);
5834
    wlr_alpha_modifier_v1_create(dpy);
5835
5836
    /* Accept application requests for attention. */
5837
    activation = wlr_xdg_activation_v1_create(dpy);
5838
    listen_global(&activation->events.request_activate, &request_activate);
5839
5840
    system_bell = wlr_xdg_system_bell_v1_create(dpy, 1);
5841
    listen_global(&system_bell->events.ring, &system_bell_ring);
5842
5843
    wlr_scene_set_gamma_control_manager_v1(scene, wlr_gamma_control_manager_v1_create(dpy));
5844
5845
    power_mgr = wlr_output_power_manager_v1_create(dpy);
5846
    listen_global(&power_mgr->events.set_mode, &output_power_mgr_set_mode);
5847
5848
    /* Track the position of each display in the combined desktop. */
5849
    output_layout = wlr_output_layout_create(dpy);
5850
    listen_global(&output_layout->events.change, &layout_change);
5851
5852
    wlr_xdg_output_manager_v1_create(dpy, output_layout);
5853
5854
    /* Workspaces are global and independent of outputs. */
5855
    for (i = 0; i < WSCOUNT; i++) {
5856
        workspaces[i].idx = i;
5857
        snprintf(workspaces[i].name, sizeof(workspaces[i].name), "%d", i + 1);
5858
        workspaces[i].lt     = &layouts[0];
5859
        workspaces[i].prevlt = nullptr;
5860
        workspaces[i].v = workspaces[i].h = (stack_state_t){ SLICE / 2, 1, 1 };
5861
        wl_list_init(&workspaces[i].handles);
5862
    }
5863
    wl_list_init(&ws_managers);
5864
    wl_list_init(&metadata_managers);
5865
    wl_list_init(&pending_spawns);
5866
    wl_list_init(&window_states);
5867
    wl_list_init(&input_popups);
5868
    load_window_states();
5869
    wl_global_create(dpy, &ext_workspace_manager_v1_interface, 1, nullptr, workspace_manager_bind);
5870
    wl_global_create(dpy, &swm_workspace_manager_v1_interface, 1, nullptr, workspace_metadata_bind);
5871
    wl_global_create(dpy, &swm_toplevel_manager_v1_interface, 1, nullptr, toplevel_control_bind);
5872
5873
    /* Publish window lists for taskbars and switchers. */
5874
    ftl_mgr      = wlr_foreign_toplevel_manager_v1_create(dpy);
5875
    ext_ftl_list = wlr_ext_foreign_toplevel_list_v1_create(dpy, 1);
5876
    wlr_ext_image_copy_capture_manager_v1_create(dpy, 1);
5877
    ext_ftl_capture_mgr = wlr_ext_foreign_toplevel_image_capture_source_manager_v1_create(dpy, 1);
5878
    listen_global(&ext_ftl_capture_mgr->events.new_request, &ext_ftl_capture_request);
5879
5880
    /* Let virtual machines and remote desktops capture window-manager shortcuts. */
5881
    kb_inhibit_mgr = wlr_keyboard_shortcuts_inhibit_v1_create(dpy);
5882
    listen_global(&kb_inhibit_mgr->events.new_inhibitor, &new_shortcuts_inhibitor_listener);
5883
5884
    /* Listen for newly connected displays. */
5885
    wl_list_init(&mons);
5886
    listen_global(&backend->events.new_output, &new_output);
5887
5888
    /* Set up application windows, panels, backgrounds, and other desktop surfaces. */
5889
    wl_list_init(&clients);
5890
    wl_list_init(&fstack);
5891
5892
    xdg_shell = wlr_xdg_shell_create(dpy, 6);
5893
    listen_global(&xdg_shell->events.new_toplevel, &new_xdg_toplevel);
5894
    listen_global(&xdg_shell->events.new_popup, &new_xdg_popup);
5895
5896
    xdg_dialog_mgr = wlr_xdg_wm_dialog_v1_create(dpy, 1);
5897
    listen_global(&xdg_dialog_mgr->events.new_dialog, &new_xdg_dialog);
5898
5899
    layer_shell = wlr_layer_shell_v1_create(dpy, 4);
5900
    listen_global(&layer_shell->events.new_surface, &new_layer_surface);
5901
5902
    idle_notifier = wlr_idle_notifier_v1_create(dpy);
5903
5904
    idle_inhibit_mgr = wlr_idle_inhibit_v1_create(dpy);
5905
    listen_global(&idle_inhibit_mgr->events.new_inhibitor, &new_idle_inhibitor);
5906
5907
    session_lock_mgr = wlr_session_lock_manager_v1_create(dpy);
5908
    listen_global(&session_lock_mgr->events.new_lock, &new_session_lock);
5909
    locked_bg = wlr_scene_rect_create(
5910
        layers[LAYER_BLOCK], sgeom.width, sgeom.height, (float[4]){ 0.1f, 0.1f, 0.1f, 1.0f }
5911
    );
5912
5913
    if (!locked_bg)
5914
        die("failed to create lock background");
5915
    wlr_scene_node_set_enabled(&locked_bg->node, 0);
5916
5917
    /* Prefer window borders drawn by the compositor. */
5918
    wlr_server_decoration_manager_set_default_mode(
5919
        wlr_server_decoration_manager_create(dpy), WLR_SERVER_DECORATION_MANAGER_MODE_SERVER
5920
    );
5921
    xdg_decoration_mgr = wlr_xdg_decoration_manager_v1_create(dpy);
5922
    listen_global(&xdg_decoration_mgr->events.new_toplevel_decoration, &new_xdg_decoration);
5923
5924
    pointer_constraints = wlr_pointer_constraints_v1_create(dpy);
5925
    listen_global(&pointer_constraints->events.new_constraint, &new_pointer_constraint);
5926
5927
    relative_pointer_mgr = wlr_relative_pointer_manager_v1_create(dpy);
5928
    pointer_gestures     = wlr_pointer_gestures_v1_create(dpy);
5929
5930
    /* Track the pointer across all displays. */
5931
    cursor = wlr_cursor_create();
5932
    wlr_cursor_attach_output_layout(cursor, output_layout);
5933
5934
    /* Load cursor images at the scales required by connected displays. */
5935
    cursor_mgr = wlr_xcursor_manager_create(nullptr, 24);
5936
    setenv("XCURSOR_SIZE", "24", 1);
5937
5938
    /* Combine pointer devices and handle their movement, buttons, scrolling, and frames. */
5939
    listen_global(&cursor->events.motion, &cursor_motion);
5940
    listen_global(&cursor->events.motion_absolute, &cursor_motion_absolute);
5941
    listen_global(&cursor->events.button, &cursor_button);
5942
    listen_global(&cursor->events.axis, &cursor_axis);
5943
    listen_global(&cursor->events.frame, &cursor_frame_listener);
5944
    listen_global(&cursor->events.swipe_begin, &cursor_swipe_begin);
5945
    listen_global(&cursor->events.swipe_update, &cursor_swipe_update);
5946
    listen_global(&cursor->events.swipe_end, &cursor_swipe_end);
5947
    listen_global(&cursor->events.pinch_begin, &cursor_pinch_begin);
5948
    listen_global(&cursor->events.pinch_update, &cursor_pinch_update);
5949
    listen_global(&cursor->events.pinch_end, &cursor_pinch_end);
5950
    listen_global(&cursor->events.hold_begin, &cursor_hold_begin);
5951
    listen_global(&cursor->events.hold_end, &cursor_hold_end);
5952
5953
    cursor_shape_mgr = wlr_cursor_shape_manager_v1_create(dpy, 1);
5954
    listen_global(&cursor_shape_mgr->events.request_set_shape, &request_set_cursor_shape);
5955
5956
    /* A seat groups the input devices controlled by one user. */
5957
    listen_global(&backend->events.new_input, &new_input_device);
5958
    virtual_keyboard_mgr = wlr_virtual_keyboard_manager_v1_create(dpy);
5959
    listen_global(&virtual_keyboard_mgr->events.new_virtual_keyboard, &new_virtual_keyboard);
5960
    virtual_pointer_mgr = wlr_virtual_pointer_manager_v1_create(dpy);
5961
    listen_global(&virtual_pointer_mgr->events.new_virtual_pointer, &new_virtual_pointer);
5962
5963
    seat = wlr_seat_create(dpy, "seat0");
5964
    listen_global(&seat->events.request_set_cursor, &request_cursor);
5965
    listen_global(&seat->events.request_set_selection, &request_set_sel);
5966
    listen_global(&seat->events.request_set_primary_selection, &request_set_psel);
5967
    listen_global(&seat->events.request_start_drag, &request_start_drag_listener);
5968
    listen_global(&seat->events.start_drag, &start_drag_listener);
5969
5970
    /* Connect applications to an input method such as fcitx5. */
5971
    im_mgr = wlr_input_method_manager_v2_create(dpy);
5972
    listen_global(&im_mgr->events.new_input_method, &new_input_method);
5973
    ti_mgr = wlr_text_input_manager_v3_create(dpy);
5974
    listen_global(&ti_mgr->events.new_text_input, &new_text_input);
5975
5976
    kb_group = create_keyboard_group(false);
5977
5978
    if (!kb_group)
5979
        die("failed to allocate primary keyboard group");
5980
    wl_list_init(&kb_group->destroy.link);
5981
5982
    output_mgr = wlr_output_manager_v1_create(dpy);
5983
    listen_global(&output_mgr->events.apply, &output_mgr_apply);
5984
    listen_global(&output_mgr->events.test, &output_mgr_test);
5985
5986
    /* Keep XWayland applications on this compositor rather than a parent X server. */
5987
    unsetenv("DISPLAY");
5988
    /* Create the XWayland server; it starts when the first X11 application opens. */
5989
    if ((xwayland = wlr_xwayland_create(dpy, compositor, 1))) {
5990
        listen_global(&xwayland->events.ready, &xwayland_ready_listener);
5991
        listen_global(&xwayland->events.new_surface, &new_xwayland_surface);
5992
5993
        setenv("DISPLAY", xwayland->display_name, 1);
5994
    } else {
5995
        fprintf(stderr, "failed to setup XWayland X server, continuing without it\n");
5996
    }
5997
}
5998
5999
/* Start a command and assign its first window to the current workspace. */
6000
void spawn(const arg_t *arg) {
6001
    pid_t            pid;
6002
    pending_spawn_t *ps;
6003
6004
    if ((pid = fork()) == 0) {
6005
        char *argv[MAX_COMMAND_ARGS], storage[MAX_COMMAND_SIZE];
6006
6007
        expand_argv(arg->v, argv, storage);
6008
6009
        prepare_child();
6010
        dup2(STDERR_FILENO, STDOUT_FILENO);
6011
        setsid();
6012
        execute_command(argv, "key binding");
6013
    }
6014
    if (pid > 0 && selmon && selmon->ws) {
6015
        ps = pool_take(&pending_spawn_pool);
6016
6017
        if (!ps)
6018
            return;
6019
        ps->pid = pid;
6020
        ps->ws  = selmon->ws;
6021
        wl_list_insert(&pending_spawns, &ps->link);
6022
    }
6023
}
6024
6025
/* Display and track an application's drag icon. */
6026
void start_drag(struct wl_listener *listener, void *data) {
6027
    struct wlr_drag       *drag = data;
6028
    struct wlr_scene_tree *icon;
6029
6030
    if (!drag->icon)
6031
        return;
6032
6033
    icon = wlr_scene_drag_icon_create(drag_icon, drag->icon);
6034
6035
    if (!icon)
6036
        return;
6037
    drag->icon->data = &icon->node;
6038
    listen_static(&drag->icon->events.destroy, destroy_drag_icon);
6039
}
6040
6041
/* Move the focused window to the requested workspace. */
6042
void tag(const arg_t *arg) {
6043
    client_t *sel = focus_top(selmon);
6044
6045
    if (!sel || arg->i < 0 || arg->i >= WSCOUNT)
6046
        return;
6047
6048
    set_workspace(sel, &workspaces[arg->i]);
6049
    arrange(selmon);
6050
    print_status();
6051
}
6052
6053
/* Move the focused window to the workspace on a neighboring display. */
6054
void tag_monitor(const arg_t *arg) {
6055
    client_t *sel = focus_top(selmon);
6056
6057
    if (sel)
6058
        set_monitor(sel, direction_to_monitor(arg->i), nullptr);
6059
}
6060
6061
/* Adjust the master size, master count, stack count, or layout direction. */
6062
void stack_config(const arg_t *arg) {
6063
    /* adjust the master/stack parameters of the focused workspace's
6064
     * current layout. */
6065
    workspace_t           *ws;
6066
    stack_state_t         *st;
6067
    const layout_t        *next;
6068
    master_layout_t        ml;
6069
    struct swm_stack_state state;
6070
    int                    newside;
6071
6072
    if (!arg || !selmon || !(ws = selmon->ws) || !layout_master(ws->lt, &ml))
6073
        return;
6074
    st    = ml.rotate ? &ws->h : &ws->v;
6075
    state = (struct swm_stack_state){ st->msize, st->mwin, st->stacks };
6076
6077
    if (!swm_stack_configure(&state, arg->i, ml.side, &newside))
6078
        return;
6079
    *st = (stack_state_t){ state.msize, state.mwin, state.stacks };
6080
6081
    if (newside != ml.side && (next = layout_from_side(newside))) {
6082
        ws->prevlt = ws->lt;
6083
        ws->lt     = next;
6084
    }
6085
    arrange(selmon);
6086
    print_status();
6087
}
6088
6089
/* Tile windows into a master area followed by evenly divided stacks. */
6090
void stack_master(monitor_t *m, int side) {
6091
    /* Master/stack tiler: a master area holding up to mwin
6092
     * windows and a stacking area split into st->stacks columns. All
6093
     * geometry is computed in "vertical" coordinates and transposed at
6094
     * the end for the horizontal layout. */
6095
    bool                   rot  = master_rotates(side);
6096
    bool                   flip = master_flips(side);
6097
    client_t              *c;
6098
    stack_state_t         *st = rot ? &m->ws->h : &m->ws->v;
6099
    struct swm_box         boxes[MAX_CLIENTS];
6100
    struct swm_stack_state state = { st->msize, st->mwin, st->stacks };
6101
    int                    n     = 0;
6102
    size_t                 i     = 0;
6103
6104
    wl_list_for_each(c, &clients, link) if (VISIBLEON(c, m) && !c->is_floating && !c->is_fullscreen)
6105
        n++;
6106
    swm_stack_layout((const struct swm_box *)&m->w, &state, rot, flip, (size_t)n, boxes);
6107
    wl_list_for_each(c, &clients, link) {
6108
        if (!VISIBLEON(c, m) || c->is_floating || c->is_fullscreen)
6109
            continue;
6110
        resize(c, (struct wlr_box){ boxes[i].x, boxes[i].y, boxes[i].width, boxes[i].height }, 0);
6111
        i++;
6112
    }
6113
}
6114
6115
/* Tile the master area along the bottom edge. */
6116
void master_bottom(monitor_t *m) {
6117
    stack_master(m, SWM_MASTER_BOTTOM);
6118
}
6119
6120
/* Tile the master area along the left edge. */
6121
void master_left(monitor_t *m) {
6122
    stack_master(m, SWM_MASTER_LEFT);
6123
}
6124
6125
/* Tile the master area along the right edge. */
6126
void master_right(monitor_t *m) {
6127
    stack_master(m, SWM_MASTER_RIGHT);
6128
}
6129
6130
/* Tile the master area along the top edge. */
6131
void master_top(monitor_t *m) {
6132
    stack_master(m, SWM_MASTER_TOP);
6133
}
6134
6135
/* Toggle the focused window between tiled and floating. */
6136
void toggle_floating(const arg_t *arg) {
6137
    client_t *sel = focus_top(selmon);
6138
    /* Fullscreen windows cannot become floating. */
6139
    if (!sel || sel->is_fullscreen)
6140
        return;
6141
6142
    restore_max_stack(sel->mon);
6143
    if (sel->is_floating) {
6144
        sel->persist_float = 0;
6145
        set_floating(sel, 0);
6146
        forget_client(sel);
6147
    } else {
6148
        sel->persist_float = 1;
6149
        set_floating(sel, 1);
6150
    }
6151
}
6152
6153
/* Toggle fullscreen for the focused window. */
6154
void toggle_fullscreen(const arg_t *arg) {
6155
    client_t *sel = focus_top(selmon);
6156
6157
    if (sel)
6158
        set_fullscreen(sel, !sel->is_fullscreen);
6159
}
6160
6161
/* Toggle the workspace between its normal layout and overlapping maximized windows. */
6162
void toggle_max_stack(const arg_t *arg) {
6163
    workspace_t    *ws;
6164
    const layout_t *max = nullptr;
6165
    size_t          i;
6166
6167
    if (!selmon || !(ws = selmon->ws))
6168
        return;
6169
6170
    for (i = 0; i < LENGTH(layouts); i++)
6171
6172
        if (layouts[i].arrange == max_stack) {
6173
            max = &layouts[i];
6174
            break;
6175
        }
6176
    if (!max)
6177
        return;
6178
6179
    if (ws->lt == max) {
6180
        restore_max_stack(selmon);
6181
        return;
6182
    }
6183
6184
    ws->prevlt = ws->lt;
6185
    ws->lt     = max;
6186
    arrange(selmon);
6187
    print_status();
6188
}
6189
6190
/* Remove the active session lock after the locker authenticates the user. */
6191
void unlock_session(struct wl_listener *listener, void *data) {
6192
    session_lock_t *lock = wl_container_of(listener, lock, unlock);
6193
    destroy_lock(lock, 1);
6194
}
6195
6196
/* Hide an unmapped desktop-layer surface and reclaim its reserved space. */
6197
void layer_surface_unmap_notify(struct wl_listener *listener, void *data) {
6198
    layer_surface_t *l = wl_container_of(listener, l, unmap);
6199
6200
    l->mapped = 0;
6201
    wlr_scene_node_set_enabled(&l->scene->node, 0);
6202
6203
    if (l->dim)
6204
        wlr_scene_node_set_enabled(&l->dim->node, 0);
6205
6206
    if (l == exclusive_focus)
6207
        exclusive_focus = nullptr;
6208
6209
    if (l->layer_surface->output && (l->mon = l->layer_surface->output->data))
6210
        arrange_layers(l->mon);
6211
6212
    if (l->layer_surface->surface == seat->keyboard_state.focused_surface)
6213
        focus_client(focus_top(selmon), 1);
6214
    motion_notify(0, nullptr, 0, 0, 0, 0, 1);
6215
}
6216
6217
/* Hide an unmapped window, save its state, and choose new focus. */
6218
void unmap_notify(struct wl_listener *listener, void *data) {
6219
    /* Called when the surface is unmapped, and should no longer be shown. */
6220
    client_t  *c    = wl_container_of(listener, c, unmap);
6221
    client_t  *next = nullptr, *under = nullptr;
6222
    monitor_t *oldmon     = c->mon;
6223
    bool       hadfocus   = client_surface(c) == seat->keyboard_state.focused_surface;
6224
    int        wasfocused = !client_is_unmanaged(c) && c == focus_top(c->mon);
6225
6226
    if (c == grabc) {
6227
        if (cursor_mode == CURSOR_RESIZE)
6228
            client_set_resizing(c, 0);
6229
        cursor_mode = CURSOR_NORMAL;
6230
        grabc       = nullptr;
6231
    }
6232
    if (!c->scene)
6233
        return;
6234
6235
    if (client_is_unmanaged(c)) {
6236
        if (c == exclusive_focus) {
6237
            exclusive_focus = nullptr;
6238
            focus_client(focus_top(selmon), 1);
6239
        }
6240
    } else {
6241
        remember_client(c);
6242
6243
        if (wasfocused)
6244
            next = focus_close(c);
6245
        wl_list_remove(&c->link);
6246
        wl_list_remove(&c->flink);
6247
        /* The window outlives this list membership, so leave both links in a
6248
         * state that a second removal cannot corrupt. */
6249
        wl_list_init(&c->link);
6250
        wl_list_init(&c->flink);
6251
        c->mon = nullptr;
6252
6253
        if (hadfocus && !sloppyfocus)
6254
            focus_client(next ? next : focus_top(selmon), 1);
6255
6256
        if (oldmon)
6257
            arrange(oldmon);
6258
    }
6259
    if (c->ftl) {
6260
        wl_list_remove(&c->ftl_activate.link);
6261
        wl_list_remove(&c->ftl_close.link);
6262
        wl_list_remove(&c->ftl_fullscreen.link);
6263
        wlr_foreign_toplevel_handle_v1_destroy(c->ftl);
6264
        c->ftl         = nullptr;
6265
        c->ftl_monitor = nullptr;
6266
    }
6267
    if (c->extftl) {
6268
        wlr_ext_foreign_toplevel_handle_v1_destroy(c->extftl);
6269
        c->extftl = nullptr;
6270
    }
6271
    c->resize       = 0;
6272
    c->resize_edges = WLR_EDGE_NONE;
6273
    /* Hide the old scene before hit-testing, but keep its borders alive until
6274
     * focus_client() has finished deactivating the old surface. */
6275
    wlr_scene_node_set_enabled(&c->scene->node, 0);
6276
6277
    if (hadfocus && sloppyfocus) {
6278
        /* Hit-test after hiding the old scene node so focus follows the
6279
         * surface newly exposed beneath the stationary cursor. */
6280
        point_to_node(cursor->x, cursor->y, nullptr, &under, nullptr, nullptr, nullptr);
6281
6282
        if (under && !client_is_unmanaged(under))
6283
            focus_client(under, 0);
6284
        else
6285
            focus_client(next ? next : focus_top(selmon), 1);
6286
    }
6287
    wlr_scene_node_destroy(&c->scene->node);
6288
    c->scene = client_surface(c)->data = nullptr;
6289
    c->scene_surface                   = nullptr;
6290
    memset(c->border, 0, sizeof(c->border));
6291
    print_status();
6292
    motion_notify(0, nullptr, 0, 0, 0, 0, 1);
6293
}
6294
6295
/* Reconcile display layout changes with workspaces, windows, focus, and color state. */
6296
void update_monitors(struct wl_listener *listener, void *data) {
6297
    /*
6298
     * Called whenever the output layout changes: adding or removing a
6299
     * monitor, changing an output's mode or position, etc. This is where
6300
     * the change officially happens and we update geometry, window
6301
     * positions, focus, and the stored configuration in wlroots'
6302
     * output-manager implementation.
6303
     */
6304
    struct wlr_output_configuration_v1      *config = wlr_output_configuration_v1_create();
6305
    client_t                                *c;
6306
    struct wlr_output_configuration_head_v1 *config_head;
6307
    monitor_t                               *m;
6308
6309
    /* Remove disabled displays from the layout first. */
6310
    wl_list_for_each(m, &mons, link) {
6311
        if (m->wlr_output->enabled || m->asleep)
6312
            continue;
6313
        config_head                = wlr_output_configuration_head_v1_create(config, m->wlr_output);
6314
        config_head->state.enabled = 0;
6315
        /* Remove the display so the pointer cannot enter it. */
6316
        wlr_output_layout_remove(output_layout, m->wlr_output);
6317
        close_monitor(m);
6318
        m->m = m->w = (struct wlr_box){};
6319
    }
6320
    /* Add newly enabled displays to the layout. */
6321
    wl_list_for_each(m, &mons, link) {
6322
        if (m->wlr_output->enabled && !wlr_output_layout_get(output_layout, m->wlr_output))
6323
            wlr_output_layout_add_auto(output_layout, m->wlr_output);
6324
    }
6325
    /* Recalculate the bounds of the combined desktop. */
6326
    wlr_output_layout_get_box(output_layout, nullptr, &sgeom);
6327
6328
    wlr_scene_node_set_position(&root_bg->node, sgeom.x, sgeom.y);
6329
    wlr_scene_rect_set_size(root_bg, sgeom.width, sgeom.height);
6330
6331
    /* Cover all windows while the session is locked. */
6332
    wlr_scene_node_set_position(&locked_bg->node, sgeom.x, sgeom.y);
6333
    wlr_scene_rect_set_size(locked_bg, sgeom.width, sgeom.height);
6334
6335
    wl_list_for_each(m, &mons, link) {
6336
        if (!m->wlr_output->enabled)
6337
            continue;
6338
        config_head = wlr_output_configuration_head_v1_create(config, m->wlr_output);
6339
6340
        /* Read the display's position in the combined desktop. */
6341
        wlr_output_layout_get_box(output_layout, m->wlr_output, &m->m);
6342
        m->w = m->m;
6343
        wlr_scene_output_set_position(m->scene_output, m->m.x, m->m.y);
6344
6345
        wlr_scene_node_set_position(&m->fullscreen_bg->node, m->m.x, m->m.y);
6346
        wlr_scene_rect_set_size(m->fullscreen_bg, m->m.width, m->m.height);
6347
6348
        if (m->lock_surface) {
6349
            struct wlr_scene_tree *scene_tree = m->lock_surface->surface->data;
6350
            wlr_scene_node_set_position(&scene_tree->node, m->m.x, m->m.y);
6351
            wlr_session_lock_surface_v1_configure(m->lock_surface, m->m.width, m->m.height);
6352
        }
6353
        /* Reserve panel space before arranging regular windows. */
6354
        arrange_layers(m);
6355
        /* Sync clients of this output's workspace (it may have been
6356
         * hidden or shown elsewhere before this display appeared). */
6357
        if (m->ws)
6358
            assign_workspace(m->ws, m);
6359
        /* Keep windows on their workspace when displays are connected. */
6360
        arrange(m);
6361
        /* Resize fullscreen windows to fill their display. */
6362
        if ((c = focus_top(m)) && c->is_fullscreen)
6363
            resize(c, m->m, 0);
6364
6365
        /* Reapply color correction in case this display was just re-enabled. */
6366
        m->gamma_lut_changed = 1;
6367
6368
        config_head->state.x = m->m.x;
6369
        config_head->state.y = m->m.y;
6370
6371
        if (!selmon) {
6372
            selmon = m;
6373
        }
6374
    }
6375
    if (selmon && selmon->wlr_output->enabled) {
6376
        wl_list_for_each(c, &clients, link) {
6377
            if (!c->mon && client_surface(c)->mapped)
6378
                set_monitor(c, selmon, c->ws);
6379
        }
6380
        focus_client(focus_top(selmon), 1);
6381
6382
        if (selmon->lock_surface) {
6383
            client_notify_enter(selmon->lock_surface->surface, wlr_seat_get_keyboard(seat));
6384
            client_activate_surface(selmon->lock_surface->surface, 1);
6385
        }
6386
    }
6387
    /* Refresh the cursor image after displays return without reporting false
6388
     * pointer movement to applications. */
6389
    wlr_cursor_move(cursor, nullptr, 0, 0);
6390
6391
    wlr_output_manager_v1_set_configuration(output_mgr, config);
6392
}
6393
6394
/* Publish a window's changed application ID. */
6395
void update_app_id(struct wl_listener *listener, void *data) {
6396
    client_t *c = wl_container_of(listener, c, set_appid);
6397
6398
    ftl_sync(c);
6399
6400
    if (c == focus_top(c->mon))
6401
        print_status();
6402
}
6403
6404
/* Publish a window's changed title. */
6405
void update_title(struct wl_listener *listener, void *data) {
6406
    client_t *c = wl_container_of(listener, c, set_title);
6407
6408
    ftl_sync(c);
6409
6410
    if (c == focus_top(c->mon))
6411
        print_status();
6412
}
6413
6414
/* Mark an unfocused managed window as needing attention. */
6415
void mark_urgent(client_t *c) {
6416
    if (!c || c == focus_top(selmon))
6417
        return;
6418
6419
    c->is_urgent = true;
6420
    print_status();
6421
6422
    if (client_surface(c)->mapped)
6423
        client_set_border_color(c, urgentcolor);
6424
}
6425
6426
/* Handle an activation request as an urgency notification. */
6427
void urgent(struct wl_listener *listener, void *data) {
6428
    struct wlr_xdg_activation_v1_request_activate_event *event = data;
6429
    client_t                                            *c     = nullptr;
6430
6431
    toplevel_from_wlr_surface(event->surface, &c, nullptr);
6432
    mark_urgent(c);
6433
}
6434
6435
/* Turn a system bell request associated with a surface into window urgency. */
6436
void ring_system_bell(struct wl_listener *listener, void *data) {
6437
    struct wlr_xdg_system_bell_v1_ring_event *event = data;
6438
    client_t                                 *c     = nullptr;
6439
6440
    toplevel_from_wlr_surface(event->surface, &c, nullptr);
6441
    mark_urgent(c);
6442
}
6443
6444
/* Show a numbered workspace or return to the previously shown one. */
6445
void view(const arg_t *arg) {
6446
    /* A negative index selects the previously shown workspace. */
6447
    if (!selmon)
6448
        return;
6449
6450
    if (arg->i < 0)
6451
        view_workspace(selmon->previous_workspace, selmon);
6452
    else if (arg->i < WSCOUNT)
6453
        view_workspace(&workspaces[arg->i], selmon);
6454
}
6455
6456
/* Create an independent keyboard group for a virtual keyboard. */
6457
void virtual_keyboard(struct wl_listener *listener, void *data) {
6458
    struct wlr_virtual_keyboard_v1 *kb = data;
6459
6460
    /* Give each virtual keyboard its own group. */
6461
    keyboard_group_t *group = create_keyboard_group(true);
6462
6463
    if (!group) {
6464
        wl_resource_post_no_memory(kb->resource);
6465
        return;
6466
    }
6467
    /* Use the virtual keyboard's key mapping. */
6468
    wlr_keyboard_set_keymap(&kb->keyboard, group->wlr_group->keyboard.keymap);
6469
    LISTEN(&kb->keyboard.base.events.destroy, &group->destroy, destroy_keyboard_group);
6470
6471
    /* Add the virtual keyboard to its group. */
6472
    wlr_keyboard_group_add_keyboard(group->wlr_group, &kb->keyboard);
6473
    virtual_keyboards++;
6474
    wlr_seat_set_capabilities(seat, WL_SEAT_CAPABILITY_POINTER | WL_SEAT_CAPABILITY_KEYBOARD);
6475
}
6476
6477
/* Attach a virtual pointer to the shared cursor. */
6478
void virtual_pointer(struct wl_listener *listener, void *data) {
6479
    struct wlr_virtual_pointer_v1_new_pointer_event *event  = data;
6480
    struct wlr_input_device                         *device = &event->new_pointer->pointer.base;
6481
6482
    wlr_cursor_attach_input_device(cursor, device);
6483
6484
    if (event->suggested_output)
6485
        wlr_cursor_map_input_to_output(cursor, device, event->suggested_output);
6486
}
6487
6488
/* Return the display containing a point, or the nearest display to it. */
6489
monitor_t *point_to_monitor(double x, double y) {
6490
    struct wlr_output *o = wlr_output_layout_output_at(output_layout, x, y);
6491
6492
    return o ? o->data : nullptr;
6493
}
6494
6495
/* Find the surface, window, and local coordinates at a layout position. */
6496
void point_to_node(
6497
    double               x,
6498
    double               y,
6499
    struct wlr_surface **psurface,
6500
    client_t           **pc,
6501
    layer_surface_t    **pl,
6502
    double              *nx,
6503
    double              *ny
6504
) {
6505
    struct wlr_scene_node    *node, *pnode;
6506
    struct wlr_scene_surface *scene_surface;
6507
    struct wlr_surface       *surface = nullptr;
6508
    client_t                 *c       = nullptr;
6509
    layer_surface_t          *l       = nullptr;
6510
    int                       layer;
6511
6512
    for (layer = NUM_LAYERS - 1; !surface && layer >= 0; layer--) {
6513
        if (!(node = wlr_scene_node_at(&layers[layer]->node, x, y, nx, ny)))
6514
            continue;
6515
6516
        if (node->type == WLR_SCENE_NODE_BUFFER &&
6517
            (scene_surface = wlr_scene_surface_try_from_buffer(wlr_scene_buffer_from_node(node))))
6518
            surface = scene_surface->surface;
6519
        /* Walk upward until a node identifies its window. */
6520
        for (pnode = node; pnode && !c;) {
6521
            c = pnode->data;
6522
6523
            if (!c)
6524
                pnode = pnode->parent ? &pnode->parent->node : nullptr;
6525
        }
6526
        if (c && c->type == LAYER_SHELL) {
6527
            c = nullptr;
6528
            l = pnode->data;
6529
        }
6530
        if (c && client_is_blocked(c)) {
6531
            c       = nullptr;
6532
            surface = nullptr;
6533
        }
6534
    }
6535
    if (psurface)
6536
        *psurface = surface;
6537
    if (pc)
6538
        *pc = c;
6539
    if (pl)
6540
        *pl = l;
6541
}
6542
6543
/* Swap the focused tiled window with its neighbor. */
6544
void swap_client(const arg_t *arg) {
6545
    /* swap the focused window with the next/previous window in the
6546
     * stacking order. */
6547
    client_t *c, *sel = focus_top(selmon);
6548
6549
    if (!sel || !selmon || !selmon->ws || !selmon->ws->lt->arrange || sel->is_floating ||
6550
        sel->is_fullscreen)
6551
        return;
6552
6553
    for (c = client_step(sel, arg->i); c != sel; c = client_step(c, arg->i)) {
6554
        if (VISIBLEON(c, selmon) && !c->is_floating && !c->is_fullscreen)
6555
            break;
6556
    }
6557
    if (c == sel)
6558
        return;
6559
6560
    /* Move the selected window next to its swap target. */
6561
    wl_list_remove(&sel->link);
6562
6563
    if (arg->i > 0)
6564
        wl_list_insert(&c->link, &sel->link);
6565
    else
6566
        wl_list_insert(c->link.prev, &sel->link);
6567
6568
    arrange(selmon);
6569
    print_status();
6570
}
6571
6572
/* Raise the focused floating window above other windows. */
6573
void raise_client(const arg_t *arg) {
6574
    /* Raise the focused window above the others. */
6575
    client_t *sel = focus_top(selmon);
6576
6577
    if (sel)
6578
        wlr_scene_node_raise_to_top(&sel->scene->node);
6579
}
6580
6581
/* Swap the focused tiled window with the master window. */
6582
void zoom(const arg_t *arg) {
6583
    client_t *c, *sel = focus_top(selmon);
6584
6585
    if (!sel || !selmon || !selmon->ws || !selmon->ws->lt->arrange || sel->is_floating)
6586
        return;
6587
6588
    /* Search for the first tiled window that is not sel, marking sel as
6589
     * nullptr if we pass it along the way. */
6590
    wl_list_for_each(c, &clients, link) {
6591
        if (VISIBLEON(c, selmon) && !c->is_floating) {
6592
            if (c != sel)
6593
                break;
6594
            sel = nullptr;
6595
        }
6596
    }
6597
    /* Stop if there is no other tiled window. */
6598
    if (&c->link == &clients)
6599
        return;
6600
6601
    /* If we passed sel, move c to the front; otherwise, move sel to the
6602
     * front. */
6603
    if (!sel)
6604
        sel = c;
6605
    wl_list_remove(&sel->link);
6606
    wl_list_insert(&clients, &sel->link);
6607
6608
    focus_client(sel, 1);
6609
    arrange(selmon);
6610
}
6611
6612
/* Honor an activation request for a managed X11 window. */
6613
void activate_x11(struct wl_listener *listener, void *data) {
6614
    client_t *c = wl_container_of(listener, c, activate);
6615
6616
    /* Only managed X11 windows can be activated. */
6617
    if (!client_is_unmanaged(c))
6618
        wlr_xwayland_surface_activate(c->surface.xwayland, 1);
6619
}
6620
6621
/* Attach a newly ready X11 surface to its window state. */
6622
void associate_x11(struct wl_listener *listener, void *data) {
6623
    client_t *c = wl_container_of(listener, c, associate);
6624
6625
    LISTEN(&client_surface(c)->events.map, &c->map, map_notify);
6626
    LISTEN(&client_surface(c)->events.unmap, &c->unmap, unmap_notify);
6627
}
6628
6629
/* Apply an unmanaged X11 window's requested position and size. */
6630
void configure_x11(struct wl_listener *listener, void *data) {
6631
    client_t                                    *c     = wl_container_of(listener, c, configure);
6632
    struct wlr_xwayland_surface_configure_event *event = data;
6633
6634
    if (!client_surface(c) || !client_surface(c)->mapped) {
6635
        wlr_xwayland_surface_configure(
6636
            c->surface.xwayland, event->x, event->y, event->width, event->height
6637
        );
6638
        return;
6639
    }
6640
    if (client_is_unmanaged(c)) {
6641
        wlr_scene_node_set_position(&c->scene->node, event->x, event->y);
6642
        wlr_xwayland_surface_configure(
6643
            c->surface.xwayland, event->x, event->y, event->width, event->height
6644
        );
6645
        return;
6646
    }
6647
    if ((c->is_floating && c != grabc) || !c->mon || !c->mon->ws || !c->mon->ws->lt->arrange) {
6648
        resize(
6649
            c,
6650
            (struct wlr_box){ .x      = event->x - c->bw,
6651
                              .y      = event->y - c->bw,
6652
                              .width  = event->width + c->bw * 2,
6653
                              .height = event->height + c->bw * 2 },
6654
            0
6655
        );
6656
    } else {
6657
        arrange(c->mon);
6658
    }
6659
}
6660
6661
/* Allocate state and listeners for a new X11 window. */
6662
void create_notify_x11(struct wl_listener *listener, void *data) {
6663
    struct wlr_xwayland_surface *xsurface = data;
6664
    client_t                    *c;
6665
6666
    /* Allocate window state for this X11 surface. */
6667
    if (!(c = pool_take(&client_pool))) {
6668
        wlr_xwayland_surface_close(xsurface);
6669
        return;
6670
    }
6671
    xsurface->data      = c;
6672
    c->surface.xwayland = xsurface;
6673
    c->type             = X11;
6674
    c->bw               = client_is_unmanaged(c) ? 0 : borderwidth;
6675
6676
    /* Listen for changes to the X11 window. */
6677
    LISTEN(&xsurface->events.associate, &c->associate, associate_x11);
6678
    LISTEN(&xsurface->events.destroy, &c->destroy, destroy_notify);
6679
    LISTEN(&xsurface->events.dissociate, &c->dissociate, dissociate_x11);
6680
    LISTEN(&xsurface->events.request_activate, &c->activate, activate_x11);
6681
    LISTEN(&xsurface->events.request_configure, &c->configure, configure_x11);
6682
    LISTEN(&xsurface->events.request_fullscreen, &c->fullscreen, fullscreen_notify);
6683
    LISTEN(&xsurface->events.set_hints, &c->set_hints, set_hints);
6684
    LISTEN(&xsurface->events.set_title, &c->set_title, update_title);
6685
    LISTEN(&xsurface->events.set_class, &c->set_appid, update_app_id);
6686
}
6687
6688
/* Detach an X11 surface while retaining its window state for reassociation. */
6689
void dissociate_x11(struct wl_listener *listener, void *data) {
6690
    client_t *c = wl_container_of(listener, c, dissociate);
6691
    wl_list_remove(&c->map.link);
6692
    wl_list_remove(&c->unmap.link);
6693
}
6694
6695
/* Update urgency and input behavior from an X11 window's hints. */
6696
void set_hints(struct wl_listener *listener, void *data) {
6697
    client_t           *c       = wl_container_of(listener, c, set_hints);
6698
    struct wlr_surface *surface = client_surface(c);
6699
6700
    if (c == focus_top(selmon) || !c->surface.xwayland->hints)
6701
        return;
6702
6703
    c->is_urgent = xcb_icccm_wm_hints_get_urgency(c->surface.xwayland->hints);
6704
    print_status();
6705
6706
    if (c->is_urgent && surface && surface->mapped)
6707
        client_set_border_color(c, urgentcolor);
6708
}
6709
6710
/* Connect XWayland to swm's seat and set its default cursor. */
6711
void xwayland_ready(struct wl_listener *listener, void *data) {
6712
    struct wlr_xcursor *xcursor;
6713
6714
    /* Assign swm's single seat to XWayland. */
6715
    wlr_xwayland_set_seat(xwayland, seat);
6716
6717
    /* Set the default XWayland cursor to match the rest of swm. */
6718
    if ((xcursor = wlr_xcursor_manager_get_xcursor(cursor_mgr, "default", 1)))
6719
        wlr_xwayland_set_cursor(
6720
            xwayland,
6721
            wlr_xcursor_image_get_buffer(xcursor->images[0]),
6722
            xcursor->images[0]->hotspot_x,
6723
            xcursor->images[0]->hotspot_y
6724
        );
6725
}
6726
6727
/* Parse command-line options, validate the environment, and run swm. */
6728
int main(int argc, char *argv[]) {
6729
    char *startup_cmd = nullptr;
6730
    int   c;
6731
6732
    while ((c = getopt(argc, argv, "s:hdv")) != -1) {
6733
        if (c == 's')
6734
            startup_cmd = optarg;
6735
        else if (c == 'd')
6736
            log_level = WLR_DEBUG;
6737
        else if (c == 'v') {
6738
            printf("swm %s\n", VERSION);
6739
            return EXIT_SUCCESS;
6740
        } else
6741
            goto usage;
6742
    }
6743
    if (optind < argc)
6744
        goto usage;
6745
6746
    /* Wayland needs XDG_RUNTIME_DIR to create its communication socket. */
6747
    if (!getenv("XDG_RUNTIME_DIR"))
6748
        die("XDG_RUNTIME_DIR must be set");
6749
    setup();
6750
    run(startup_cmd);
6751
    cleanup();
6752
    return EXIT_SUCCESS;
6753
6754
usage:
6755
    die("usage: %s [-v] [-d] [-s startup command]", argv[0]);
6756
}