ohmu
·
2026-10-18
1/*
2 * See LICENSE file for copyright and license details.
3 */
4#include <getopt.h>
5#include <libinput.h>
6#include <linux/input-event-codes.h>
7#include <math.h>
8#include <signal.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <sys/wait.h>
12#include <time.h>
13#include <unistd.h>
14#include <wayland-server-core.h>
15#include <wlr/backend.h>
16#include <wlr/backend/libinput.h>
17#include <wlr/render/allocator.h>
18#include <wlr/render/wlr_renderer.h>
19#include <wlr/types/wlr_alpha_modifier_v1.h>
20#include <wlr/types/wlr_compositor.h>
21#include <wlr/types/wlr_cursor.h>
22#include <wlr/types/wlr_cursor_shape_v1.h>
23#include <wlr/types/wlr_data_control_v1.h>
24#include <wlr/types/wlr_data_device.h>
25#include <wlr/types/wlr_drm.h>
26#include <wlr/types/wlr_export_dmabuf_v1.h>
27#include <wlr/types/wlr_ext_data_control_v1.h>
28#include <wlr/types/wlr_fractional_scale_v1.h>
29#include <wlr/types/wlr_gamma_control_v1.h>
30#include <wlr/types/wlr_idle_inhibit_v1.h>
31#include <wlr/types/wlr_idle_notify_v1.h>
32#include <wlr/types/wlr_input_device.h>
33#include <wlr/types/wlr_keyboard.h>
34#include <wlr/types/wlr_keyboard_group.h>
35#include <wlr/types/wlr_layer_shell_v1.h>
36#include <wlr/types/wlr_linux_dmabuf_v1.h>
37#include <wlr/types/wlr_linux_drm_syncobj_v1.h>
38#include <wlr/types/wlr_output.h>
39#include <wlr/types/wlr_output_layout.h>
40#include <wlr/types/wlr_output_management_v1.h>
41#include <wlr/types/wlr_output_power_management_v1.h>
42#include <wlr/types/wlr_pointer.h>
43#include <wlr/types/wlr_pointer_constraints_v1.h>
44#include <wlr/types/wlr_pointer_gestures_v1.h>
45#include <wlr/types/wlr_presentation_time.h>
46#include <wlr/types/wlr_primary_selection.h>
47#include <wlr/types/wlr_primary_selection_v1.h>
48#include <wlr/types/wlr_relative_pointer_v1.h>
49#include <wlr/types/wlr_scene.h>
50#include <wlr/types/wlr_screencopy_v1.h>
51#include <wlr/types/wlr_seat.h>
52#include <wlr/types/wlr_server_decoration.h>
53#include <wlr/types/wlr_session_lock_v1.h>
54#include <wlr/types/wlr_single_pixel_buffer_v1.h>
55#include <wlr/types/wlr_subcompositor.h>
56#include <wlr/types/wlr_viewporter.h>
57#include <wlr/types/wlr_virtual_keyboard_v1.h>
58#include <wlr/types/wlr_virtual_pointer_v1.h>
59#include <wlr/types/wlr_xcursor_manager.h>
60#include <wlr/types/wlr_xdg_activation_v1.h>
61#include <wlr/types/wlr_xdg_decoration_v1.h>
62#include <wlr/types/wlr_xdg_output_v1.h>
63#include <wlr/types/wlr_xdg_shell.h>
64#include <wlr/util/log.h>
65#include <wlr/util/region.h>
66#include <xkbcommon/xkbcommon.h>
67#ifdef XWAYLAND
68#include <wlr/xwayland.h>
69#include <xcb/xcb.h>
70#include <xcb/xcb_icccm.h>
71#endif
72/* tablet-input */
73#include <wlr/types/wlr_tablet_pad.h>
74#include <wlr/types/wlr_tablet_tool.h>
75#include <wlr/types/wlr_tablet_v2.h>
76/* tablet-input end */
77
78#include "util.h"
79
80/* macros */
81#define MAX(A, B) ((A) > (B) ? (A) : (B))
82#define MIN(A, B) ((A) < (B) ? (A) : (B))
83#define CLEANMASK(mask) (mask & ~WLR_MODIFIER_CAPS)
84#define VISIBLEON(C, M) ((M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags]))
85#define LENGTH(X) (sizeof X / sizeof X[0])
86#define END(A) ((A) + LENGTH(A))
87#define TAGMASK ((1u << TAGCOUNT) - 1)
88#define LISTEN(E, L, H) wl_signal_add((E), ((L)->notify = (H), (L)))
89#define LISTEN_STATIC(E, H) do { struct wl_listener *_l = ecalloc(1, sizeof(*_l)); _l->notify = (H); wl_signal_add((E), _l); } while (0)
90
91/* enums */
92enum { CurNormal, CurPressed, CurMove, CurResize, CurSpawnRegion }; /* cursor */
93enum { SnapLeft, SnapRight, SnapTop, SnapBottom,
94 SnapTopLeft, SnapTopRight, SnapBottomLeft, SnapBottomRight }; /* labwc-style keybind snap positions */
95enum { XDGShell, LayerShell, X11 }; /* client types */
96enum { LyrBg, LyrBottom, LyrTile, LyrFloat, LyrTop, LyrFS, LyrOverlay, LyrBlock, NUM_LAYERS }; /* scene layers */
97/* click */
98enum { ClkClient, ClkLayerShell, ClkRoot }; /* clicks */
99/* click end */
100typedef union {
101 int i;
102 uint32_t ui;
103 float f;
104 const void *v;
105} Arg;
106
107typedef struct {
108 /* click */
109 unsigned int click;
110 /* click end */
111 unsigned int mod;
112 unsigned int button;
113 void (*func)(const Arg *);
114 const Arg arg;
115} Button;
116
117typedef struct Monitor Monitor;
118typedef struct {
119 /* Must keep this field first */
120 unsigned int type; /* XDGShell or X11* */
121
122 Monitor *mon;
123 struct wlr_scene_tree *scene;
124
125 struct wlr_scene_rect *inner_border[4]; /* inner top, bottom, left, right */
126 struct wlr_scene_rect *outer_border[4]; /* outer top, bottom, left, right */
127
128 struct wlr_scene_tree *scene_surface;
129 struct wl_list link;
130 struct wl_list flink;
131 struct wlr_box geom; /* layout-relative, includes border */
132 struct wlr_box prev; /* layout-relative, includes border */
133 struct wlr_box bounds; /* only width and height are used */
134 union {
135 struct wlr_xdg_surface *xdg;
136 struct wlr_xwayland_surface *xwayland;
137 } surface;
138 struct wlr_xdg_toplevel_decoration_v1 *decoration;
139 struct wl_listener commit;
140 struct wl_listener map;
141 struct wl_listener maximize;
142 struct wl_listener unmap;
143 struct wl_listener destroy;
144 struct wl_listener set_title;
145 struct wl_listener fullscreen;
146 struct wl_listener set_decoration_mode;
147 struct wl_listener destroy_decoration;
148#ifdef XWAYLAND
149 struct wl_listener activate;
150 struct wl_listener associate;
151 struct wl_listener dissociate;
152 struct wl_listener configure;
153 struct wl_listener set_hints;
154#endif
155 unsigned int bw;
156 unsigned int ibw; // inner border width
157 unsigned int obw; // outer border width
158 uint32_t tags;
159 int isfloating, isurgent, isfullscreen, isfakefullscreen;
160 uint32_t resize; /* configure serial of a pending resize */
161 /* infinite_canvas */
162 int saved_cx, saved_cy;
163 int saved_cw, saved_ch;
164 int was_on_canvas;
165 int home_cx, home_cy; /* pan value in effect when this window was placed */
166 /* infinite_canvas_end */
167} Client;
168
169/* infinite_canvas */
170typedef struct {
171 int cx, cy;
172 int saved_cx, saved_cy;
173} CanvasOffset;
174/* infinite_canvas_end */
175
176typedef struct {
177 uint32_t mod;
178 xkb_keysym_t keysym;
179 void (*func)(const Arg *);
180 const Arg arg;
181} Key;
182
183typedef struct {
184 struct wlr_keyboard_group *wlr_group;
185
186 int nsyms;
187 const xkb_keysym_t *keysyms; /* invalid if nsyms == 0 */
188 uint32_t mods; /* invalid if nsyms == 0 */
189 struct wl_event_source *key_repeat_source;
190
191 struct wl_listener modifiers;
192 struct wl_listener key;
193 struct wl_listener destroy;
194} KeyboardGroup;
195
196typedef struct {
197 /* Must keep this field first */
198 unsigned int type; /* LayerShell */
199
200 Monitor *mon;
201 struct wlr_scene_tree *scene;
202 struct wlr_scene_tree *popups;
203 struct wlr_scene_layer_surface_v1 *scene_layer;
204 struct wl_list link;
205 int mapped;
206 struct wlr_layer_surface_v1 *layer_surface;
207
208 struct wl_listener destroy;
209 struct wl_listener unmap;
210 struct wl_listener surface_commit;
211} LayerSurface;
212
213typedef struct {
214 const char *symbol;
215 void (*arrange)(Monitor *);
216} Layout;
217
218typedef struct {
219 struct wl_list link;
220 char name[64];
221 unsigned int seltags;
222 unsigned int sellt;
223 uint32_t tagset[2];
224 float mfact;
225 int nmaster;
226 char ltsymbol[16];
227 const Layout *lt[2];
228 CanvasOffset *canvas;
229 double zoom_scale;
230 double zoom_target;
231 double zoom_pan_x;
232 double zoom_pan_y;
233} MonitorState;
234
235struct Monitor {
236 struct wl_list link;
237 struct wlr_output *wlr_output;
238 struct wlr_scene_output *scene_output;
239 struct wlr_scene_rect *fullscreen_bg; /* See createmon() for info */
240 struct wl_listener frame;
241 struct wl_listener destroy;
242 struct wl_listener request_state;
243 struct wl_listener destroy_lock_surface;
244 struct wlr_session_lock_surface_v1 *lock_surface;
245 struct wlr_box m; /* monitor area, layout-relative */
246 struct wlr_box w; /* window area, layout-relative */
247 struct wl_list layers[4]; /* LayerSurface.link */
248 const Layout *lt[2];
249 unsigned int seltags;
250 unsigned int sellt;
251 uint32_t tagset[2];
252 float mfact;
253 int gamma_lut_changed;
254 int nmaster;
255 char ltsymbol[16];
256 int asleep;
257 /* infinite_canvas */
258 CanvasOffset *canvas;
259 /* infinite_canvas_end */
260 /* zoom */
261 double zoom_scale;
262 double zoom_target;
263 double zoom_pan_x, zoom_pan_y;
264 double zoom_anchor_fx, zoom_anchor_fy;
265 double zoom_anchor_lx, zoom_anchor_ly;
266 double zoom_anim_t;
267 double zoom_start_scale;
268 double zoom_start_pan_x, zoom_start_pan_y;
269 int zoom_popups_dirty;
270 /* zoom end */
271#ifdef TILING
272 int enablegaps; /* enable gaps, used by togglegaps */
273 int gappih; /* horizontal gap between windows */
274 int gappiv; /* vertical gap between windows */
275 int gappoh; /* horizontal outer gaps */
276 int gappov; /* vertical outer gaps */
277#endif
278};
279
280typedef struct {
281 const char *name;
282 float mfact;
283 int nmaster;
284 float scale;
285 const Layout *lt;
286 enum wl_output_transform rr;
287 int x, y;
288 int resx;
289 int resy;
290 float rate;
291 int mode;
292 int adaptive;
293} MonitorRule;
294
295typedef struct {
296 struct wlr_pointer_constraint_v1 *constraint;
297 struct wl_listener destroy;
298} PointerConstraint;
299
300typedef struct {
301 const char *id;
302 const char *title;
303 uint32_t tags;
304 int isfloating;
305 int monitor;
306} Rule;
307
308typedef struct {
309 struct wlr_scene_tree *scene;
310
311 struct wlr_session_lock_v1 *lock;
312 struct wl_listener new_surface;
313 struct wl_listener unlock;
314 struct wl_listener destroy;
315} SessionLock;
316
317/* tablet-input */
318typedef struct {
319 uint32_t button;
320 uint32_t state;
321 void (*func)(const Arg *);
322 const Arg arg;
323} TabletPadButton;
324
325typedef struct {
326 uint32_t button;
327 uint32_t state;
328 void (*func)(const Arg *);
329 const Arg arg;
330} TabletButton;
331/* tablet-input end */
332
333/* function declarations */
334static void applybounds(Client *c, struct wlr_box *bbox);
335static void applyrules(Client *c);
336static void arrange(Monitor *m);
337static void arrangelayer(Monitor *m, struct wl_list *list,
338 struct wlr_box *usable_area, int exclusive);
339static void arrangelayers(Monitor *m);
340static void axisnotify(struct wl_listener *listener, void *data);
341static void buttonpress(struct wl_listener *listener, void *data);
342static void chvt(const Arg *arg);
343static void checkidleinhibitor(struct wlr_surface *exclude);
344static void cleanup(void);
345static void cleanupmon(struct wl_listener *listener, void *data);
346static void cleanuplisteners(void);
347static void closemon(Monitor *m);
348static void commitlayersurfacenotify(struct wl_listener *listener, void *data);
349static void commitnotify(struct wl_listener *listener, void *data);
350static void commitpopup(struct wl_listener *listener, void *data);
351static void createdecoration(struct wl_listener *listener, void *data);
352static void createidleinhibitor(struct wl_listener *listener, void *data);
353static void createkeyboard(struct wlr_keyboard *keyboard);
354static KeyboardGroup *createkeyboardgroup(void);
355static void createlayersurface(struct wl_listener *listener, void *data);
356static void createlocksurface(struct wl_listener *listener, void *data);
357static void createmon(struct wl_listener *listener, void *data);
358static void createnotify(struct wl_listener *listener, void *data);
359static void createpointer(struct wlr_pointer *pointer);
360static void createpointerconstraint(struct wl_listener *listener, void *data);
361static void createpopup(struct wl_listener *listener, void *data);
362static void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint);
363static void cursorframe(struct wl_listener *listener, void *data);
364static void cursorwarptohint(void);
365static void destroydecoration(struct wl_listener *listener, void *data);
366static void destroydragicon(struct wl_listener *listener, void *data);
367static void destroyidleinhibitor(struct wl_listener *listener, void *data);
368static void destroylayersurfacenotify(struct wl_listener *listener, void *data);
369static void destroylock(SessionLock *lock, int unlocked);
370static void destroylocksurface(struct wl_listener *listener, void *data);
371static void destroynotify(struct wl_listener *listener, void *data);
372static void destroypointerconstraint(struct wl_listener *listener, void *data);
373static void destroysessionlock(struct wl_listener *listener, void *data);
374static void destroykeyboardgroup(struct wl_listener *listener, void *data);
375static Monitor *dirtomon(enum wlr_direction dir);
376static void focusclient(Client *c, int lift);
377static void focusmon(const Arg *arg);
378static void focusstack(const Arg *arg);
379static Client *focustop(Monitor *m);
380static void fullscreennotify(struct wl_listener *listener, void *data);
381static void gpureset(struct wl_listener *listener, void *data);
382static void handlesig(int signo);
383static void incnmaster(const Arg *arg);
384static void inputdevice(struct wl_listener *listener, void *data);
385static int keybinding(uint32_t mods, xkb_keysym_t sym);
386static void keypress(struct wl_listener *listener, void *data);
387static void keypressmod(struct wl_listener *listener, void *data);
388static int keyrepeat(void *data);
389static void killclient(const Arg *arg);
390static void locksession(struct wl_listener *listener, void *data);
391static void mapnotify(struct wl_listener *listener, void *data);
392static void maximizenotify(struct wl_listener *listener, void *data);
393static void monocle(Monitor *m);
394static void motionabsolute(struct wl_listener *listener, void *data);
395static void motionnotify(uint32_t time, struct wlr_input_device *device, double sx,
396 double sy, double sx_unaccel, double sy_unaccel);
397static void motionrelative(struct wl_listener *listener, void *data);
398static void moveresize(const Arg *arg);
399static void outputmgrapply(struct wl_listener *listener, void *data);
400static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test);
401static void outputmgrtest(struct wl_listener *listener, void *data);
402static void pointerfocus(Client *c, struct wlr_surface *surface,
403 double sx, double sy, uint32_t time);
404static void printstatus(void);
405static void powermgrsetmode(struct wl_listener *listener, void *data);
406static void quit(const Arg *arg);
407static void rendermon(struct wl_listener *listener, void *data);
408static void requestdecorationmode(struct wl_listener *listener, void *data);
409static void requeststartdrag(struct wl_listener *listener, void *data);
410static void requestmonstate(struct wl_listener *listener, void *data);
411static void resize(Client *c, struct wlr_box geo, int interact);
412static void run(char *startup_cmd);
413static void setcursor(struct wl_listener *listener, void *data);
414static void setcursorshape(struct wl_listener *listener, void *data);
415static void setfloating(Client *c, int floating);
416static void setfullscreen(Client *c, int fullscreen);
417static void setlayout(const Arg *arg);
418static void setmfact(const Arg *arg);
419static void setmon(Client *c, Monitor *m, uint32_t newtags);
420static void setpsel(struct wl_listener *listener, void *data);
421static void setsel(struct wl_listener *listener, void *data);
422static void setup(void);
423static void spawn(const Arg *arg);
424static void startdrag(struct wl_listener *listener, void *data);
425static void tag(const Arg *arg);
426static void tagmon(const Arg *arg);
427#ifdef TILING
428static void tile(Monitor *m);
429static void togglefloating(const Arg *arg);
430#endif // togglefloating is in #ifdef too because why would you need it without tiling?
431static void togglefullscreen(const Arg *arg);
432static void togglefakefullscreen(const Arg *arg);
433static void snaptoposition(const Arg *arg);
434static void toggletag(const Arg *arg);
435static void toggleview(const Arg *arg);
436static void unlocksession(struct wl_listener *listener, void *data);
437static void unmaplayersurfacenotify(struct wl_listener *listener, void *data);
438static void unmapnotify(struct wl_listener *listener, void *data);
439static void updatemons(struct wl_listener *listener, void *data);
440static void updatetitle(struct wl_listener *listener, void *data);
441static void urgent(struct wl_listener *listener, void *data);
442static void view(const Arg *arg);
443static void virtualkeyboard(struct wl_listener *listener, void *data);
444static void virtualpointer(struct wl_listener *listener, void *data);
445static Monitor *xytomon(double x, double y);
446static void xytonode(double x, double y, struct wlr_surface **psurface,
447 Client **pc, LayerSurface **pl, double *nx, double *ny);
448static void swapmaster(const Arg *arg);
449/* infinite_canvas */
450static int getcurrenttag(Monitor *m);
451static void canvas_move_window(Client *c, int x, int y);
452static void snap_edges(Client *c, Monitor *m, int *nx, int *ny);
453static void resize_magnetically(Client *c, Monitor *m, struct wlr_box *geo);
454static void homecanvas(const Arg *arg);
455static void movecanvas(const Arg *arg);
456static void movewinbykeys(const Arg *arg);
457static void resizewinbykeys(const Arg *arg);
458static void movecanvasmouse(const Arg *arg);
459static void centerwindow(const Arg *arg);
460static void bringintoview(Client *c);
461static void save_canvas_positions(Monitor *m);
462static void restore_canvas_positions(Monitor *m);
463/* infinite_canvas_end */
464/* zoom */
465static void zoom(const Arg *arg);
466static void zoomreset(const Arg *arg);
467static void zoom_apply(Monitor *m);
468static void zoom_update_scene_client(Client *c);
469static void zoom_scale_buffers(struct wlr_scene_buffer *buf, int sx, int sy, void *data);
470static void zoom_frame(Monitor *m);
471static void popup_zoom_commit(struct wl_listener *listener, void *data);
472static void popup_zoom_destroy(struct wl_listener *listener, void *data);
473/* zoom end */
474/* spawnregion */
475static void spawnregion(const Arg *arg);
476static void spawnregion_update_rect(void);
477static void spawnregion_finish(void);
478/* spawnregion_end */
479#ifdef TILING
480void arrangegaps(Monitor *m);
481static void defaultgaps(const Arg *arg);
482static void incgaps(const Arg *arg);
483static void incigaps(const Arg *arg);
484static void incihgaps(const Arg *arg);
485static void incivgaps(const Arg *arg);
486static void incogaps(const Arg *arg);
487static void incohgaps(const Arg *arg);
488static void incovgaps(const Arg *arg);
489static void preparegaps(Monitor *m);
490static void setgaps(int oh, int ov, int ih, int iv);
491static void togglegaps(const Arg *arg);
492#endif
493/* tablet-input */
494static void createtablet(struct wlr_input_device *device);
495static void destroytablet(struct wl_listener *listener, void *data);
496static void destroytabletpad(struct wl_listener *listener, void *data);
497static void destroytabletsurfacenotify(struct wl_listener *listener, void *data);
498static void destroytablettool(struct wl_listener *listener, void *data);
499static void tablettoolmotion(struct wlr_tablet_v2_tablet_tool *tool, bool change_x, bool change_y, double x, double y, double dx, double dy);
500static void tablettoolproximity(struct wl_listener *listener, void *data);
501static void tabletpadbutton(struct wl_listener *listener, void *data);
502static void tablettoolaxis(struct wl_listener *listener, void *data);
503static void tablettoolbutton(struct wl_listener *listener, void *data);
504static void tablettooltip(struct wl_listener *listener, void *data);
505/* tablet-input end */
506
507/* variables */
508static pid_t child_pid = -1;
509static int locked;
510static void *exclusive_focus;
511static struct wl_display *dpy;
512static struct wl_event_loop *event_loop;
513static struct wlr_backend *backend;
514static struct wlr_scene *scene;
515static struct wlr_scene_tree *layers[NUM_LAYERS];
516static struct wlr_scene_tree *drag_icon;
517/* Map from ZWLR_LAYER_SHELL_* constants to Lyr* enum */
518static const int layermap[] = { LyrBg, LyrBottom, LyrTop, LyrOverlay };
519static struct wlr_renderer *drw;
520static struct wlr_allocator *alloc;
521static struct wlr_compositor *compositor;
522static struct wlr_session *session;
523
524/* tablet-input */
525static struct wlr_tablet_manager_v2 *tablet_mgr;
526static struct wlr_tablet_v2_tablet *tablet = NULL;
527static struct wlr_tablet_v2_tablet_tool *tablet_tool = NULL;
528static struct wlr_tablet_v2_tablet_pad *tablet_pad = NULL;
529static struct wlr_surface *tablet_curr_surface = NULL;
530static struct wl_listener destroy_tablet_surface_listener = {.notify = destroytabletsurfacenotify};
531
532static struct wl_listener tablet_device_destroy = {.notify = destroytablet};
533static struct wl_listener tablet_pad_destroy = {.notify = destroytabletpad};
534static struct wl_listener tablet_pad_button = {.notify = tabletpadbutton};
535static struct wl_listener tablet_tool_axis = {.notify = tablettoolaxis};
536static struct wl_listener tablet_tool_button = {.notify = tablettoolbutton};
537static struct wl_listener tablet_tool_destroy = {.notify = destroytablettool};
538static struct wl_listener tablet_tool_proximity = {.notify = tablettoolproximity};
539static struct wl_listener tablet_tool_tip = {.notify = tablettooltip};
540/* tablet-input end */
541
542static struct wlr_xdg_shell *xdg_shell;
543static struct wlr_xdg_activation_v1 *activation;
544static struct wlr_xdg_decoration_manager_v1 *xdg_decoration_mgr;
545static struct wl_list clients; /* tiling order */
546static struct wl_list fstack; /* focus order */
547static struct wlr_idle_notifier_v1 *idle_notifier;
548static struct wlr_idle_inhibit_manager_v1 *idle_inhibit_mgr;
549static struct wlr_layer_shell_v1 *layer_shell;
550static struct wlr_output_manager_v1 *output_mgr;
551static struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard_mgr;
552static struct wlr_virtual_pointer_manager_v1 *virtual_pointer_mgr;
553static struct wlr_cursor_shape_manager_v1 *cursor_shape_mgr;
554static struct wlr_output_power_manager_v1 *power_mgr;
555
556static struct wlr_pointer_constraints_v1 *pointer_constraints;
557static struct wlr_pointer_gestures_v1 *pointer_gestures;
558static struct wlr_relative_pointer_manager_v1 *relative_pointer_mgr;
559static struct wlr_pointer_constraint_v1 *active_constraint;
560
561static struct wlr_cursor *cursor;
562static struct wlr_xcursor_manager *cursor_mgr;
563
564static struct wlr_scene_rect *root_bg;
565static struct wlr_session_lock_manager_v1 *session_lock_mgr;
566static struct wlr_scene_rect *locked_bg;
567static struct wlr_session_lock_v1 *cur_lock;
568
569static struct wlr_seat *seat;
570static KeyboardGroup *kb_group;
571static unsigned int cursor_mode;
572static Client *grabc;
573static int grabcx, grabcy; /* client-relative */
574static struct wl_event_source *openanim_timer = NULL;
575static Client *openanim_client = NULL;
576static uint64_t openanim_start_ms = 0;
577static int openanim_running = 0;
578/* zoom */
579static double resize_grab_world_x = 0.0;
580static double resize_grab_world_y = 0.0;
581static int resize_snap_x_target = 0;
582static int resize_snap_y_target = 0;
583typedef struct {
584 struct wl_list link;
585 struct wl_listener commit;
586 struct wl_listener destroy;
587 Client *client;
588 struct wlr_xdg_popup *popup;
589 struct wlr_scene_node *node;
590} PopupZoomData;
591static struct wl_list popup_zoom_list;
592/* zoom end */
593
594/* infinite_canvas */
595static int canvas_panning = 0;
596static double canvas_pan_last_x = 0, canvas_pan_last_y = 0;
597static float canvas_pan_multiplier = 1.0f;
598static float canvas_pan_accum_x = 0.0f, canvas_pan_accum_y = 0.0f;
599/* infinite_canvas_end */
600/* spawnregion */
601static int spawnregion_active = 0;
602static double spawnregion_x1 = 0, spawnregion_y1 = 0;
603static double spawnregion_x2 = 0, spawnregion_y2 = 0;
604static struct wlr_scene_rect *spawnregion_rect = NULL;
605static struct wlr_box pending_spawn_geo = {0};
606static int has_pending_spawn_geo = 0;
607Client *waiting_geo_client = NULL;
608/* spawnregion_end */
609
610static struct wlr_output_layout *output_layout;
611static struct wlr_box sgeom;
612static struct wl_list mons;
613static Monitor *selmon;
614static struct wl_list savedstates;
615
616#ifdef TILING
617static int resizelock = 0; /* do not actually resize during arrange */
618#endif
619
620/* global event handlers */
621static struct wl_listener cursor_axis = {.notify = axisnotify};
622static struct wl_listener cursor_button = {.notify = buttonpress};
623static struct wl_listener cursor_frame = {.notify = cursorframe};
624static struct wl_listener cursor_motion = {.notify = motionrelative};
625static struct wl_listener cursor_motion_absolute = {.notify = motionabsolute};
626static struct wl_listener gpu_reset = {.notify = gpureset};
627static struct wl_listener layout_change = {.notify = updatemons};
628static struct wl_listener new_idle_inhibitor = {.notify = createidleinhibitor};
629static struct wl_listener new_input_device = {.notify = inputdevice};
630static struct wl_listener new_virtual_keyboard = {.notify = virtualkeyboard};
631static struct wl_listener new_virtual_pointer = {.notify = virtualpointer};
632static struct wl_listener new_pointer_constraint = {.notify = createpointerconstraint};
633static struct wl_listener new_output = {.notify = createmon};
634static struct wl_listener new_xdg_toplevel = {.notify = createnotify};
635static struct wl_listener new_xdg_popup = {.notify = createpopup};
636static struct wl_listener new_xdg_decoration = {.notify = createdecoration};
637static struct wl_listener new_layer_surface = {.notify = createlayersurface};
638static struct wl_listener output_mgr_apply = {.notify = outputmgrapply};
639static struct wl_listener output_mgr_test = {.notify = outputmgrtest};
640static struct wl_listener output_power_mgr_set_mode = {.notify = powermgrsetmode};
641static struct wl_listener request_activate = {.notify = urgent};
642static struct wl_listener request_cursor = {.notify = setcursor};
643static struct wl_listener request_set_psel = {.notify = setpsel};
644static struct wl_listener request_set_sel = {.notify = setsel};
645static struct wl_listener request_set_cursor_shape = {.notify = setcursorshape};
646static struct wl_listener request_start_drag = {.notify = requeststartdrag};
647static struct wl_listener start_drag = {.notify = startdrag};
648static struct wl_listener new_session_lock = {.notify = locksession};
649
650#ifdef XWAYLAND
651static void activatex11(struct wl_listener *listener, void *data);
652static void associatex11(struct wl_listener *listener, void *data);
653static void configurex11(struct wl_listener *listener, void *data);
654static void createnotifyx11(struct wl_listener *listener, void *data);
655static void dissociatex11(struct wl_listener *listener, void *data);
656static void sethints(struct wl_listener *listener, void *data);
657static void xwaylandready(struct wl_listener *listener, void *data);
658static struct wl_listener new_xwayland_surface = {.notify = createnotifyx11};
659static struct wl_listener xwayland_ready = {.notify = xwaylandready};
660static struct wlr_xwayland *xwayland;
661#endif
662
663/* zoom */
664#define SCROLL_UP 0x14b
665#define SCROLL_DOWN 0x14c
666/* zoom end */
667
668/* configuration, allows nested code to access above variables */
669#include "config.h"
670
671/* spawnregion, define it after config.h was included so SPAWNREGION_STYLE is defined */
672#if SPAWNREGION_STYLE == 2
673static struct wlr_scene_rect *spawnregion_border[8] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
674#endif
675/* spawnregion end */
676
677#ifndef SPAWNREGION_COLOR
678#define SPAWNREGION_COLOR { 0.2f, 0.5f, 1.0f, 0.35f }
679#endif
680
681/* zoom */
682#ifndef ZOOMMIN
683#define ZOOMMIN 0.1
684#endif
685#ifndef ZOOMMAX
686#define ZOOMMAX 5.0
687#endif
688#ifndef animatezoom
689#define animatezoom 1
690#endif
691#ifndef zoomduration
692#define zoomduration 0.18
693#endif
694#ifndef zoombezier
695#define zoombezier "0.22,1.0,0.36,1.0"
696#endif
697#ifndef movecanvasmouse_multiplier_is_affected_by_zoom
698#define movecanvasmouse_multiplier_is_affected_by_zoom 1
699#endif
700/* zoom end */
701
702/* attempt to encapsulate suck into one file */
703#include "client.h"
704
705/* function implementations */
706void
707applybounds(Client *c, struct wlr_box *bbox)
708{
709 /* set minimum possible */
710 c->geom.width = MAX(1 + 2 * (int)c->bw, c->geom.width);
711 c->geom.height = MAX(1 + 2 * (int)c->bw, c->geom.height);
712
713 if (c->geom.x >= bbox->x + bbox->width)
714 c->geom.x = bbox->x + bbox->width - c->geom.width;
715 if (c->geom.y >= bbox->y + bbox->height)
716 c->geom.y = bbox->y + bbox->height - c->geom.height;
717 if (c->geom.x + c->geom.width <= bbox->x)
718 c->geom.x = bbox->x;
719 if (c->geom.y + c->geom.height <= bbox->y)
720 c->geom.y = bbox->y;
721}
722
723void
724applyrules(Client *c)
725{
726 /* rule matching */
727 const char *appid, *title;
728 uint32_t newtags = 0;
729 int i;
730 const Rule *r;
731 Monitor *mon = selmon, *m;
732
733 appid = client_get_appid(c);
734 title = client_get_title(c);
735
736 for (r = rules; r < END(rules); r++) {
737 if ((!r->title || strstr(title, r->title))
738 && (!r->id || strstr(appid, r->id))) {
739 c->isfloating = r->isfloating;
740 newtags |= r->tags;
741 i = 0;
742 wl_list_for_each(m, &mons, link) {
743 if (r->monitor == i++)
744 mon = m;
745 }
746 }
747 }
748
749 c->isfloating |= client_is_float_type(c);
750 setmon(c, mon, newtags);
751}
752
753void
754arrange(Monitor *m)
755{
756 Client *c;
757
758 if (!m->wlr_output->enabled)
759 return;
760
761 wl_list_for_each(c, &clients, link) {
762 if (c->mon == m) {
763 wlr_scene_node_set_enabled(&c->scene->node, VISIBLEON(c, m) && c != waiting_geo_client);
764 client_set_suspended(c, !VISIBLEON(c, m));
765 }
766 }
767
768 wlr_scene_node_set_enabled(&m->fullscreen_bg->node,
769 (c = focustop(m)) && c->isfullscreen);
770
771 strncpy(m->ltsymbol, layouts[0].symbol, LENGTH(m->ltsymbol));
772
773 /* We move all clients (except fullscreen and unmanaged) to LyrTile while
774 * in floating layout to avoid "real" floating clients be always on top */
775 wl_list_for_each(c, &clients, link) {
776 if (c->mon != m || c->scene->node.parent == layers[LyrFS])
777 continue;
778
779 wlr_scene_node_reparent(&c->scene->node,
780 (m->lt[m->sellt] && !m->lt[m->sellt]->arrange && c->isfloating)
781 ? layers[LyrTile]
782 : (m->lt[m->sellt] && m->lt[m->sellt]->arrange && c->isfloating)
783 ? layers[LyrFloat]
784 : c->scene->node.parent);
785 }
786
787 if (m->lt[m->sellt] && m->lt[m->sellt]->arrange) {
788#ifdef TILING
789 preparegaps(m);
790#endif
791 m->lt[m->sellt]->arrange(m);
792#ifdef TILING
793 arrangegaps(m);
794#endif
795 }
796 /* zoom */
797 if (layouts[m->sellt].arrange == NULL)
798 zoom_apply(m);
799 /* zoom end */
800 motionnotify(0, NULL, 0, 0, 0, 0);
801 checkidleinhibitor(NULL);
802}
803
804void
805arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, int exclusive)
806{
807 LayerSurface *l;
808 struct wlr_box full_area = m->m;
809
810 wl_list_for_each(l, list, link) {
811 struct wlr_layer_surface_v1 *layer_surface = l->layer_surface;
812
813 if (!layer_surface->initialized)
814 continue;
815
816 if (exclusive != (layer_surface->current.exclusive_zone > 0))
817 continue;
818
819 wlr_scene_layer_surface_v1_configure(l->scene_layer, &full_area, usable_area);
820 wlr_scene_node_set_position(&l->popups->node, l->scene->node.x, l->scene->node.y);
821 }
822}
823
824void
825arrangelayers(Monitor *m)
826{
827 int i;
828 struct wlr_box usable_area = m->m;
829 LayerSurface *l;
830 uint32_t layers_above_shell[] = {
831 ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
832 ZWLR_LAYER_SHELL_V1_LAYER_TOP,
833 };
834 if (!m->wlr_output->enabled)
835 return;
836
837 /* Arrange exclusive surfaces from top->bottom */
838 for (i = 3; i >= 0; i--)
839 arrangelayer(m, &m->layers[i], &usable_area, 1);
840
841 if (!wlr_box_equal(&usable_area, &m->w)) {
842 m->w = usable_area;
843 arrange(m);
844 }
845
846 /* Arrange non-exclusive surfaces from top->bottom */
847 for (i = 3; i >= 0; i--)
848 arrangelayer(m, &m->layers[i], &usable_area, 0);
849
850 /* Find topmost keyboard interactive layer, if such a layer exists */
851 for (i = 0; i < (int)LENGTH(layers_above_shell); i++) {
852 wl_list_for_each_reverse(l, &m->layers[layers_above_shell[i]], link) {
853 if (locked || !l->layer_surface->current.keyboard_interactive || !l->mapped)
854 continue;
855 /* Deactivate the focused client. */
856 focusclient(NULL, 0);
857 exclusive_focus = l;
858 client_notify_enter(l->layer_surface->surface, wlr_seat_get_keyboard(seat));
859 return;
860 }
861 }
862}
863
864void
865axisnotify(struct wl_listener *listener, void *data)
866{
867 /* click */
868 unsigned int click;
869 Client *c;
870 LayerSurface *l;
871 /* click end */
872 struct wlr_pointer_axis_event *event = data;
873 struct wlr_keyboard *keyboard;
874 uint32_t mods;
875 const Button *b;
876
877 wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
878
879 /* click */
880 click = ClkRoot;
881 xytonode(cursor->x, cursor->y, NULL, &c, &l, NULL, NULL);
882 if (c)
883 click = ClkClient;
884 else if (l)
885 click = ClkLayerShell;
886 /* click end */
887
888 keyboard = wlr_seat_get_keyboard(seat);
889 mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
890
891 if (event->orientation == WL_POINTER_AXIS_VERTICAL_SCROLL) {
892 /* zoom */
893 unsigned int btn = (event->delta > 0 || event->delta_discrete > 0)
894 ? SCROLL_DOWN : SCROLL_UP;
895 for (b = buttons; b < END(buttons); b++) {
896 if (CLEANMASK(mods) == CLEANMASK(b->mod)
897 // / click /
898 && btn == b->button && click == b->click && b->func) {
899 b->func(&b->arg);
900 return;
901 }
902 }
903 /* zoom end */
904 }
905
906 wlr_seat_pointer_notify_axis(seat,
907 event->time_msec, event->orientation, event->delta,
908 event->delta_discrete, event->source, event->relative_direction);
909}
910
911void
912buttonpress(struct wl_listener *listener, void *data)
913{
914 /* click */
915 unsigned int click;
916 /* click end */
917 struct wlr_pointer_button_event *event = data;
918 struct wlr_keyboard *keyboard;
919 uint32_t mods;
920 Client *c;
921 LayerSurface *l;
922 const Button *b;
923
924 wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
925
926 /* click */
927 click = ClkRoot;
928 xytonode(cursor->x, cursor->y, NULL, &c, &l, NULL, NULL);
929 if (c)
930 click = ClkClient;
931 else if (l)
932 click = ClkLayerShell;
933 wlr_log(WLR_DEBUG, "buttonpress: c=%p l=%p click=%u (0=Client,1=LayerShell,2=Root)", (void*)c, (void*)l, click);
934 /* click end */
935
936 switch (event->state) {
937 case WL_POINTER_BUTTON_STATE_PRESSED:
938 cursor_mode = CurPressed;
939 selmon = xytomon(cursor->x, cursor->y);
940 if (locked)
941 break;
942
943 /* spawnregion */
944 if (spawnregion_active && event->button == BTN_LEFT) {
945 spawnregion_x1 = cursor->x;
946 spawnregion_y1 = cursor->y;
947 spawnregion_x2 = cursor->x;
948 spawnregion_y2 = cursor->y;
949#if SPAWNREGION_STYLE == 2
950 {
951 int i;
952 for (i = 0; i < 4; i++) {
953 if (!spawnregion_border[i]) {
954 const float *oc = spawnregion_outer_border_color;
955 spawnregion_border[i] = wlr_scene_rect_create(layers[LyrBlock],
956 1, 1, (float[4]){oc[0], oc[1], oc[2], oc[3]});
957 }
958 }
959 for (i = 4; i < 8; i++) {
960 if (!spawnregion_border[i]) {
961 const float *ic = spawnregion_inner_border_color;
962 spawnregion_border[i] = wlr_scene_rect_create(layers[LyrBlock],
963 1, 1, (float[4]){ic[0], ic[1], ic[2], ic[3]});
964 }
965 }
966 }
967#else
968 if (!spawnregion_rect)
969# if SPAWNREGION_STYLE == 1
970 spawnregion_rect = wlr_scene_rect_create(layers[LyrBlock],
971 1, 1, (float[4])SPAWNREGION_COLOR);
972# else
973 spawnregion_rect = wlr_scene_rect_create(layers[LyrBlock],
974 1, 1, (float[4]){0.2f, 0.5f, 1.0f, 0.0f});
975# endif
976 wlr_scene_node_set_position(&spawnregion_rect->node,
977 (int)spawnregion_x1, (int)spawnregion_y1);
978 wlr_scene_rect_set_size(spawnregion_rect, 1, 1);
979 wlr_scene_node_set_enabled(&spawnregion_rect->node, 1);
980#endif
981 cursor_mode = CurSpawnRegion;
982 return;
983 }
984 /* spawnregion_end */
985
986 /* Change focus if the button was _pressed_ over a client */
987 xytonode(cursor->x, cursor->y, NULL, &c, NULL, NULL, NULL);
988 // if (c && (!client_is_unmanaged(c) || client_wants_focus(c))) // not click
989 if (click == ClkClient && (!client_is_unmanaged(c) || client_wants_focus(c))) // click
990 focusclient(c, 1);
991
992 keyboard = wlr_seat_get_keyboard(seat);
993 mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
994 for (b = buttons; b < END(buttons); b++) {
995 if (CLEANMASK(mods) == CLEANMASK(b->mod) &&
996 // / click /
997 event->button == b->button && click == b->click && b->func) {
998 b->func(&b->arg);
999 return;
1000 }
1001 }
1002 break;
1003 case WL_POINTER_BUTTON_STATE_RELEASED:
1004 /* If you released any buttons, we exit interactive move/resize mode. */
1005 /* TODO: should reset to the pointer focus's current setcursor */
1006 if (!locked && cursor_mode != CurNormal && cursor_mode != CurPressed) {
1007 /* infinite_canvas */
1008 if (canvas_panning) {
1009 canvas_panning = 0;
1010 wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
1011 cursor_mode = CurNormal;
1012 return;
1013 }
1014 /* infinite_canvas_end */
1015 /* spawnregion */
1016 if (cursor_mode == CurSpawnRegion) {
1017 spawnregion_finish();
1018 return;
1019 }
1020 /* spawnregion_end */
1021 wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
1022 cursor_mode = CurNormal;
1023 /* infinite_canvas: a manual move/resize just finished
1024 * under whatever pan is currently active -- that's now
1025 * this window's natural framing for future teleports. */
1026 if (grabc && grabc->mon) {
1027 int tagidx = getcurrenttag(grabc->mon);
1028 grabc->home_cx = grabc->mon->canvas[tagidx].cx;
1029 grabc->home_cy = grabc->mon->canvas[tagidx].cy;
1030 }
1031 /* infinite_canvas_end */
1032 /* Drop the window off on its new monitor */
1033 selmon = xytomon(cursor->x, cursor->y);
1034 setmon(grabc, selmon, 0);
1035 grabc = NULL;
1036 return;
1037 }
1038 cursor_mode = CurNormal;
1039 break;
1040 }
1041 /* If the event wasn't handled by the compositor, notify the client with
1042 * pointer focus that a button press has occurred */
1043 wlr_seat_pointer_notify_button(seat,
1044 event->time_msec, event->button, event->state);
1045}
1046
1047void
1048chvt(const Arg *arg)
1049{
1050 wlr_session_change_vt(session, arg->ui);
1051}
1052
1053void
1054checkidleinhibitor(struct wlr_surface *exclude)
1055{
1056 int inhibited = 0, unused_lx, unused_ly;
1057 struct wlr_idle_inhibitor_v1 *inhibitor;
1058 wl_list_for_each(inhibitor, &idle_inhibit_mgr->inhibitors, link) {
1059 struct wlr_surface *surface = wlr_surface_get_root_surface(inhibitor->surface);
1060 struct wlr_scene_tree *tree = surface->data;
1061 if (exclude != surface && (bypass_surface_visibility || (!tree
1062 || wlr_scene_node_coords(&tree->node, &unused_lx, &unused_ly)))) {
1063 inhibited = 1;
1064 break;
1065 }
1066 }
1067
1068 wlr_idle_notifier_v1_set_inhibited(idle_notifier, inhibited);
1069}
1070
1071void
1072cleanup(void)
1073{
1074 cleanuplisteners();
1075#ifdef XWAYLAND
1076 wlr_xwayland_destroy(xwayland);
1077 xwayland = NULL;
1078#endif
1079 wl_display_destroy_clients(dpy);
1080 if (child_pid > 0) {
1081 kill(-child_pid, SIGTERM);
1082 waitpid(child_pid, NULL, 0);
1083 }
1084 wlr_xcursor_manager_destroy(cursor_mgr);
1085
1086 destroykeyboardgroup(&kb_group->destroy, NULL);
1087
1088 /* If it's not destroyed manually, it will cause a use-after-free of wlr_seat.
1089 * Destroy it until it's fixed on the wlroots side */
1090 wlr_backend_destroy(backend);
1091
1092 wl_display_destroy(dpy);
1093 /* Destroy after the wayland display (when the monitors are already destroyed)
1094 to avoid destroying them with an invalid scene output. */
1095 wlr_scene_node_destroy(&scene->tree.node);
1096}
1097
1098void
1099cleanupmon(struct wl_listener *listener, void *data)
1100{
1101 Monitor *m = wl_container_of(listener, m, destroy);
1102 LayerSurface *l, *tmp;
1103 size_t i;
1104
1105 /* m->layers[i] are intentionally not unlinked */
1106 for (i = 0; i < LENGTH(m->layers); i++) {
1107 wl_list_for_each_safe(l, tmp, &m->layers[i], link)
1108 wlr_layer_surface_v1_destroy(l->layer_surface);
1109 }
1110
1111 wl_list_remove(&m->destroy.link);
1112 wl_list_remove(&m->frame.link);
1113 wl_list_remove(&m->link);
1114 wl_list_remove(&m->request_state.link);
1115 if (m->lock_surface)
1116 destroylocksurface(&m->destroy_lock_surface, NULL);
1117 m->wlr_output->data = NULL;
1118 wlr_output_layout_remove(output_layout, m->wlr_output);
1119 wlr_scene_output_destroy(m->scene_output);
1120
1121 closemon(m);
1122 wlr_scene_node_destroy(&m->fullscreen_bg->node);
1123 /* infinite_canvas */
1124 {
1125 MonitorState *ms, *tmp_ms;
1126 wl_list_for_each_safe(ms, tmp_ms, &savedstates, link) {
1127 if (strcmp(ms->name, m->wlr_output->name) == 0) {
1128 wl_list_remove(&ms->link);
1129 free(ms->canvas);
1130 free(ms);
1131 break;
1132 }
1133 }
1134 ms = ecalloc(1, sizeof(MonitorState));
1135 strncpy(ms->name, m->wlr_output->name, sizeof(ms->name) - 1);
1136 ms->seltags = m->seltags;
1137 ms->sellt = m->sellt;
1138 ms->tagset[0] = m->tagset[0];
1139 ms->tagset[1] = m->tagset[1];
1140 ms->mfact = m->mfact;
1141 ms->nmaster = m->nmaster;
1142 strncpy(ms->ltsymbol, m->ltsymbol, sizeof(ms->ltsymbol));
1143 ms->lt[0] = m->lt[0];
1144 ms->lt[1] = m->lt[1];
1145 ms->canvas = m->canvas;
1146 m->canvas = NULL;
1147 ms->zoom_scale = m->zoom_scale;
1148 ms->zoom_target = m->zoom_target;
1149 ms->zoom_pan_x = m->zoom_pan_x;
1150 ms->zoom_pan_y = m->zoom_pan_y;
1151 wl_list_insert(&savedstates, &ms->link);
1152 }
1153 /* infinite_canvas_end */
1154 free(m);
1155}
1156
1157void
1158cleanuplisteners(void)
1159{
1160 wl_list_remove(&cursor_axis.link);
1161 wl_list_remove(&cursor_button.link);
1162 wl_list_remove(&cursor_frame.link);
1163 wl_list_remove(&cursor_motion.link);
1164 wl_list_remove(&cursor_motion_absolute.link);
1165 wl_list_remove(&gpu_reset.link);
1166 wl_list_remove(&new_idle_inhibitor.link);
1167 wl_list_remove(&layout_change.link);
1168 wl_list_remove(&new_input_device.link);
1169 wl_list_remove(&new_virtual_keyboard.link);
1170 wl_list_remove(&new_virtual_pointer.link);
1171 wl_list_remove(&new_pointer_constraint.link);
1172 wl_list_remove(&new_output.link);
1173 wl_list_remove(&new_xdg_toplevel.link);
1174 wl_list_remove(&new_xdg_decoration.link);
1175 wl_list_remove(&new_xdg_popup.link);
1176 wl_list_remove(&new_layer_surface.link);
1177 wl_list_remove(&output_mgr_apply.link);
1178 wl_list_remove(&output_mgr_test.link);
1179 wl_list_remove(&output_power_mgr_set_mode.link);
1180 wl_list_remove(&request_activate.link);
1181 wl_list_remove(&request_cursor.link);
1182 wl_list_remove(&request_set_psel.link);
1183 wl_list_remove(&request_set_sel.link);
1184 wl_list_remove(&request_set_cursor_shape.link);
1185 wl_list_remove(&request_start_drag.link);
1186 wl_list_remove(&start_drag.link);
1187 wl_list_remove(&new_session_lock.link);
1188#ifdef XWAYLAND
1189 wl_list_remove(&new_xwayland_surface.link);
1190 wl_list_remove(&xwayland_ready.link);
1191#endif
1192}
1193
1194void
1195closemon(Monitor *m)
1196{
1197 /* update selmon if needed and
1198 * move closed monitor's clients to the focused one */
1199 Client *c;
1200 int i = 0, nmons = wl_list_length(&mons);
1201 if (!nmons) {
1202 selmon = NULL;
1203 } else if (m == selmon) {
1204 do /* don't switch to disabled mons */
1205 selmon = wl_container_of(mons.next, selmon, link);
1206 while (!selmon->wlr_output->enabled && i++ < nmons);
1207
1208 if (!selmon->wlr_output->enabled)
1209 selmon = NULL;
1210 }
1211
1212 wl_list_for_each(c, &clients, link) {
1213 if (c->isfloating && c->geom.x > m->m.width)
1214 resize(c, (struct wlr_box){.x = c->geom.x - m->w.width, .y = c->geom.y,
1215 .width = c->geom.width, .height = c->geom.height}, 0);
1216 if (c->mon == m)
1217 setmon(c, selmon, c->tags);
1218 }
1219 focusclient(focustop(selmon), 1);
1220 printstatus();
1221}
1222
1223void
1224commitlayersurfacenotify(struct wl_listener *listener, void *data)
1225{
1226 LayerSurface *l = wl_container_of(listener, l, surface_commit);
1227 struct wlr_layer_surface_v1 *layer_surface = l->layer_surface;
1228 struct wlr_scene_tree *scene_layer = layers[layermap[layer_surface->current.layer]];
1229 struct wlr_layer_surface_v1_state old_state;
1230
1231 if (l->layer_surface->initial_commit) {
1232 client_set_scale(layer_surface->surface, l->mon->wlr_output->scale);
1233
1234 /* Temporarily set the layer's current state to pending
1235 * so that we can easily arrange it */
1236 old_state = l->layer_surface->current;
1237 l->layer_surface->current = l->layer_surface->pending;
1238 arrangelayers(l->mon);
1239 l->layer_surface->current = old_state;
1240 return;
1241 }
1242
1243 if (layer_surface->current.committed == 0 && l->mapped == layer_surface->surface->mapped)
1244 return;
1245 l->mapped = layer_surface->surface->mapped;
1246
1247 if (scene_layer != l->scene->node.parent) {
1248 wlr_scene_node_reparent(&l->scene->node, scene_layer);
1249 wl_list_remove(&l->link);
1250 wl_list_insert(&l->mon->layers[layer_surface->current.layer], &l->link);
1251 wlr_scene_node_reparent(&l->popups->node, (layer_surface->current.layer
1252 < ZWLR_LAYER_SHELL_V1_LAYER_TOP ? layers[LyrTop] : scene_layer));
1253 }
1254
1255 arrangelayers(l->mon);
1256}
1257
1258void
1259commitnotify(struct wl_listener *listener, void *data)
1260{
1261 Client *c = wl_container_of(listener, c, commit);
1262
1263 if (c->surface.xdg->initial_commit) {
1264 /*
1265 * Get the monitor this client will be rendered on
1266 * Note that if the user set a rule in which the client is placed on
1267 * a different monitor based on its title, this will likely select
1268 * a wrong monitor.
1269 */
1270 applyrules(c);
1271 if (c->mon) {
1272 client_set_scale(client_surface(c), c->mon->wlr_output->scale);
1273 }
1274 setmon(c, NULL, 0); /* Make sure to reapply rules in mapnotify() */
1275
1276 wlr_xdg_toplevel_set_wm_capabilities(c->surface.xdg->toplevel,
1277 WLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN);
1278 if (c->decoration)
1279 requestdecorationmode(&c->set_decoration_mode, c->decoration);
1280 wlr_xdg_toplevel_set_size(c->surface.xdg->toplevel, 0, 0);
1281 return;
1282 }
1283
1284 resize(c, c->geom, (c->isfloating && !c->isfullscreen));
1285
1286 /* mark a pending resize as completed */
1287 if (c->resize && c->resize <= c->surface.xdg->current.configure_serial)
1288 c->resize = 0;
1289
1290 /* zoom */
1291 if (c->mon && fabs(c->mon->zoom_scale - 1.0) >= 1e-9)
1292 wlr_scene_node_for_each_buffer(&c->scene->node, zoom_scale_buffers, c);
1293 /* zoom end */
1294}
1295
1296void
1297commitpopup(struct wl_listener *listener, void *data)
1298{
1299 struct wlr_surface *surface = data;
1300 struct wlr_xdg_popup *popup = wlr_xdg_popup_try_from_wlr_surface(surface);
1301 LayerSurface *l = NULL;
1302 Client *c = NULL;
1303 struct wlr_box box;
1304 int type = -1;
1305
1306 if (!popup->base->initial_commit)
1307 return;
1308
1309 type = toplevel_from_wlr_surface(popup->base->surface, &c, &l);
1310 if (!popup->parent || type < 0)
1311 return;
1312 popup->base->surface->data = wlr_scene_xdg_surface_create(
1313 popup->parent->data, popup->base);
1314 if ((l && !l->mon) || (c && !c->mon)) {
1315 wlr_xdg_popup_destroy(popup);
1316 return;
1317 }
1318 box = type == LayerShell ? l->mon->m : c->mon->w;
1319 box.x -= (type == LayerShell ? l->scene->node.x : c->geom.x);
1320 box.y -= (type == LayerShell ? l->scene->node.y : c->geom.y);
1321 /* zoom */
1322 if (type != LayerShell && c->mon && fabs(c->mon->zoom_scale - 1.0) >= 1e-9) {
1323 Monitor *_m = c->mon;
1324 double inv_s = 1.0 / _m->zoom_scale;
1325 box.x = (int)llround(-_m->zoom_pan_x * inv_s) - c->geom.x;
1326 box.y = (int)llround(-_m->zoom_pan_y * inv_s) - c->geom.y;
1327 box.width = MAX(1, (int)llround((double)_m->m.width * inv_s));
1328 box.height = MAX(1, (int)llround((double)_m->m.height * inv_s));
1329 }
1330 /* zoom end */
1331 wlr_xdg_popup_unconstrain_from_box(popup, &box);
1332 /* zoom */
1333 if (c && c->mon) {
1334 struct wlr_scene_tree *ptree = popup->parent->data;
1335 struct wlr_scene_node *pnode;
1336 PopupZoomData *pzd;
1337 pnode = wl_container_of(ptree->children.prev, pnode, link);
1338 pzd = ecalloc(1, sizeof(*pzd));
1339 pzd->client = c;
1340 pzd->popup = popup;
1341 pzd->node = pnode;
1342 pzd->commit.notify = popup_zoom_commit;
1343 pzd->destroy.notify = popup_zoom_destroy;
1344 wl_list_insert(&popup_zoom_list, &pzd->link);
1345 wl_signal_add(&popup->base->surface->events.commit, &pzd->commit);
1346 wl_signal_add(&popup->base->events.destroy, &pzd->destroy);
1347 popup_zoom_commit(&pzd->commit, NULL);
1348 }
1349 /* zoom end */
1350 wl_list_remove(&listener->link);
1351 free(listener);
1352}
1353
1354void
1355createdecoration(struct wl_listener *listener, void *data)
1356{
1357 struct wlr_xdg_toplevel_decoration_v1 *deco = data;
1358 Client *c = deco->toplevel->base->data;
1359 c->decoration = deco;
1360
1361 LISTEN(&deco->events.request_mode, &c->set_decoration_mode, requestdecorationmode);
1362 LISTEN(&deco->events.destroy, &c->destroy_decoration, destroydecoration);
1363
1364 requestdecorationmode(&c->set_decoration_mode, deco);
1365}
1366
1367void
1368createidleinhibitor(struct wl_listener *listener, void *data)
1369{
1370 struct wlr_idle_inhibitor_v1 *idle_inhibitor = data;
1371 LISTEN_STATIC(&idle_inhibitor->events.destroy, destroyidleinhibitor);
1372
1373 checkidleinhibitor(NULL);
1374}
1375
1376void
1377createkeyboard(struct wlr_keyboard *keyboard)
1378{
1379 /* Set the keymap to match the group keymap */
1380 wlr_keyboard_set_keymap(keyboard, kb_group->wlr_group->keyboard.keymap);
1381
1382 /* Add the new keyboard to the group */
1383 wlr_keyboard_group_add_keyboard(kb_group->wlr_group, keyboard);
1384}
1385
1386KeyboardGroup *
1387createkeyboardgroup(void)
1388{
1389 KeyboardGroup *group = ecalloc(1, sizeof(*group));
1390 struct xkb_context *context;
1391 struct xkb_keymap *keymap;
1392
1393 group->wlr_group = wlr_keyboard_group_create();
1394 group->wlr_group->data = group;
1395
1396 /* Prepare an XKB keymap and assign it to the keyboard group. */
1397 context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
1398 if (!(keymap = xkb_keymap_new_from_names(context, &xkb_rules,
1399 XKB_KEYMAP_COMPILE_NO_FLAGS)))
1400 die("failed to compile keymap");
1401
1402 wlr_keyboard_set_keymap(&group->wlr_group->keyboard, keymap);
1403 xkb_keymap_unref(keymap);
1404 xkb_context_unref(context);
1405
1406 wlr_keyboard_set_repeat_info(&group->wlr_group->keyboard, repeat_rate, repeat_delay);
1407
1408 /* Set up listeners for keyboard events */
1409 LISTEN(&group->wlr_group->keyboard.events.key, &group->key, keypress);
1410 LISTEN(&group->wlr_group->keyboard.events.modifiers, &group->modifiers, keypressmod);
1411
1412 group->key_repeat_source = wl_event_loop_add_timer(event_loop, keyrepeat, group);
1413
1414 /* A seat can only have one keyboard, but this is a limitation of the
1415 * Wayland protocol - not wlroots. We assign all connected keyboards to the
1416 * same wlr_keyboard_group, which provides a single wlr_keyboard interface for
1417 * all of them. Set this combined wlr_keyboard as the seat keyboard.
1418 */
1419 wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard);
1420 return group;
1421}
1422
1423void
1424createlayersurface(struct wl_listener *listener, void *data)
1425{
1426 struct wlr_layer_surface_v1 *layer_surface = data;
1427 LayerSurface *l;
1428 struct wlr_surface *surface = layer_surface->surface;
1429 struct wlr_scene_tree *scene_layer = layers[layermap[layer_surface->pending.layer]];
1430
1431 if (!layer_surface->output
1432 && !(layer_surface->output = selmon ? selmon->wlr_output : NULL)) {
1433 wlr_layer_surface_v1_destroy(layer_surface);
1434 return;
1435 }
1436
1437 l = layer_surface->data = ecalloc(1, sizeof(*l));
1438 l->type = LayerShell;
1439 LISTEN(&surface->events.commit, &l->surface_commit, commitlayersurfacenotify);
1440 LISTEN(&surface->events.unmap, &l->unmap, unmaplayersurfacenotify);
1441 LISTEN(&layer_surface->events.destroy, &l->destroy, destroylayersurfacenotify);
1442
1443 l->layer_surface = layer_surface;
1444 l->mon = layer_surface->output->data;
1445 l->scene_layer = wlr_scene_layer_surface_v1_create(scene_layer, layer_surface);
1446 l->scene = l->scene_layer->tree;
1447 l->popups = surface->data = wlr_scene_tree_create(layer_surface->current.layer
1448 < ZWLR_LAYER_SHELL_V1_LAYER_TOP ? layers[LyrTop] : scene_layer);
1449 l->scene->node.data = l->popups->node.data = l;
1450
1451 wl_list_insert(&l->mon->layers[layer_surface->pending.layer],&l->link);
1452 wlr_surface_send_enter(surface, layer_surface->output);
1453}
1454
1455void
1456createlocksurface(struct wl_listener *listener, void *data)
1457{
1458 SessionLock *lock = wl_container_of(listener, lock, new_surface);
1459 struct wlr_session_lock_surface_v1 *lock_surface = data;
1460 Monitor *m = lock_surface->output->data;
1461 struct wlr_scene_tree *scene_tree = lock_surface->surface->data
1462 = wlr_scene_subsurface_tree_create(lock->scene, lock_surface->surface);
1463 m->lock_surface = lock_surface;
1464
1465 wlr_scene_node_set_position(&scene_tree->node, m->m.x, m->m.y);
1466 wlr_session_lock_surface_v1_configure(lock_surface, m->m.width, m->m.height);
1467
1468 LISTEN(&lock_surface->events.destroy, &m->destroy_lock_surface, destroylocksurface);
1469
1470 if (m == selmon)
1471 client_notify_enter(lock_surface->surface, wlr_seat_get_keyboard(seat));
1472}
1473
1474void
1475createmon(struct wl_listener *listener, void *data)
1476{
1477 /* This event is raised by the backend when a new output (aka a display or
1478 * monitor) becomes available. */
1479 struct wlr_output *wlr_output = data;
1480 struct wlr_output_mode *mode = wl_container_of(wlr_output->modes.next, mode, link);
1481 const MonitorRule *r;
1482 size_t i;
1483 struct wlr_output_state state;
1484 Monitor *m;
1485
1486 if (!wlr_output_init_render(wlr_output, alloc, drw))
1487 return;
1488
1489 m = wlr_output->data = ecalloc(1, sizeof(*m));
1490 m->wlr_output = wlr_output;
1491
1492 for (i = 0; i < LENGTH(m->layers); i++)
1493 wl_list_init(&m->layers[i]);
1494
1495#ifdef TILING
1496 m->enablegaps = enablegaps;
1497 m->gappih = gappih;
1498 m->gappiv = gappiv;
1499 m->gappoh = gappoh;
1500 m->gappov = gappov;
1501#endif
1502 wlr_output_state_init(&state);
1503 /* Initialize monitor state using configured rules */
1504 m->tagset[0] = m->tagset[1] = 1;
1505 for (r = monrules; r < END(monrules); r++) {
1506 if (!r->name || strstr(wlr_output->name, r->name)) {
1507 m->m.x = r->x;
1508 m->m.y = r->y;
1509 m->mfact = r->mfact;
1510 m->nmaster = r->nmaster;
1511 m->lt[0] = r->lt ? r->lt : &layouts[0];
1512 m->lt[1] = &layouts[LENGTH(layouts) > 1 && r->lt != &layouts[1]];
1513 if (m->lt[m->sellt] && m->lt[m->sellt]->symbol) { strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, LENGTH(m->ltsymbol)); } else { strncpy(m->ltsymbol, "[]=", LENGTH(m->ltsymbol)); }
1514 wlr_output_state_set_scale(&state, r->scale);
1515 wlr_output_state_set_transform(&state, r->rr);
1516
1517 wlr_output_state_set_adaptive_sync_enabled(&state, r->adaptive);
1518
1519 if(r->mode == -1)
1520 wlr_output_state_set_custom_mode(&state, r->resx, r->resy,
1521 (int) (r->rate > 0 ? r->rate * 1000 : 0));
1522 else if (!wl_list_empty(&wlr_output->modes)) {
1523 for (int j = 0; j < r->mode; j++) {
1524 mode = wl_container_of(mode->link.next, mode, link);
1525 }
1526 wlr_output_state_set_mode(&state, mode);
1527 }
1528
1529 break;
1530 }
1531 }
1532
1533 /* Safety net: if no monrules entry matched this output's name (and
1534 * there's no wildcard fallback rule in config.h), m->lt[0]/m->lt[1]
1535 * are left NULL by ecalloc(). Every layout dereference elsewhere in
1536 * this file assumes they're never NULL, so guarantee that here. */
1537 if (!m->lt[0])
1538 m->lt[0] = &layouts[0];
1539 if (!m->lt[1])
1540 m->lt[1] = &layouts[LENGTH(layouts) > 1];
1541 if (!m->ltsymbol[0]) {
1542 if (m->lt[m->sellt] && m->lt[m->sellt]->symbol)
1543 strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, LENGTH(m->ltsymbol));
1544 else
1545 strncpy(m->ltsymbol, "[]=", LENGTH(m->ltsymbol));
1546 }
1547
1548 /* infinite_canvas */
1549 {
1550 int restored = 0;
1551 MonitorState *ms = NULL, *iter;
1552 unsigned int _i;
1553 wl_list_for_each(iter, &savedstates, link) {
1554 if (strcmp(iter->name, wlr_output->name) == 0) {
1555 ms = iter;
1556 break;
1557 }
1558 }
1559 if (ms) {
1560 m->seltags = ms->seltags;
1561 m->sellt = ms->sellt;
1562 m->tagset[0] = ms->tagset[0];
1563 m->tagset[1] = ms->tagset[1];
1564 m->mfact = ms->mfact;
1565 m->nmaster = ms->nmaster;
1566 strncpy(m->ltsymbol, ms->ltsymbol, sizeof(m->ltsymbol));
1567 m->lt[0] = ms->lt[0];
1568 m->lt[1] = ms->lt[1];
1569 m->canvas = ms->canvas;
1570 ms->canvas = NULL;
1571 m->zoom_scale = ms->zoom_scale;
1572 m->zoom_target = ms->zoom_target;
1573 m->zoom_pan_x = ms->zoom_pan_x;
1574 m->zoom_pan_y = ms->zoom_pan_y;
1575 wl_list_remove(&ms->link);
1576 free(ms);
1577 restored = 1;
1578 } else {
1579 m->canvas = ecalloc(TAGCOUNT, sizeof(CanvasOffset));
1580 for (_i = 0; _i < (unsigned int)TAGCOUNT; _i++) {
1581 m->canvas[_i].cx = 0;
1582 m->canvas[_i].cy = 0;
1583 }
1584 }
1585 if (!restored) {
1586 m->zoom_scale = 1.0;
1587 m->zoom_target = 1.0;
1588 m->zoom_pan_x = 0.0;
1589 m->zoom_pan_y = 0.0;
1590 }
1591 m->zoom_anim_t = 1.0;
1592 m->zoom_start_scale = 1.0;
1593 m->zoom_start_pan_x = 0.0;
1594 m->zoom_start_pan_y = 0.0;
1595 m->zoom_popups_dirty = 0;
1596 }
1597 /* infinite_canvas_end */
1598 wlr_output_state_set_mode(&state, wlr_output_preferred_mode(wlr_output));
1599
1600 /* Set up event listeners */
1601 LISTEN(&wlr_output->events.frame, &m->frame, rendermon);
1602 LISTEN(&wlr_output->events.destroy, &m->destroy, cleanupmon);
1603 LISTEN(&wlr_output->events.request_state, &m->request_state, requestmonstate);
1604
1605 wlr_output_state_set_enabled(&state, 1);
1606 wlr_output_commit_state(wlr_output, &state);
1607 wlr_output_state_finish(&state);
1608
1609 wl_list_insert(&mons, &m->link);
1610 printstatus();
1611
1612 /* The xdg-protocol specifies:
1613 *
1614 * If the fullscreened surface is not opaque, the compositor must make
1615 * sure that other screen content not part of the same surface tree (made
1616 * up of subsurfaces, popups or similarly coupled surfaces) are not
1617 * visible below the fullscreened surface.
1618 *
1619 */
1620 /* updatemons() will resize and set correct position */
1621 m->fullscreen_bg = wlr_scene_rect_create(layers[LyrFS], 0, 0, fullscreen_bg);
1622 wlr_scene_node_set_enabled(&m->fullscreen_bg->node, 0);
1623
1624 /* Adds this to the output layout in the order it was configured.
1625 *
1626 * The output layout utility automatically adds a wl_output global to the
1627 * display, which Wayland clients can see to find out information about the
1628 * output (such as DPI, scale factor, manufacturer, etc).
1629 */
1630 m->scene_output = wlr_scene_output_create(scene, wlr_output);
1631 if (m->m.x == -1 && m->m.y == -1)
1632 wlr_output_layout_add_auto(output_layout, wlr_output);
1633 else
1634 wlr_output_layout_add(output_layout, wlr_output, m->m.x, m->m.y);
1635}
1636
1637void
1638createnotify(struct wl_listener *listener, void *data)
1639{
1640 /* This event is raised when a client creates a new toplevel (application window). */
1641 struct wlr_xdg_toplevel *toplevel = data;
1642 Client *c = NULL;
1643
1644 /* Allocate a Client for this surface */
1645 c = toplevel->base->data = ecalloc(1, sizeof(*c));
1646 c->surface.xdg = toplevel->base;
1647
1648 c->ibw = innerborderpx;
1649 c->obw = outerborderpx;
1650 c->bw = c->ibw + c->obw;
1651
1652 LISTEN(&toplevel->base->surface->events.commit, &c->commit, commitnotify);
1653 LISTEN(&toplevel->base->surface->events.map, &c->map, mapnotify);
1654 LISTEN(&toplevel->base->surface->events.unmap, &c->unmap, unmapnotify);
1655 LISTEN(&toplevel->events.destroy, &c->destroy, destroynotify);
1656 LISTEN(&toplevel->events.request_fullscreen, &c->fullscreen, fullscreennotify);
1657 LISTEN(&toplevel->events.request_maximize, &c->maximize, maximizenotify);
1658 LISTEN(&toplevel->events.set_title, &c->set_title, updatetitle);
1659}
1660
1661typedef struct {
1662 struct wl_listener pinch_begin;
1663 struct wl_listener pinch_update;
1664 struct wl_listener pinch_end;
1665 struct wl_listener destroy;
1666 struct wlr_pointer *pointer;
1667} PinchListeners;
1668
1669static void
1670pinchbegin(struct wl_listener *listener, void *data)
1671{
1672 struct wlr_pointer_pinch_begin_event *event = data;
1673 (void)listener;
1674 if (!pointer_gestures || !event)
1675 return;
1676 wlr_pointer_gestures_v1_send_pinch_begin(pointer_gestures, seat,
1677 event->time_msec, event->fingers);
1678}
1679
1680static void
1681pinchupdate(struct wl_listener *listener, void *data)
1682{
1683 struct wlr_pointer_pinch_update_event *event = data;
1684 (void)listener;
1685 if (!pointer_gestures || !event)
1686 return;
1687 wlr_pointer_gestures_v1_send_pinch_update(pointer_gestures, seat,
1688 event->time_msec, event->dx, event->dy,
1689 event->scale, event->rotation);
1690}
1691
1692static void
1693pinchend(struct wl_listener *listener, void *data)
1694{
1695 struct wlr_pointer_pinch_end_event *event = data;
1696 (void)listener;
1697 if (!pointer_gestures || !event)
1698 return;
1699 wlr_pointer_gestures_v1_send_pinch_end(pointer_gestures, seat,
1700 event->time_msec, event->cancelled);
1701}
1702
1703static void
1704pinchdestroy(struct wl_listener *listener, void *data)
1705{
1706 PinchListeners *pl = wl_container_of(listener, pl, destroy);
1707 wl_list_remove(&pl->pinch_begin.link);
1708 wl_list_remove(&pl->pinch_update.link);
1709 wl_list_remove(&pl->pinch_end.link);
1710 wl_list_remove(&pl->destroy.link);
1711 free(pl);
1712}
1713
1714void
1715createpointer(struct wlr_pointer *pointer)
1716{
1717 struct libinput_device *device;
1718 PinchListeners *pl;
1719 if (wlr_input_device_is_libinput(&pointer->base)
1720 && (device = wlr_libinput_get_device_handle(&pointer->base))) {
1721
1722 if (libinput_device_config_tap_get_finger_count(device)) {
1723 libinput_device_config_tap_set_enabled(device, tap_to_click);
1724 libinput_device_config_tap_set_drag_enabled(device, tap_and_drag);
1725 libinput_device_config_tap_set_drag_lock_enabled(device, drag_lock);
1726 libinput_device_config_tap_set_button_map(device, button_map);
1727 }
1728
1729 if (libinput_device_config_scroll_has_natural_scroll(device))
1730 libinput_device_config_scroll_set_natural_scroll_enabled(device, natural_scrolling);
1731
1732 if (libinput_device_config_dwt_is_available(device))
1733 libinput_device_config_dwt_set_enabled(device, disable_while_typing);
1734
1735 if (libinput_device_config_left_handed_is_available(device))
1736 libinput_device_config_left_handed_set(device, left_handed);
1737
1738 if (libinput_device_config_middle_emulation_is_available(device))
1739 libinput_device_config_middle_emulation_set_enabled(device, middle_button_emulation);
1740
1741 if (libinput_device_config_scroll_get_methods(device) != LIBINPUT_CONFIG_SCROLL_NO_SCROLL)
1742 libinput_device_config_scroll_set_method(device, scroll_method);
1743
1744 if (libinput_device_config_click_get_methods(device) != LIBINPUT_CONFIG_CLICK_METHOD_NONE)
1745 libinput_device_config_click_set_method(device, click_method);
1746
1747 if (libinput_device_config_send_events_get_modes(device))
1748 libinput_device_config_send_events_set_mode(device, send_events_mode);
1749
1750 if (libinput_device_config_accel_is_available(device)) {
1751 libinput_device_config_accel_set_profile(device, accel_profile);
1752 libinput_device_config_accel_set_speed(device, accel_speed);
1753 }
1754 }
1755
1756 wlr_cursor_attach_input_device(cursor, &pointer->base);
1757
1758 pl = ecalloc(1, sizeof(*pl));
1759 pl->pointer = pointer;
1760 pl->pinch_begin.notify = pinchbegin;
1761 pl->pinch_update.notify = pinchupdate;
1762 pl->pinch_end.notify = pinchend;
1763 pl->destroy.notify = pinchdestroy;
1764 wl_signal_add(&pointer->events.pinch_begin, &pl->pinch_begin);
1765 wl_signal_add(&pointer->events.pinch_update, &pl->pinch_update);
1766 wl_signal_add(&pointer->events.pinch_end, &pl->pinch_end);
1767 wl_signal_add(&pointer->base.events.destroy, &pl->destroy);
1768}
1769
1770void
1771createpointerconstraint(struct wl_listener *listener, void *data)
1772{
1773 PointerConstraint *pointer_constraint = ecalloc(1, sizeof(*pointer_constraint));
1774 pointer_constraint->constraint = data;
1775 LISTEN(&pointer_constraint->constraint->events.destroy,
1776 &pointer_constraint->destroy, destroypointerconstraint);
1777}
1778
1779void
1780createpopup(struct wl_listener *listener, void *data)
1781{
1782 /* This event is raised when a client (either xdg-shell or layer-shell)
1783 * creates a new popup. */
1784 struct wlr_xdg_popup *popup = data;
1785 LISTEN_STATIC(&popup->base->surface->events.commit, commitpopup);
1786}
1787
1788void
1789cursorconstrain(struct wlr_pointer_constraint_v1 *constraint)
1790{
1791 if (active_constraint == constraint)
1792 return;
1793
1794 if (active_constraint)
1795 wlr_pointer_constraint_v1_send_deactivated(active_constraint);
1796
1797 active_constraint = constraint;
1798 wlr_pointer_constraint_v1_send_activated(constraint);
1799}
1800
1801void
1802cursorframe(struct wl_listener *listener, void *data)
1803{
1804 /* This event is forwarded by the cursor when a pointer emits a frame
1805 * event. Frame events are sent after regular pointer events to group
1806 * multiple events together. For instance, two axis events may happen at the
1807 * same time, in which case a frame event won't be sent in between. */
1808 /* Notify the client with pointer focus of the frame event. */
1809 wlr_seat_pointer_notify_frame(seat);
1810}
1811
1812void
1813cursorwarptohint(void)
1814{
1815 Client *c = NULL;
1816 double sx = active_constraint->current.cursor_hint.x;
1817 double sy = active_constraint->current.cursor_hint.y;
1818
1819 toplevel_from_wlr_surface(active_constraint->surface, &c, NULL);
1820 if (c && active_constraint->current.cursor_hint.enabled) {
1821 wlr_cursor_warp(cursor, NULL, sx + c->geom.x + c->bw, sy + c->geom.y + c->bw);
1822 wlr_seat_pointer_warp(active_constraint->seat, sx, sy);
1823 }
1824}
1825
1826void
1827destroydecoration(struct wl_listener *listener, void *data)
1828{
1829 Client *c = wl_container_of(listener, c, destroy_decoration);
1830
1831 wl_list_remove(&c->destroy_decoration.link);
1832 wl_list_remove(&c->set_decoration_mode.link);
1833}
1834
1835void
1836destroydragicon(struct wl_listener *listener, void *data)
1837{
1838 /* Focus enter isn't sent during drag, so refocus the focused node. */
1839 focusclient(focustop(selmon), 1);
1840 motionnotify(0, NULL, 0, 0, 0, 0);
1841 wl_list_remove(&listener->link);
1842 free(listener);
1843}
1844
1845void
1846destroyidleinhibitor(struct wl_listener *listener, void *data)
1847{
1848 /* `data` is the wlr_surface of the idle inhibitor being destroyed,
1849 * at this point the idle inhibitor is still in the list of the manager */
1850 checkidleinhibitor(wlr_surface_get_root_surface(data));
1851 wl_list_remove(&listener->link);
1852 free(listener);
1853}
1854
1855void
1856destroylayersurfacenotify(struct wl_listener *listener, void *data)
1857{
1858 LayerSurface *l = wl_container_of(listener, l, destroy);
1859
1860 wl_list_remove(&l->link);
1861 wl_list_remove(&l->destroy.link);
1862 wl_list_remove(&l->unmap.link);
1863 wl_list_remove(&l->surface_commit.link);
1864 wlr_scene_node_destroy(&l->scene->node);
1865 wlr_scene_node_destroy(&l->popups->node);
1866 free(l);
1867}
1868
1869void
1870destroylock(SessionLock *lock, int unlock)
1871{
1872 wlr_seat_keyboard_notify_clear_focus(seat);
1873 if ((locked = !unlock))
1874 goto destroy;
1875
1876 wlr_scene_node_set_enabled(&locked_bg->node, 0);
1877
1878 focusclient(focustop(selmon), 0);
1879 motionnotify(0, NULL, 0, 0, 0, 0);
1880
1881destroy:
1882 wl_list_remove(&lock->new_surface.link);
1883 wl_list_remove(&lock->unlock.link);
1884 wl_list_remove(&lock->destroy.link);
1885
1886 wlr_scene_node_destroy(&lock->scene->node);
1887 cur_lock = NULL;
1888 free(lock);
1889}
1890
1891void
1892destroylocksurface(struct wl_listener *listener, void *data)
1893{
1894 Monitor *m = wl_container_of(listener, m, destroy_lock_surface);
1895 struct wlr_session_lock_surface_v1 *surface, *lock_surface = m->lock_surface;
1896
1897 m->lock_surface = NULL;
1898 wl_list_remove(&m->destroy_lock_surface.link);
1899
1900 if (lock_surface->surface != seat->keyboard_state.focused_surface)
1901 return;
1902
1903 if (locked && cur_lock && !wl_list_empty(&cur_lock->surfaces)) {
1904 surface = wl_container_of(cur_lock->surfaces.next, surface, link);
1905 client_notify_enter(surface->surface, wlr_seat_get_keyboard(seat));
1906 } else if (!locked) {
1907 focusclient(focustop(selmon), 1);
1908 } else {
1909 wlr_seat_keyboard_clear_focus(seat);
1910 }
1911}
1912
1913void
1914destroynotify(struct wl_listener *listener, void *data)
1915{
1916 /* Called when the xdg_toplevel is destroyed. */
1917 Client *c = wl_container_of(listener, c, destroy);
1918 wl_list_remove(&c->destroy.link);
1919 wl_list_remove(&c->set_title.link);
1920 wl_list_remove(&c->fullscreen.link);
1921#ifdef XWAYLAND
1922 if (c->type != XDGShell) {
1923 wl_list_remove(&c->activate.link);
1924 wl_list_remove(&c->associate.link);
1925 wl_list_remove(&c->configure.link);
1926 wl_list_remove(&c->dissociate.link);
1927 wl_list_remove(&c->set_hints.link);
1928 } else
1929#endif
1930 {
1931 wl_list_remove(&c->commit.link);
1932 wl_list_remove(&c->map.link);
1933 wl_list_remove(&c->unmap.link);
1934 wl_list_remove(&c->maximize.link);
1935 }
1936 free(c);
1937}
1938
1939void
1940destroypointerconstraint(struct wl_listener *listener, void *data)
1941{
1942 PointerConstraint *pointer_constraint = wl_container_of(listener, pointer_constraint, destroy);
1943
1944 if (active_constraint == pointer_constraint->constraint) {
1945 cursorwarptohint();
1946 active_constraint = NULL;
1947 }
1948
1949 wl_list_remove(&pointer_constraint->destroy.link);
1950 free(pointer_constraint);
1951}
1952
1953void
1954destroysessionlock(struct wl_listener *listener, void *data)
1955{
1956 SessionLock *lock = wl_container_of(listener, lock, destroy);
1957 destroylock(lock, 0);
1958}
1959
1960void
1961destroykeyboardgroup(struct wl_listener *listener, void *data)
1962{
1963 KeyboardGroup *group = wl_container_of(listener, group, destroy);
1964 wl_event_source_remove(group->key_repeat_source);
1965 wl_list_remove(&group->key.link);
1966 wl_list_remove(&group->modifiers.link);
1967 wl_list_remove(&group->destroy.link);
1968 wlr_keyboard_group_destroy(group->wlr_group);
1969 free(group);
1970}
1971
1972Monitor *
1973dirtomon(enum wlr_direction dir)
1974{
1975 struct wlr_output *next;
1976 if (!wlr_output_layout_get(output_layout, selmon->wlr_output))
1977 return selmon;
1978 if ((next = wlr_output_layout_adjacent_output(output_layout,
1979 dir, selmon->wlr_output, selmon->m.x, selmon->m.y)))
1980 return next->data;
1981 if ((next = wlr_output_layout_farthest_output(output_layout,
1982 dir ^ (WLR_DIRECTION_LEFT|WLR_DIRECTION_RIGHT),
1983 selmon->wlr_output, selmon->m.x, selmon->m.y)))
1984 return next->data;
1985 return selmon;
1986}
1987
1988void
1989focusclient(Client *c, int lift)
1990{
1991 struct wlr_surface *old = seat->keyboard_state.focused_surface;
1992 int unused_lx, unused_ly, old_client_type;
1993 Client *old_c = NULL;
1994 LayerSurface *old_l = NULL;
1995
1996 if (locked)
1997 return;
1998
1999 /* Raise client in stacking order if requested */
2000 if (c && lift)
2001 wlr_scene_node_raise_to_top(&c->scene->node);
2002
2003 if (c && client_surface(c) == old)
2004 return;
2005
2006 if ((old_client_type = toplevel_from_wlr_surface(old, &old_c, &old_l)) == XDGShell) {
2007 struct wlr_xdg_popup *popup, *tmp;
2008 wl_list_for_each_safe(popup, tmp, &old_c->surface.xdg->popups, link)
2009 wlr_xdg_popup_destroy(popup);
2010 }
2011
2012 /* Put the new client atop the focus stack and select its monitor */
2013 if (c && !client_is_unmanaged(c)) {
2014 wl_list_remove(&c->flink);
2015 wl_list_insert(&fstack, &c->flink);
2016 selmon = c->mon;
2017 c->isurgent = 0;
2018
2019 /* Don't change border color if there is an exclusive focus or we are
2020 * handling a drag operation */
2021 if (!exclusive_focus && !seat->drag)
2022 client_set_border_color(c, focused_inner_border_color, focused_outer_border_color);
2023 }
2024
2025 /* Deactivate old client if focus is changing */
2026 if (old && (!c || client_surface(c) != old)) {
2027 /* If an overlay is focused, don't focus or activate the client,
2028 * but only update its position in fstack to render its border with focuscolor
2029 * and focus it after the overlay is closed. */
2030 if (old_client_type == LayerShell && wlr_scene_node_coords(
2031 &old_l->scene->node, &unused_lx, &unused_ly)
2032 && old_l->layer_surface->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) {
2033 return;
2034 } else if (old_c && old_c == exclusive_focus && client_wants_focus(old_c)) {
2035 return;
2036 /* Don't deactivate old client if the new one wants focus, as this causes issues with winecfg
2037 * and probably other clients */
2038 } else if (old_c && !client_is_unmanaged(old_c) && (!c || !client_wants_focus(c))) {
2039 client_set_border_color(old_c, unfocused_inner_border_color, unfocused_outer_border_color);
2040
2041 client_activate_surface(old, 0);
2042 }
2043 }
2044 printstatus();
2045
2046 if (!c) {
2047 /* With no client, all we have left is to clear focus */
2048 wlr_seat_keyboard_notify_clear_focus(seat);
2049 return;
2050 }
2051
2052 /* Change cursor surface */
2053 motionnotify(0, NULL, 0, 0, 0, 0);
2054
2055 /* Have a client, so focus its top-level wlr_surface */
2056 client_notify_enter(client_surface(c), wlr_seat_get_keyboard(seat));
2057
2058 /* Activate the new client */
2059 client_activate_surface(client_surface(c), 1);
2060}
2061
2062void
2063focusmon(const Arg *arg)
2064{
2065 int i = 0, nmons = wl_list_length(&mons);
2066 if (nmons) {
2067 do /* don't switch to disabled mons */
2068 selmon = dirtomon(arg->i);
2069 while (!selmon->wlr_output->enabled && i++ < nmons);
2070 }
2071 focusclient(focustop(selmon), 1);
2072}
2073
2074void
2075focusstack(const Arg *arg)
2076{
2077 /* Focus the next or previous client (in tiling order) on selmon */
2078 Client *c, *sel = focustop(selmon);
2079 if (!sel || (sel->isfullscreen && !client_has_children(sel)))
2080 return;
2081 if (arg->i > 0) {
2082 wl_list_for_each(c, &sel->link, link) {
2083 if (&c->link == &clients)
2084 continue; /* wrap past the sentinel node */
2085 if (VISIBLEON(c, selmon))
2086 break; /* found it */
2087 }
2088 } else {
2089 wl_list_for_each_reverse(c, &sel->link, link) {
2090 if (&c->link == &clients)
2091 continue; /* wrap past the sentinel node */
2092 if (VISIBLEON(c, selmon))
2093 break; /* found it */
2094 }
2095 }
2096 /* If only one client is visible on selmon, then c == sel */
2097 focusclient(c, 1);
2098 /* infinite_canvas */
2099 if (selmon && !selmon->lt[selmon->sellt]->arrange)
2100 bringintoview(c);
2101 /* infinite_canvas_end */
2102}
2103
2104/* We probably should change the name of this: it sounds like it
2105 * will focus the topmost client of this mon, when actually will
2106 * only return that client */
2107Client *
2108focustop(Monitor *m)
2109{
2110 Client *c;
2111 wl_list_for_each(c, &fstack, flink) {
2112 if (VISIBLEON(c, m))
2113 return c;
2114 }
2115 return NULL;
2116}
2117
2118void
2119fullscreennotify(struct wl_listener *listener, void *data)
2120{
2121 Client *c = wl_container_of(listener, c, fullscreen);
2122 setfullscreen(c, client_wants_fullscreen(c));
2123}
2124
2125void
2126gpureset(struct wl_listener *listener, void *data)
2127{
2128 struct wlr_renderer *old_drw = drw;
2129 struct wlr_allocator *old_alloc = alloc;
2130 struct Monitor *m;
2131 if (!(drw = wlr_renderer_autocreate(backend)))
2132 die("couldn't recreate renderer");
2133
2134 if (!(alloc = wlr_allocator_autocreate(backend, drw)))
2135 die("couldn't recreate allocator");
2136
2137 wl_list_remove(&gpu_reset.link);
2138 wl_signal_add(&drw->events.lost, &gpu_reset);
2139
2140 wlr_compositor_set_renderer(compositor, drw);
2141
2142 wl_list_for_each(m, &mons, link) {
2143 wlr_output_init_render(m->wlr_output, alloc, drw);
2144 }
2145
2146 wlr_allocator_destroy(old_alloc);
2147 wlr_renderer_destroy(old_drw);
2148}
2149
2150void
2151handlesig(int signo)
2152{
2153 if (signo == SIGCHLD)
2154 while (waitpid(-1, NULL, WNOHANG) > 0);
2155 else if (signo == SIGINT || signo == SIGTERM)
2156 quit(NULL);
2157}
2158
2159void
2160incnmaster(const Arg *arg)
2161{
2162 if (!arg || !selmon)
2163 return;
2164 selmon->nmaster = MAX(selmon->nmaster + arg->i, 0);
2165 arrange(selmon);
2166}
2167
2168void
2169inputdevice(struct wl_listener *listener, void *data)
2170{
2171 /* This event is raised by the backend when a new input device becomes
2172 * available. */
2173 struct wlr_input_device *device = data;
2174 /* tablet-input */
2175 struct wlr_tablet_pad *pad = NULL;
2176 /* tablet-input end */
2177 uint32_t caps;
2178
2179 switch (device->type) {
2180 case WLR_INPUT_DEVICE_KEYBOARD:
2181 createkeyboard(wlr_keyboard_from_input_device(device));
2182 break;
2183 case WLR_INPUT_DEVICE_POINTER:
2184 createpointer(wlr_pointer_from_input_device(device));
2185 break;
2186 /* tablet-input */
2187 case WLR_INPUT_DEVICE_TABLET:
2188 createtablet(device);
2189 break;
2190 case WLR_INPUT_DEVICE_TABLET_PAD:
2191 pad = wlr_tablet_pad_from_input_device(device);
2192 tablet_pad = wlr_tablet_pad_create(tablet_mgr, seat, device);
2193 wl_signal_add(&pad->events.button, &tablet_pad_button);
2194 wl_signal_add(&device->events.destroy, &tablet_pad_destroy);
2195 break;
2196 /* tablet-input end */
2197 default:
2198 /* TODO handle other input device types */
2199 break;
2200 }
2201
2202 /* We need to let the wlr_seat know what our capabilities are, which is
2203 * communiciated to the client. In leeloo we always have a cursor, even if
2204 * there are no pointer devices, so we always include that capability. */
2205 /* TODO do we actually require a cursor? */
2206 caps = WL_SEAT_CAPABILITY_POINTER;
2207 if (!wl_list_empty(&kb_group->wlr_group->devices))
2208 caps |= WL_SEAT_CAPABILITY_KEYBOARD;
2209 wlr_seat_set_capabilities(seat, caps);
2210}
2211
2212int
2213keybinding(uint32_t mods, xkb_keysym_t sym)
2214{
2215 /*
2216 * Here we handle compositor keybindings. This is when the compositor is
2217 * processing keys, rather than passing them on to the client for its own
2218 * processing.
2219 */
2220 const Key *k;
2221 for (k = keys; k < END(keys); k++) {
2222 if (CLEANMASK(mods) == CLEANMASK(k->mod)
2223 && xkb_keysym_to_lower(sym) == xkb_keysym_to_lower(k->keysym)
2224 && k->func) {
2225 k->func(&k->arg);
2226 return 1;
2227 }
2228 }
2229 return 0;
2230}
2231
2232void
2233keypress(struct wl_listener *listener, void *data)
2234{
2235 int i;
2236 /* This event is raised when a key is pressed or released. */
2237 KeyboardGroup *group = wl_container_of(listener, group, key);
2238 struct wlr_keyboard_key_event *event = data;
2239
2240 /* Translate libinput keycode -> xkbcommon */
2241 uint32_t keycode = event->keycode + 8;
2242 /* Get a list of keysyms based on the keymap for this keyboard */
2243 const xkb_keysym_t *syms;
2244 int nsyms = xkb_state_key_get_syms(
2245 group->wlr_group->keyboard.xkb_state, keycode, &syms);
2246
2247 int handled = 0;
2248 uint32_t mods = wlr_keyboard_get_modifiers(&group->wlr_group->keyboard);
2249
2250 wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
2251
2252 /* On _press_ if there is no active screen locker,
2253 * attempt to process a compositor keybinding. */
2254 if (!locked && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
2255 for (i = 0; i < nsyms; i++)
2256 handled = keybinding(mods, syms[i]) || handled;
2257 }
2258
2259 if (handled && group->wlr_group->keyboard.repeat_info.delay > 0) {
2260 group->mods = mods;
2261 group->keysyms = syms;
2262 group->nsyms = nsyms;
2263 wl_event_source_timer_update(group->key_repeat_source,
2264 group->wlr_group->keyboard.repeat_info.delay);
2265 } else {
2266 group->nsyms = 0;
2267 wl_event_source_timer_update(group->key_repeat_source, 0);
2268 }
2269
2270 if (handled)
2271 return;
2272
2273 wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard);
2274 /* Pass unhandled keycodes along to the client. */
2275 wlr_seat_keyboard_notify_key(seat, event->time_msec,
2276 event->keycode, event->state);
2277}
2278
2279void
2280keypressmod(struct wl_listener *listener, void *data)
2281{
2282 /* This event is raised when a modifier key, such as shift or alt, is
2283 * pressed. We simply communicate this to the client. */
2284 KeyboardGroup *group = wl_container_of(listener, group, modifiers);
2285
2286 wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard);
2287 /* Send modifiers to the client. */
2288 wlr_seat_keyboard_notify_modifiers(seat,
2289 &group->wlr_group->keyboard.modifiers);
2290}
2291
2292int
2293keyrepeat(void *data)
2294{
2295 KeyboardGroup *group = data;
2296 int i;
2297 if (!group->nsyms || group->wlr_group->keyboard.repeat_info.rate <= 0)
2298 return 0;
2299
2300 wl_event_source_timer_update(group->key_repeat_source,
2301 1000 / group->wlr_group->keyboard.repeat_info.rate);
2302
2303 for (i = 0; i < group->nsyms; i++)
2304 keybinding(group->mods, group->keysyms[i]);
2305
2306 return 0;
2307}
2308
2309void
2310killclient(const Arg *arg)
2311{
2312 Client *sel = focustop(selmon);
2313 if (sel)
2314 client_send_close(sel);
2315}
2316
2317void
2318locksession(struct wl_listener *listener, void *data)
2319{
2320 struct wlr_session_lock_v1 *session_lock = data;
2321 SessionLock *lock;
2322 wlr_scene_node_set_enabled(&locked_bg->node, 1);
2323 if (cur_lock) {
2324 wlr_session_lock_v1_destroy(session_lock);
2325 return;
2326 }
2327 lock = session_lock->data = ecalloc(1, sizeof(*lock));
2328 focusclient(NULL, 0);
2329
2330 lock->scene = wlr_scene_tree_create(layers[LyrBlock]);
2331 cur_lock = lock->lock = session_lock;
2332 locked = 1;
2333
2334 LISTEN(&session_lock->events.new_surface, &lock->new_surface, createlocksurface);
2335 LISTEN(&session_lock->events.destroy, &lock->destroy, destroysessionlock);
2336 LISTEN(&session_lock->events.unlock, &lock->unlock, unlocksession);
2337
2338 wlr_session_lock_v1_send_locked(session_lock);
2339}
2340
2341static uint64_t
2342leeloo_monotonic_ms(void)
2343{
2344 struct timespec ts;
2345 clock_gettime(CLOCK_MONOTONIC, &ts);
2346 return (uint64_t)ts.tv_sec * 1000ULL + (uint64_t)ts.tv_nsec / 1000000ULL;
2347}
2348
2349static void
2350openanim_buffers(struct wlr_scene_buffer *buf, int sx, int sy, void *data)
2351{
2352 Client *c = data;
2353 double t, k;
2354 int tw, th;
2355 (void)sx;
2356 (void)sy;
2357 if (!c || !buf->buffer || c != openanim_client || !openanim_running)
2358 return;
2359
2360 t = (double)(leeloo_monotonic_ms() - openanim_start_ms) / 140.0;
2361 if (t < 0.0)
2362 t = 0.0;
2363 if (t > 1.0)
2364 t = 1.0;
2365
2366 /* Subtle 94% -> 100% ease-out. */
2367 k = 0.94 + 0.06 * (1.0 - pow(1.0 - t, 3.0));
2368 if (t >= 1.0) {
2369 wlr_scene_buffer_set_dest_size(buf, 0, 0);
2370 wlr_scene_buffer_set_opacity(buf, 1.0f);
2371 return;
2372 }
2373
2374 tw = MAX(1, (int)llround((double)buf->buffer->width * k));
2375 th = MAX(1, (int)llround((double)buf->buffer->height * k));
2376 wlr_scene_buffer_set_dest_size(buf, tw, th);
2377 wlr_scene_buffer_set_opacity(buf, (float)k);
2378}
2379
2380static int
2381openanim_tick(void *data)
2382{
2383 Client *c = data;
2384 uint64_t now;
2385 if (!c || c != openanim_client || !c->scene_surface) {
2386 openanim_running = 0;
2387 openanim_client = NULL;
2388 return 0;
2389 }
2390
2391 now = leeloo_monotonic_ms();
2392 wlr_scene_node_for_each_buffer(&c->scene_surface->node, openanim_buffers, c);
2393 if (now - openanim_start_ms >= 140) {
2394 openanim_running = 0;
2395 wlr_scene_node_for_each_buffer(&c->scene_surface->node, openanim_buffers, c);
2396 openanim_client = NULL;
2397 wl_event_source_timer_update(openanim_timer, 0);
2398 return 0;
2399 }
2400 wl_event_source_timer_update(openanim_timer, 16);
2401 return 0;
2402}
2403
2404static void
2405start_open_animation(Client *c)
2406{
2407 if (!c || !c->scene_surface)
2408 return;
2409 if (!openanim_timer)
2410 openanim_timer = wl_event_loop_add_timer(event_loop, openanim_tick, c);
2411 if (!openanim_timer)
2412 return;
2413 openanim_client = c;
2414 openanim_start_ms = leeloo_monotonic_ms();
2415 openanim_running = 1;
2416 wl_event_source_timer_update(openanim_timer, 0);
2417}
2418
2419void
2420mapnotify(struct wl_listener *listener, void *data)
2421{
2422 /* Called when the surface is mapped, or ready to display on-screen. */
2423 Client *p = NULL;
2424 Client *w, *c = wl_container_of(listener, c, map);
2425 Monitor *m;
2426 int i;
2427
2428 /* Create scene tree for this client and its border */
2429 c->scene = client_surface(c)->data = wlr_scene_tree_create(layers[LyrTile]);
2430 /* Enabled later by a call to arrange() */
2431 wlr_scene_node_set_enabled(&c->scene->node, client_is_unmanaged(c));
2432 c->scene_surface = c->type == XDGShell
2433 ? wlr_scene_xdg_surface_create(c->scene, c->surface.xdg)
2434 : wlr_scene_subsurface_tree_create(c->scene, client_surface(c));
2435 c->scene->node.data = c->scene_surface->node.data = c;
2436
2437 client_get_geometry(c, &c->geom);
2438
2439 /* Handle unmanaged clients first so we can return prior create borders */
2440 if (client_is_unmanaged(c)) {
2441 /* Unmanaged clients always are floating */
2442 wlr_scene_node_reparent(&c->scene->node, layers[LyrFloat]);
2443 wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y);
2444 client_set_size(c, c->geom.width, c->geom.height);
2445 if (client_wants_focus(c)) {
2446 focusclient(c, 1);
2447 exclusive_focus = c;
2448 }
2449 goto unset_fullscreen;
2450 }
2451
2452 for (i = 0; i < 4; i++) {
2453 c->inner_border[i] = wlr_scene_rect_create(c->scene, 0, 0,
2454 c->isurgent ? urgent_inner_border_color : unfocused_inner_border_color);
2455 c->inner_border[i]->node.data = c;
2456
2457 c->outer_border[i] = wlr_scene_rect_create(c->scene, 0, 0,
2458 c->isurgent ? urgent_outer_border_color : unfocused_outer_border_color);
2459 c->outer_border[i]->node.data = c;
2460 }
2461
2462 /* Initialize client geometry with room for border */
2463 client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT);
2464
2465 c->geom.width += 2 * c->bw;
2466 c->geom.height += 2 * c->bw;
2467
2468 /* Insert this client into client lists. */
2469 wl_list_insert(&clients, &c->link);
2470 wl_list_insert(&fstack, &c->flink);
2471
2472 /* Set initial monitor, tags, floating status, and focus:
2473 * we always consider floating, clients that have parent and thus
2474 * we set the same tags and monitor as its parent.
2475 * If there is no parent, apply rules */
2476 if ((p = client_get_parent(c))) {
2477 c->isfloating = 1;
2478 setmon(c, p->mon, p->tags);
2479 } else {
2480 applyrules(c);
2481 }
2482 printstatus();
2483
2484 /* spawnregion */
2485 if (!client_is_unmanaged(c)) {
2486 if (c->isfullscreen || (!c->isfloating && c->mon && c->mon->lt[c->mon->sellt]->arrange) || waiting_geo_client) {
2487 wlr_scene_node_set_enabled(&c->scene->node, 1);
2488 } else if (has_pending_spawn_geo) {
2489 has_pending_spawn_geo = 0;
2490 c->isfloating = 1;
2491 if (!c->mon)
2492 c->mon = selmon;
2493 if (c->mon) {
2494 struct wlr_box min_hint = {0};
2495 int bw2 = 2 * (int)c->bw;
2496 if (pending_spawn_geo.width < min_hint.width + bw2 && min_hint.width > 0)
2497 pending_spawn_geo.width = min_hint.width + bw2;
2498 if (pending_spawn_geo.height < min_hint.height + bw2 && min_hint.height > 0)
2499 pending_spawn_geo.height = min_hint.height + bw2;
2500 c->geom = pending_spawn_geo;
2501 }
2502 } else {
2503 wlr_scene_node_set_enabled(&c->scene->node, 0);
2504 waiting_geo_client = c;
2505 spawnregion(NULL);
2506 }
2507 }
2508 /* spawnregion_end */
2509unset_fullscreen:
2510 m = c->mon ? c->mon : xytomon(c->geom.x, c->geom.y);
2511 wl_list_for_each(w, &clients, link) {
2512 if (w != c && w != p && w->isfullscreen && m == w->mon && (w->tags & c->tags))
2513 setfullscreen(w, 0);
2514 }
2515
2516 /* infinite_canvas: remember the pan active right now as this
2517 * window's own natural framing, so focusing it later teleports
2518 * back to exactly how it looked when placed -- independent of
2519 * whatever other windows' framing happens to be. */
2520 if (m) {
2521 int tagidx = getcurrenttag(m);
2522 c->home_cx = m->canvas[tagidx].cx;
2523 c->home_cy = m->canvas[tagidx].cy;
2524 }
2525 start_open_animation(c);
2526
2527}
2528
2529void
2530maximizenotify(struct wl_listener *listener, void *data)
2531{
2532 /* This event is raised when a client would like to maximize itself,
2533 * typically because the user clicked on the maximize button on
2534 * client-side decorations. leeloo doesn't support maximization, but
2535 * to conform to xdg-shell protocol we still must send a configure.
2536 * Since xdg-shell protocol v5 we should ignore request of unsupported
2537 * capabilities, just schedule a empty configure when the client uses <5
2538 * protocol version
2539 * wlr_xdg_surface_schedule_configure() is used to send an empty reply. */
2540 Client *c = wl_container_of(listener, c, maximize);
2541 if (c->surface.xdg->initialized
2542 && wl_resource_get_version(c->surface.xdg->toplevel->resource)
2543 < XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION)
2544 wlr_xdg_surface_schedule_configure(c->surface.xdg);
2545}
2546
2547void
2548monocle(Monitor *m)
2549{
2550 Client *c;
2551 int n = 0;
2552
2553 wl_list_for_each(c, &clients, link) {
2554 if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
2555 continue;
2556 resize(c, m->w, 0);
2557 n++;
2558 }
2559 if (n)
2560 snprintf(m->ltsymbol, LENGTH(m->ltsymbol), "[%d]", n);
2561 if ((c = focustop(m)))
2562 wlr_scene_node_raise_to_top(&c->scene->node);
2563}
2564
2565void
2566motionabsolute(struct wl_listener *listener, void *data)
2567{
2568 /* This event is forwarded by the cursor when a pointer emits an _absolute_
2569 * motion event, from 0..1 on each axis. This happens, for example, when
2570 * wlroots is running under a Wayland window rather than KMS+DRM, and you
2571 * move the mouse over the window. You could enter the window from any edge,
2572 * so we have to warp the mouse there. Also, some hardware emits these events. */
2573 struct wlr_pointer_motion_absolute_event *event = data;
2574 double lx, ly, dx, dy;
2575
2576 if (!event->time_msec) /* this is 0 with virtual pointers */
2577 wlr_cursor_warp_absolute(cursor, &event->pointer->base, event->x, event->y);
2578
2579 wlr_cursor_absolute_to_layout_coords(cursor, &event->pointer->base, event->x, event->y, &lx, &ly);
2580 dx = lx - cursor->x;
2581 dy = ly - cursor->y;
2582 motionnotify(event->time_msec, &event->pointer->base, dx, dy, dx, dy);
2583}
2584
2585void
2586motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double dy,
2587 double dx_unaccel, double dy_unaccel)
2588{
2589 double sx = 0, sy = 0, sx_confined, sy_confined;
2590 Client *c = NULL, *w = NULL;
2591 LayerSurface *l = NULL;
2592 struct wlr_surface *surface = NULL;
2593 struct wlr_pointer_constraint_v1 *constraint;
2594
2595 /* Find the client under the pointer and send the event along. */
2596 xytonode(cursor->x, cursor->y, &surface, &c, &l, &sx, &sy);
2597
2598 if (cursor_mode == CurPressed && !seat->drag
2599 && surface != seat->pointer_state.focused_surface
2600 && toplevel_from_wlr_surface(seat->pointer_state.focused_surface, &w, &l) >= 0) {
2601 c = w;
2602 surface = seat->pointer_state.focused_surface;
2603 /* zoom */
2604 if (l) {
2605 sx = cursor->x - l->scene->node.x;
2606 sy = cursor->y - l->scene->node.y;
2607 } else if (w && w->mon && fabs(w->mon->zoom_scale - 1.0) >= 1e-9) {
2608 double s = w->mon->zoom_scale;
2609 int scene_x = (int)llround((double)w->geom.x * s + w->mon->zoom_pan_x);
2610 int scene_y = (int)llround((double)w->geom.y * s + w->mon->zoom_pan_y);
2611 int bw_s = MAX(1, (int)llround((double)w->bw * s));
2612 int gw_s = (int)llround((double)w->geom.width * s);
2613 int gh_s = (int)llround((double)w->geom.height * s);
2614 int inner_w = MAX(1, gw_s - 2 * bw_s);
2615 int inner_h = MAX(1, gh_s - 2 * bw_s);
2616 double cw = (double)MAX(1, w->geom.width - 2 * w->bw);
2617 double ch = (double)MAX(1, w->geom.height - 2 * w->bw);
2618 sx = (cursor->x - (scene_x + bw_s)) * cw / (double)inner_w;
2619 sy = (cursor->y - (scene_y + bw_s)) * ch / (double)inner_h;
2620 } else {
2621 sx = cursor->x - w->geom.x;
2622 sy = cursor->y - w->geom.y;
2623 }
2624 /* zoom end */
2625 }
2626
2627 /* time is 0 in internal calls meant to restore pointer focus. */
2628 if (time) {
2629 wlr_relative_pointer_manager_v1_send_relative_motion(
2630 relative_pointer_mgr, seat, (uint64_t)time * 1000,
2631 dx, dy, dx_unaccel, dy_unaccel);
2632
2633 wl_list_for_each(constraint, &pointer_constraints->constraints, link)
2634 cursorconstrain(constraint);
2635
2636 if (active_constraint && cursor_mode != CurResize && cursor_mode != CurMove) {
2637 toplevel_from_wlr_surface(active_constraint->surface, &c, NULL);
2638 if (c && active_constraint->surface == seat->pointer_state.focused_surface) {
2639 sx = cursor->x - c->geom.x - c->bw;
2640 sy = cursor->y - c->geom.y - c->bw;
2641 if (wlr_region_confine(&active_constraint->region, sx, sy,
2642 sx + dx, sy + dy, &sx_confined, &sy_confined)) {
2643 dx = sx_confined - sx;
2644 dy = sy_confined - sy;
2645 }
2646
2647 if (active_constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED)
2648 return;
2649 }
2650 }
2651
2652 wlr_cursor_move(cursor, device, dx, dy);
2653 wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
2654
2655 /* Update selmon (even while dragging a window) */
2656 if (sloppyfocus)
2657 selmon = xytomon(cursor->x, cursor->y);
2658 }
2659
2660 /* Update drag icon's position */
2661 wlr_scene_node_set_position(&drag_icon->node, (int)round(cursor->x), (int)round(cursor->y));
2662
2663 /* If we are currently grabbing the mouse, handle and return */
2664 if (cursor_mode == CurSpawnRegion) {
2665 spawnregion_x2 = cursor->x;
2666 spawnregion_y2 = cursor->y;
2667 spawnregion_update_rect();
2668 return;
2669 }
2670 if (cursor_mode == CurMove) {
2671 /* infinite_canvas */
2672 if (canvas_panning) {
2673 int tagidx;
2674 double raw_dx, raw_dy;
2675 double pdx_f, pdy_f;
2676 int pdx, pdy;
2677 Client *pc;
2678 if (!selmon || selmon->lt[selmon->sellt]->arrange != NULL) {
2679 canvas_panning = 0;
2680 cursor_mode = CurNormal;
2681 wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
2682 return;
2683 }
2684 tagidx = getcurrenttag(selmon);
2685 raw_dx = cursor->x - canvas_pan_last_x;
2686 raw_dy = cursor->y - canvas_pan_last_y;
2687 canvas_pan_last_x = cursor->x;
2688 canvas_pan_last_y = cursor->y;
2689 /* zoom */
2690 if (movecanvasmouse_multiplier_is_affected_by_zoom) {
2691 double s = selmon->zoom_scale;
2692 if (fabs(s) < 1e-9) s = 1.0;
2693 pdx_f = raw_dx * canvas_pan_multiplier / s;
2694 pdy_f = raw_dy * canvas_pan_multiplier / s;
2695 } else {
2696 pdx_f = raw_dx * canvas_pan_multiplier;
2697 pdy_f = raw_dy * canvas_pan_multiplier;
2698 }
2699 /* zoom end */
2700 canvas_pan_accum_x += (float)pdx_f;
2701 canvas_pan_accum_y += (float)pdy_f;
2702 pdx = (int)canvas_pan_accum_x;
2703 pdy = (int)canvas_pan_accum_y;
2704 canvas_pan_accum_x -= pdx;
2705 canvas_pan_accum_y -= pdy;
2706 selmon->canvas[tagidx].cx += pdx;
2707 selmon->canvas[tagidx].cy += pdy;
2708 wl_list_for_each(pc, &clients, link) {
2709 if (!VISIBLEON(pc, selmon))
2710 continue;
2711 pc->geom.x += pdx;
2712 pc->geom.y += pdy;
2713 }
2714 /* zoom */
2715 zoom_apply(selmon);
2716 /* zoom end */
2717 } else if (grabc) {
2718 /* zoom */
2719 {
2720 double zmv_s = grabc->mon ? grabc->mon->zoom_scale : 1.0;
2721 int zmv_nx, zmv_ny;
2722 if (fabs(zmv_s) < 1e-9) zmv_s = 1.0;
2723 zmv_nx = (int)llround((cursor->x - grabcx - (grabc->mon ? grabc->mon->zoom_pan_x : 0.0)) / zmv_s);
2724 zmv_ny = (int)llround((cursor->y - grabcy - (grabc->mon ? grabc->mon->zoom_pan_y : 0.0)) / zmv_s);
2725 if (grabc->mon)
2726 snap_edges(grabc, grabc->mon, &zmv_nx, &zmv_ny);
2727 canvas_move_window(grabc, zmv_nx, zmv_ny);
2728 }
2729 /* zoom end */
2730 }
2731 /* infinite_canvas_end */
2732 return;
2733 } else if (cursor_mode == CurResize) {
2734 /* zoom */
2735 if (grabc && grabc->mon) {
2736 double rzs = grabc->mon->zoom_scale;
2737 int rzsx, rzsy, new_w, new_h;
2738 struct wlr_box geo;
2739 if (fabs(rzs) < 1e-9) rzs = 1.0;
2740 rzsx = (int)llround(resize_grab_world_x * rzs + grabc->mon->zoom_pan_x);
2741 rzsy = (int)llround(resize_grab_world_y * rzs + grabc->mon->zoom_pan_y);
2742 new_w = (int)llround((cursor->x - rzsx) / rzs);
2743 new_h = (int)llround((cursor->y - rzsy) / rzs);
2744 geo = (struct wlr_box){.x = grabc->geom.x, .y = grabc->geom.y,
2745 .width = new_w, .height = new_h};
2746 resize_magnetically(grabc, grabc->mon, &geo);
2747 resize(grabc, geo, 1);
2748 } else {
2749 struct wlr_box geo;
2750 geo = (struct wlr_box){.x = grabc->geom.x, .y = grabc->geom.y,
2751 .width = (int)round(cursor->x) - grabc->geom.x,
2752 .height = (int)round(cursor->y) - grabc->geom.y};
2753 resize_magnetically(grabc, grabc->mon, &geo);
2754 resize(grabc, geo, 1);
2755 }
2756 /* zoom end */
2757 return;
2758 }
2759
2760 /* If there's no client surface under the cursor, set the cursor image to a
2761 * default. This is what makes the cursor image appear when you move it
2762 * off of a client or over its border.
2763 * Don't reset the cursor while spawnregion is pending a draw. */
2764 if (!surface && !seat->drag && !spawnregion_active)
2765 wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
2766
2767 /* zoom */
2768 if (c && !surface && c->mon && fabs(c->mon->zoom_scale - 1.0) >= 1e-9)
2769 surface = client_surface(c);
2770 /* zoom end */
2771
2772 pointerfocus(c, surface, sx, sy, time);
2773}
2774
2775void
2776motionrelative(struct wl_listener *listener, void *data)
2777{
2778 /* This event is forwarded by the cursor when a pointer emits a _relative_
2779 * pointer motion event (i.e. a delta) */
2780 struct wlr_pointer_motion_event *event = data;
2781 /* The cursor doesn't move unless we tell it to. The cursor automatically
2782 * handles constraining the motion to the output layout, as well as any
2783 * special configuration applied for the specific input device which
2784 * generated the event. You can pass NULL for the device if you want to move
2785 * the cursor around without any input. */
2786 motionnotify(event->time_msec, &event->pointer->base, event->delta_x, event->delta_y,
2787 event->unaccel_dx, event->unaccel_dy);
2788}
2789
2790void
2791moveresize(const Arg *arg)
2792{
2793 if (cursor_mode != CurNormal && cursor_mode != CurPressed)
2794 return;
2795 xytonode(cursor->x, cursor->y, NULL, &grabc, NULL, NULL, NULL);
2796 if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen)
2797 return;
2798
2799 setfloating(grabc, 1);
2800 resize_snap_x_target = 0;
2801 resize_snap_y_target = 0;
2802 switch (cursor_mode = arg->ui) {
2803 case CurMove: {
2804 /* zoom */
2805 Monitor *m = grabc->mon;
2806 double s;
2807 int mvsx, mvsy;
2808 s = m ? m->zoom_scale : 1.0;
2809 if (fabs(s) < 1e-9) s = 1.0;
2810 mvsx = m ? (int)llround((double)grabc->geom.x * s + m->zoom_pan_x) : grabc->geom.x;
2811 mvsy = m ? (int)llround((double)grabc->geom.y * s + m->zoom_pan_y) : grabc->geom.y;
2812 grabcx = (int)round(cursor->x) - mvsx;
2813 grabcy = (int)round(cursor->y) - mvsy;
2814 /* zoom end */
2815 wlr_cursor_set_xcursor(cursor, cursor_mgr, "all-scroll");
2816 break;
2817 }
2818 case CurResize: {
2819 /* zoom */
2820 Monitor *m = grabc->mon;
2821 double s;
2822 int ex, ey;
2823 s = m ? m->zoom_scale : 1.0;
2824 if (fabs(s) < 1e-9) s = 1.0;
2825 resize_grab_world_x = grabc->geom.x;
2826 resize_grab_world_y = grabc->geom.y;
2827 ex = m ? (int)llround((double)(grabc->geom.x + grabc->geom.width) * s + m->zoom_pan_x)
2828 : grabc->geom.x + grabc->geom.width;
2829 ey = m ? (int)llround((double)(grabc->geom.y + grabc->geom.height) * s + m->zoom_pan_y)
2830 : grabc->geom.y + grabc->geom.height;
2831 wlr_cursor_warp_closest(cursor, NULL, ex, ey);
2832 /* zoom end */
2833 wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize");
2834 break;
2835 }
2836 }
2837}
2838
2839void
2840outputmgrapply(struct wl_listener *listener, void *data)
2841{
2842 struct wlr_output_configuration_v1 *config = data;
2843 outputmgrapplyortest(config, 0);
2844}
2845
2846void
2847outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test)
2848{
2849 /*
2850 * Called when a client such as wlr-randr requests a change in output
2851 * configuration. This is only one way that the layout can be changed,
2852 * so any Monitor information should be updated by updatemons() after an
2853 * output_layout.change event, not here.
2854 */
2855 struct wlr_output_configuration_head_v1 *config_head;
2856 int ok = 1;
2857
2858 wl_list_for_each(config_head, &config->heads, link) {
2859 struct wlr_output *wlr_output = config_head->state.output;
2860 Monitor *m = wlr_output->data;
2861 struct wlr_output_state state;
2862
2863 /* Ensure displays previously disabled by wlr-output-power-management-v1
2864 * are properly handled*/
2865 m->asleep = 0;
2866
2867 wlr_output_state_init(&state);
2868 wlr_output_state_set_enabled(&state, config_head->state.enabled);
2869 if (!config_head->state.enabled)
2870 goto apply_or_test;
2871
2872 if (config_head->state.mode)
2873 wlr_output_state_set_mode(&state, config_head->state.mode);
2874 else
2875 wlr_output_state_set_custom_mode(&state,
2876 config_head->state.custom_mode.width,
2877 config_head->state.custom_mode.height,
2878 config_head->state.custom_mode.refresh);
2879
2880 wlr_output_state_set_transform(&state, config_head->state.transform);
2881 wlr_output_state_set_scale(&state, config_head->state.scale);
2882 wlr_output_state_set_adaptive_sync_enabled(&state,
2883 config_head->state.adaptive_sync_enabled);
2884
2885apply_or_test:
2886 ok &= test ? wlr_output_test_state(wlr_output, &state)
2887 : wlr_output_commit_state(wlr_output, &state);
2888
2889 /* Don't move monitors if position wouldn't change. This avoids
2890 * wlroots marking the output as manually configured.
2891 * wlr_output_layout_add does not like disabled outputs */
2892 if (!test && wlr_output->enabled && (m->m.x != config_head->state.x || m->m.y != config_head->state.y))
2893 wlr_output_layout_add(output_layout, wlr_output,
2894 config_head->state.x, config_head->state.y);
2895
2896 wlr_output_state_finish(&state);
2897 }
2898
2899 if (ok)
2900 wlr_output_configuration_v1_send_succeeded(config);
2901 else
2902 wlr_output_configuration_v1_send_failed(config);
2903 wlr_output_configuration_v1_destroy(config);
2904
2905 /* https://codeberg.org/dwl/dwl/issues/577 */
2906 updatemons(NULL, NULL);
2907}
2908
2909void
2910outputmgrtest(struct wl_listener *listener, void *data)
2911{
2912 struct wlr_output_configuration_v1 *config = data;
2913 outputmgrapplyortest(config, 1);
2914}
2915
2916void
2917pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy,
2918 uint32_t time)
2919{
2920 struct timespec now;
2921
2922 if (surface != seat->pointer_state.focused_surface &&
2923 sloppyfocus && time && c && !client_is_unmanaged(c))
2924 focusclient(c, 1);
2925
2926 /* If surface is NULL, clear pointer focus */
2927 if (!surface) {
2928 wlr_seat_pointer_notify_clear_focus(seat);
2929 return;
2930 }
2931
2932 if (!time) {
2933 clock_gettime(CLOCK_MONOTONIC, &now);
2934 time = now.tv_sec * 1000 + now.tv_nsec / 1000000;
2935 }
2936
2937 /* Let the client know that the mouse cursor has entered one
2938 * of its surfaces, and make keyboard focus follow if desired.
2939 * wlroots makes this a no-op if surface is already focused */
2940 wlr_seat_pointer_notify_enter(seat, surface, sx, sy);
2941 wlr_seat_pointer_notify_motion(seat, time, sx, sy);
2942}
2943
2944void
2945printstatus(void)
2946{
2947 Monitor *m = NULL;
2948 Client *c;
2949 uint32_t occ, urg, sel;
2950
2951 wl_list_for_each(m, &mons, link) {
2952 occ = urg = 0;
2953 wl_list_for_each(c, &clients, link) {
2954 if (c->mon != m)
2955 continue;
2956 occ |= c->tags;
2957 if (c->isurgent)
2958 urg |= c->tags;
2959 }
2960 if ((c = focustop(m))) {
2961 printf("%s title %s\n", m->wlr_output->name, client_get_title(c));
2962 printf("%s appid %s\n", m->wlr_output->name, client_get_appid(c));
2963 printf("%s fullscreen %d\n", m->wlr_output->name, c->isfullscreen);
2964 printf("%s floating %d\n", m->wlr_output->name, c->isfloating);
2965 sel = c->tags;
2966 } else {
2967 printf("%s title \n", m->wlr_output->name);
2968 printf("%s appid \n", m->wlr_output->name);
2969 printf("%s fullscreen \n", m->wlr_output->name);
2970 printf("%s floating \n", m->wlr_output->name);
2971 sel = 0;
2972 }
2973
2974 printf("%s selmon %u\n", m->wlr_output->name, m == selmon);
2975 printf("%s tags %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32"\n",
2976 m->wlr_output->name, occ, m->tagset[m->seltags], sel, urg);
2977 printf("%s layout %s\n", m->wlr_output->name, m->ltsymbol);
2978 }
2979 fflush(stdout);
2980}
2981
2982void
2983powermgrsetmode(struct wl_listener *listener, void *data)
2984{
2985 struct wlr_output_power_v1_set_mode_event *event = data;
2986 struct wlr_output_state state = {0};
2987 Monitor *m = event->output->data;
2988
2989 if (!m)
2990 return;
2991
2992 m->gamma_lut_changed = 1; /* Reapply gamma LUT when re-enabling the output */
2993 wlr_output_state_set_enabled(&state, event->mode);
2994 wlr_output_commit_state(m->wlr_output, &state);
2995
2996 m->asleep = !event->mode;
2997 updatemons(NULL, NULL);
2998}
2999
3000void
3001quit(const Arg *arg)
3002{
3003 wl_display_terminate(dpy);
3004}
3005
3006void
3007rendermon(struct wl_listener *listener, void *data)
3008{
3009 Monitor *m = wl_container_of(listener, m, frame);
3010 Client *c;
3011 struct wlr_output_state pending = {0};
3012 struct timespec now;
3013
3014 /* zoom */
3015 zoom_frame(m);
3016 /* zoom end */
3017
3018 wl_list_for_each(c, &clients, link) {
3019 if (c->resize && !c->isfloating && client_is_rendered_on_mon(c, m) && !client_is_stopped(c))
3020 goto skip;
3021 }
3022
3023 /* zoom */
3024 if (fabs(m->zoom_scale - 1.0) > 1e-5)
3025 zoom_apply(m);
3026 /* zoom end */
3027
3028 /* Build the complete scene state first, then hand that fully-rendered state
3029 * to the output backend. This separates rendering from DRM submission and
3030 * lets wlroots apply its explicit-sync state when the backend supports it.
3031 * It is the closest wlroots-native equivalent to waiting for frame
3032 * completion before queueing the next frame. */
3033 if (!wlr_scene_output_build_state(m->scene_output, &pending, NULL))
3034 goto skip;
3035 if (!wlr_output_commit_state(m->wlr_output, &pending))
3036 wlr_log(WLR_ERROR, "Failed to commit rendered frame on %s", m->wlr_output->name);
3037
3038skip:
3039 clock_gettime(CLOCK_MONOTONIC, &now);
3040 wlr_scene_output_send_frame_done(m->scene_output, &now);
3041 wlr_output_state_finish(&pending);
3042}
3043
3044void
3045requestdecorationmode(struct wl_listener *listener, void *data)
3046{
3047 Client *c = wl_container_of(listener, c, set_decoration_mode);
3048 if (c->surface.xdg->initialized)
3049 wlr_xdg_toplevel_decoration_v1_set_mode(c->decoration,
3050 WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
3051}
3052
3053void
3054requeststartdrag(struct wl_listener *listener, void *data)
3055{
3056 struct wlr_seat_request_start_drag_event *event = data;
3057
3058 if (wlr_seat_validate_pointer_grab_serial(seat, event->origin,
3059 event->serial))
3060 wlr_seat_start_pointer_drag(seat, event->drag, event->serial);
3061 else
3062 wlr_data_source_destroy(event->drag->source);
3063}
3064
3065void
3066requestmonstate(struct wl_listener *listener, void *data)
3067{
3068 struct wlr_output_event_request_state *event = data;
3069 wlr_output_commit_state(event->output, event->state);
3070 updatemons(NULL, NULL);
3071}
3072
3073void
3074resize(Client *c, struct wlr_box geo, int interact)
3075{
3076 struct wlr_box *bbox;
3077 struct wlr_box clip;
3078
3079#ifdef TILING
3080 if (resizelock) {
3081 c->geom = geo;
3082 return;
3083 }
3084#endif
3085 if (!c->mon || !client_surface(c)->mapped)
3086 return;
3087
3088 bbox = interact ? &sgeom : &c->mon->w;
3089
3090 client_set_bounds(c, geo.width, geo.height);
3091 c->geom = geo;
3092 /* zoom */
3093 if (c->mon && c->mon->lt[c->mon->sellt]->arrange == NULL
3094 && fabs(c->mon->zoom_scale - 1.0) >= 1e-9) {
3095 c->geom.width = MAX(1 + 2 * (int)c->bw, c->geom.width);
3096 c->geom.height = MAX(1 + 2 * (int)c->bw, c->geom.height);
3097 } else
3098 /* zoom end */
3099 if (!(c->isfloating && c->mon->lt[c->mon->sellt]->arrange == NULL) || cursor_mode == CurResize)
3100 applybounds(c, bbox);
3101
3102 /* zoom */
3103 zoom_update_scene_client(c);
3104 /* zoom end */
3105
3106 c->resize = client_set_size(c, c->geom.width - 2 * c->bw,
3107 c->geom.height - 2 * c->bw);
3108 client_get_clip(c, &clip);
3109 wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip);
3110}
3111
3112void
3113run(char *startup_cmd)
3114{
3115 /* Add a Unix socket to the Wayland display. */
3116 const char *socket = wl_display_add_socket_auto(dpy);
3117 if (!socket)
3118 die("startup: display_add_socket_auto");
3119 setenv("WAYLAND_DISPLAY", socket, 1);
3120
3121 /* Start the backend. This will enumerate outputs and inputs, become the DRM
3122 * master, etc */
3123 if (!wlr_backend_start(backend))
3124 die("startup: backend_start");
3125
3126 /* Now that the socket exists and the backend is started, run the startup command */
3127 if (startup_cmd) {
3128 int piperw[2];
3129 if (pipe(piperw) < 0)
3130 die("startup: pipe:");
3131 if ((child_pid = fork()) < 0)
3132 die("startup: fork:");
3133 if (child_pid == 0) {
3134 setsid();
3135 dup2(piperw[0], STDIN_FILENO);
3136 close(piperw[0]);
3137 close(piperw[1]);
3138 execl("/bin/sh", "/bin/sh", "-c", startup_cmd, NULL);
3139 die("startup: execl:");
3140 }
3141 dup2(piperw[1], STDOUT_FILENO);
3142 close(piperw[1]);
3143 close(piperw[0]);
3144 }
3145
3146 /* Mark stdout as non-blocking to avoid the startup script
3147 * causing leeloo to freeze when a user neither closes stdin
3148 * nor consumes standard input in his startup script */
3149
3150 if (fd_set_nonblock(STDOUT_FILENO) < 0)
3151 close(STDOUT_FILENO);
3152
3153 printstatus();
3154
3155 /* At this point the outputs are initialized, choose initial selmon based on
3156 * cursor position, and set default cursor image */
3157 selmon = xytomon(cursor->x, cursor->y);
3158
3159 /* TODO hack to get cursor to display in its initial location (100, 100)
3160 * instead of (0, 0) and then jumping. Still may not be fully
3161 * initialized, as the image/coordinates are not transformed for the
3162 * monitor when displayed here */
3163 wlr_cursor_warp_closest(cursor, NULL, cursor->x, cursor->y);
3164 wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
3165
3166 /* Run the Wayland event loop. This does not return until you exit the
3167 * compositor. Starting the backend rigged up all of the necessary event
3168 * loop configuration to listen to libinput events, DRM events, generate
3169 * frame events at the refresh rate, and so on. */
3170 wl_display_run(dpy);
3171}
3172
3173void
3174setcursor(struct wl_listener *listener, void *data)
3175{
3176 /* This event is raised by the seat when a client provides a cursor image */
3177 struct wlr_seat_pointer_request_set_cursor_event *event = data;
3178 /* If we're "grabbing" the cursor, don't use the client's image, we will
3179 * restore it after "grabbing" sending a leave event, followed by a enter
3180 * event, which will result in the client requesting set the cursor surface */
3181 if (spawnregion_active)
3182 return;
3183 if (cursor_mode != CurNormal && cursor_mode != CurPressed)
3184 return;
3185 /* This can be sent by any client, so we check to make sure this one
3186 * actually has pointer focus first. If so, we can tell the cursor to
3187 * use the provided surface as the cursor image. It will set the
3188 * hardware cursor on the output that it's currently on and continue to
3189 * do so as the cursor moves between outputs. */
3190 if (event->seat_client == seat->pointer_state.focused_client)
3191 wlr_cursor_set_surface(cursor, event->surface,
3192 event->hotspot_x, event->hotspot_y);
3193}
3194
3195void
3196setcursorshape(struct wl_listener *listener, void *data)
3197{
3198 struct wlr_cursor_shape_manager_v1_request_set_shape_event *event = data;
3199
3200 if (spawnregion_active)
3201 return;
3202 if (cursor_mode != CurNormal && cursor_mode != CurPressed)
3203 return;
3204 /* This can be sent by any client, so we check to make sure this one
3205 * actually has pointer focus first. If so, we can tell the cursor to
3206 * use the provided cursor shape. */
3207 if (event->seat_client == seat->pointer_state.focused_client)
3208 wlr_cursor_set_xcursor(cursor, cursor_mgr,
3209 wlr_cursor_shape_v1_name(event->shape));
3210}
3211
3212void
3213setfloating(Client *c, int floating)
3214{
3215 Client *p = client_get_parent(c);
3216 c->isfloating = floating;
3217 /* If in floating layout do not change the client's layer */
3218 if (!c->mon || !client_surface(c)->mapped || !c->mon->lt[c->mon->sellt]->arrange)
3219 return;
3220 wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen ||
3221 (p && p->isfullscreen) ? LyrFS
3222 : c->isfloating ? LyrFloat : LyrTile]);
3223 arrange(c->mon);
3224 printstatus();
3225}
3226
3227void
3228setfullscreen(Client *c, int fullscreen)
3229{
3230 if (c->isfakefullscreen)
3231 return; /* avoid clobbering c->prev while fake fullscreen owns it */
3232 c->isfullscreen = fullscreen;
3233 if (!c->mon || !client_surface(c)->mapped)
3234 return;
3235
3236 c->bw = fullscreen ? 0 : (innerborderpx + outerborderpx);
3237 c->ibw = fullscreen ? 0 : innerborderpx;
3238 c->obw = fullscreen ? 0 : outerborderpx;
3239
3240 client_set_fullscreen(c, fullscreen);
3241 wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen
3242 ? LyrFS : c->isfloating ? LyrFloat : LyrTile]);
3243
3244 if (fullscreen) {
3245 c->prev = c->geom;
3246 resize(c, c->mon->m, 0);
3247 } else {
3248 /* restore previous size instead of arrange for floating windows since
3249 * client positions are set by the user and cannot be recalculated */
3250 resize(c, c->prev, 0);
3251 }
3252 arrange(c->mon);
3253 printstatus();
3254}
3255
3256void
3257setlayout(const Arg *arg)
3258{
3259 /* infinite_canvas */
3260 const Layout *old_layout;
3261 const Layout *new_layout;
3262 Client *_c;
3263 /* infinite_canvas_end */
3264 if (!selmon)
3265 return;
3266
3267 /* infinite_canvas */
3268 old_layout = selmon->lt[selmon->sellt];
3269 /* infinite_canvas_end */
3270
3271 if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt])
3272 selmon->sellt ^= 1;
3273 if (arg && arg->v)
3274 selmon->lt[selmon->sellt] = (Layout *)arg->v;
3275
3276 /* infinite_canvas */
3277 new_layout = selmon->lt[selmon->sellt];
3278 if (old_layout->arrange == NULL && new_layout->arrange != NULL) {
3279 save_canvas_positions(selmon);
3280 homecanvas(NULL);
3281 wl_list_for_each(_c, &clients, link)
3282 if (_c->mon == selmon && !_c->isfullscreen)
3283 _c->isfloating = 0;
3284 } else if (old_layout->arrange != NULL && new_layout->arrange == NULL) {
3285 restore_canvas_positions(selmon);
3286 wl_list_for_each(_c, &clients, link)
3287 if (_c->mon == selmon)
3288 _c->isfloating = 1;
3289 }
3290 /* infinite_canvas_end */
3291
3292 strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, LENGTH(selmon->ltsymbol));
3293 arrange(selmon);
3294 printstatus();
3295}
3296
3297/* arg > 1.0 will set mfact absolutely */
3298void
3299setmfact(const Arg *arg)
3300{
3301 float f;
3302
3303 if (!arg || !selmon || !selmon->lt[selmon->sellt]->arrange)
3304 return;
3305 f = arg->f < 1.0f ? arg->f + selmon->mfact : arg->f - 1.0f;
3306 if (f < 0.1 || f > 0.9)
3307 return;
3308 selmon->mfact = f;
3309 arrange(selmon);
3310}
3311
3312void
3313setmon(Client *c, Monitor *m, uint32_t newtags)
3314{
3315 Monitor *oldmon = c->mon;
3316
3317 if (oldmon == m)
3318 return;
3319 c->mon = m;
3320 c->prev = c->geom;
3321
3322 /* Scene graph sends surface leave/enter events on move and resize */
3323 if (oldmon)
3324 arrange(oldmon);
3325 if (m) {
3326 /* Make sure window actually overlaps with the monitor */
3327 resize(c, c->geom, 0);
3328 c->tags = newtags ? newtags : m->tagset[m->seltags]; /* assign tags of target monitor */
3329#if CENTER_NEW_WINDOWS
3330 c->prev.x = (m->w.width - c->prev.width) / 2 + m->m.x;
3331 c->prev.y = (m->w.height - c->prev.height) / 2 + m->m.y;
3332#endif
3333 setfullscreen(c, c->isfullscreen); /* This will call arrange(c->mon) */
3334 setfloating(c, c->isfloating);
3335 /* infinite_canvas: this window's home pan was captured under the
3336 * OLD monitor's independent canvas scale; that's meaningless on
3337 * the new monitor, so re-anchor it here exactly like a freshly
3338 * placed window (see mapnotify()), or bringintoview() will
3339 * compute a wrong/garbage shift for this window going forward. */
3340 {
3341 int tagidx = getcurrenttag(m);
3342 c->home_cx = m->canvas[tagidx].cx;
3343 c->home_cy = m->canvas[tagidx].cy;
3344 }
3345 }
3346 focusclient(focustop(selmon), 1);
3347}
3348
3349void
3350setpsel(struct wl_listener *listener, void *data)
3351{
3352 /* This event is raised by the seat when a client wants to set the selection,
3353 * usually when the user copies something. wlroots allows compositors to
3354 * ignore such requests if they so choose, but in leeloo we always honor them
3355 */
3356 struct wlr_seat_request_set_primary_selection_event *event = data;
3357 wlr_seat_set_primary_selection(seat, event->source, event->serial);
3358}
3359
3360void
3361setsel(struct wl_listener *listener, void *data)
3362{
3363 /* This event is raised by the seat when a client wants to set the selection,
3364 * usually when the user copies something. wlroots allows compositors to
3365 * ignore such requests if they so choose, but in leeloo we always honor them
3366 */
3367 struct wlr_seat_request_set_selection_event *event = data;
3368 wlr_seat_set_selection(seat, event->source, event->serial);
3369}
3370
3371void
3372setup(void)
3373{
3374 int drm_fd, i, sig[] = {SIGCHLD, SIGINT, SIGTERM, SIGPIPE};
3375 struct sigaction sa = {.sa_flags = SA_RESTART, .sa_handler = handlesig};
3376 sigemptyset(&sa.sa_mask);
3377
3378 for (i = 0; i < (int)LENGTH(sig); i++)
3379 sigaction(sig[i], &sa, NULL);
3380
3381 wlr_log_init(log_level, NULL);
3382
3383 /* The Wayland display is managed by libwayland. It handles accepting
3384 * clients from the Unix socket, managing Wayland globals, and so on. */
3385 dpy = wl_display_create();
3386 event_loop = wl_display_get_event_loop(dpy);
3387
3388 /* The backend is a wlroots feature which abstracts the underlying input and
3389 * output hardware. The autocreate option will choose the most suitable
3390 * backend based on the current environment, such as opening an X11 window
3391 * if an X11 server is running. */
3392 if (!(backend = wlr_backend_autocreate(event_loop, &session)))
3393 die("couldn't create backend");
3394
3395 /* Initialize the scene graph used to lay out windows */
3396 scene = wlr_scene_create();
3397 root_bg = wlr_scene_rect_create(&scene->tree, 0, 0, rootcolor);
3398 for (i = 0; i < NUM_LAYERS; i++)
3399 layers[i] = wlr_scene_tree_create(&scene->tree);
3400 drag_icon = wlr_scene_tree_create(&scene->tree);
3401 wlr_scene_node_place_below(&drag_icon->node, &layers[LyrBlock]->node);
3402
3403 /* Autocreates a renderer, either Pixman, GLES2 or Vulkan for us. The user
3404 * can also specify a renderer using the WLR_RENDERER env var.
3405 * The renderer is responsible for defining the various pixel formats it
3406 * supports for shared memory, this configures that for clients. */
3407 if (!(drw = wlr_renderer_autocreate(backend)))
3408 die("couldn't create renderer");
3409 wl_signal_add(&drw->events.lost, &gpu_reset);
3410
3411 /* Create shm, drm and linux_dmabuf interfaces by ourselves.
3412 * The simplest way is to call:
3413 * wlr_renderer_init_wl_display(drw);
3414 * but we need to create the linux_dmabuf interface manually to integrate it
3415 * with wlr_scene. */
3416 wlr_renderer_init_wl_shm(drw, dpy);
3417
3418 if (wlr_renderer_get_texture_formats(drw, WLR_BUFFER_CAP_DMABUF)) {
3419 wlr_drm_create(dpy, drw);
3420 wlr_scene_set_linux_dmabuf_v1(scene,
3421 wlr_linux_dmabuf_v1_create_with_renderer(dpy, 5, drw));
3422 }
3423
3424 if ((drm_fd = wlr_renderer_get_drm_fd(drw)) >= 0 && drw->features.timeline
3425 && backend->features.timeline)
3426 wlr_linux_drm_syncobj_manager_v1_create(dpy, 1, drm_fd);
3427
3428 /* Autocreates an allocator for us.
3429 * The allocator is the bridge between the renderer and the backend. It
3430 * handles the buffer creation, allowing wlroots to render onto the
3431 * screen */
3432 if (!(alloc = wlr_allocator_autocreate(backend, drw)))
3433 die("couldn't create allocator");
3434
3435 /* This creates some hands-off wlroots interfaces. The compositor is
3436 * necessary for clients to allocate surfaces and the data device manager
3437 * handles the clipboard. Each of these wlroots interfaces has room for you
3438 * to dig your fingers in and play with their behavior if you want. Note that
3439 * the clients cannot set the selection directly without compositor approval,
3440 * see the setsel() function. */
3441 compositor = wlr_compositor_create(dpy, 6, drw);
3442 wlr_subcompositor_create(dpy);
3443 wlr_data_device_manager_create(dpy);
3444 wlr_export_dmabuf_manager_v1_create(dpy);
3445 wlr_screencopy_manager_v1_create(dpy);
3446 wlr_data_control_manager_v1_create(dpy);
3447 wlr_ext_data_control_manager_v1_create(dpy, 1);
3448 wlr_primary_selection_v1_device_manager_create(dpy);
3449 wlr_viewporter_create(dpy);
3450 wlr_single_pixel_buffer_manager_v1_create(dpy);
3451 wlr_fractional_scale_manager_v1_create(dpy, 1);
3452 wlr_presentation_create(dpy, backend, 2);
3453 wlr_alpha_modifier_v1_create(dpy);
3454
3455 /* Initializes the interface used to implement urgency hints */
3456 activation = wlr_xdg_activation_v1_create(dpy);
3457 wl_signal_add(&activation->events.request_activate, &request_activate);
3458
3459 wlr_scene_set_gamma_control_manager_v1(scene, wlr_gamma_control_manager_v1_create(dpy));
3460
3461 power_mgr = wlr_output_power_manager_v1_create(dpy);
3462 wl_signal_add(&power_mgr->events.set_mode, &output_power_mgr_set_mode);
3463
3464 /* Creates an output layout, which is a wlroots utility for working with an
3465 * arrangement of screens in a physical layout. */
3466 output_layout = wlr_output_layout_create(dpy);
3467 wl_signal_add(&output_layout->events.change, &layout_change);
3468
3469 wlr_xdg_output_manager_v1_create(dpy, output_layout);
3470
3471 /* Configure a listener to be notified when new outputs are available on the
3472 * backend. */
3473 wl_list_init(&mons);
3474 wl_list_init(&savedstates);
3475 wl_signal_add(&backend->events.new_output, &new_output);
3476
3477 /* Set up our client lists, the xdg-shell and the layer-shell. The xdg-shell is a
3478 * Wayland protocol which is used for application windows. For more
3479 * detail on shells, refer to the article:
3480 *
3481 * https://drewdevault.com/2018/07/29/Wayland-shells.html
3482 */
3483 wl_list_init(&clients);
3484 wl_list_init(&fstack);
3485 /* zoom */
3486 wl_list_init(&popup_zoom_list);
3487 /* zoom end */
3488
3489 xdg_shell = wlr_xdg_shell_create(dpy, 6);
3490 wl_signal_add(&xdg_shell->events.new_toplevel, &new_xdg_toplevel);
3491 wl_signal_add(&xdg_shell->events.new_popup, &new_xdg_popup);
3492
3493 layer_shell = wlr_layer_shell_v1_create(dpy, 3);
3494 wl_signal_add(&layer_shell->events.new_surface, &new_layer_surface);
3495
3496 idle_notifier = wlr_idle_notifier_v1_create(dpy);
3497
3498 idle_inhibit_mgr = wlr_idle_inhibit_v1_create(dpy);
3499 wl_signal_add(&idle_inhibit_mgr->events.new_inhibitor, &new_idle_inhibitor);
3500
3501 session_lock_mgr = wlr_session_lock_manager_v1_create(dpy);
3502 wl_signal_add(&session_lock_mgr->events.new_lock, &new_session_lock);
3503 locked_bg = wlr_scene_rect_create(layers[LyrBlock], sgeom.width, sgeom.height,
3504 (float [4]){0.1f, 0.1f, 0.1f, 1.0f});
3505 wlr_scene_node_set_enabled(&locked_bg->node, 0);
3506
3507 /* Use decoration protocols to negotiate server-side decorations */
3508 wlr_server_decoration_manager_set_default_mode(
3509 wlr_server_decoration_manager_create(dpy),
3510 WLR_SERVER_DECORATION_MANAGER_MODE_SERVER);
3511 xdg_decoration_mgr = wlr_xdg_decoration_manager_v1_create(dpy);
3512 wl_signal_add(&xdg_decoration_mgr->events.new_toplevel_decoration, &new_xdg_decoration);
3513
3514 pointer_constraints = wlr_pointer_constraints_v1_create(dpy);
3515 pointer_gestures = wlr_pointer_gestures_v1_create(dpy);
3516 wl_signal_add(&pointer_constraints->events.new_constraint, &new_pointer_constraint);
3517
3518 relative_pointer_mgr = wlr_relative_pointer_manager_v1_create(dpy);
3519
3520 /* tablet-input */
3521 tablet_mgr = wlr_tablet_v2_create(dpy);
3522 /* tablet-input end */
3523
3524 /*
3525 * Creates a cursor, which is a wlroots utility for tracking the cursor
3526 * image shown on screen.
3527 */
3528 cursor = wlr_cursor_create();
3529 wlr_cursor_attach_output_layout(cursor, output_layout);
3530
3531 /* Creates an xcursor manager, another wlroots utility which loads up
3532 * Xcursor themes to source cursor images from and makes sure that cursor
3533 * images are available at all scale factors on the screen (necessary for
3534 * HiDPI support). Scaled cursors will be loaded with each output. */
3535 cursor_mgr = wlr_xcursor_manager_create(getenv("XCURSOR_THEME"),
3536 getenv("XCURSOR_SIZE") ? atoi(getenv("XCURSOR_SIZE")) : 24);
3537 setenv("XCURSOR_SIZE", getenv("XCURSOR_SIZE") ? getenv("XCURSOR_SIZE") : "24", 1);
3538
3539 /*
3540 * wlr_cursor *only* displays an image on screen. It does not move around
3541 * when the pointer moves. However, we can attach input devices to it, and
3542 * it will generate aggregate events for all of them. In these events, we
3543 * can choose how we want to process them, forwarding them to clients and
3544 * moving the cursor around. More detail on this process is described in
3545 * https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html
3546 *
3547 * And more comments are sprinkled throughout the notify functions above.
3548 */
3549 wl_signal_add(&cursor->events.motion, &cursor_motion);
3550 wl_signal_add(&cursor->events.motion_absolute, &cursor_motion_absolute);
3551 wl_signal_add(&cursor->events.button, &cursor_button);
3552 wl_signal_add(&cursor->events.axis, &cursor_axis);
3553 wl_signal_add(&cursor->events.frame, &cursor_frame);
3554
3555 /* tablet-input */
3556 wl_signal_add(&cursor->events.tablet_tool_proximity, &tablet_tool_proximity);
3557 wl_signal_add(&cursor->events.tablet_tool_axis, &tablet_tool_axis);
3558 wl_signal_add(&cursor->events.tablet_tool_button, &tablet_tool_button);
3559 wl_signal_add(&cursor->events.tablet_tool_tip, &tablet_tool_tip);
3560 /* tablet-input end */
3561
3562 cursor_shape_mgr = wlr_cursor_shape_manager_v1_create(dpy, 1);
3563 wl_signal_add(&cursor_shape_mgr->events.request_set_shape, &request_set_cursor_shape);
3564
3565 /*
3566 * Configures a seat, which is a single "seat" at which a user sits and
3567 * operates the computer. This conceptually includes up to one keyboard,
3568 * pointer, touch, and drawing tablet device. We also rig up a listener to
3569 * let us know when new input devices are available on the backend.
3570 */
3571 wl_signal_add(&backend->events.new_input, &new_input_device);
3572 virtual_keyboard_mgr = wlr_virtual_keyboard_manager_v1_create(dpy);
3573 wl_signal_add(&virtual_keyboard_mgr->events.new_virtual_keyboard,
3574 &new_virtual_keyboard);
3575 virtual_pointer_mgr = wlr_virtual_pointer_manager_v1_create(dpy);
3576 wl_signal_add(&virtual_pointer_mgr->events.new_virtual_pointer,
3577 &new_virtual_pointer);
3578
3579 seat = wlr_seat_create(dpy, "seat0");
3580 wl_signal_add(&seat->events.request_set_cursor, &request_cursor);
3581 wl_signal_add(&seat->events.request_set_selection, &request_set_sel);
3582 wl_signal_add(&seat->events.request_set_primary_selection, &request_set_psel);
3583 wl_signal_add(&seat->events.request_start_drag, &request_start_drag);
3584 wl_signal_add(&seat->events.start_drag, &start_drag);
3585
3586 kb_group = createkeyboardgroup();
3587 wl_list_init(&kb_group->destroy.link);
3588
3589 output_mgr = wlr_output_manager_v1_create(dpy);
3590 wl_signal_add(&output_mgr->events.apply, &output_mgr_apply);
3591 wl_signal_add(&output_mgr->events.test, &output_mgr_test);
3592
3593 /* Make sure XWayland clients don't connect to the parent X server,
3594 * e.g when running in the x11 backend or the wayland backend and the
3595 * compositor has Xwayland support */
3596 unsetenv("DISPLAY");
3597#ifdef XWAYLAND
3598 /*
3599 * Initialise the XWayland X server.
3600 * It will be started when the first X client is started.
3601 */
3602 if ((xwayland = wlr_xwayland_create(dpy, compositor, 1))) {
3603 wl_signal_add(&xwayland->events.ready, &xwayland_ready);
3604 wl_signal_add(&xwayland->events.new_surface, &new_xwayland_surface);
3605
3606 setenv("DISPLAY", xwayland->display_name, 1);
3607 } else {
3608 fprintf(stderr, "failed to setup XWayland X server, continuing without it\n");
3609 }
3610#endif
3611}
3612
3613void
3614spawn(const Arg *arg)
3615{
3616 if (fork() == 0) {
3617 dup2(STDERR_FILENO, STDOUT_FILENO);
3618 setsid();
3619 execvp(((char **)arg->v)[0], (char **)arg->v);
3620 die("leeloo: execvp %s failed:", ((char **)arg->v)[0]);
3621 }
3622}
3623
3624void
3625startdrag(struct wl_listener *listener, void *data)
3626{
3627 struct wlr_drag *drag = data;
3628 if (!drag->icon)
3629 return;
3630
3631 drag->icon->data = &wlr_scene_drag_icon_create(drag_icon, drag->icon)->node;
3632 LISTEN_STATIC(&drag->icon->events.destroy, destroydragicon);
3633}
3634
3635void
3636tag(const Arg *arg)
3637{
3638 Client *sel = focustop(selmon);
3639 if (!sel || (arg->ui & TAGMASK) == 0)
3640 return;
3641
3642 sel->tags = arg->ui & TAGMASK;
3643 /* infinite_canvas */
3644 if (selmon && selmon->lt[selmon->sellt]->arrange == NULL) {
3645 int _i;
3646 for (_i = 0; _i < TAGCOUNT; _i++) {
3647 if (arg->ui & (1u << _i)) {
3648 sel->saved_cx = selmon->canvas[_i].cx + (selmon->w.width - sel->geom.width) / 2;
3649 sel->saved_cy = selmon->canvas[_i].cy + (selmon->w.height - sel->geom.height) / 2;
3650 sel->saved_cw = sel->geom.width;
3651 sel->saved_ch = sel->geom.height;
3652 sel->was_on_canvas = 1;
3653 break;
3654 }
3655 }
3656 }
3657 /* infinite_canvas_end */
3658 focusclient(focustop(selmon), 1);
3659 arrange(selmon);
3660 printstatus();
3661}
3662
3663void
3664tagmon(const Arg *arg)
3665{
3666 Client *sel = focustop(selmon);
3667 if (sel)
3668 setmon(sel, dirtomon(arg->i), 0);
3669}
3670
3671#ifndef MAGNET_SNAP_DIST
3672#define MAGNET_SNAP_DIST 12 /* on-screen pixels within which a dragged window's edge magnetically aligns to another edge */
3673#endif
3674
3675/* Adjusts a candidate drag position (*nx, *ny) so that if any edge of the
3676 * dragged window c is within MAGNET_SNAP_DIST screen-pixels of an edge of
3677 * another visible window (or the monitor's own usable area), it snaps
3678 * flush against it -- GNOME/driftwm style magnetic edge alignment. Unlike
3679 * trysnap(), this never resizes the window or forces it into a half/quarter
3680 * region; it only nudges position, and only when actually close to an edge. */
3681#ifndef ALIGN_SNAP_DIST
3682#define ALIGN_SNAP_DIST 60 /* on-screen pixels: how much top/bottom mismatch still counts as "close enough" to auto-align when snapping side-by-side against a window */
3683#endif
3684
3685static void
3686snap_edges(Client *c, Monitor *m, int *nx, int *ny)
3687{
3688 Client *o, *xpartner = NULL;
3689 struct wlr_box cand;
3690 int best_dx = 0, best_dy = 0, have_dx = 0, have_dy = 0;
3691 int thresh, align_tol;
3692
3693 if (!c || !m)
3694 return;
3695
3696 thresh = MAGNET_SNAP_DIST;
3697 if (m->zoom_scale > 1e-9)
3698 thresh = (int)llround(MAGNET_SNAP_DIST / m->zoom_scale);
3699 align_tol = ALIGN_SNAP_DIST;
3700 if (m->zoom_scale > 1e-9)
3701 align_tol = (int)llround(ALIGN_SNAP_DIST / m->zoom_scale);
3702
3703 cand.x = *nx;
3704 cand.y = *ny;
3705 cand.width = c->geom.width;
3706 cand.height = c->geom.height;
3707
3708 /* Snap against the monitor's own usable-area edges. */
3709 {
3710 struct wlr_box *w = &m->w;
3711 int d;
3712 d = abs(cand.x - w->x);
3713 if (d <= thresh && (!have_dx || d < abs(best_dx))) { best_dx = w->x - cand.x; have_dx = 1; }
3714 d = abs((cand.x + cand.width) - (w->x + w->width));
3715 if (d <= thresh && (!have_dx || d < abs(best_dx))) { best_dx = (w->x + w->width) - (cand.x + cand.width); have_dx = 1; }
3716 d = abs(cand.y - w->y);
3717 if (d <= thresh && (!have_dy || d < abs(best_dy))) { best_dy = w->y - cand.y; have_dy = 1; }
3718 d = abs((cand.y + cand.height) - (w->y + w->height));
3719 if (d <= thresh && (!have_dy || d < abs(best_dy))) { best_dy = (w->y + w->height) - (cand.y + cand.height); have_dy = 1; }
3720 }
3721
3722 /* Snap against every other visible window's edges, at whatever
3723 * size each window actually is -- no resizing involved. */
3724 wl_list_for_each(o, &clients, link) {
3725 int d;
3726 if (o == c || !VISIBLEON(o, m))
3727 continue;
3728
3729 /* Only snap horizontally if the two windows vertically overlap
3730 * (and vice versa) -- otherwise aligning to a window nowhere
3731 * near it vertically would look wrong, not like a real snap. */
3732 if (cand.y < o->geom.y + o->geom.height && o->geom.y < cand.y + cand.height) {
3733 d = abs(cand.x - o->geom.x);
3734 if (d <= thresh && (!have_dx || d < abs(best_dx))) { best_dx = o->geom.x - cand.x; have_dx = 1; xpartner = o; }
3735 d = abs(cand.x - (o->geom.x + o->geom.width));
3736 if (d <= thresh && (!have_dx || d < abs(best_dx))) { best_dx = (o->geom.x + o->geom.width) - cand.x; have_dx = 1; xpartner = o; }
3737 d = abs((cand.x + cand.width) - (o->geom.x + o->geom.width));
3738 if (d <= thresh && (!have_dx || d < abs(best_dx))) { best_dx = (o->geom.x + o->geom.width) - (cand.x + cand.width); have_dx = 1; xpartner = o; }
3739 d = abs((cand.x + cand.width) - o->geom.x);
3740 if (d <= thresh && (!have_dx || d < abs(best_dx))) { best_dx = o->geom.x - (cand.x + cand.width); have_dx = 1; xpartner = o; }
3741 }
3742
3743 if (cand.x < o->geom.x + o->geom.width && o->geom.x < cand.x + cand.width) {
3744 d = abs(cand.y - o->geom.y);
3745 if (d <= thresh && (!have_dy || d < abs(best_dy))) { best_dy = o->geom.y - cand.y; have_dy = 1; }
3746 d = abs(cand.y - (o->geom.y + o->geom.height));
3747 if (d <= thresh && (!have_dy || d < abs(best_dy))) { best_dy = (o->geom.y + o->geom.height) - cand.y; have_dy = 1; }
3748 d = abs((cand.y + cand.height) - (o->geom.y + o->geom.height));
3749 if (d <= thresh && (!have_dy || d < abs(best_dy))) { best_dy = (o->geom.y + o->geom.height) - (cand.y + cand.height); have_dy = 1; }
3750 d = abs((cand.y + cand.height) - o->geom.y);
3751 if (d <= thresh && (!have_dy || d < abs(best_dy))) { best_dy = o->geom.y - (cand.y + cand.height); have_dy = 1; }
3752 }
3753 }
3754
3755 /* Compound alignment: if we're snapping side-by-side against a
3756 * specific window (xpartner) and didn't already find a tight,
3757 * independent Y-edge snap, check whether that partner's top or
3758 * bottom edge is within a moderate tolerance -- close enough that
3759 * it looks like it was "meant" to line up (a bit higher/lower is
3760 * fine). If the mismatch is large (genuinely far apart vertically,
3761 * not just a small offset), leave the vertical position untouched. */
3762 if (have_dx && xpartner && !have_dy) {
3763 int top_d = abs(cand.y - xpartner->geom.y);
3764 int bot_d = abs((cand.y + cand.height) - (xpartner->geom.y + xpartner->geom.height));
3765 if (top_d <= align_tol || bot_d <= align_tol) {
3766 if (top_d <= bot_d)
3767 best_dy = xpartner->geom.y - cand.y;
3768 else
3769 best_dy = (xpartner->geom.y + xpartner->geom.height) - (cand.y + cand.height);
3770 have_dy = 1;
3771 }
3772 }
3773
3774 *nx += best_dx;
3775 *ny += best_dy;
3776}
3777
3778#ifndef RESIZE_MAGNET_SNAP_DIST
3779#define RESIZE_MAGNET_SNAP_DIST 48
3780#endif
3781#ifndef RESIZE_MAGNET_RELEASE_DIST
3782#define RESIZE_MAGNET_RELEASE_DIST 80
3783#endif
3784
3785/* When a floating window is already magnetically touching another window,
3786 * resizing its lower/right edge acts like a shared divider. Near the
3787 * balanced size, the divider becomes sticky and snaps to the exact
3788 * half-way point of the two-window cluster. */
3789static void
3790resize_magnetically(Client *c, Monitor *m, struct wlr_box *geo)
3791{
3792 Client *o, *right = NULL, *below = NULL;
3793 int minw, minh, thresh, release;
3794 int right_edge, bottom_edge;
3795 int x_best = 0, y_best = 0;
3796 int xdist = 0x3fffffff, ydist = 0x3fffffff;
3797 int cluster_left, cluster_right, cluster_top, cluster_bottom;
3798 int balance_x, balance_y;
3799 int proposed_x, proposed_y;
3800
3801 if (!c || !m || !geo)
3802 return;
3803
3804 thresh = RESIZE_MAGNET_SNAP_DIST;
3805 release = RESIZE_MAGNET_RELEASE_DIST;
3806 if (m->zoom_scale > 1e-9) {
3807 thresh = (int)llround((double)RESIZE_MAGNET_SNAP_DIST / m->zoom_scale);
3808 release = (int)llround((double)RESIZE_MAGNET_RELEASE_DIST / m->zoom_scale);
3809 }
3810
3811 minw = 1 + 2 * (int)c->bw;
3812 minh = 1 + 2 * (int)c->bw;
3813
3814 /* Find a window magnetically attached to the moving right edge. */
3815 wl_list_for_each(o, &clients, link) {
3816 int gap, overlap;
3817 if (o == c || o->mon != m || !VISIBLEON(o, m) || o->isfullscreen)
3818 continue;
3819
3820 overlap = MIN(c->geom.y + c->geom.height, o->geom.y + o->geom.height)
3821 - MAX(c->geom.y, o->geom.y);
3822 if (overlap <= 0)
3823 continue;
3824
3825 gap = abs((c->geom.x + c->geom.width) - o->geom.x);
3826 if (gap <= MAGNET_SNAP_DIST && o->geom.x >= c->geom.x + minw
3827 && gap < xdist) {
3828 right = o;
3829 xdist = gap;
3830 }
3831 }
3832
3833 /* Find a window magnetically attached to the moving bottom edge. */
3834 wl_list_for_each(o, &clients, link) {
3835 int gap, overlap;
3836 if (o == c || o->mon != m || !VISIBLEON(o, m) || o->isfullscreen)
3837 continue;
3838
3839 overlap = MIN(c->geom.x + c->geom.width, o->geom.x + o->geom.width)
3840 - MAX(c->geom.x, o->geom.x);
3841 if (overlap <= 0)
3842 continue;
3843
3844 gap = abs((c->geom.y + c->geom.height) - o->geom.y);
3845 if (gap <= MAGNET_SNAP_DIST && o->geom.y >= c->geom.y + minh
3846 && gap < ydist) {
3847 below = o;
3848 ydist = gap;
3849 }
3850 }
3851
3852 /* Horizontal cluster: c | right
3853 * Keep the shared divider intact and snap it to the exact 50/50 point
3854 * when the two widths are close enough. */
3855 if (right) {
3856 right_edge = geo->x + geo->width;
3857 cluster_left = c->geom.x;
3858 cluster_right = right->geom.x + right->geom.width;
3859 balance_x = cluster_left + (cluster_right - cluster_left) / 2;
3860 proposed_x = right_edge;
3861
3862 if (resize_snap_x_target) {
3863 if (abs(proposed_x - resize_snap_x_target) <= release) {
3864 proposed_x = resize_snap_x_target;
3865 } else {
3866 resize_snap_x_target = 0;
3867 }
3868 }
3869
3870 if (!resize_snap_x_target &&
3871 abs((proposed_x - cluster_left) - right->geom.width) <= thresh) {
3872 resize_snap_x_target = balance_x;
3873 proposed_x = balance_x;
3874 }
3875
3876 /* Respect minimum sizes on both sides. */
3877 proposed_x = MAX(cluster_left + minw, proposed_x);
3878 proposed_x = MIN(cluster_right - (1 + 2 * (int)right->bw), proposed_x);
3879
3880 if (resize_snap_x_target)
3881 resize_snap_x_target = proposed_x;
3882
3883 geo->width = MAX(minw, proposed_x - c->geom.x);
3884
3885 /* Resize the partner so the magnetic connection stays intact. */
3886 {
3887 struct wlr_box other = right->geom;
3888 other.x = proposed_x;
3889 other.width = cluster_right - proposed_x;
3890 if (other.width >= 1 + 2 * (int)right->bw)
3891 resize(right, other, 1);
3892 }
3893 } else {
3894 resize_snap_x_target = 0;
3895 }
3896
3897 /* Vertical cluster: c
3898 * ---
3899 * below
3900 *
3901 * Same sticky 50/50 divider behavior for the bottom edge. */
3902 if (below) {
3903 bottom_edge = geo->y + geo->height;
3904 cluster_top = c->geom.y;
3905 cluster_bottom = below->geom.y + below->geom.height;
3906 balance_y = cluster_top + (cluster_bottom - cluster_top) / 2;
3907 proposed_y = bottom_edge;
3908
3909 if (resize_snap_y_target) {
3910 if (abs(proposed_y - resize_snap_y_target) <= release) {
3911 proposed_y = resize_snap_y_target;
3912 } else {
3913 resize_snap_y_target = 0;
3914 }
3915 }
3916
3917 if (!resize_snap_y_target &&
3918 abs((proposed_y - cluster_top) - below->geom.height) <= thresh) {
3919 resize_snap_y_target = balance_y;
3920 proposed_y = balance_y;
3921 }
3922
3923 proposed_y = MAX(cluster_top + minh, proposed_y);
3924 proposed_y = MIN(cluster_bottom - (1 + 2 * (int)below->bw), proposed_y);
3925
3926 if (resize_snap_y_target)
3927 resize_snap_y_target = proposed_y;
3928
3929 geo->height = MAX(minh, proposed_y - c->geom.y);
3930
3931 /* Resize the partner so the magnetic connection stays intact. */
3932 {
3933 struct wlr_box other = below->geom;
3934 other.y = proposed_y;
3935 other.height = cluster_bottom - proposed_y;
3936 if (other.height >= 1 + 2 * (int)below->bw)
3937 resize(below, other, 1);
3938 }
3939 } else {
3940 resize_snap_y_target = 0;
3941 }
3942}
3943
3944
3945#ifdef TILING
3946void
3947tile(Monitor *m)
3948{
3949 unsigned int mw, my, ty;
3950 int i, n = 0;
3951 Client *c;
3952
3953 wl_list_for_each(c, &clients, link)
3954 if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen)
3955 n++;
3956 if (n == 0)
3957 return;
3958
3959 if (n > m->nmaster)
3960 mw = m->nmaster ? (int)roundf(m->w.width * m->mfact) : 0;
3961 else
3962 mw = m->w.width;
3963 i = my = ty = 0;
3964 wl_list_for_each(c, &clients, link) {
3965 if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
3966 continue;
3967 if (i < m->nmaster) {
3968 resize(c, (struct wlr_box){.x = m->w.x, .y = m->w.y + my, .width = mw,
3969 .height = (m->w.height - my) / (MIN(n, m->nmaster) - i)}, 0);
3970 my += c->geom.height;
3971 } else {
3972 resize(c, (struct wlr_box){.x = m->w.x + mw, .y = m->w.y + ty,
3973 .width = m->w.width - mw, .height = (m->w.height - ty) / (n - i)}, 0);
3974 ty += c->geom.height;
3975 }
3976 i++;
3977 }
3978}
3979
3980void
3981togglefloating(const Arg *arg)
3982{
3983 Client *sel = focustop(selmon);
3984 /* return if fullscreen */
3985 if (sel && !sel->isfullscreen)
3986 setfloating(sel, !sel->isfloating);
3987}
3988#endif
3989
3990void
3991togglefullscreen(const Arg *arg)
3992{
3993 Client *sel = focustop(selmon);
3994 if (sel)
3995 setfullscreen(sel, !sel->isfullscreen);
3996}
3997
3998void
3999togglefakefullscreen(const Arg *arg)
4000{
4001 Client *c = focustop(selmon);
4002
4003 if (!c || !c->mon || c->isfullscreen)
4004 return; /* real fullscreen already owns the screen; don't fight it */
4005
4006 if (!c->isfakefullscreen) {
4007 /* Fake fullscreen covers the whole usable monitor, but keeps Leeloo's
4008 * normal double border visible. Remember the old geometry first so the
4009 * exact window position and size can be restored when toggled off. */
4010 c->prev = c->geom;
4011 c->isfakefullscreen = 1;
4012 if (!c->isfloating)
4013 setfloating(c, 1);
4014
4015 c->bw = innerborderpx + outerborderpx;
4016 c->ibw = innerborderpx;
4017 c->obw = outerborderpx;
4018 resize(c, c->mon->m, 0);
4019 } else {
4020 c->isfakefullscreen = 0;
4021 c->bw = innerborderpx + outerborderpx;
4022 c->ibw = innerborderpx;
4023 c->obw = outerborderpx;
4024 resize(c, c->prev, 0);
4025 }
4026 arrange(c->mon);
4027 printstatus();
4028}
4029
4030/* labwc-style directional snap state machine.
4031 *
4032 * The 8 snap states are:
4033 *
4034 * TL T TR
4035 * L F R
4036 * BL B BR
4037 *
4038 * Directional keys move between adjacent states along the corresponding
4039 * axis. F is the bridge between opposite full-height/full-width states.
4040 */
4041void
4042snaptoposition(const Arg *arg)
4043{
4044 Client *c = focustop(selmon);
4045 struct wlr_box *w;
4046 struct wlr_box left, right, top, bottom;
4047 struct wlr_box tl, tr, bl, br;
4048 struct wlr_box box, cur;
4049 int state = -1;
4050 int halfw, halfh, rightw, bottomh;
4051
4052 if (!c || !c->mon || c->isfullscreen || c->isfakefullscreen)
4053 return;
4054 if (!c->isfloating)
4055 setfloating(c, 1);
4056
4057 w = &c->mon->w;
4058
4059 halfw = w->width / 2;
4060 halfh = w->height / 2;
4061 rightw = w->width - halfw;
4062 bottomh = w->height - halfh;
4063
4064 left = (struct wlr_box){w->x, w->y, halfw, w->height};
4065 right = (struct wlr_box){w->x + halfw, w->y, rightw, w->height};
4066 top = (struct wlr_box){w->x, w->y, w->width, halfh};
4067 bottom = (struct wlr_box){w->x, w->y + halfh, w->width, bottomh};
4068 tl = (struct wlr_box){w->x, w->y, halfw, halfh};
4069 tr = (struct wlr_box){w->x + halfw, w->y, rightw, halfh};
4070 bl = (struct wlr_box){w->x, w->y + halfh, halfw, bottomh};
4071 br = (struct wlr_box){w->x + halfw, w->y + halfh, rightw, bottomh};
4072
4073 cur = c->geom;
4074
4075 /* c->geom stores the exact snap box, including the compositor border.
4076 * Use exact geometry matching here: >= would let a larger side or full-screen
4077 * box also match a corner state and break the directional state machine. */
4078 if (cur.x == tl.x && cur.y == tl.y &&
4079 cur.width == tl.width &&
4080 cur.height == tl.height)
4081 state = 1; /* top-left */
4082 else if (cur.x == top.x && cur.y == top.y &&
4083 cur.width == top.width &&
4084 cur.height == top.height)
4085 state = 2; /* top */
4086 else if (cur.x == tr.x && cur.y == tr.y &&
4087 cur.width == tr.width &&
4088 cur.height == tr.height)
4089 state = 3; /* top-right */
4090 else if (cur.x == right.x && cur.y == right.y &&
4091 cur.width == right.width &&
4092 cur.height == right.height)
4093 state = 4; /* right */
4094 else if (cur.x == br.x && cur.y == br.y &&
4095 cur.width == br.width &&
4096 cur.height == br.height)
4097 state = 5; /* bottom-right */
4098 else if (cur.x == bottom.x && cur.y == bottom.y &&
4099 cur.width == bottom.width &&
4100 cur.height == bottom.height)
4101 state = 6; /* bottom */
4102 else if (cur.x == bl.x && cur.y == bl.y &&
4103 cur.width == bl.width &&
4104 cur.height == bl.height)
4105 state = 7; /* bottom-left */
4106 else if (cur.x == left.x && cur.y == left.y &&
4107 cur.width == left.width &&
4108 cur.height == left.height)
4109 state = 0; /* left */
4110 else if (cur.x == w->x && cur.y == w->y &&
4111 cur.width == w->width &&
4112 cur.height == w->height)
4113 state = 8; /* fake/fullscreen snap bridge */
4114
4115 /* Unknown geometry: a directional key simply snaps into its requested
4116 * destination rather than trying to infer a previous state. */
4117 if (state < 0) {
4118 switch (arg->i) {
4119 case SnapLeft: box = left; break;
4120 case SnapRight: box = right; break;
4121 case SnapTop: box = top; break;
4122 case SnapBottom: box = bottom; break;
4123 case SnapTopLeft: box = tl; break;
4124 case SnapTopRight: box = tr; break;
4125 case SnapBottomLeft: box = bl; break;
4126 case SnapBottomRight: box = br; break;
4127 default: return;
4128 }
4129 resize(c, box, 0);
4130 return;
4131 }
4132
4133 /* Explicit directional state graph. This keeps every edge deterministic
4134 * and avoids the old fullscreen/corner fall-through bugs. */
4135 switch (arg->i) {
4136 case SnapLeft:
4137 switch (state) {
4138 case 2: box = tl; break; /* T -> TL */
4139 case 3: box = top; break; /* TR -> T */
4140 case 4: box = *w; break;
4141 case 5: box = bottom; break; /* BR -> B */
4142 case 6: box = bl; break; /* B -> BL */
4143 case 7: box = br; break; /* BL -> BR */
4144 case 8: box = left; break; /* F -> L */
4145 default: return;
4146 }
4147 break;
4148
4149 case SnapRight:
4150 switch (state) {
4151 case 0: box = *w; break; /* L -> F */
4152 case 1: box = top; break; /* TL -> T */
4153 case 2: box = tr; break; /* T -> TR */
4154 case 5: box = bl; break; /* BR -> BL */
4155 case 6: box = br; break; /* B -> BR */
4156 case 7: box = bottom; break; /* BL -> B */
4157 case 8: box = right; break; /* F -> R */
4158 default: return;
4159 }
4160 break;
4161
4162 case SnapTop:
4163 switch (state) {
4164 case 0: box = tl; break; /* L -> TL */
4165 case 1: box = top; break; /* TL -> T */
4166 case 3: box = top; break; /* TR -> T */
4167 case 4: box = tr; break; /* R -> TR */
4168 case 5: box = right; break; /* BR -> R */
4169 case 6: box = *w; break; /* B -> F */
4170 case 7: box = left; break; /* BL -> L */
4171 case 8: box = top; break; /* F -> T */
4172 default: return;
4173 }
4174 break;
4175
4176 case SnapBottom:
4177 switch (state) {
4178 case 0: box = bl; break; /* L -> BL */
4179 case 1: box = left; break; /* TL -> L */
4180 case 2: box = *w; break; /* T -> F */
4181 case 3: box = right; break; /* TR -> R */
4182 case 4: box = br; break; /* R -> BR */
4183 case 5: box = right; break; /* BR -> R */
4184 case 7: box = bottom; break; /* BL -> B */
4185 case 8: box = bottom; break; /* F -> B */
4186 default: return;
4187 }
4188 break;
4189
4190 case SnapTopLeft: box = tl; break;
4191 case SnapTopRight: box = tr; break;
4192 case SnapBottomLeft: box = bl; break;
4193 case SnapBottomRight: box = br; break;
4194 default: return;
4195 }
4196
4197 resize(c, box, 0);
4198}
4199
4200void
4201toggletag(const Arg *arg)
4202{
4203 uint32_t newtags;
4204 Client *sel = focustop(selmon);
4205 if (!sel || !(newtags = sel->tags ^ (arg->ui & TAGMASK)))
4206 return;
4207
4208 sel->tags = newtags;
4209 focusclient(focustop(selmon), 1);
4210 arrange(selmon);
4211 printstatus();
4212}
4213
4214void
4215toggleview(const Arg *arg)
4216{
4217 uint32_t newtagset;
4218 if (!(newtagset = selmon ? selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK) : 0))
4219 return;
4220
4221 selmon->tagset[selmon->seltags] = newtagset;
4222 focusclient(focustop(selmon), 1);
4223 arrange(selmon);
4224 printstatus();
4225}
4226
4227void
4228unlocksession(struct wl_listener *listener, void *data)
4229{
4230 SessionLock *lock = wl_container_of(listener, lock, unlock);
4231 destroylock(lock, 1);
4232}
4233
4234void
4235unmaplayersurfacenotify(struct wl_listener *listener, void *data)
4236{
4237 LayerSurface *l = wl_container_of(listener, l, unmap);
4238
4239 l->mapped = 0;
4240 wlr_scene_node_set_enabled(&l->scene->node, 0);
4241 if (l == exclusive_focus)
4242 exclusive_focus = NULL;
4243 if (l->layer_surface->output && (l->mon = l->layer_surface->output->data))
4244 arrangelayers(l->mon);
4245 if (l->layer_surface->surface == seat->keyboard_state.focused_surface)
4246 focusclient(focustop(selmon), 1);
4247 motionnotify(0, NULL, 0, 0, 0, 0);
4248}
4249
4250void
4251unmapnotify(struct wl_listener *listener, void *data)
4252{
4253 /* Called when the surface is unmapped, and should no longer be shown. */
4254 Client *c = wl_container_of(listener, c, unmap);
4255 if (c == grabc) {
4256 cursor_mode = CurNormal;
4257 grabc = NULL;
4258 }
4259
4260 if (client_is_unmanaged(c)) {
4261 if (c == exclusive_focus) {
4262 exclusive_focus = NULL;
4263 focusclient(focustop(selmon), 1);
4264 }
4265 } else {
4266 wl_list_remove(&c->link);
4267 setmon(c, NULL, 0);
4268 wl_list_remove(&c->flink);
4269 }
4270
4271 if (c == waiting_geo_client)
4272 waiting_geo_client = NULL;
4273
4274 wlr_scene_node_destroy(&c->scene->node);
4275 printstatus();
4276 motionnotify(0, NULL, 0, 0, 0, 0);
4277}
4278
4279void
4280updatemons(struct wl_listener *listener, void *data)
4281{
4282 /*
4283 * Called whenever the output layout changes: adding or removing a
4284 * monitor, changing an output's mode or position, etc. This is where
4285 * the change officially happens and we update geometry, window
4286 * positions, focus, and the stored configuration in wlroots'
4287 * output-manager implementation.
4288 */
4289 struct wlr_output_configuration_v1 *config
4290 = wlr_output_configuration_v1_create();
4291 Client *c;
4292 struct wlr_output_configuration_head_v1 *config_head;
4293 Monitor *m;
4294
4295 /* First remove from the layout the disabled monitors */
4296 wl_list_for_each(m, &mons, link) {
4297 if (m->wlr_output->enabled || m->asleep)
4298 continue;
4299 config_head = wlr_output_configuration_head_v1_create(config, m->wlr_output);
4300 config_head->state.enabled = 0;
4301 /* Remove this output from the layout to avoid cursor enter inside it */
4302 wlr_output_layout_remove(output_layout, m->wlr_output);
4303 closemon(m);
4304 m->m = m->w = (struct wlr_box){0};
4305 }
4306 /* Insert outputs that need to */
4307 wl_list_for_each(m, &mons, link) {
4308 if (m->wlr_output->enabled
4309 && !wlr_output_layout_get(output_layout, m->wlr_output))
4310 wlr_output_layout_add_auto(output_layout, m->wlr_output);
4311 }
4312
4313 /* Now that we update the output layout we can get its box */
4314 wlr_output_layout_get_box(output_layout, NULL, &sgeom);
4315
4316 wlr_scene_node_set_position(&root_bg->node, sgeom.x, sgeom.y);
4317 wlr_scene_rect_set_size(root_bg, sgeom.width, sgeom.height);
4318
4319 /* Make sure the clients are hidden when leeloo is locked */
4320 wlr_scene_node_set_position(&locked_bg->node, sgeom.x, sgeom.y);
4321 wlr_scene_rect_set_size(locked_bg, sgeom.width, sgeom.height);
4322
4323 wl_list_for_each(m, &mons, link) {
4324 if (!m->wlr_output->enabled)
4325 continue;
4326 config_head = wlr_output_configuration_head_v1_create(config, m->wlr_output);
4327
4328 /* Get the effective monitor geometry to use for surfaces */
4329 wlr_output_layout_get_box(output_layout, m->wlr_output, &m->m);
4330 m->w = m->m;
4331 wlr_scene_output_set_position(m->scene_output, m->m.x, m->m.y);
4332
4333 wlr_scene_node_set_position(&m->fullscreen_bg->node, m->m.x, m->m.y);
4334 wlr_scene_rect_set_size(m->fullscreen_bg, m->m.width, m->m.height);
4335
4336 if (m->lock_surface) {
4337 struct wlr_scene_tree *scene_tree = m->lock_surface->surface->data;
4338 wlr_scene_node_set_position(&scene_tree->node, m->m.x, m->m.y);
4339 wlr_session_lock_surface_v1_configure(m->lock_surface, m->m.width, m->m.height);
4340 }
4341
4342 /* Calculate the effective monitor geometry to use for clients */
4343 arrangelayers(m);
4344 /* Don't move clients to the left output when plugging monitors */
4345 arrange(m);
4346 /* make sure fullscreen clients have the right size */
4347 if ((c = focustop(m)) && c->isfullscreen)
4348 resize(c, m->m, 0);
4349
4350 /* Try to re-set the gamma LUT when updating monitors,
4351 * it's only really needed when enabling a disabled output, but meh. */
4352 m->gamma_lut_changed = 1;
4353
4354 config_head->state.x = m->m.x;
4355 config_head->state.y = m->m.y;
4356
4357 if (!selmon) {
4358 selmon = m;
4359 }
4360 }
4361
4362 if (selmon && selmon->wlr_output->enabled) {
4363 wl_list_for_each(c, &clients, link) {
4364 if (!c->mon && client_surface(c)->mapped)
4365 setmon(c, selmon, c->tags);
4366 }
4367 focusclient(focustop(selmon), 1);
4368 if (selmon->lock_surface) {
4369 client_notify_enter(selmon->lock_surface->surface,
4370 wlr_seat_get_keyboard(seat));
4371 client_activate_surface(selmon->lock_surface->surface, 1);
4372 }
4373 }
4374
4375 /* FIXME: figure out why the cursor image is at 0,0 after turning all
4376 * the monitors on.
4377 * Move the cursor image where it used to be. It does not generate a
4378 * wl_pointer.motion event for the clients, it's only the image what it's
4379 * at the wrong position after all. */
4380 wlr_cursor_move(cursor, NULL, 0, 0);
4381
4382 wlr_output_manager_v1_set_configuration(output_mgr, config);
4383}
4384
4385void
4386updatetitle(struct wl_listener *listener, void *data)
4387{
4388 Client *c = wl_container_of(listener, c, set_title);
4389 if (c == focustop(c->mon))
4390 printstatus();
4391}
4392
4393void
4394urgent(struct wl_listener *listener, void *data)
4395{
4396 struct wlr_xdg_activation_v1_request_activate_event *event = data;
4397 Client *c = NULL;
4398 toplevel_from_wlr_surface(event->surface, &c, NULL);
4399 if (!c || c == focustop(selmon))
4400 return;
4401
4402 c->isurgent = 1;
4403 printstatus();
4404
4405 if (client_surface(c)->mapped)
4406 client_set_border_color(c, urgent_inner_border_color, urgent_outer_border_color);
4407}
4408
4409void
4410view(const Arg *arg)
4411{
4412 if (!selmon || (arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
4413 return;
4414
4415 /* infinite_canvas */
4416 if (selmon->lt[selmon->sellt]->arrange == NULL)
4417 save_canvas_positions(selmon);
4418 /* infinite_canvas_end */
4419
4420 selmon->seltags ^= 1; /* toggle sel tagset */
4421 if (arg->ui & TAGMASK)
4422 selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
4423
4424 /* infinite_canvas */
4425 {
4426 int _newtag = getcurrenttag(selmon);
4427 Client *_c;
4428 if (selmon->lt[selmon->sellt]->arrange != NULL) {
4429 selmon->canvas[_newtag].cx = 0;
4430 selmon->canvas[_newtag].cy = 0;
4431 } else {
4432 restore_canvas_positions(selmon);
4433 wl_list_for_each(_c, &clients, link)
4434 if (VISIBLEON(_c, selmon))
4435 _c->isfloating = 1;
4436 }
4437 }
4438 /* infinite_canvas_end */
4439
4440 focusclient(focustop(selmon), 1);
4441 arrange(selmon);
4442 printstatus();
4443}
4444
4445void
4446virtualkeyboard(struct wl_listener *listener, void *data)
4447{
4448 struct wlr_virtual_keyboard_v1 *kb = data;
4449 /* virtual keyboards shouldn't share keyboard group */
4450 KeyboardGroup *group = createkeyboardgroup();
4451 /* Set the keymap to match the group keymap */
4452 wlr_keyboard_set_keymap(&kb->keyboard, group->wlr_group->keyboard.keymap);
4453 LISTEN(&kb->keyboard.base.events.destroy, &group->destroy, destroykeyboardgroup);
4454
4455 /* Add the new keyboard to the group */
4456 wlr_keyboard_group_add_keyboard(group->wlr_group, &kb->keyboard);
4457}
4458
4459void
4460virtualpointer(struct wl_listener *listener, void *data)
4461{
4462 struct wlr_virtual_pointer_v1_new_pointer_event *event = data;
4463 struct wlr_input_device *device = &event->new_pointer->pointer.base;
4464
4465 wlr_cursor_attach_input_device(cursor, device);
4466 if (event->suggested_output)
4467 wlr_cursor_map_input_to_output(cursor, device, event->suggested_output);
4468}
4469
4470Monitor *
4471xytomon(double x, double y)
4472{
4473 struct wlr_output *o = wlr_output_layout_output_at(output_layout, x, y);
4474 return o ? o->data : NULL;
4475}
4476
4477void
4478xytonode(double x, double y, struct wlr_surface **psurface,
4479 Client **pc, LayerSurface **pl, double *nx, double *ny)
4480{
4481 struct wlr_scene_node *node, *pnode;
4482 struct wlr_surface *surface = NULL;
4483 Client *c = NULL;
4484 LayerSurface *l = NULL;
4485 int layer;
4486
4487 for (layer = NUM_LAYERS - 1; !surface && layer >= 0; layer--) {
4488 if (!(node = wlr_scene_node_at(&layers[layer]->node, x, y, nx, ny)))
4489 continue;
4490
4491 if (node->type == WLR_SCENE_NODE_BUFFER)
4492 surface = wlr_scene_surface_try_from_buffer(
4493 wlr_scene_buffer_from_node(node))->surface;
4494 for (pnode = node; pnode && !c; pnode = &pnode->parent->node)
4495 c = pnode->data;
4496 if (c && c->type == LayerShell) {
4497 l = (LayerSurface *)c;
4498 c = NULL;
4499 }
4500 }
4501
4502 /* zoom */
4503 if (c && surface && !c->isfullscreen
4504 && c->mon && fabs(c->mon->zoom_scale - 1.0) >= 1e-9 && nx && ny) {
4505 Monitor *m = c->mon;
4506 double s = m->zoom_scale;
4507 if (surface == client_surface(c)) {
4508 int scene_x = (int)llround((double)c->geom.x * s + m->zoom_pan_x);
4509 int scene_y = (int)llround((double)c->geom.y * s + m->zoom_pan_y);
4510 int bw_s = MAX(1, (int)llround((double)c->bw * s));
4511 int gw_s = (int)llround((double)c->geom.width * s);
4512 int gh_s = (int)llround((double)c->geom.height * s);
4513 int inner_w = MAX(1, gw_s - 2 * bw_s);
4514 int inner_h = MAX(1, gh_s - 2 * bw_s);
4515 double cw = (double)MAX(1, c->geom.width - 2 * c->bw);
4516 double ch = (double)MAX(1, c->geom.height - 2 * c->bw);
4517 double ox = (double)(scene_x + bw_s);
4518 double oy = (double)(scene_y + bw_s);
4519 *nx = (x - ox) * cw / (double)inner_w;
4520 *ny = (y - oy) * ch / (double)inner_h;
4521 } else {
4522 int abs_nx = 0, abs_ny = 0;
4523 if (node && wlr_scene_node_coords(node, &abs_nx, &abs_ny)) {
4524 double cw = (double)MAX(1, c->geom.width - 2 * c->bw);
4525 double ch = (double)MAX(1, c->geom.height - 2 * c->bw);
4526 int gw_s = (int)llround((double)c->geom.width * s);
4527 int gh_s = (int)llround((double)c->geom.height * s);
4528 int bw_s = MAX(1, (int)llround((double)c->bw * s));
4529 int inner_w = MAX(1, gw_s - 2 * bw_s);
4530 int inner_h = MAX(1, gh_s - 2 * bw_s);
4531 *nx = (x - abs_nx) * cw / (double)inner_w;
4532 *ny = (y - abs_ny) * ch / (double)inner_h;
4533 }
4534 }
4535 }
4536 /* zoom end */
4537
4538 if (psurface) *psurface = surface;
4539 if (pc) *pc = c;
4540 if (pl) *pl = l;
4541}
4542
4543void
4544swapmaster(const Arg *arg)
4545{
4546 Client *c, *sel = focustop(selmon);
4547
4548 if (!sel || !selmon || !selmon->lt[selmon->sellt]->arrange || sel->isfloating)
4549 return;
4550
4551 /* Search for the first tiled window that is not sel, marking sel as
4552 * NULL if we pass it along the way */
4553 wl_list_for_each(c, &clients, link) {
4554 if (VISIBLEON(c, selmon) && !c->isfloating) {
4555 if (c != sel)
4556 break;
4557 sel = NULL;
4558 }
4559 }
4560
4561 /* Return if no other tiled window was found */
4562 if (&c->link == &clients)
4563 return;
4564
4565 /* If we passed sel, move c to the front; otherwise, move sel to the
4566 * front */
4567 if (!sel)
4568 sel = c;
4569 wl_list_remove(&sel->link);
4570 wl_list_insert(&clients, &sel->link);
4571
4572 focusclient(sel, 1);
4573 arrange(selmon);
4574}
4575
4576#ifdef XWAYLAND
4577void
4578activatex11(struct wl_listener *listener, void *data)
4579{
4580 Client *c = wl_container_of(listener, c, activate);
4581
4582 /* Only "managed" windows can be activated */
4583 if (!client_is_unmanaged(c))
4584 wlr_xwayland_surface_activate(c->surface.xwayland, 1);
4585}
4586
4587void
4588associatex11(struct wl_listener *listener, void *data)
4589{
4590 Client *c = wl_container_of(listener, c, associate);
4591
4592 LISTEN(&client_surface(c)->events.map, &c->map, mapnotify);
4593 LISTEN(&client_surface(c)->events.unmap, &c->unmap, unmapnotify);
4594}
4595
4596void
4597configurex11(struct wl_listener *listener, void *data)
4598{
4599 Client *c = wl_container_of(listener, c, configure);
4600 struct wlr_xwayland_surface_configure_event *event = data;
4601 if (!client_surface(c) || !client_surface(c)->mapped) {
4602 wlr_xwayland_surface_configure(c->surface.xwayland,
4603 event->x, event->y, event->width, event->height);
4604 return;
4605 }
4606 if (client_is_unmanaged(c)) {
4607 wlr_scene_node_set_position(&c->scene->node, event->x, event->y);
4608 wlr_xwayland_surface_configure(c->surface.xwayland,
4609 event->x, event->y, event->width, event->height);
4610 return;
4611 }
4612 if ((c->isfloating && c != grabc) || !c->mon->lt[c->mon->sellt]->arrange) {
4613 resize(c, (struct wlr_box){.x = event->x - c->bw,
4614 .y = event->y - c->bw, .width = event->width + c->bw * 2,
4615 .height = event->height + c->bw * 2}, 0);
4616 } else {
4617 arrange(c->mon);
4618 }
4619}
4620
4621void
4622createnotifyx11(struct wl_listener *listener, void *data)
4623{
4624 struct wlr_xwayland_surface *xsurface = data;
4625 Client *c;
4626
4627 /* Allocate a Client for this surface */
4628 c = xsurface->data = ecalloc(1, sizeof(*c));
4629 c->surface.xwayland = xsurface;
4630 c->type = X11;
4631 c->bw = client_is_unmanaged(c) ? 0 : innerborderpx;
4632
4633 /* Listen to the various events it can emit */
4634 LISTEN(&xsurface->events.associate, &c->associate, associatex11);
4635 LISTEN(&xsurface->events.destroy, &c->destroy, destroynotify);
4636 LISTEN(&xsurface->events.dissociate, &c->dissociate, dissociatex11);
4637 LISTEN(&xsurface->events.request_activate, &c->activate, activatex11);
4638 LISTEN(&xsurface->events.request_configure, &c->configure, configurex11);
4639 LISTEN(&xsurface->events.request_fullscreen, &c->fullscreen, fullscreennotify);
4640 LISTEN(&xsurface->events.set_hints, &c->set_hints, sethints);
4641 LISTEN(&xsurface->events.set_title, &c->set_title, updatetitle);
4642}
4643
4644void
4645dissociatex11(struct wl_listener *listener, void *data)
4646{
4647 Client *c = wl_container_of(listener, c, dissociate);
4648 wl_list_remove(&c->map.link);
4649 wl_list_remove(&c->unmap.link);
4650}
4651
4652void
4653sethints(struct wl_listener *listener, void *data)
4654{
4655 Client *c = wl_container_of(listener, c, set_hints);
4656 struct wlr_surface *surface = client_surface(c);
4657 if (c == focustop(selmon) || !c->surface.xwayland->hints)
4658 return;
4659
4660 c->isurgent = xcb_icccm_wm_hints_get_urgency(c->surface.xwayland->hints);
4661 printstatus();
4662
4663 if (c->isurgent && surface && surface->mapped)
4664 client_set_border_color(c, urgent_inner_border_color, urgent_outer_border_color);
4665}
4666
4667void
4668xwaylandready(struct wl_listener *listener, void *data)
4669{
4670 struct wlr_xcursor *xcursor;
4671
4672 /* assign the one and only seat */
4673 wlr_xwayland_set_seat(xwayland, seat);
4674
4675 /* Set the default XWayland cursor to match the rest of leeloo. */
4676 if ((xcursor = wlr_xcursor_manager_get_xcursor(cursor_mgr, "default", 1)))
4677 wlr_xwayland_set_cursor(xwayland,
4678 xcursor->images[0]->buffer, xcursor->images[0]->width * 4,
4679 xcursor->images[0]->width, xcursor->images[0]->height,
4680 xcursor->images[0]->hotspot_x, xcursor->images[0]->hotspot_y);
4681}
4682#endif
4683
4684/* infinite_canvas */
4685
4686int
4687getcurrenttag(Monitor *m)
4688{
4689 unsigned int i;
4690 for (i = 0; i < (unsigned int)TAGCOUNT && !(m->tagset[m->seltags] & (1u << i)); i++);
4691 return i < (unsigned int)TAGCOUNT ? (int)i : 0;
4692}
4693
4694void
4695canvas_move_window(Client *c, int x, int y)
4696{
4697 if (!c->mon || !client_surface(c)->mapped)
4698 return;
4699
4700 c->geom.x = x;
4701 c->geom.y = y;
4702
4703 /* zoom */
4704 zoom_update_scene_client(c);
4705 /* zoom end */
4706}
4707
4708void
4709homecanvas(const Arg *arg)
4710{
4711 Client *c;
4712 int tagidx, cx, cy;
4713 if (!selmon)
4714 return;
4715
4716 tagidx = getcurrenttag(selmon);
4717 cx = selmon->canvas[tagidx].cx;
4718 cy = selmon->canvas[tagidx].cy;
4719
4720 wl_list_for_each(c, &clients, link) {
4721 if (!(c->tags & (1u << tagidx)) || c->mon != selmon)
4722 continue;
4723 c->geom.x -= cx;
4724 c->geom.y -= cy;
4725 }
4726
4727 selmon->canvas[tagidx].cx = 0;
4728 selmon->canvas[tagidx].cy = 0;
4729
4730 /* zoom */
4731 zoom_apply(selmon);
4732 /* zoom end */
4733}
4734
4735#ifndef MOVE_CANVAS_STEP
4736#define MOVE_CANVAS_STEP 120
4737#endif
4738
4739void
4740movecanvas(const Arg *arg)
4741{
4742 Client *c;
4743 int tagidx, dx, dy;
4744 if (!selmon || selmon->lt[selmon->sellt]->arrange != NULL)
4745 return;
4746
4747 tagidx = getcurrenttag(selmon);
4748 dx = 0;
4749 dy = 0;
4750
4751 switch (arg->i) {
4752 case 0: dx = -MOVE_CANVAS_STEP; break;
4753 case 1: dx = MOVE_CANVAS_STEP; break;
4754 case 2: dy = -MOVE_CANVAS_STEP; break;
4755 case 3: dy = MOVE_CANVAS_STEP; break;
4756 }
4757
4758 selmon->canvas[tagidx].cx -= dx;
4759 selmon->canvas[tagidx].cy -= dy;
4760
4761 wl_list_for_each(c, &clients, link) {
4762 if (!VISIBLEON(c, selmon))
4763 continue;
4764 c->geom.x -= dx;
4765 c->geom.y -= dy;
4766 }
4767 /* zoom */
4768 zoom_apply(selmon);
4769 /* zoom end */
4770}
4771
4772#ifndef WINMOVE_STEP_PX
4773#define WINMOVE_STEP_PX 5 /* pixels nudged per keypress -- niri-style Ctrl+Alt+Arrow */
4774#endif
4775#ifndef WINRESIZE_STEP_PX
4776#define WINRESIZE_STEP_PX 5 /* pixels resized per keypress -- niri-style Ctrl+Shift+Arrow */
4777#endif
4778
4779/* Nudges the focused window by a fixed step in one direction (0=left,
4780 * 1=right, 2=up, 3=down -- same convention as movecanvas()). Forces the
4781 * window floating first, matching how interactive dragging already
4782 * behaves, so the bind has a visible effect regardless of current layout. */
4783void
4784movewinbykeys(const Arg *arg)
4785{
4786 Client *c = focustop(selmon);
4787 struct wlr_box geo;
4788
4789 if (!c || !c->mon || c->isfullscreen || c->isfakefullscreen)
4790 return;
4791 if (!c->isfloating)
4792 setfloating(c, 1);
4793
4794 geo = c->geom;
4795 switch (arg->i) {
4796 case 0: geo.x -= WINMOVE_STEP_PX; break; /* left */
4797 case 1: geo.x += WINMOVE_STEP_PX; break; /* right */
4798 case 2: geo.y -= WINMOVE_STEP_PX; break; /* up */
4799 case 3: geo.y += WINMOVE_STEP_PX; break; /* down */
4800 default: return;
4801 }
4802 resize(c, geo, 1);
4803}
4804
4805/* Resizes the focused window by a fixed step (0=left, 1=right, 2=up,
4806 * 3=down). Follows the traditional i3/sway-style resize-mode convention:
4807 * left/right adjust width (left shrinks, right grows), up/down adjust
4808 * height (up shrinks, down grows) -- anchored at the window's current
4809 * top-left corner, so only the size changes, not the position. */
4810void
4811resizewinbykeys(const Arg *arg)
4812{
4813 Client *c = focustop(selmon);
4814 struct wlr_box geo;
4815 int minw, minh;
4816
4817 if (!c || !c->mon || c->isfullscreen || c->isfakefullscreen)
4818 return;
4819 if (!c->isfloating)
4820 setfloating(c, 1);
4821
4822 minw = 1 + 2 * (int)c->bw;
4823 minh = 1 + 2 * (int)c->bw;
4824 geo = c->geom;
4825 switch (arg->i) {
4826 case 0: geo.width = MAX(minw, geo.width - WINRESIZE_STEP_PX); break; /* left: shrink width */
4827 case 1: geo.width += WINRESIZE_STEP_PX; break; /* right: grow width */
4828 case 2: geo.height = MAX(minh, geo.height - WINRESIZE_STEP_PX); break; /* up: shrink height */
4829 case 3: geo.height += WINRESIZE_STEP_PX; break; /* down: grow height */
4830 default: return;
4831 }
4832 resize(c, geo, 1);
4833}
4834
4835void
4836movecanvasmouse(const Arg *arg)
4837{
4838 if (!selmon || selmon->lt[selmon->sellt]->arrange != NULL)
4839 return;
4840 if (cursor_mode != CurNormal && cursor_mode != CurPressed)
4841 return;
4842
4843 canvas_pan_multiplier = arg ? arg->f : 1.0f;
4844 canvas_pan_accum_x = 0.0f;
4845 canvas_pan_accum_y = 0.0f;
4846
4847 wlr_cursor_set_xcursor(cursor, cursor_mgr, "all-scroll");
4848 cursor_mode = CurMove;
4849 grabc = NULL;
4850 canvas_panning = 1;
4851 canvas_pan_last_x = cursor->x;
4852 canvas_pan_last_y = cursor->y;
4853}
4854
4855void
4856save_canvas_positions(Monitor *m)
4857{
4858 Client *c;
4859 int tagidx = getcurrenttag(m);
4860
4861 m->canvas[tagidx].saved_cx = m->canvas[tagidx].cx;
4862 m->canvas[tagidx].saved_cy = m->canvas[tagidx].cy;
4863
4864 wl_list_for_each(c, &clients, link) {
4865 if (!VISIBLEON(c, m))
4866 continue;
4867 c->saved_cx = c->geom.x + m->canvas[tagidx].cx;
4868 c->saved_cy = c->geom.y + m->canvas[tagidx].cy;
4869 c->saved_cw = c->geom.width;
4870 c->saved_ch = c->geom.height;
4871 c->was_on_canvas = 1;
4872 }
4873}
4874
4875void
4876restore_canvas_positions(Monitor *m)
4877{
4878 Client *c;
4879 int tagidx = getcurrenttag(m);
4880
4881 m->canvas[tagidx].cx = m->canvas[tagidx].saved_cx;
4882 m->canvas[tagidx].cy = m->canvas[tagidx].saved_cy;
4883
4884 wl_list_for_each(c, &clients, link) {
4885 int tx, ty;
4886
4887 if (!VISIBLEON(c, m) || !c->was_on_canvas)
4888 continue;
4889 c->isfloating = 1;
4890
4891 tx = c->saved_cx - m->canvas[tagidx].cx;
4892 ty = c->saved_cy - m->canvas[tagidx].cy;
4893
4894 c->geom.width = c->saved_cw;
4895 c->geom.height = c->saved_ch;
4896
4897 c->geom.x = tx;
4898 c->geom.y = ty;
4899
4900 /* zoom */
4901 zoom_update_scene_client(c);
4902 /* zoom end */
4903
4904 c->resize = client_set_size(c,
4905 c->geom.width - 2 * c->bw,
4906 c->geom.height - 2 * c->bw);
4907 }
4908}
4909
4910void
4911centerwindow(const Arg *arg)
4912{
4913 Monitor *m;
4914 Client *tmp;
4915 int tagidx, screen_cx, screen_cy, win_cx, win_cy, dx, dy;
4916 Client *c = (arg && arg->v) ? (Client *)arg->v : focustop(selmon);
4917
4918 if (!c || !c->mon || c->mon->lt[c->mon->sellt]->arrange != NULL)
4919 return;
4920
4921 m = c->mon;
4922 tagidx = getcurrenttag(m);
4923
4924 screen_cx = m->w.x + m->w.width / 2;
4925 screen_cy = m->w.y + m->w.height / 2;
4926 win_cx = c->geom.x + c->geom.width / 2;
4927 win_cy = c->geom.y + c->geom.height / 2;
4928
4929 dx = screen_cx - win_cx;
4930 dy = screen_cy - win_cy;
4931
4932 if (dx == 0 && dy == 0)
4933 return;
4934 wl_list_for_each(tmp, &clients, link) {
4935 if (!VISIBLEON(tmp, m))
4936 continue;
4937 tmp->geom.x += dx;
4938 tmp->geom.y += dy;
4939 }
4940
4941 m->canvas[tagidx].cx += dx;
4942 m->canvas[tagidx].cy += dy;
4943
4944 /* zoom */
4945 zoom_apply(m);
4946 /* zoom end */
4947}
4948
4949void
4950bringintoview(Client *c)
4951{
4952 Monitor *m;
4953 Client *tmp;
4954 int tagidx, dx, dy;
4955 struct wlr_box *w, *g;
4956
4957 if (!c || !c->mon || c->mon->lt[c->mon->sellt]->arrange != NULL)
4958 return;
4959
4960 m = c->mon;
4961 w = &m->w;
4962 g = &c->geom;
4963
4964 /* Only skip teleportation when the target window is FULLY visible.
4965 * A partially visible window must still trigger natural teleportation,
4966 * even if a corner/edge of it is visible on the current canvas. This
4967 * keeps focus and teleportation independent: fully visible nearby windows
4968 * are simply focused, while any clipped/off-canvas window is brought to
4969 * its own natural framing. */
4970 if (g->x >= w->x && g->y >= w->y &&
4971 g->x + g->width <= w->x + w->width &&
4972 g->y + g->height <= w->y + w->height)
4973 return; /* fully visible -- focus only, no teleport */
4974
4975 tagidx = getcurrenttag(m);
4976
4977 /* Each window remembers the pan (home_cx/home_cy) that was active
4978 * when it was placed or last manually moved -- its own natural
4979 * framing, independent of any other window's. Prefer restoring
4980 * exactly that pan: no viewport clamping, no edges, no padding. */
4981 dx = c->home_cx - m->canvas[tagidx].cx;
4982 dy = c->home_cy - m->canvas[tagidx].cy;
4983
4984 /* But if home-restore wouldn't actually reveal it (e.g. it was
4985 * placed off-screen to begin with, so "home" is off-screen too),
4986 * fall back to the minimal shift that brings its box into view. */
4987 if (g->x + dx < w->x || g->y + dy < w->y ||
4988 g->x + dx + g->width > w->x + w->width ||
4989 g->y + dy + g->height > w->y + w->height) {
4990 dx = 0;
4991 dy = 0;
4992 if (g->x < w->x)
4993 dx = w->x - g->x;
4994 else if (g->x + g->width > w->x + w->width)
4995 dx = (w->x + w->width) - (g->x + g->width);
4996 if (g->y < w->y)
4997 dy = w->y - g->y;
4998 else if (g->y + g->height > w->y + w->height)
4999 dy = (w->y + w->height) - (g->y + g->height);
5000 }
5001
5002 if (dx == 0 && dy == 0)
5003 return; /* already framed exactly as it was */
5004
5005 wl_list_for_each(tmp, &clients, link) {
5006 if (!VISIBLEON(tmp, m))
5007 continue;
5008 tmp->geom.x += dx;
5009 tmp->geom.y += dy;
5010 }
5011
5012 m->canvas[tagidx].cx += dx;
5013 m->canvas[tagidx].cy += dy;
5014
5015 /* zoom */
5016 zoom_apply(m);
5017 /* zoom end */
5018}
5019
5020/* infinite_canvas_end */
5021
5022/* zoom */
5023
5024void
5025popup_zoom_destroy(struct wl_listener *listener, void *data)
5026{
5027 PopupZoomData *pzd = wl_container_of(listener, pzd, destroy);
5028 wl_list_remove(&pzd->commit.link);
5029 wl_list_remove(&pzd->destroy.link);
5030 wl_list_remove(&pzd->link);
5031 free(pzd);
5032}
5033
5034void
5035popup_zoom_commit(struct wl_listener *listener, void *data)
5036{
5037 double s, px, py;
5038 Client *c;
5039 Monitor *m;
5040 PopupZoomData *pzd = wl_container_of(listener, pzd, commit);
5041
5042 if (!pzd->client || !pzd->client->mon || !pzd->node)
5043 return;
5044 if (!pzd->popup->base || !pzd->popup->base->surface->mapped)
5045 return;
5046
5047 c = pzd->client;
5048 m = c->mon;
5049 s = m->zoom_scale;
5050 wlr_xdg_popup_get_position(pzd->popup, &px, &py);
5051
5052 if (fabs(s - 1.0) < 1e-9) {
5053 wlr_scene_node_set_position(pzd->node, (int)llround(px), (int)llround(py));
5054 return;
5055 }
5056
5057 /* zoom */
5058 wlr_scene_node_set_position(pzd->node,
5059 (int)llround(px * s),
5060 (int)llround(py * s));
5061 /* zoom end */
5062}
5063
5064void
5065zoom_scale_buffers(struct wlr_scene_buffer *buf, int sx, int sy, void *data)
5066{
5067 Client *c = data;
5068 Monitor *m;
5069 struct wlr_scene_surface *ss;
5070 struct wlr_surface *surf;
5071 double s;
5072 int lw, lh;
5073
5074 (void)sx; (void)sy;
5075
5076 if (!c || !(m = c->mon) || !buf->buffer)
5077 return;
5078
5079 if (c->isfullscreen) {
5080 wlr_scene_buffer_set_dest_size(buf, 0, 0);
5081 return;
5082 }
5083
5084 s = m->zoom_scale;
5085 if (fabs(s - 1.0) < 1e-9) {
5086 wlr_scene_buffer_set_dest_size(buf, 0, 0);
5087 return;
5088 }
5089
5090 ss = wlr_scene_surface_try_from_buffer(buf);
5091 if (ss && (surf = ss->surface)) {
5092 if (surf->current.viewport.has_dst) {
5093 lw = surf->current.viewport.dst_width;
5094 lh = surf->current.viewport.dst_height;
5095 } else {
5096 lw = surf->current.width;
5097 lh = surf->current.height;
5098 }
5099 if (lw <= 0 || lh <= 0) {
5100 lw = buf->buffer->width;
5101 lh = buf->buffer->height;
5102 }
5103 } else {
5104 lw = buf->buffer->width;
5105 lh = buf->buffer->height;
5106 }
5107
5108 {
5109 int _bw = (int)llround((double)c->bw * s);
5110 int gw = (int)llround((double)c->geom.width * s);
5111 int gh = (int)llround((double)c->geom.height * s);
5112 int inner_w = MAX(1, gw - 2 * _bw);
5113 int inner_h = MAX(1, gh - 2 * _bw);
5114
5115 if (ss && ss->surface == client_surface(c)) {
5116 wlr_scene_buffer_set_dest_size(buf, inner_w, inner_h);
5117 return;
5118 }
5119
5120 {
5121 double cw = (double)MAX(1, c->geom.width - 2 * c->bw);
5122 double ch = (double)MAX(1, c->geom.height - 2 * c->bw);
5123 wlr_scene_buffer_set_dest_size(buf,
5124 MAX(1, (int)llround((double)lw * (double)inner_w / cw)),
5125 MAX(1, (int)llround((double)lh * (double)inner_h / ch)));
5126 }
5127 }
5128}
5129
5130void
5131zoom_update_scene_client(Client *c)
5132{
5133 Monitor *m = c->mon;
5134 double s;
5135 int gw, gh, w, h, ib, ob, ib_s, ob_s, bw_pos;
5136
5137 if (!m || !client_surface(c)->mapped)
5138 return;
5139
5140 s = m->zoom_scale;
5141 w = c->geom.width;
5142 h = c->geom.height;
5143 ib = c->ibw;
5144 ob = c->obw;
5145
5146 if (c->isfullscreen) {
5147 wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y);
5148 wlr_scene_node_set_position(&c->scene_surface->node, c->bw, c->bw);
5149 wlr_scene_rect_set_size(c->inner_border[0], MAX(1, w - 2*ob), MAX(1, ib));
5150 wlr_scene_node_set_position(&c->inner_border[0]->node, ob, ob);
5151 wlr_scene_rect_set_size(c->inner_border[1], MAX(1, w - 2*ob), MAX(1, ib));
5152 wlr_scene_node_set_position(&c->inner_border[1]->node, ob, MAX(0, h - ob - ib));
5153 wlr_scene_rect_set_size(c->inner_border[2], MAX(1, ib), MAX(1, h - 2*ob));
5154 wlr_scene_node_set_position(&c->inner_border[2]->node, ob, ob);
5155 wlr_scene_rect_set_size(c->inner_border[3], MAX(1, ib), MAX(1, h - 2*ob));
5156 wlr_scene_node_set_position(&c->inner_border[3]->node, MAX(0, w - ob - ib), ob);
5157 wlr_scene_rect_set_size(c->outer_border[0], MAX(1, w), MAX(1, ob));
5158 wlr_scene_node_set_position(&c->outer_border[0]->node, 0, 0);
5159 wlr_scene_rect_set_size(c->outer_border[1], MAX(1, w), MAX(1, ob));
5160 wlr_scene_node_set_position(&c->outer_border[1]->node, 0, MAX(0, h - ob));
5161 wlr_scene_rect_set_size(c->outer_border[2], MAX(1, ob), MAX(1, h - 2*ob));
5162 wlr_scene_node_set_position(&c->outer_border[2]->node, 0, ob);
5163 wlr_scene_rect_set_size(c->outer_border[3], MAX(1, ob), MAX(1, h - 2*ob));
5164 wlr_scene_node_set_position(&c->outer_border[3]->node, MAX(0, w - ob), ob);
5165 return;
5166 }
5167
5168 gw = MAX(1, (int)llround((double)w * s));
5169 gh = MAX(1, (int)llround((double)h * s));
5170 bw_pos = (int)llround((double)c->bw * s);
5171 ib_s = (int)llround((double)ib * s);
5172 ob_s = (int)llround((double)ob * s);
5173
5174 wlr_scene_node_set_position(&c->scene->node,
5175 (int)llround((double)c->geom.x * s + m->zoom_pan_x),
5176 (int)llround((double)c->geom.y * s + m->zoom_pan_y));
5177 wlr_scene_node_set_position(&c->scene_surface->node, bw_pos, bw_pos);
5178
5179 wlr_scene_rect_set_size(c->inner_border[0], MAX(0, gw - 2*ob_s), ib_s);
5180 wlr_scene_node_set_position(&c->inner_border[0]->node, ob_s, ob_s);
5181 wlr_scene_rect_set_size(c->inner_border[1], MAX(1, gw - 2*ob_s), ib_s);
5182 wlr_scene_node_set_position(&c->inner_border[1]->node, ob_s, MAX(0, gh - ob_s - ib_s));
5183 wlr_scene_rect_set_size(c->inner_border[2], ib_s, MAX(0, gh - 2*ob_s));
5184 wlr_scene_node_set_position(&c->inner_border[2]->node, ob_s, ob_s);
5185 wlr_scene_rect_set_size(c->inner_border[3], ib_s, MAX(1, gh - 2*ob_s));
5186 wlr_scene_node_set_position(&c->inner_border[3]->node, MAX(0, gw - ob_s - ib_s), ob_s);
5187 wlr_scene_rect_set_size(c->outer_border[0], gw, ob_s);
5188 wlr_scene_node_set_position(&c->outer_border[0]->node, 0, 0);
5189 wlr_scene_rect_set_size(c->outer_border[1], gw, ob_s);
5190 wlr_scene_node_set_position(&c->outer_border[1]->node, 0, MAX(0, gh - ob_s));
5191 wlr_scene_rect_set_size(c->outer_border[2], ob_s, MAX(0, gh - 2*ob_s));
5192 wlr_scene_node_set_position(&c->outer_border[2]->node, 0, ob_s);
5193 wlr_scene_rect_set_size(c->outer_border[3], ob_s, MAX(1, gh - 2*ob_s));
5194 wlr_scene_node_set_position(&c->outer_border[3]->node, MAX(0, gw - ob_s), ob_s);
5195
5196 /* touch dest_size only when zoom isnt 1 to avoid glitch in gpu apps */
5197 if (fabs(s - 1.0) >= 1e-9)
5198 wlr_scene_node_for_each_buffer(&c->scene->node, zoom_scale_buffers, c);
5199}
5200
5201void
5202zoom_apply(Monitor *m)
5203{
5204 Client *c;
5205 PopupZoomData *pzd;
5206
5207 wl_list_for_each(c, &clients, link) {
5208 if (c->mon == m && client_surface(c)->mapped)
5209 zoom_update_scene_client(c);
5210 }
5211
5212 wl_list_for_each(pzd, &popup_zoom_list, link) {
5213 if (pzd->client && pzd->client->mon == m)
5214 popup_zoom_commit(&pzd->commit, NULL);
5215 }
5216
5217 /* zoom */
5218 if (m->zoom_popups_dirty && m->zoom_anim_t >= 1.0) {
5219 double inv_s = 1.0 / m->zoom_scale;
5220 double vis_x = -m->zoom_pan_x * inv_s;
5221 double vis_y = -m->zoom_pan_y * inv_s;
5222 double vis_w = (double)m->m.width * inv_s;
5223 double vis_h = (double)m->m.height * inv_s;
5224 wl_list_for_each(pzd, &popup_zoom_list, link) {
5225 Client *pc;
5226 struct wlr_box pbox;
5227 if (!pzd->client || pzd->client->mon != m)
5228 continue;
5229 pc = pzd->client;
5230 pbox.x = (int)llround(vis_x) - pc->geom.x;
5231 pbox.y = (int)llround(vis_y) - pc->geom.y;
5232 pbox.width = MAX(1, (int)llround(vis_w));
5233 pbox.height = MAX(1, (int)llround(vis_h));
5234 wlr_xdg_popup_unconstrain_from_box(pzd->popup, &pbox);
5235 }
5236 m->zoom_popups_dirty = 0;
5237 }
5238 /* zoom end */
5239}
5240
5241void
5242zoom_frame(Monitor *m)
5243{
5244 double t, u, target_s, end_pan_x, end_pan_y;
5245 double bx1, by1, bx2, by2;
5246 double cx, bx, ax, cy, by, ay, s;
5247 int i;
5248
5249 if (!m->lt[m->sellt] || m->lt[m->sellt]->arrange != NULL)
5250 return;
5251 if (m->zoom_anim_t >= 1.0)
5252 return;
5253
5254 sscanf(zoombezier, "%lf,%lf,%lf,%lf", &bx1, &by1, &bx2, &by2);
5255
5256 target_s = m->zoom_target;
5257 end_pan_x = m->zoom_anchor_fx - m->zoom_anchor_lx * target_s;
5258 end_pan_y = m->zoom_anchor_fy - m->zoom_anchor_ly * target_s;
5259
5260 /* zoom */
5261 m->zoom_anim_t += 1.0 / (zoomduration *
5262 ((double)m->wlr_output->current_mode->refresh / 1000.0));
5263 /* zoom end */
5264 if (m->zoom_anim_t > 1.0)
5265 m->zoom_anim_t = 1.0;
5266
5267 t = m->zoom_anim_t;
5268
5269 cx = 3.0 * bx1;
5270 bx = 3.0 * (bx2 - bx1) - cx;
5271 ax = 1.0 - cx - bx;
5272 cy = 3.0 * by1;
5273 by = 3.0 * (by2 - by1) - cy;
5274 ay = 1.0 - cy - by;
5275
5276 s = t;
5277 for (i = 0; i < 8; i++) {
5278 double xs = ((ax * s + bx) * s + cx) * s;
5279 double dxs = (3.0 * ax * s + 2.0 * bx) * s + cx;
5280 if (fabs(dxs) < 1e-9) break;
5281 s -= (xs - t) / dxs;
5282 if (s < 0.0) s = 0.0;
5283 if (s > 1.0) s = 1.0;
5284 }
5285 u = ((ay * s + by) * s + cy) * s;
5286
5287 if (m->zoom_anim_t >= 1.0) {
5288 m->zoom_scale = target_s;
5289 m->zoom_pan_x = end_pan_x;
5290 m->zoom_pan_y = end_pan_y;
5291 } else {
5292 m->zoom_scale = m->zoom_start_scale + (target_s - m->zoom_start_scale) * u;
5293 m->zoom_pan_x = m->zoom_start_pan_x + (end_pan_x - m->zoom_start_pan_x) * u;
5294 m->zoom_pan_y = m->zoom_start_pan_y + (end_pan_y - m->zoom_start_pan_y) * u;
5295 }
5296
5297 zoom_apply(m);
5298
5299 if (m->zoom_anim_t < 1.0)
5300 wlr_output_schedule_frame(m->wlr_output);
5301}
5302
5303void
5304zoom(const Arg *arg)
5305{
5306 double old_s, delta, new_s, fx, fy;
5307 if (!selmon || selmon->lt[selmon->sellt]->arrange != NULL)
5308 return;
5309
5310 old_s = selmon->zoom_scale;
5311 delta = arg ? arg->f : 0.0;
5312 new_s = old_s + delta;
5313
5314 if (new_s < ZOOMMIN) new_s = ZOOMMIN;
5315 if (new_s > ZOOMMAX) new_s = ZOOMMAX;
5316
5317 fx = selmon->w.x + selmon->w.width / 2.0;
5318 fy = selmon->w.y + selmon->w.height / 2.0;
5319
5320 selmon->zoom_anchor_fx = fx;
5321 selmon->zoom_anchor_fy = fy;
5322 selmon->zoom_anchor_lx = (fx - selmon->zoom_pan_x) / old_s;
5323 selmon->zoom_anchor_ly = (fy - selmon->zoom_pan_y) / old_s;
5324 selmon->zoom_target = new_s;
5325
5326 if (!animatezoom) {
5327 selmon->zoom_scale = new_s;
5328 selmon->zoom_pan_x = fx - selmon->zoom_anchor_lx * new_s;
5329 selmon->zoom_pan_y = fy - selmon->zoom_anchor_ly * new_s;
5330 /* zoom */
5331 selmon->zoom_anim_t = 1.0;
5332 selmon->zoom_popups_dirty = 1;
5333 /* zoom end */
5334 zoom_apply(selmon);
5335 } else {
5336 selmon->zoom_start_scale = old_s;
5337 selmon->zoom_start_pan_x = selmon->zoom_pan_x;
5338 selmon->zoom_start_pan_y = selmon->zoom_pan_y;
5339 /* zoom */
5340 selmon->zoom_anim_t = 0.0;
5341 selmon->zoom_popups_dirty = 1;
5342 /* zoom end */
5343 wlr_output_schedule_frame(selmon->wlr_output);
5344 }
5345}
5346
5347void
5348zoomreset(const Arg *arg)
5349{
5350 double old_s, fx, fy;
5351
5352 if (!selmon || selmon->lt[selmon->sellt]->arrange != NULL)
5353 return;
5354
5355 old_s = selmon->zoom_scale;
5356 fx = selmon->w.x + selmon->w.width / 2.0;
5357 fy = selmon->w.y + selmon->w.height / 2.0;
5358
5359 selmon->zoom_anchor_fx = fx;
5360 selmon->zoom_anchor_fy = fy;
5361 selmon->zoom_anchor_lx = (fx - selmon->zoom_pan_x) / old_s;
5362 selmon->zoom_anchor_ly = (fy - selmon->zoom_pan_y) / old_s;
5363 selmon->zoom_target = 1.0;
5364
5365 if (!animatezoom) {
5366 selmon->zoom_scale = 1.0;
5367 selmon->zoom_pan_x = fx - selmon->zoom_anchor_lx * 1.0;
5368 selmon->zoom_pan_y = fy - selmon->zoom_anchor_ly * 1.0;
5369 /* zoom */
5370 selmon->zoom_anim_t = 1.0;
5371 selmon->zoom_popups_dirty = 1;
5372 /* zoom end */
5373 zoom_apply(selmon);
5374 } else {
5375 selmon->zoom_start_scale = old_s;
5376 selmon->zoom_start_pan_x = selmon->zoom_pan_x;
5377 selmon->zoom_start_pan_y = selmon->zoom_pan_y;
5378 /* zoom */
5379 selmon->zoom_anim_t = 0.0;
5380 selmon->zoom_popups_dirty = 1;
5381 /* zoom end */
5382 wlr_output_schedule_frame(selmon->wlr_output);
5383 }
5384}
5385
5386/* zoom end */
5387
5388void
5389spawnregion_update_rect(void)
5390{
5391 int x, y, w, h;
5392 x = (int)MIN(spawnregion_x1, spawnregion_x2);
5393 y = (int)MIN(spawnregion_y1, spawnregion_y2);
5394 w = (int)fabs(spawnregion_x2 - spawnregion_x1);
5395 h = (int)fabs(spawnregion_y2 - spawnregion_y1);
5396 if (w < 1) w = 1;
5397 if (h < 1) h = 1;
5398#if SPAWNREGION_STYLE == 2
5399 {
5400 int ob = (int)spawnregion_outerborderpx;
5401 int ib = (int)spawnregion_innerborderpx;
5402 int side_h = MAX(0, h - 2 * ib);
5403 int i;
5404 if (!spawnregion_border[0]) return;
5405 for (i = 0; i < 8; i++)
5406 wlr_scene_node_set_enabled(&spawnregion_border[i]->node, 1);
5407 wlr_scene_node_set_position(&spawnregion_border[0]->node, x - ob, y - ob);
5408 wlr_scene_rect_set_size(spawnregion_border[0], w + 2 * ob, ob);
5409 wlr_scene_node_set_position(&spawnregion_border[1]->node, x - ob, y + h);
5410 wlr_scene_rect_set_size(spawnregion_border[1], w + 2 * ob, ob);
5411 wlr_scene_node_set_position(&spawnregion_border[2]->node, x - ob, y);
5412 wlr_scene_rect_set_size(spawnregion_border[2], ob, h);
5413 wlr_scene_node_set_position(&spawnregion_border[3]->node, x + w, y);
5414 wlr_scene_rect_set_size(spawnregion_border[3], ob, h);
5415 wlr_scene_node_set_position(&spawnregion_border[4]->node, x, y);
5416 wlr_scene_rect_set_size(spawnregion_border[4], w, ib);
5417 wlr_scene_node_set_position(&spawnregion_border[5]->node, x, y + h - ib);
5418 wlr_scene_rect_set_size(spawnregion_border[5], w, ib);
5419 wlr_scene_node_set_position(&spawnregion_border[6]->node, x, y + ib);
5420 wlr_scene_rect_set_size(spawnregion_border[6], ib, side_h);
5421 wlr_scene_node_set_position(&spawnregion_border[7]->node, x + w - ib, y + ib);
5422 wlr_scene_rect_set_size(spawnregion_border[7], ib, side_h);
5423 }
5424#else
5425 if (!spawnregion_rect)
5426 return;
5427 wlr_scene_node_set_position(&spawnregion_rect->node, x, y);
5428 wlr_scene_rect_set_size(spawnregion_rect, w, h);
5429#endif
5430}
5431
5432void
5433spawnregion_finish(void)
5434{
5435 int x, y, w, h;
5436
5437 if (spawnregion_rect)
5438 wlr_scene_node_set_enabled(&spawnregion_rect->node, 0);
5439#if SPAWNREGION_STYLE == 2
5440 {
5441 int i;
5442 for (i = 0; i < 8; i++)
5443 if (spawnregion_border[i])
5444 wlr_scene_node_set_enabled(&spawnregion_border[i]->node, 0);
5445 }
5446#endif
5447
5448 spawnregion_active = 0;
5449 cursor_mode = CurNormal;
5450 wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
5451
5452 /* zoom */
5453 if (selmon && selmon->lt[selmon->sellt]->arrange == NULL) {
5454 double s = selmon->zoom_scale;
5455 if (fabs(s) >= 1e-9 && fabs(s - 1.0) >= 1e-9) {
5456 double lx1 = (spawnregion_x1 - selmon->zoom_pan_x) / s;
5457 double ly1 = (spawnregion_y1 - selmon->zoom_pan_y) / s;
5458 double lx2 = (spawnregion_x2 - selmon->zoom_pan_x) / s;
5459 double ly2 = (spawnregion_y2 - selmon->zoom_pan_y) / s;
5460 x = (int)MIN(lx1, lx2);
5461 y = (int)MIN(ly1, ly2);
5462 w = (int)fabs(lx2 - lx1);
5463 h = (int)fabs(ly2 - ly1);
5464 goto coords_done;
5465 }
5466 }
5467 /* zoom end */
5468 x = (int)MIN(spawnregion_x1, spawnregion_x2);
5469 y = (int)MIN(spawnregion_y1, spawnregion_y2);
5470 w = (int)fabs(spawnregion_x2 - spawnregion_x1);
5471 h = (int)fabs(spawnregion_y2 - spawnregion_y1);
5472 /* zoom */
5473coords_done:
5474 /* zoom end */
5475
5476 if (w < 10 || h < 10) {
5477 if (waiting_geo_client) {
5478 wlr_scene_node_set_enabled(&waiting_geo_client->scene->node, 1);
5479 focusclient(waiting_geo_client, 1);
5480 waiting_geo_client = NULL;
5481 }
5482 return;
5483 }
5484
5485 pending_spawn_geo.x = x;
5486 pending_spawn_geo.y = y;
5487 pending_spawn_geo.width = w;
5488 pending_spawn_geo.height = h;
5489 has_pending_spawn_geo = 1;
5490
5491 if (waiting_geo_client) {
5492 if (selmon)
5493 waiting_geo_client->tags = selmon->tagset[selmon->seltags];
5494 waiting_geo_client->isfloating = 1;
5495 resize(waiting_geo_client, pending_spawn_geo, 0);
5496 wlr_scene_node_set_enabled(&waiting_geo_client->scene->node, 1);
5497 focusclient(waiting_geo_client, 1);
5498
5499 waiting_geo_client = NULL;
5500 has_pending_spawn_geo = 0;
5501 }
5502}
5503
5504void
5505spawnregion(const Arg *arg)
5506{
5507 if (!selmon || selmon->lt[selmon->sellt]->arrange != NULL)
5508 return;
5509 if (cursor_mode != CurNormal && cursor_mode != CurPressed)
5510 return;
5511 if (!waiting_geo_client)
5512 return;
5513
5514 spawnregion_active = 1;
5515 has_pending_spawn_geo = 0;
5516 wlr_cursor_set_xcursor(cursor, cursor_mgr, "crosshair");
5517}
5518
5519/* spawnregion_end */
5520
5521/*
5522 gaps are from this patch:
5523 https://codeberg.org/dwl/dwl-patches/src/branch/main/patches/genericgaps/genericgaps-0.7.patch
5524*/
5525
5526#ifdef TILING
5527void
5528defaultgaps(const Arg *arg)
5529{
5530 setgaps(gappoh, gappov, gappih, gappiv);
5531}
5532
5533void
5534incgaps(const Arg *arg)
5535{
5536 setgaps(
5537 selmon->gappoh + arg->i,
5538 selmon->gappov + arg->i,
5539 selmon->gappih + arg->i,
5540 selmon->gappiv + arg->i
5541 );
5542}
5543
5544void
5545incigaps(const Arg *arg)
5546{
5547 setgaps(
5548 selmon->gappoh,
5549 selmon->gappov,
5550 selmon->gappih + arg->i,
5551 selmon->gappiv + arg->i
5552 );
5553}
5554
5555void
5556incihgaps(const Arg *arg)
5557{
5558 setgaps(
5559 selmon->gappoh,
5560 selmon->gappov,
5561 selmon->gappih + arg->i,
5562 selmon->gappiv
5563 );
5564}
5565
5566void
5567incivgaps(const Arg *arg)
5568{
5569 setgaps(
5570 selmon->gappoh,
5571 selmon->gappov,
5572 selmon->gappih,
5573 selmon->gappiv + arg->i
5574 );
5575}
5576
5577void
5578incogaps(const Arg *arg)
5579{
5580 setgaps(
5581 selmon->gappoh + arg->i,
5582 selmon->gappov + arg->i,
5583 selmon->gappih,
5584 selmon->gappiv
5585 );
5586}
5587
5588void
5589incohgaps(const Arg *arg)
5590{
5591 setgaps(
5592 selmon->gappoh + arg->i,
5593 selmon->gappov,
5594 selmon->gappih,
5595 selmon->gappiv
5596 );
5597}
5598
5599void
5600incovgaps(const Arg *arg)
5601{
5602 setgaps(
5603 selmon->gappoh,
5604 selmon->gappov + arg->i,
5605 selmon->gappih,
5606 selmon->gappiv
5607 );
5608}
5609
5610void
5611preparegaps(Monitor *m)
5612{
5613 Client *c;
5614 int n;
5615
5616 if (!m->enablegaps)
5617 return;
5618
5619 n = 0;
5620 wl_list_for_each(c, &clients, link) {
5621 if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
5622 continue;
5623 n++;
5624 }
5625
5626 resizelock = 1;
5627
5628 if ((smartgaps && n == 1) || (!monoclegaps && m->lt[m->sellt]->arrange == monocle))
5629 return;
5630
5631 m->w.width -= m->gappih + 2 * m->gappoh;
5632 m->w.height -= m->gappiv + 2 * m->gappov;
5633}
5634
5635void
5636setgaps(int oh, int ov, int ih, int iv)
5637{
5638 selmon->gappoh = MAX(oh, 0);
5639 selmon->gappov = MAX(ov, 0);
5640 selmon->gappih = MAX(ih, 0);
5641 selmon->gappiv = MAX(iv, 0);
5642 arrange(selmon);
5643}
5644
5645void
5646togglegaps(const Arg *arg)
5647{
5648 if (!selmon)
5649 return;
5650 selmon->enablegaps = !selmon->enablegaps;
5651 arrange(selmon);
5652}
5653
5654void
5655arrangegaps(Monitor *m)
5656{
5657 Client *c;
5658 int n, gaps;
5659
5660 if (!m->enablegaps)
5661 return;
5662
5663 resizelock = 0;
5664
5665 n = 0;
5666 wl_list_for_each(c, &clients, link) {
5667 if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
5668 continue;
5669 n++;
5670 }
5671
5672 gaps = !(smartgaps && n == 1) &&
5673 (monoclegaps || m->lt[m->sellt]->arrange != monocle);
5674 if (gaps) {
5675 m->w.width += m->gappih + 2 * m->gappoh;
5676 m->w.height += m->gappiv + 2 * m->gappov;
5677 }
5678 wl_list_for_each(c, &clients, link) {
5679 if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
5680 continue;
5681 if (gaps) {
5682 c->geom.x += c->mon->gappih + c->mon->gappoh;
5683 c->geom.y += c->mon->gappiv + c->mon->gappov;
5684 c->geom.width -= c->mon->gappih;
5685 c->geom.height -= c->mon->gappiv;
5686 }
5687 resize(c, c->geom, 0);
5688 }
5689}
5690#endif // why would you need gaps without tiling?
5691
5692/* tablet-input is from this patch:
5693 https://codeberg.org/dwl/dwl-patches/src/branch/main/patches/tablet-input/tablet-input-0.8.patch
5694*/
5695
5696void
5697createtablet(struct wlr_input_device *device)
5698{
5699 if (!tablet) {
5700 struct libinput_device *device_handle = NULL;
5701 if (!wlr_input_device_is_libinput(device) ||
5702 !(device_handle = wlr_libinput_get_device_handle(device)))
5703 return;
5704 tablet = wlr_tablet_create(tablet_mgr, seat, device);
5705 wl_signal_add(&tablet->wlr_device->events.destroy, &tablet_device_destroy);
5706 if (libinput_device_config_send_events_get_modes(device_handle)) {
5707 libinput_device_config_send_events_set_mode(device_handle, send_events_mode);
5708 wlr_cursor_attach_input_device(cursor, device);
5709 }
5710 } else if (device == tablet->wlr_device) {
5711 wlr_log(WLR_ERROR, "createtablet: duplicate device");
5712 } else {
5713 wlr_log(WLR_ERROR, "createtablet: already have one tablet");
5714 }
5715}
5716
5717void
5718destroytablet(struct wl_listener *listener, void *data)
5719{
5720 wl_list_remove(&tablet_device_destroy.link);
5721 wlr_cursor_detach_input_device(cursor, tablet->wlr_device);
5722 tablet = NULL;
5723}
5724
5725void
5726destroytabletpad(struct wl_listener *listener, void *data)
5727{
5728 wl_list_remove(&tablet_pad_button.link);
5729 wl_list_remove(&tablet_pad_destroy.link);
5730 tablet_pad = NULL;
5731}
5732
5733void
5734destroytabletsurfacenotify(struct wl_listener *listener, void *data)
5735{
5736 if (tablet_curr_surface)
5737 wl_list_remove(&destroy_tablet_surface_listener.link);
5738 tablet_curr_surface = NULL;
5739}
5740
5741void
5742destroytablettool(struct wl_listener *listener, void *data)
5743{
5744 destroytabletsurfacenotify(NULL, NULL);
5745 tablet_tool = NULL;
5746}
5747
5748void
5749tabletapplymap(double x, double y, struct wlr_input_device *dev)
5750{
5751 Client *p;
5752 struct wlr_box geom = {0};
5753 if (tabletmaptosurface && tablet_curr_surface) {
5754 toplevel_from_wlr_surface(tablet_curr_surface, &p, NULL);
5755 if (p) {
5756 for (; client_get_parent(p); p = client_get_parent(p));
5757 geom.x = p->geom.x + p->bw;
5758 geom.y = p->geom.y + p->bw;
5759 geom.width = p->geom.width - 2 * p->bw;
5760 geom.height = p->geom.height - 2 * p->bw;
5761 wlr_cursor_map_input_to_region(cursor, dev, &geom);
5762 }
5763 }
5764 wlr_cursor_map_input_to_output(cursor, dev, selmon->wlr_output);
5765}
5766
5767void
5768tabletpadbutton(struct wl_listener *listener, void *data)
5769{
5770 struct wlr_tablet_pad_button_event *event = data;
5771 const TabletPadButton *pb;
5772
5773 for (pb = tabletpadbuttons; pb < END(tabletpadbuttons); pb++) {
5774 if (event->button == pb->button
5775 && event->state == pb->state
5776 && pb->func) {
5777 pb->func(&pb->arg);
5778 }
5779 }
5780
5781 if (tablet_pad)
5782 wlr_tablet_v2_tablet_pad_notify_button(tablet_pad,
5783 event->button, event->time_msec,
5784 (enum zwp_tablet_pad_v2_button_state)event->state);
5785}
5786
5787void
5788tablettoolmotion(struct wlr_tablet_v2_tablet_tool *tool, bool change_x, bool change_y,
5789 double x, double y, double dx, double dy)
5790{
5791 struct wlr_surface *surface = NULL;
5792 double sx, sy;
5793
5794 if (!change_x && !change_y)
5795 return;
5796
5797 tabletapplymap(x, y, tablet->wlr_device);
5798
5799 x = (MAX(tablet_area[0], MIN(tablet_area[0] + tablet_area[2], x)) - tablet_area[0]) / tablet_area[2];
5800 y = (MAX(tablet_area[1], MIN(tablet_area[1] + tablet_area[3], y)) - tablet_area[1]) / tablet_area[3];
5801
5802
5803 // TODO: apply constraints
5804 switch (tablet_tool->wlr_tool->type) {
5805 case WLR_TABLET_TOOL_TYPE_LENS:
5806 case WLR_TABLET_TOOL_TYPE_MOUSE:
5807 wlr_cursor_move(cursor, tablet->wlr_device, dx, dy);
5808 break;
5809 default:
5810 wlr_cursor_warp_absolute(cursor, tablet->wlr_device, change_x ? x : NAN, change_y ? y : NAN);
5811 break;
5812 }
5813
5814 motionnotify(0, NULL, 0, 0, 0, 0);
5815
5816 xytonode(cursor->x, cursor->y, &surface, NULL, NULL, &sx, &sy);
5817 if (surface && !wlr_surface_accepts_tablet_v2(surface, tablet))
5818 surface = NULL;
5819
5820 if (surface != tablet_curr_surface) {
5821 if (tablet_curr_surface) {
5822 // TODO: wait until all buttons released before leaving
5823 if (tablet_tool)
5824 wlr_tablet_v2_tablet_tool_notify_proximity_out(tablet_tool);
5825 if (tablet_pad)
5826 wlr_tablet_v2_tablet_pad_notify_leave(tablet_pad, tablet_curr_surface);
5827 wl_list_remove(&destroy_tablet_surface_listener.link);
5828 }
5829 if (surface) {
5830 if (tablet_pad)
5831 wlr_tablet_v2_tablet_pad_notify_enter(tablet_pad, tablet, surface);
5832 if (tablet_tool)
5833 wlr_tablet_v2_tablet_tool_notify_proximity_in(tablet_tool, tablet, surface);
5834 wl_signal_add(&surface->events.destroy, &destroy_tablet_surface_listener);
5835 }
5836 tablet_curr_surface = surface;
5837 }
5838
5839 if (surface)
5840 wlr_tablet_v2_tablet_tool_notify_motion(tablet_tool, sx, sy);
5841}
5842
5843void
5844tablettoolproximity(struct wl_listener *listener, void *data)
5845{
5846 struct wlr_tablet_tool_proximity_event *event = data;
5847 struct wlr_tablet_tool *tool = event->tool;
5848
5849 if (!tablet_tool) {
5850 tablet_tool = wlr_tablet_tool_create(tablet_mgr, seat, tool);
5851 wl_signal_add(&tablet_tool->wlr_tool->events.destroy, &tablet_tool_destroy);
5852 wl_signal_add(&tablet_tool->events.set_cursor, &request_cursor);
5853 }
5854
5855 switch (event->state) {
5856 case WLR_TABLET_TOOL_PROXIMITY_OUT:
5857 wlr_tablet_v2_tablet_tool_notify_proximity_out(tablet_tool);
5858 destroytabletsurfacenotify(NULL, NULL);
5859 break;
5860 case WLR_TABLET_TOOL_PROXIMITY_IN:
5861 tablettoolmotion(tablet_tool, true, true, event->x, event->y, 0, 0);
5862 break;
5863 }
5864}
5865
5866double tilt_x = 0;
5867double tilt_y = 0;
5868
5869void
5870tablettoolaxis(struct wl_listener *listener, void *data)
5871{
5872 struct wlr_tablet_tool_axis_event *event = data;
5873
5874 tablettoolmotion(tablet_tool,
5875 event->updated_axes & WLR_TABLET_TOOL_AXIS_X,
5876 event->updated_axes & WLR_TABLET_TOOL_AXIS_Y,
5877 event->x, event->y, event->dx, event->dy);
5878
5879 if (event->updated_axes & WLR_TABLET_TOOL_AXIS_PRESSURE)
5880 wlr_tablet_v2_tablet_tool_notify_pressure(tablet_tool, event->pressure);
5881 if (event->updated_axes & WLR_TABLET_TOOL_AXIS_DISTANCE)
5882 wlr_tablet_v2_tablet_tool_notify_distance(tablet_tool, event->distance);
5883 if (event->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_X)
5884 tilt_x = event->tilt_x;
5885 if (event->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_Y)
5886 tilt_y = event->tilt_y;
5887 if (event->updated_axes & (WLR_TABLET_TOOL_AXIS_TILT_X | WLR_TABLET_TOOL_AXIS_TILT_Y))
5888 wlr_tablet_v2_tablet_tool_notify_tilt(tablet_tool, tilt_x, tilt_y);
5889 if (event->updated_axes & WLR_TABLET_TOOL_AXIS_ROTATION)
5890 wlr_tablet_v2_tablet_tool_notify_rotation(tablet_tool, event->rotation);
5891 if (event->updated_axes & WLR_TABLET_TOOL_AXIS_SLIDER)
5892 wlr_tablet_v2_tablet_tool_notify_slider(tablet_tool, event->slider);
5893 if (event->updated_axes & WLR_TABLET_TOOL_AXIS_WHEEL)
5894 wlr_tablet_v2_tablet_tool_notify_wheel(tablet_tool, event->wheel_delta, 0);
5895}
5896
5897void
5898tablettoolbutton(struct wl_listener *listener, void *data)
5899{
5900 struct wlr_tablet_tool_button_event *event = data;
5901 const TabletButton *tb;
5902
5903 for (tb = tabletbuttons; tb < END(tabletbuttons); tb++) {
5904 if (event->button == tb->button
5905 && event->state == tb->state
5906 && tb->func) {
5907 tb->func(&tb->arg);
5908 }
5909 }
5910
5911 wlr_tablet_v2_tablet_tool_notify_button(tablet_tool, event->button,
5912 (enum zwp_tablet_pad_v2_button_state)event->state);
5913}
5914
5915void
5916tablettooltip(struct wl_listener *listener, void *data)
5917{
5918 struct wlr_tablet_tool_tip_event *event = data;
5919
5920 if (!tablet_curr_surface) {
5921 struct wlr_pointer_button_event fakeptrbtnevent = {
5922 .button = BTN_LEFT,
5923 .state = event->state == WLR_TABLET_TOOL_TIP_UP ?
5924 WL_POINTER_BUTTON_STATE_RELEASED : WL_POINTER_BUTTON_STATE_PRESSED,
5925 .time_msec = event->time_msec,
5926 };
5927 buttonpress(NULL, (void *)&fakeptrbtnevent);
5928 }
5929
5930 if (event->state == WLR_TABLET_TOOL_TIP_UP) {
5931 wlr_tablet_v2_tablet_tool_notify_up(tablet_tool);
5932 return;
5933 }
5934
5935 wlr_tablet_v2_tablet_tool_notify_down(tablet_tool);
5936 wlr_tablet_tool_v2_start_implicit_grab(tablet_tool);
5937}
5938
5939/* tablet-input end */
5940int
5941main(int argc, char *argv[])
5942{
5943 char *startup_cmd = NULL;
5944 int c;
5945
5946 while ((c = getopt(argc, argv, "s:hdv")) != -1) {
5947 if (c == 's')
5948 startup_cmd = optarg;
5949 else if (c == 'd')
5950 log_level = WLR_DEBUG;
5951 else if (c == 'v')
5952 die("leeloo " VERSION);
5953 else
5954 goto usage;
5955 }
5956 if (optind < argc)
5957 goto usage;
5958
5959 /* Wayland requires XDG_RUNTIME_DIR for creating its communications socket */
5960 if (!getenv("XDG_RUNTIME_DIR"))
5961 die("XDG_RUNTIME_DIR must be set");
5962 setup();
5963 run(startup_cmd);
5964 cleanup();
5965 return EXIT_SUCCESS;
5966
5967usage:
5968 die("Usage: %s [-v] [-d] [-s startup command]", argv[0]);
5969}