master ohmu/leeloo / src / leeloo.c
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}