diff --git a/config.h b/config.h index 00941b2..2c34d97 100644 --- a/config.h +++ b/config.h @@ -13,8 +13,6 @@ static const float focuscolor[] = COLOR(0x005577ff); static const float urgentcolor[] = COLOR(0xff0000ff); /* This conforms to the xdg-protocol. Set the alpha to zero to restore the old behavior */ static const float fullscreen_bg[] = {0.0f, 0.0f, 0.0f, 1.0f}; /* You can also use glsl colors */ -static int enableautoswallow = 1; /* enables autoswallowing newly spawned clients */ -static float swallowborder = 1.0f; /* add this multiplied by borderpx to border when a client is swallowed */ /* tagging - TAGCOUNT must be no greater than 31 */ #define TAGCOUNT (9) @@ -24,12 +22,14 @@ static int log_level = WLR_ERROR; /* NOTE: ALWAYS keep a rule declared even if you don't use rules (e.g leave at least one example) */ static const Rule rules[] = { - /* app_id title tags mask isfloating isterm noswallow monitor */ - { "EVE", NULL, 1 << 0, 0, 0, 0, -1 }, // Workspace 1 for EVE - { "QQ", NULL, 1 << 5, 0, 0, 0, -1 }, // Workspace 6 for QQ - { "discord", NULL, 1 << 5, 0, 0, 0, -1 }, // Workspace 6 for discord - { "foot", NULL, 1 << 3, 0, 1, 0, -1 }, // Workspace 4 for kitty - { "qutebrowser", NULL, 1 << 4, 0, 0, 0, -1 }, // Workspace 5 for firefox + /* app_id title tags mask isfloating monitor */ + /* examples: */ + { "EVE", NULL, 1 << 0, 0, -1 }, // Workspace 1 for EVE + { "QQ", NULL, 1 << 5, 0, -1 }, // Workspace 6 for QQ + { "discord", NULL, 1 << 5, 0, -1 }, // Workspace 6 for discord + { "foot", NULL, 1 << 3, 0, -1 }, // Workspace 4 for kitty + { "qutebrowser", NULL, 1 << 4, 0, -1 }, // Workspace 5 for firefox + { NULL, "图片查看器", 0, 1, -1 }, // Float the picture viewer by title }; /* layout(s) */ @@ -148,8 +148,6 @@ static const Key keys[] = { { MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[2]} }, { MODKEY, XKB_KEY_v, togglefloating, {0} }, { MODKEY, XKB_KEY_e, togglefullscreen, {0} }, - { MODKEY, XKB_KEY_a, toggleswallow, {0} }, - { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_A, toggleautoswallow,{0} }, { MODKEY, XKB_KEY_0, view, {.ui = ~0} }, { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} }, TAGKEYS( XKB_KEY_1, XKB_KEY_exclam, 0), diff --git a/flake.nix b/flake.nix index 40629a6..2d9e4ad 100644 --- a/flake.nix +++ b/flake.nix @@ -44,9 +44,7 @@ xorg.xcbutilwm # ✅ for xcb_ewmh.h ]; - patches = [ - ./patches/combined.patch - ]; + patches = [./patches/autostart.patch]; # The build and installation process buildPhase = '' diff --git a/patches/combined.patch b/patches/combined.patch deleted file mode 100644 index fea7646..0000000 --- a/patches/combined.patch +++ /dev/null @@ -1,4324 +0,0 @@ -diff --git a/client.h b/client.h -index d9f90bb..83e1c57 100644 ---- a/client.h -+++ b/client.h -@@ -131,6 +131,18 @@ client_get_appid(Client *c) - return c->surface.xdg->toplevel->app_id ? c->surface.xdg->toplevel->app_id : "broken"; - } - -+static inline int -+client_get_pid(Client *c) -+{ -+ pid_t pid; -+#ifdef XWAYLAND -+ if (client_is_x11(c)) -+ return c->surface.xwayland->pid; -+#endif -+ wl_client_get_credentials(c->surface.xdg->client->client, &pid, NULL, NULL); -+ return pid; -+} -+ - static inline void - client_get_clip(Client *c, struct wlr_box *clip) - { -diff --git a/client.h.orig b/client.h.orig -new file mode 100644 -index 0000000..d9f90bb ---- /dev/null -+++ b/client.h.orig -@@ -0,0 +1,404 @@ -+/* -+ * Attempt to consolidate unavoidable suck into one file, away from dwl.c. This -+ * file is not meant to be pretty. We use a .h file with static inline -+ * functions instead of a separate .c module, or function pointers like sway, so -+ * that they will simply compile out if the chosen #defines leave them unused. -+ */ -+ -+/* Leave these functions first; they're used in the others */ -+static inline int -+client_is_x11(Client *c) -+{ -+#ifdef XWAYLAND -+ return c->type == X11; -+#endif -+ return 0; -+} -+ -+static inline struct wlr_surface * -+client_surface(Client *c) -+{ -+#ifdef XWAYLAND -+ if (client_is_x11(c)) -+ return c->surface.xwayland->surface; -+#endif -+ return c->surface.xdg->surface; -+} -+ -+static inline int -+toplevel_from_wlr_surface(struct wlr_surface *s, Client **pc, LayerSurface **pl) -+{ -+ struct wlr_xdg_surface *xdg_surface, *tmp_xdg_surface; -+ struct wlr_surface *root_surface; -+ struct wlr_layer_surface_v1 *layer_surface; -+ Client *c = NULL; -+ LayerSurface *l = NULL; -+ int type = -1; -+#ifdef XWAYLAND -+ struct wlr_xwayland_surface *xsurface; -+#endif -+ -+ if (!s) -+ return -1; -+ root_surface = wlr_surface_get_root_surface(s); -+ -+#ifdef XWAYLAND -+ if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(root_surface))) { -+ c = xsurface->data; -+ type = c->type; -+ goto end; -+ } -+#endif -+ -+ if ((layer_surface = wlr_layer_surface_v1_try_from_wlr_surface(root_surface))) { -+ l = layer_surface->data; -+ type = LayerShell; -+ goto end; -+ } -+ -+ xdg_surface = wlr_xdg_surface_try_from_wlr_surface(root_surface); -+ while (xdg_surface) { -+ tmp_xdg_surface = NULL; -+ switch (xdg_surface->role) { -+ case WLR_XDG_SURFACE_ROLE_POPUP: -+ if (!xdg_surface->popup || !xdg_surface->popup->parent) -+ return -1; -+ -+ tmp_xdg_surface = wlr_xdg_surface_try_from_wlr_surface(xdg_surface->popup->parent); -+ -+ if (!tmp_xdg_surface) -+ return toplevel_from_wlr_surface(xdg_surface->popup->parent, pc, pl); -+ -+ xdg_surface = tmp_xdg_surface; -+ break; -+ case WLR_XDG_SURFACE_ROLE_TOPLEVEL: -+ c = xdg_surface->data; -+ type = c->type; -+ goto end; -+ case WLR_XDG_SURFACE_ROLE_NONE: -+ return -1; -+ } -+ } -+ -+end: -+ if (pl) -+ *pl = l; -+ if (pc) -+ *pc = c; -+ return type; -+} -+ -+/* The others */ -+static inline void -+client_activate_surface(struct wlr_surface *s, int activated) -+{ -+ struct wlr_xdg_toplevel *toplevel; -+#ifdef XWAYLAND -+ struct wlr_xwayland_surface *xsurface; -+ if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(s))) { -+ wlr_xwayland_surface_activate(xsurface, activated); -+ return; -+ } -+#endif -+ if ((toplevel = wlr_xdg_toplevel_try_from_wlr_surface(s))) -+ wlr_xdg_toplevel_set_activated(toplevel, activated); -+} -+ -+static inline uint32_t -+client_set_bounds(Client *c, int32_t width, int32_t height) -+{ -+#ifdef XWAYLAND -+ if (client_is_x11(c)) -+ return 0; -+#endif -+ if (wl_resource_get_version(c->surface.xdg->toplevel->resource) >= -+ XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION && width >= 0 && height >= 0 -+ && (c->bounds.width != width || c->bounds.height != height)) { -+ c->bounds.width = width; -+ c->bounds.height = height; -+ return wlr_xdg_toplevel_set_bounds(c->surface.xdg->toplevel, width, height); -+ } -+ return 0; -+} -+ -+static inline const char * -+client_get_appid(Client *c) -+{ -+#ifdef XWAYLAND -+ if (client_is_x11(c)) -+ return c->surface.xwayland->class ? c->surface.xwayland->class : "broken"; -+#endif -+ return c->surface.xdg->toplevel->app_id ? c->surface.xdg->toplevel->app_id : "broken"; -+} -+ -+static inline void -+client_get_clip(Client *c, struct wlr_box *clip) -+{ -+ *clip = (struct wlr_box){ -+ .x = 0, -+ .y = 0, -+ .width = c->geom.width - c->bw, -+ .height = c->geom.height - c->bw, -+ }; -+ -+#ifdef XWAYLAND -+ if (client_is_x11(c)) -+ return; -+#endif -+ -+ clip->x = c->surface.xdg->geometry.x; -+ clip->y = c->surface.xdg->geometry.y; -+} -+ -+static inline void -+client_get_geometry(Client *c, struct wlr_box *geom) -+{ -+#ifdef XWAYLAND -+ if (client_is_x11(c)) { -+ geom->x = c->surface.xwayland->x; -+ geom->y = c->surface.xwayland->y; -+ geom->width = c->surface.xwayland->width; -+ geom->height = c->surface.xwayland->height; -+ return; -+ } -+#endif -+ *geom = c->surface.xdg->geometry; -+} -+ -+static inline Client * -+client_get_parent(Client *c) -+{ -+ Client *p = NULL; -+#ifdef XWAYLAND -+ if (client_is_x11(c)) { -+ if (c->surface.xwayland->parent) -+ toplevel_from_wlr_surface(c->surface.xwayland->parent->surface, &p, NULL); -+ return p; -+ } -+#endif -+ if (c->surface.xdg->toplevel->parent) -+ toplevel_from_wlr_surface(c->surface.xdg->toplevel->parent->base->surface, &p, NULL); -+ return p; -+} -+ -+static inline int -+client_has_children(Client *c) -+{ -+#ifdef XWAYLAND -+ if (client_is_x11(c)) -+ return !wl_list_empty(&c->surface.xwayland->children); -+#endif -+ /* surface.xdg->link is never empty because it always contains at least the -+ * surface itself. */ -+ return wl_list_length(&c->surface.xdg->link) > 1; -+} -+ -+static inline const char * -+client_get_title(Client *c) -+{ -+#ifdef XWAYLAND -+ if (client_is_x11(c)) -+ return c->surface.xwayland->title ? c->surface.xwayland->title : "broken"; -+#endif -+ return c->surface.xdg->toplevel->title ? c->surface.xdg->toplevel->title : "broken"; -+} -+ -+static inline int -+client_is_float_type(Client *c) -+{ -+ struct wlr_xdg_toplevel *toplevel; -+ struct wlr_xdg_toplevel_state state; -+ -+#ifdef XWAYLAND -+ if (client_is_x11(c)) { -+ struct wlr_xwayland_surface *surface = c->surface.xwayland; -+ xcb_size_hints_t *size_hints = surface->size_hints; -+ if (surface->modal) -+ return 1; -+ -+ if (wlr_xwayland_surface_has_window_type(surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DIALOG) -+ || wlr_xwayland_surface_has_window_type(surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_SPLASH) -+ || wlr_xwayland_surface_has_window_type(surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_TOOLBAR) -+ || wlr_xwayland_surface_has_window_type(surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_UTILITY)) { -+ return 1; -+ } -+ -+ return size_hints && size_hints->min_width > 0 && size_hints->min_height > 0 -+ && (size_hints->max_width == size_hints->min_width -+ || size_hints->max_height == size_hints->min_height); -+ } -+#endif -+ -+ toplevel = c->surface.xdg->toplevel; -+ state = toplevel->current; -+ return toplevel->parent || (state.min_width != 0 && state.min_height != 0 -+ && (state.min_width == state.max_width -+ || state.min_height == state.max_height)); -+} -+ -+static inline int -+client_is_rendered_on_mon(Client *c, Monitor *m) -+{ -+ /* This is needed for when you don't want to check formal assignment, -+ * but rather actual displaying of the pixels. -+ * Usually VISIBLEON suffices and is also faster. */ -+ struct wlr_surface_output *s; -+ int unused_lx, unused_ly; -+ if (!wlr_scene_node_coords(&c->scene->node, &unused_lx, &unused_ly)) -+ return 0; -+ wl_list_for_each(s, &client_surface(c)->current_outputs, link) -+ if (s->output == m->wlr_output) -+ return 1; -+ return 0; -+} -+ -+static inline int -+client_is_stopped(Client *c) -+{ -+ int pid; -+ siginfo_t in = {0}; -+#ifdef XWAYLAND -+ if (client_is_x11(c)) -+ return 0; -+#endif -+ -+ wl_client_get_credentials(c->surface.xdg->client->client, &pid, NULL, NULL); -+ if (waitid(P_PID, pid, &in, WNOHANG|WCONTINUED|WSTOPPED|WNOWAIT) < 0) { -+ /* This process is not our child process, while is very unlikely that -+ * it is stopped, in order to do not skip frames, assume that it is. */ -+ if (errno == ECHILD) -+ return 1; -+ } else if (in.si_pid) { -+ if (in.si_code == CLD_STOPPED || in.si_code == CLD_TRAPPED) -+ return 1; -+ if (in.si_code == CLD_CONTINUED) -+ return 0; -+ } -+ -+ return 0; -+} -+ -+static inline int -+client_is_unmanaged(Client *c) -+{ -+#ifdef XWAYLAND -+ if (client_is_x11(c)) -+ return c->surface.xwayland->override_redirect; -+#endif -+ return 0; -+} -+ -+static inline void -+client_notify_enter(struct wlr_surface *s, struct wlr_keyboard *kb) -+{ -+ if (kb) -+ wlr_seat_keyboard_notify_enter(seat, s, kb->keycodes, -+ kb->num_keycodes, &kb->modifiers); -+ else -+ wlr_seat_keyboard_notify_enter(seat, s, NULL, 0, NULL); -+} -+ -+static inline void -+client_send_close(Client *c) -+{ -+#ifdef XWAYLAND -+ if (client_is_x11(c)) { -+ wlr_xwayland_surface_close(c->surface.xwayland); -+ return; -+ } -+#endif -+ wlr_xdg_toplevel_send_close(c->surface.xdg->toplevel); -+} -+ -+static inline void -+client_set_border_color(Client *c, const float color[static 4]) -+{ -+ int i; -+ for (i = 0; i < 4; i++) -+ wlr_scene_rect_set_color(c->border[i], color); -+} -+ -+static inline void -+client_set_fullscreen(Client *c, int fullscreen) -+{ -+#ifdef XWAYLAND -+ if (client_is_x11(c)) { -+ wlr_xwayland_surface_set_fullscreen(c->surface.xwayland, fullscreen); -+ return; -+ } -+#endif -+ wlr_xdg_toplevel_set_fullscreen(c->surface.xdg->toplevel, fullscreen); -+} -+ -+static inline void -+client_set_scale(struct wlr_surface *s, float scale) -+{ -+ wlr_fractional_scale_v1_notify_scale(s, scale); -+ wlr_surface_set_preferred_buffer_scale(s, (int32_t)ceilf(scale)); -+} -+ -+static inline uint32_t -+client_set_size(Client *c, uint32_t width, uint32_t height) -+{ -+#ifdef XWAYLAND -+ if (client_is_x11(c)) { -+ wlr_xwayland_surface_configure(c->surface.xwayland, -+ c->geom.x + c->bw, c->geom.y + c->bw, width, height); -+ return 0; -+ } -+#endif -+ if ((int32_t)width == c->surface.xdg->toplevel->current.width -+ && (int32_t)height == c->surface.xdg->toplevel->current.height) -+ return 0; -+ return wlr_xdg_toplevel_set_size(c->surface.xdg->toplevel, (int32_t)width, (int32_t)height); -+} -+ -+static inline void -+client_set_tiled(Client *c, uint32_t edges) -+{ -+#ifdef XWAYLAND -+ if (client_is_x11(c)) { -+ wlr_xwayland_surface_set_maximized(c->surface.xwayland, -+ edges != WLR_EDGE_NONE, edges != WLR_EDGE_NONE); -+ return; -+ } -+#endif -+ if (wl_resource_get_version(c->surface.xdg->toplevel->resource) -+ >= XDG_TOPLEVEL_STATE_TILED_RIGHT_SINCE_VERSION) { -+ wlr_xdg_toplevel_set_tiled(c->surface.xdg->toplevel, edges); -+ } else { -+ wlr_xdg_toplevel_set_maximized(c->surface.xdg->toplevel, edges != WLR_EDGE_NONE); -+ } -+} -+ -+static inline void -+client_set_suspended(Client *c, int suspended) -+{ -+#ifdef XWAYLAND -+ if (client_is_x11(c)) -+ return; -+#endif -+ -+ wlr_xdg_toplevel_set_suspended(c->surface.xdg->toplevel, suspended); -+} -+ -+static inline int -+client_wants_focus(Client *c) -+{ -+#ifdef XWAYLAND -+ return client_is_unmanaged(c) -+ && wlr_xwayland_surface_override_redirect_wants_focus(c->surface.xwayland) -+ && wlr_xwayland_surface_icccm_input_model(c->surface.xwayland) != WLR_ICCCM_INPUT_MODEL_NONE; -+#endif -+ return 0; -+} -+ -+static inline int -+client_wants_fullscreen(Client *c) -+{ -+#ifdef XWAYLAND -+ if (client_is_x11(c)) -+ return c->surface.xwayland->fullscreen; -+#endif -+ return c->surface.xdg->toplevel->requested.fullscreen; -+} -diff --git a/config.def.h b/config.def.h -index 95c2afa..e24819c 100644 ---- a/config.def.h -+++ b/config.def.h -@@ -13,6 +13,8 @@ static const float focuscolor[] = COLOR(0x005577ff); - static const float urgentcolor[] = COLOR(0xff0000ff); - /* This conforms to the xdg-protocol. Set the alpha to zero to restore the old behavior */ - static const float fullscreen_bg[] = {0.0f, 0.0f, 0.0f, 1.0f}; /* You can also use glsl colors */ -+static int enableautoswallow = 1; /* enables autoswallowing newly spawned clients */ -+static float swallowborder = 1.0f; /* add this multiplied by borderpx to border when a client is swallowed */ - - /* tagging - TAGCOUNT must be no greater than 31 */ - #define TAGCOUNT (9) -@@ -20,12 +22,20 @@ static const float fullscreen_bg[] = {0.0f, 0.0f, 0.0f, 1.0f}; /* You ca - /* logging */ - static int log_level = WLR_ERROR; - -+/* Autostart */ -+static const char *const autostart[] = { -+ "wbg", "/path/to/your/image", NULL, -+ NULL /* terminate */ -+}; -+ -+ - /* NOTE: ALWAYS keep a rule declared even if you don't use rules (e.g leave at least one example) */ - static const Rule rules[] = { -- /* app_id title tags mask isfloating monitor */ -+ /* app_id title tags mask isfloating isterm noswallow monitor */ - /* examples: */ -- { "Gimp_EXAMPLE", NULL, 0, 1, -1 }, /* Start on currently visible tags floating, not tiled */ -- { "firefox_EXAMPLE", NULL, 1 << 8, 0, -1 }, /* Start on ONLY tag "9" */ -+ { "foot", NULL, 0, 0, 1, 1, -1 }, -+ { "Gimp_EXAMPLE", NULL, 0, 1, 0, 0, -1 }, /* Start on currently visible tags floating, not tiled */ -+ { "firefox_EXAMPLE", NULL, 1 << 8, 0, 0, 0, -1 }, /* Start on ONLY tag "9" */ - }; - - /* layout(s) */ -@@ -142,6 +152,8 @@ static const Key keys[] = { - { MODKEY, XKB_KEY_space, setlayout, {0} }, - { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} }, - { MODKEY, XKB_KEY_e, togglefullscreen, {0} }, -+ { MODKEY, XKB_KEY_a, toggleswallow, {0} }, -+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_A, toggleautoswallow,{0} }, - { MODKEY, XKB_KEY_0, view, {.ui = ~0} }, - { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} }, - { MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} }, -diff --git a/config.def.h.orig b/config.def.h.orig -new file mode 100644 -index 0000000..5311c3c ---- /dev/null -+++ b/config.def.h.orig -@@ -0,0 +1,183 @@ -+/* Taken from https://github.com/djpohly/dwl/issues/466 */ -+#define COLOR(hex) { ((hex >> 24) & 0xFF) / 255.0f, \ -+ ((hex >> 16) & 0xFF) / 255.0f, \ -+ ((hex >> 8) & 0xFF) / 255.0f, \ -+ (hex & 0xFF) / 255.0f } -+/* appearance */ -+static const int sloppyfocus = 1; /* focus follows mouse */ -+static const int bypass_surface_visibility = 0; /* 1 means idle inhibitors will disable idle tracking even if it's surface isn't visible */ -+static const unsigned int borderpx = 1; /* border pixel of windows */ -+static const float rootcolor[] = COLOR(0x222222ff); -+static const float bordercolor[] = COLOR(0x444444ff); -+static const float focuscolor[] = COLOR(0x005577ff); -+static const float urgentcolor[] = COLOR(0xff0000ff); -+/* This conforms to the xdg-protocol. Set the alpha to zero to restore the old behavior */ -+static const float fullscreen_bg[] = {0.0f, 0.0f, 0.0f, 1.0f}; /* You can also use glsl colors */ -+ -+/* tagging - TAGCOUNT must be no greater than 31 */ -+#define TAGCOUNT (9) -+ -+/* logging */ -+static int log_level = WLR_ERROR; -+ -+/* Autostart */ -+static const char *const autostart[] = { -+ "wbg", "/path/to/your/image", NULL, -+ NULL /* terminate */ -+}; -+ -+ -+/* NOTE: ALWAYS keep a rule declared even if you don't use rules (e.g leave at least one example) */ -+static const Rule rules[] = { -+ /* app_id title tags mask isfloating monitor */ -+ /* examples: */ -+ { "Gimp_EXAMPLE", NULL, 0, 1, -1 }, /* Start on currently visible tags floating, not tiled */ -+ { "firefox_EXAMPLE", NULL, 1 << 8, 0, -1 }, /* Start on ONLY tag "9" */ -+}; -+ -+/* layout(s) */ -+static const Layout layouts[] = { -+ /* symbol arrange function */ -+ { "[]=", tile }, -+ { "><>", NULL }, /* no layout function means floating behavior */ -+ { "[M]", monocle }, -+}; -+ -+/* monitors */ -+/* (x=-1, y=-1) is reserved as an "autoconfigure" monitor position indicator -+ * WARNING: negative values other than (-1, -1) cause problems with Xwayland clients -+ * https://gitlab.freedesktop.org/xorg/xserver/-/issues/899 -+*/ -+/* NOTE: ALWAYS add a fallback rule, even if you are completely sure it won't be used */ -+static const MonitorRule monrules[] = { -+ /* name mfact nmaster scale layout rotate/reflect x y */ -+ /* example of a HiDPI laptop monitor: -+ { "eDP-1", 0.5f, 1, 2, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 }, -+ */ -+ /* defaults */ -+ { NULL, 0.55f, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 }, -+}; -+ -+/* keyboard */ -+static const struct xkb_rule_names xkb_rules = { -+ /* can specify fields: rules, model, layout, variant, options */ -+ /* example: -+ .options = "ctrl:nocaps", -+ */ -+ .options = NULL, -+}; -+ -+static const int repeat_rate = 25; -+static const int repeat_delay = 600; -+ -+/* Trackpad */ -+static const int tap_to_click = 1; -+static const int tap_and_drag = 1; -+static const int drag_lock = 1; -+static const int natural_scrolling = 0; -+static const int disable_while_typing = 1; -+static const int left_handed = 0; -+static const int middle_button_emulation = 0; -+/* You can choose between: -+LIBINPUT_CONFIG_SCROLL_NO_SCROLL -+LIBINPUT_CONFIG_SCROLL_2FG -+LIBINPUT_CONFIG_SCROLL_EDGE -+LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN -+*/ -+static const enum libinput_config_scroll_method scroll_method = LIBINPUT_CONFIG_SCROLL_2FG; -+ -+/* You can choose between: -+LIBINPUT_CONFIG_CLICK_METHOD_NONE -+LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS -+LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER -+*/ -+static const enum libinput_config_click_method click_method = LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS; -+ -+/* You can choose between: -+LIBINPUT_CONFIG_SEND_EVENTS_ENABLED -+LIBINPUT_CONFIG_SEND_EVENTS_DISABLED -+LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE -+*/ -+static const uint32_t send_events_mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED; -+ -+/* You can choose between: -+LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT -+LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE -+*/ -+static const enum libinput_config_accel_profile accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE; -+static const double accel_speed = 0.0; -+ -+/* You can choose between: -+LIBINPUT_CONFIG_TAP_MAP_LRM -- 1/2/3 finger tap maps to left/right/middle -+LIBINPUT_CONFIG_TAP_MAP_LMR -- 1/2/3 finger tap maps to left/middle/right -+*/ -+static const enum libinput_config_tap_button_map button_map = LIBINPUT_CONFIG_TAP_MAP_LRM; -+ -+/* If you want to use the windows key for MODKEY, use WLR_MODIFIER_LOGO */ -+#define MODKEY WLR_MODIFIER_ALT -+ -+#define TAGKEYS(KEY,SKEY,TAG) \ -+ { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ -+ { MODKEY|WLR_MODIFIER_CTRL, KEY, toggleview, {.ui = 1 << TAG} }, \ -+ { MODKEY|WLR_MODIFIER_SHIFT, SKEY, tag, {.ui = 1 << TAG} }, \ -+ { MODKEY|WLR_MODIFIER_CTRL|WLR_MODIFIER_SHIFT,SKEY,toggletag, {.ui = 1 << TAG} } -+ -+/* helper for spawning shell commands in the pre dwm-5.0 fashion */ -+#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } -+ -+/* commands */ -+static const char *termcmd[] = { "foot", NULL }; -+static const char *menucmd[] = { "wmenu-run", NULL }; -+ -+static const Key keys[] = { -+ /* Note that Shift changes certain key codes: c -> C, 2 -> at, etc. */ -+ /* modifier key function argument */ -+ { MODKEY, XKB_KEY_p, spawn, {.v = menucmd} }, -+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, spawn, {.v = termcmd} }, -+ { MODKEY, XKB_KEY_j, focusstack, {.i = +1} }, -+ { MODKEY, XKB_KEY_k, focusstack, {.i = -1} }, -+ { MODKEY, XKB_KEY_i, incnmaster, {.i = +1} }, -+ { MODKEY, XKB_KEY_d, incnmaster, {.i = -1} }, -+ { MODKEY, XKB_KEY_h, setmfact, {.f = -0.05f} }, -+ { MODKEY, XKB_KEY_l, setmfact, {.f = +0.05f} }, -+ { MODKEY, XKB_KEY_Return, zoom, {0} }, -+ { MODKEY, XKB_KEY_Tab, view, {0} }, -+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_C, killclient, {0} }, -+ { MODKEY, XKB_KEY_t, setlayout, {.v = &layouts[0]} }, -+ { MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} }, -+ { MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} }, -+ { MODKEY, XKB_KEY_space, setlayout, {0} }, -+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} }, -+ { MODKEY, XKB_KEY_e, togglefullscreen, {0} }, -+ { MODKEY, XKB_KEY_0, view, {.ui = ~0} }, -+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} }, -+ { MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} }, -+ { MODKEY, XKB_KEY_period, focusmon, {.i = WLR_DIRECTION_RIGHT} }, -+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_less, tagmon, {.i = WLR_DIRECTION_LEFT} }, -+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_greater, tagmon, {.i = WLR_DIRECTION_RIGHT} }, -+ TAGKEYS( XKB_KEY_1, XKB_KEY_exclam, 0), -+ TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1), -+ TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2), -+ TAGKEYS( XKB_KEY_4, XKB_KEY_dollar, 3), -+ TAGKEYS( XKB_KEY_5, XKB_KEY_percent, 4), -+ TAGKEYS( XKB_KEY_6, XKB_KEY_asciicircum, 5), -+ TAGKEYS( XKB_KEY_7, XKB_KEY_ampersand, 6), -+ TAGKEYS( XKB_KEY_8, XKB_KEY_asterisk, 7), -+ TAGKEYS( XKB_KEY_9, XKB_KEY_parenleft, 8), -+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Q, quit, {0} }, -+ -+ /* Ctrl-Alt-Backspace and Ctrl-Alt-Fx used to be handled by X server */ -+ { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_Terminate_Server, quit, {0} }, -+ /* Ctrl-Alt-Fx is used to switch to another VT, if you don't know what a VT is -+ * do not remove them. -+ */ -+#define CHVT(n) { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_XF86Switch_VT_##n, chvt, {.ui = (n)} } -+ CHVT(1), CHVT(2), CHVT(3), CHVT(4), CHVT(5), CHVT(6), -+ CHVT(7), CHVT(8), CHVT(9), CHVT(10), CHVT(11), CHVT(12), -+}; -+ -+static const Button buttons[] = { -+ { MODKEY, BTN_LEFT, moveresize, {.ui = CurMove} }, -+ { MODKEY, BTN_MIDDLE, togglefloating, {0} }, -+ { MODKEY, BTN_RIGHT, moveresize, {.ui = CurResize} }, -+}; -diff --git a/dwl.c b/dwl.c -index 12f441e..54b40be 100644 ---- a/dwl.c -+++ b/dwl.c -@@ -74,12 +74,13 @@ - #define MAX(A, B) ((A) > (B) ? (A) : (B)) - #define MIN(A, B) ((A) < (B) ? (A) : (B)) - #define CLEANMASK(mask) (mask & ~WLR_MODIFIER_CAPS) --#define VISIBLEON(C, M) ((M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags])) - #define LENGTH(X) (sizeof X / sizeof X[0]) -+#define VISIBLEON(C, M) ((M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags]) && !(C)->swallowedby) - #define END(A) ((A) + LENGTH(A)) - #define TAGMASK ((1u << TAGCOUNT) - 1) - #define LISTEN(E, L, H) wl_signal_add((E), ((L)->notify = (H), (L))) - #define LISTEN_STATIC(E, H) do { struct wl_listener *_l = ecalloc(1, sizeof(*_l)); _l->notify = (H); wl_signal_add((E), _l); } while (0) -+#define BORDERPX(C) (borderpx + ((C)->swallowing ? (int)ceilf(swallowborder * (C)->swallowing->bw) : 0)) - - /* enums */ - enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */ -@@ -101,7 +102,8 @@ typedef struct { - } Button; - - typedef struct Monitor Monitor; --typedef struct { -+typedef struct Client Client; -+struct Client { - /* Must keep this field first */ - unsigned int type; /* XDGShell or X11* */ - -@@ -138,8 +140,12 @@ typedef struct { - unsigned int bw; - uint32_t tags; - int isfloating, isurgent, isfullscreen; -+ int isterm, noswallow; - uint32_t resize; /* configure serial of a pending resize */ --} Client; -+ pid_t pid; -+ Client *swallowing; /* client being hidden */ -+ Client *swallowedby; -+}; - - typedef struct { - uint32_t mod; -@@ -227,6 +233,8 @@ typedef struct { - const char *title; - uint32_t tags; - int isfloating; -+ int isterm; -+ int noswallow; - int monitor; - } Rule; - -@@ -246,6 +254,7 @@ static void arrange(Monitor *m); - static void arrangelayer(Monitor *m, struct wl_list *list, - struct wlr_box *usable_area, int exclusive); - static void arrangelayers(Monitor *m); -+static void autostartexec(void); - static void axisnotify(struct wl_listener *listener, void *data); - static void buttonpress(struct wl_listener *listener, void *data); - static void chvt(const Arg *arg); -@@ -308,6 +317,7 @@ static void moveresize(const Arg *arg); - static void outputmgrapply(struct wl_listener *listener, void *data); - static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test); - static void outputmgrtest(struct wl_listener *listener, void *data); -+static pid_t parentpid(pid_t pid); - static void pointerfocus(Client *c, struct wlr_surface *surface, - double sx, double sy, uint32_t time); - static void printstatus(void); -@@ -331,11 +341,15 @@ static void setsel(struct wl_listener *listener, void *data); - static void setup(void); - static void spawn(const Arg *arg); - static void startdrag(struct wl_listener *listener, void *data); -+static void swallow(Client *c, Client *toswallow); - static void tag(const Arg *arg); - static void tagmon(const Arg *arg); -+static Client *termforwin(Client *c); - static void tile(Monitor *m); - static void togglefloating(const Arg *arg); - static void togglefullscreen(const Arg *arg); -+static void toggleswallow(const Arg *arg); -+static void toggleautoswallow(const Arg *arg); - static void toggletag(const Arg *arg); - static void toggleview(const Arg *arg); - static void unlocksession(struct wl_listener *listener, void *data); -@@ -455,6 +469,9 @@ static struct wlr_xwayland *xwayland; - /* attempt to encapsulate suck into one file */ - #include "client.h" - -+static pid_t *autostart_pids; -+static size_t autostart_len; -+ - /* function implementations */ - void - applybounds(Client *c, struct wlr_box *bbox) -@@ -486,11 +503,15 @@ applyrules(Client *c) - appid = client_get_appid(c); - title = client_get_title(c); - -+ c->pid = client_get_pid(c); -+ - for (r = rules; r < END(rules); r++) { - if ((!r->title || strstr(title, r->title)) - && (!r->id || strstr(appid, r->id))) { - c->isfloating = r->isfloating; - newtags |= r->tags; -+ c->isterm = r->isterm; -+ c->noswallow = r->noswallow; - i = 0; - wl_list_for_each(m, &mons, link) { - if (r->monitor == i++) -@@ -500,6 +521,12 @@ applyrules(Client *c) - } - - c->isfloating |= client_is_float_type(c); -+ if (enableautoswallow && !c->noswallow && !c->isfloating && -+ !c->surface.xdg->initial_commit) { -+ Client *p = termforwin(c); -+ if (p) -+ swallow(c, p); -+ } - setmon(c, mon, newtags); - } - -@@ -603,6 +630,27 @@ arrangelayers(Monitor *m) - } - } - -+void -+autostartexec(void) { -+ const char *const *p; -+ size_t i = 0; -+ -+ /* count entries */ -+ for (p = autostart; *p; autostart_len++, p++) -+ while (*++p); -+ -+ autostart_pids = calloc(autostart_len, sizeof(pid_t)); -+ for (p = autostart; *p; i++, p++) { -+ if ((autostart_pids[i] = fork()) == 0) { -+ setsid(); -+ execvp(*p, (char *const *)p); -+ die("dwl: execvp %s:", *p); -+ } -+ /* skip arguments */ -+ while (*++p); -+ } -+} -+ - void - axisnotify(struct wl_listener *listener, void *data) - { -@@ -699,12 +747,23 @@ checkidleinhibitor(struct wlr_surface *exclude) - void - cleanup(void) - { -+ size_t i; -+ - cleanuplisteners(); - #ifdef XWAYLAND - wlr_xwayland_destroy(xwayland); - xwayland = NULL; - #endif - wl_display_destroy_clients(dpy); -+ -+ /* kill child processes */ -+ for (i = 0; i < autostart_len; i++) { -+ if (0 < autostart_pids[i]) { -+ kill(autostart_pids[i], SIGTERM); -+ waitpid(autostart_pids[i], NULL, 0); -+ } -+ } -+ - if (child_pid > 0) { - kill(-child_pid, SIGTERM); - waitpid(child_pid, NULL, 0); -@@ -1560,10 +1619,25 @@ gpureset(struct wl_listener *listener, void *data) - void - handlesig(int signo) - { -- if (signo == SIGCHLD) -- while (waitpid(-1, NULL, WNOHANG) > 0); -- else if (signo == SIGINT || signo == SIGTERM) -+ if (signo == SIGCHLD) { -+ pid_t pid, *p, *lim; -+ while ((pid = waitpid(-1, NULL, WNOHANG)) > 0) { -+ if (pid == child_pid) -+ child_pid = -1; -+ if (!(p = autostart_pids)) -+ continue; -+ lim = &p[autostart_len]; -+ -+ for (; p < lim; p++) { -+ if (*p == pid) { -+ *p = -1; -+ break; -+ } -+ } -+ } -+ } else if (signo == SIGINT || signo == SIGTERM) { - quit(NULL); -+ } - } - - void -@@ -2056,6 +2130,20 @@ outputmgrtest(struct wl_listener *listener, void *data) - outputmgrapplyortest(config, 1); - } - -+pid_t -+parentpid(pid_t pid) -+{ -+ unsigned int v = 0; -+ FILE *f; -+ char buf[256]; -+ snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)pid); -+ if (!(f = fopen(buf, "r"))) -+ return 0; -+ fscanf(f, "%*u %*s %*c %u", &v); -+ fclose(f); -+ return (pid_t)v; -+} -+ - void - pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, - uint32_t time) -@@ -2249,6 +2337,7 @@ run(char *startup_cmd) - die("startup: backend_start"); - - /* Now that the socket exists and the backend is started, run the startup command */ -+ autostartexec(); - if (startup_cmd) { - int piperw[2]; - if (pipe(piperw) < 0) -@@ -2350,7 +2439,7 @@ setfullscreen(Client *c, int fullscreen) - c->isfullscreen = fullscreen; - if (!c->mon || !client_surface(c)->mapped) - return; -- c->bw = fullscreen ? 0 : borderpx; -+ c->bw = fullscreen ? 0 : BORDERPX(c); - client_set_fullscreen(c, fullscreen); - wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen - ? LyrFS : c->isfloating ? LyrFloat : LyrTile]); -@@ -2417,6 +2506,9 @@ setmon(Client *c, Monitor *m, uint32_t newtags) - setfloating(c, c->isfloating); - } - focusclient(focustop(selmon), 1); -+ -+ if (c->swallowing) -+ setmon(c->swallowing, m, newtags); - } - - void -@@ -2687,6 +2779,44 @@ startdrag(struct wl_listener *listener, void *data) - LISTEN_STATIC(&drag->icon->events.destroy, destroydragicon); - } - -+void -+swallow(Client *c, Client *toswallow) -+{ -+ /* Do not allow a client to swallow itself */ -+ if (c == toswallow) -+ return; -+ -+ /* Swallow */ -+ if (toswallow && !c->swallowing) { -+ c->swallowing = toswallow; -+ toswallow->swallowedby = c; -+ toswallow->mon = c->mon; -+ toswallow->mon = c->mon; -+ wl_list_remove(&c->link); -+ wl_list_insert(&c->swallowing->link, &c->link); -+ wl_list_remove(&c->flink); -+ wl_list_insert(&c->swallowing->flink, &c->flink); -+ c->bw = BORDERPX(c); -+ c->tags = toswallow->tags; -+ c->isfloating = toswallow->isfloating; -+ c->geom = toswallow->geom; -+ setfullscreen(toswallow, 0); -+ } -+ -+ /* Unswallow */ -+ else if (c->swallowing) { -+ wl_list_remove(&c->swallowing->link); -+ wl_list_insert(&c->link, &c->swallowing->link); -+ wl_list_remove(&c->swallowing->flink); -+ wl_list_insert(&c->flink, &c->swallowing->flink); -+ c->swallowing->tags = c->tags; -+ c->swallowing->swallowedby = NULL; -+ c->swallowing = NULL; -+ c->bw = BORDERPX(c); -+ setfullscreen(c, 0); -+ } -+} -+ - void - tag(const Arg *arg) - { -@@ -2708,6 +2838,40 @@ tagmon(const Arg *arg) - setmon(sel, dirtomon(arg->i), 0); - } - -+Client * -+termforwin(Client *c) -+{ -+ Client *p; -+ pid_t pid; -+ pid_t pids[32]; -+ size_t i, pids_len; -+ -+ if (!c->pid || c->isterm) -+ return NULL; -+ -+ /* Get all parent pids */ -+ pids_len = 0; -+ pid = c->pid; -+ while (pids_len < LENGTH(pids)) { -+ pid = parentpid(pid); -+ if (!pid) -+ break; -+ pids[pids_len++] = pid; -+ } -+ -+ /* Find closest parent */ -+ for (i = 0; i < pids_len; i++) { -+ wl_list_for_each(p, &clients, link) { -+ if (!p->pid || !p->isterm || p->swallowedby) -+ continue; -+ if (pids[i] == p->pid) -+ return p; -+ } -+ } -+ -+ return NULL; -+} -+ - void - tile(Monitor *m) - { -@@ -2759,6 +2923,32 @@ togglefullscreen(const Arg *arg) - setfullscreen(sel, !sel->isfullscreen); - } - -+void -+toggleswallow(const Arg *arg) -+{ -+ Client *c, *sel = focustop(selmon); -+ if (!sel) -+ return; -+ -+ if (sel->swallowing) { -+ swallow(sel, NULL); -+ } else { -+ wl_list_for_each(c, &sel->flink, flink) { -+ if (&c->flink == &fstack) -+ continue; /* wrap past the sentinel node */ -+ if (VISIBLEON(c, selmon)) -+ break; /* found it */ -+ } -+ swallow(sel, c); -+ } -+} -+ -+void -+toggleautoswallow(const Arg *arg) -+{ -+ enableautoswallow = !enableautoswallow; -+} -+ - void - toggletag(const Arg *arg) - { -@@ -2819,6 +3009,12 @@ unmapnotify(struct wl_listener *listener, void *data) - grabc = NULL; - } - -+ if (c->swallowing) { -+ swallow(c, NULL); -+ } else if (c->swallowedby) { -+ swallow(c->swallowedby, NULL); -+ } -+ - if (client_is_unmanaged(c)) { - if (c == exclusive_focus) { - exclusive_focus = NULL; -diff --git a/dwl.c.orig b/dwl.c.orig -new file mode 100644 -index 0000000..792e661 ---- /dev/null -+++ b/dwl.c.orig -@@ -0,0 +1,3266 @@ -+/* -+ * See LICENSE file for copyright and license details. -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#ifdef XWAYLAND -+#include -+#include -+#include -+#endif -+ -+#include "util.h" -+ -+/* macros */ -+#define MAX(A, B) ((A) > (B) ? (A) : (B)) -+#define MIN(A, B) ((A) < (B) ? (A) : (B)) -+#define CLEANMASK(mask) (mask & ~WLR_MODIFIER_CAPS) -+#define VISIBLEON(C, M) ((M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags])) -+#define LENGTH(X) (sizeof X / sizeof X[0]) -+#define END(A) ((A) + LENGTH(A)) -+#define TAGMASK ((1u << TAGCOUNT) - 1) -+#define LISTEN(E, L, H) wl_signal_add((E), ((L)->notify = (H), (L))) -+#define LISTEN_STATIC(E, H) do { struct wl_listener *_l = ecalloc(1, sizeof(*_l)); _l->notify = (H); wl_signal_add((E), _l); } while (0) -+ -+/* enums */ -+enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */ -+enum { XDGShell, LayerShell, X11 }; /* client types */ -+enum { LyrBg, LyrBottom, LyrTile, LyrFloat, LyrTop, LyrFS, LyrOverlay, LyrBlock, NUM_LAYERS }; /* scene layers */ -+ -+typedef union { -+ int i; -+ uint32_t ui; -+ float f; -+ const void *v; -+} Arg; -+ -+typedef struct { -+ unsigned int mod; -+ unsigned int button; -+ void (*func)(const Arg *); -+ const Arg arg; -+} Button; -+ -+typedef struct Monitor Monitor; -+typedef struct { -+ /* Must keep this field first */ -+ unsigned int type; /* XDGShell or X11* */ -+ -+ Monitor *mon; -+ struct wlr_scene_tree *scene; -+ struct wlr_scene_rect *border[4]; /* top, bottom, left, right */ -+ struct wlr_scene_tree *scene_surface; -+ struct wl_list link; -+ struct wl_list flink; -+ struct wlr_box geom; /* layout-relative, includes border */ -+ struct wlr_box prev; /* layout-relative, includes border */ -+ struct wlr_box bounds; /* only width and height are used */ -+ union { -+ struct wlr_xdg_surface *xdg; -+ struct wlr_xwayland_surface *xwayland; -+ } surface; -+ struct wlr_xdg_toplevel_decoration_v1 *decoration; -+ struct wl_listener commit; -+ struct wl_listener map; -+ struct wl_listener maximize; -+ struct wl_listener unmap; -+ struct wl_listener destroy; -+ struct wl_listener set_title; -+ struct wl_listener fullscreen; -+ struct wl_listener set_decoration_mode; -+ struct wl_listener destroy_decoration; -+#ifdef XWAYLAND -+ struct wl_listener activate; -+ struct wl_listener associate; -+ struct wl_listener dissociate; -+ struct wl_listener configure; -+ struct wl_listener set_hints; -+#endif -+ unsigned int bw; -+ uint32_t tags; -+ int isfloating, isurgent, isfullscreen; -+ uint32_t resize; /* configure serial of a pending resize */ -+} Client; -+ -+typedef struct { -+ uint32_t mod; -+ xkb_keysym_t keysym; -+ void (*func)(const Arg *); -+ const Arg arg; -+} Key; -+ -+typedef struct { -+ struct wlr_keyboard_group *wlr_group; -+ -+ int nsyms; -+ const xkb_keysym_t *keysyms; /* invalid if nsyms == 0 */ -+ uint32_t mods; /* invalid if nsyms == 0 */ -+ struct wl_event_source *key_repeat_source; -+ -+ struct wl_listener modifiers; -+ struct wl_listener key; -+ struct wl_listener destroy; -+} KeyboardGroup; -+ -+typedef struct { -+ /* Must keep this field first */ -+ unsigned int type; /* LayerShell */ -+ -+ Monitor *mon; -+ struct wlr_scene_tree *scene; -+ struct wlr_scene_tree *popups; -+ struct wlr_scene_layer_surface_v1 *scene_layer; -+ struct wl_list link; -+ int mapped; -+ struct wlr_layer_surface_v1 *layer_surface; -+ -+ struct wl_listener destroy; -+ struct wl_listener unmap; -+ struct wl_listener surface_commit; -+} LayerSurface; -+ -+typedef struct { -+ const char *symbol; -+ void (*arrange)(Monitor *); -+} Layout; -+ -+struct Monitor { -+ struct wl_list link; -+ struct wlr_output *wlr_output; -+ struct wlr_scene_output *scene_output; -+ struct wlr_scene_rect *fullscreen_bg; /* See createmon() for info */ -+ struct wl_listener frame; -+ struct wl_listener destroy; -+ struct wl_listener request_state; -+ struct wl_listener destroy_lock_surface; -+ struct wlr_session_lock_surface_v1 *lock_surface; -+ struct wlr_box m; /* monitor area, layout-relative */ -+ struct wlr_box w; /* window area, layout-relative */ -+ struct wl_list layers[4]; /* LayerSurface.link */ -+ const Layout *lt[2]; -+ unsigned int seltags; -+ unsigned int sellt; -+ uint32_t tagset[2]; -+ float mfact; -+ int gamma_lut_changed; -+ int nmaster; -+ char ltsymbol[16]; -+ int asleep; -+}; -+ -+typedef struct { -+ const char *name; -+ float mfact; -+ int nmaster; -+ float scale; -+ const Layout *lt; -+ enum wl_output_transform rr; -+ int x, y; -+} MonitorRule; -+ -+typedef struct { -+ struct wlr_pointer_constraint_v1 *constraint; -+ struct wl_listener destroy; -+} PointerConstraint; -+ -+typedef struct { -+ const char *id; -+ const char *title; -+ uint32_t tags; -+ int isfloating; -+ int monitor; -+} Rule; -+ -+typedef struct { -+ struct wlr_scene_tree *scene; -+ -+ struct wlr_session_lock_v1 *lock; -+ struct wl_listener new_surface; -+ struct wl_listener unlock; -+ struct wl_listener destroy; -+} SessionLock; -+ -+/* function declarations */ -+static void applybounds(Client *c, struct wlr_box *bbox); -+static void applyrules(Client *c); -+static void arrange(Monitor *m); -+static void arrangelayer(Monitor *m, struct wl_list *list, -+ struct wlr_box *usable_area, int exclusive); -+static void arrangelayers(Monitor *m); -+static void autostartexec(void); -+static void axisnotify(struct wl_listener *listener, void *data); -+static void buttonpress(struct wl_listener *listener, void *data); -+static void chvt(const Arg *arg); -+static void checkidleinhibitor(struct wlr_surface *exclude); -+static void cleanup(void); -+static void cleanupmon(struct wl_listener *listener, void *data); -+static void cleanuplisteners(void); -+static void closemon(Monitor *m); -+static void commitlayersurfacenotify(struct wl_listener *listener, void *data); -+static void commitnotify(struct wl_listener *listener, void *data); -+static void commitpopup(struct wl_listener *listener, void *data); -+static void createdecoration(struct wl_listener *listener, void *data); -+static void createidleinhibitor(struct wl_listener *listener, void *data); -+static void createkeyboard(struct wlr_keyboard *keyboard); -+static KeyboardGroup *createkeyboardgroup(void); -+static void createlayersurface(struct wl_listener *listener, void *data); -+static void createlocksurface(struct wl_listener *listener, void *data); -+static void createmon(struct wl_listener *listener, void *data); -+static void createnotify(struct wl_listener *listener, void *data); -+static void createpointer(struct wlr_pointer *pointer); -+static void createpointerconstraint(struct wl_listener *listener, void *data); -+static void createpopup(struct wl_listener *listener, void *data); -+static void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint); -+static void cursorframe(struct wl_listener *listener, void *data); -+static void cursorwarptohint(void); -+static void destroydecoration(struct wl_listener *listener, void *data); -+static void destroydragicon(struct wl_listener *listener, void *data); -+static void destroyidleinhibitor(struct wl_listener *listener, void *data); -+static void destroylayersurfacenotify(struct wl_listener *listener, void *data); -+static void destroylock(SessionLock *lock, int unlocked); -+static void destroylocksurface(struct wl_listener *listener, void *data); -+static void destroynotify(struct wl_listener *listener, void *data); -+static void destroypointerconstraint(struct wl_listener *listener, void *data); -+static void destroysessionlock(struct wl_listener *listener, void *data); -+static void destroykeyboardgroup(struct wl_listener *listener, void *data); -+static Monitor *dirtomon(enum wlr_direction dir); -+static void focusclient(Client *c, int lift); -+static void focusmon(const Arg *arg); -+static void focusstack(const Arg *arg); -+static Client *focustop(Monitor *m); -+static void fullscreennotify(struct wl_listener *listener, void *data); -+static void gpureset(struct wl_listener *listener, void *data); -+static void handlesig(int signo); -+static void incnmaster(const Arg *arg); -+static void inputdevice(struct wl_listener *listener, void *data); -+static int keybinding(uint32_t mods, xkb_keysym_t sym); -+static void keypress(struct wl_listener *listener, void *data); -+static void keypressmod(struct wl_listener *listener, void *data); -+static int keyrepeat(void *data); -+static void killclient(const Arg *arg); -+static void locksession(struct wl_listener *listener, void *data); -+static void mapnotify(struct wl_listener *listener, void *data); -+static void maximizenotify(struct wl_listener *listener, void *data); -+static void monocle(Monitor *m); -+static void motionabsolute(struct wl_listener *listener, void *data); -+static void motionnotify(uint32_t time, struct wlr_input_device *device, double sx, -+ double sy, double sx_unaccel, double sy_unaccel); -+static void motionrelative(struct wl_listener *listener, void *data); -+static void moveresize(const Arg *arg); -+static void outputmgrapply(struct wl_listener *listener, void *data); -+static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test); -+static void outputmgrtest(struct wl_listener *listener, void *data); -+static void pointerfocus(Client *c, struct wlr_surface *surface, -+ double sx, double sy, uint32_t time); -+static void printstatus(void); -+static void powermgrsetmode(struct wl_listener *listener, void *data); -+static void quit(const Arg *arg); -+static void rendermon(struct wl_listener *listener, void *data); -+static void requestdecorationmode(struct wl_listener *listener, void *data); -+static void requeststartdrag(struct wl_listener *listener, void *data); -+static void requestmonstate(struct wl_listener *listener, void *data); -+static void resize(Client *c, struct wlr_box geo, int interact); -+static void run(char *startup_cmd); -+static void setcursor(struct wl_listener *listener, void *data); -+static void setcursorshape(struct wl_listener *listener, void *data); -+static void setfloating(Client *c, int floating); -+static void setfullscreen(Client *c, int fullscreen); -+static void setlayout(const Arg *arg); -+static void setmfact(const Arg *arg); -+static void setmon(Client *c, Monitor *m, uint32_t newtags); -+static void setpsel(struct wl_listener *listener, void *data); -+static void setsel(struct wl_listener *listener, void *data); -+static void setup(void); -+static void spawn(const Arg *arg); -+static void startdrag(struct wl_listener *listener, void *data); -+static void tag(const Arg *arg); -+static void tagmon(const Arg *arg); -+static void tile(Monitor *m); -+static void togglefloating(const Arg *arg); -+static void togglefullscreen(const Arg *arg); -+static void toggletag(const Arg *arg); -+static void toggleview(const Arg *arg); -+static void unlocksession(struct wl_listener *listener, void *data); -+static void unmaplayersurfacenotify(struct wl_listener *listener, void *data); -+static void unmapnotify(struct wl_listener *listener, void *data); -+static void updatemons(struct wl_listener *listener, void *data); -+static void updatetitle(struct wl_listener *listener, void *data); -+static void urgent(struct wl_listener *listener, void *data); -+static void view(const Arg *arg); -+static void virtualkeyboard(struct wl_listener *listener, void *data); -+static void virtualpointer(struct wl_listener *listener, void *data); -+static Monitor *xytomon(double x, double y); -+static void xytonode(double x, double y, struct wlr_surface **psurface, -+ Client **pc, LayerSurface **pl, double *nx, double *ny); -+static void zoom(const Arg *arg); -+ -+/* variables */ -+static pid_t child_pid = -1; -+static int locked; -+static void *exclusive_focus; -+static struct wl_display *dpy; -+static struct wl_event_loop *event_loop; -+static struct wlr_backend *backend; -+static struct wlr_scene *scene; -+static struct wlr_scene_tree *layers[NUM_LAYERS]; -+static struct wlr_scene_tree *drag_icon; -+/* Map from ZWLR_LAYER_SHELL_* constants to Lyr* enum */ -+static const int layermap[] = { LyrBg, LyrBottom, LyrTop, LyrOverlay }; -+static struct wlr_renderer *drw; -+static struct wlr_allocator *alloc; -+static struct wlr_compositor *compositor; -+static struct wlr_session *session; -+ -+static struct wlr_xdg_shell *xdg_shell; -+static struct wlr_xdg_activation_v1 *activation; -+static struct wlr_xdg_decoration_manager_v1 *xdg_decoration_mgr; -+static struct wl_list clients; /* tiling order */ -+static struct wl_list fstack; /* focus order */ -+static struct wlr_idle_notifier_v1 *idle_notifier; -+static struct wlr_idle_inhibit_manager_v1 *idle_inhibit_mgr; -+static struct wlr_layer_shell_v1 *layer_shell; -+static struct wlr_output_manager_v1 *output_mgr; -+static struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard_mgr; -+static struct wlr_virtual_pointer_manager_v1 *virtual_pointer_mgr; -+static struct wlr_cursor_shape_manager_v1 *cursor_shape_mgr; -+static struct wlr_output_power_manager_v1 *power_mgr; -+ -+static struct wlr_pointer_constraints_v1 *pointer_constraints; -+static struct wlr_relative_pointer_manager_v1 *relative_pointer_mgr; -+static struct wlr_pointer_constraint_v1 *active_constraint; -+ -+static struct wlr_cursor *cursor; -+static struct wlr_xcursor_manager *cursor_mgr; -+ -+static struct wlr_scene_rect *root_bg; -+static struct wlr_session_lock_manager_v1 *session_lock_mgr; -+static struct wlr_scene_rect *locked_bg; -+static struct wlr_session_lock_v1 *cur_lock; -+ -+static struct wlr_seat *seat; -+static KeyboardGroup *kb_group; -+static unsigned int cursor_mode; -+static Client *grabc; -+static int grabcx, grabcy; /* client-relative */ -+ -+static struct wlr_output_layout *output_layout; -+static struct wlr_box sgeom; -+static struct wl_list mons; -+static Monitor *selmon; -+ -+/* global event handlers */ -+static struct wl_listener cursor_axis = {.notify = axisnotify}; -+static struct wl_listener cursor_button = {.notify = buttonpress}; -+static struct wl_listener cursor_frame = {.notify = cursorframe}; -+static struct wl_listener cursor_motion = {.notify = motionrelative}; -+static struct wl_listener cursor_motion_absolute = {.notify = motionabsolute}; -+static struct wl_listener gpu_reset = {.notify = gpureset}; -+static struct wl_listener layout_change = {.notify = updatemons}; -+static struct wl_listener new_idle_inhibitor = {.notify = createidleinhibitor}; -+static struct wl_listener new_input_device = {.notify = inputdevice}; -+static struct wl_listener new_virtual_keyboard = {.notify = virtualkeyboard}; -+static struct wl_listener new_virtual_pointer = {.notify = virtualpointer}; -+static struct wl_listener new_pointer_constraint = {.notify = createpointerconstraint}; -+static struct wl_listener new_output = {.notify = createmon}; -+static struct wl_listener new_xdg_toplevel = {.notify = createnotify}; -+static struct wl_listener new_xdg_popup = {.notify = createpopup}; -+static struct wl_listener new_xdg_decoration = {.notify = createdecoration}; -+static struct wl_listener new_layer_surface = {.notify = createlayersurface}; -+static struct wl_listener output_mgr_apply = {.notify = outputmgrapply}; -+static struct wl_listener output_mgr_test = {.notify = outputmgrtest}; -+static struct wl_listener output_power_mgr_set_mode = {.notify = powermgrsetmode}; -+static struct wl_listener request_activate = {.notify = urgent}; -+static struct wl_listener request_cursor = {.notify = setcursor}; -+static struct wl_listener request_set_psel = {.notify = setpsel}; -+static struct wl_listener request_set_sel = {.notify = setsel}; -+static struct wl_listener request_set_cursor_shape = {.notify = setcursorshape}; -+static struct wl_listener request_start_drag = {.notify = requeststartdrag}; -+static struct wl_listener start_drag = {.notify = startdrag}; -+static struct wl_listener new_session_lock = {.notify = locksession}; -+ -+#ifdef XWAYLAND -+static void activatex11(struct wl_listener *listener, void *data); -+static void associatex11(struct wl_listener *listener, void *data); -+static void configurex11(struct wl_listener *listener, void *data); -+static void createnotifyx11(struct wl_listener *listener, void *data); -+static void dissociatex11(struct wl_listener *listener, void *data); -+static void sethints(struct wl_listener *listener, void *data); -+static void xwaylandready(struct wl_listener *listener, void *data); -+static struct wl_listener new_xwayland_surface = {.notify = createnotifyx11}; -+static struct wl_listener xwayland_ready = {.notify = xwaylandready}; -+static struct wlr_xwayland *xwayland; -+#endif -+ -+/* configuration, allows nested code to access above variables */ -+#include "config.h" -+ -+/* attempt to encapsulate suck into one file */ -+#include "client.h" -+ -+static pid_t *autostart_pids; -+static size_t autostart_len; -+ -+/* function implementations */ -+void -+applybounds(Client *c, struct wlr_box *bbox) -+{ -+ /* set minimum possible */ -+ c->geom.width = MAX(1 + 2 * (int)c->bw, c->geom.width); -+ c->geom.height = MAX(1 + 2 * (int)c->bw, c->geom.height); -+ -+ if (c->geom.x >= bbox->x + bbox->width) -+ c->geom.x = bbox->x + bbox->width - c->geom.width; -+ if (c->geom.y >= bbox->y + bbox->height) -+ c->geom.y = bbox->y + bbox->height - c->geom.height; -+ if (c->geom.x + c->geom.width <= bbox->x) -+ c->geom.x = bbox->x; -+ if (c->geom.y + c->geom.height <= bbox->y) -+ c->geom.y = bbox->y; -+} -+ -+void -+applyrules(Client *c) -+{ -+ /* rule matching */ -+ const char *appid, *title; -+ uint32_t newtags = 0; -+ int i; -+ const Rule *r; -+ Monitor *mon = selmon, *m; -+ -+ appid = client_get_appid(c); -+ title = client_get_title(c); -+ -+ for (r = rules; r < END(rules); r++) { -+ if ((!r->title || strstr(title, r->title)) -+ && (!r->id || strstr(appid, r->id))) { -+ c->isfloating = r->isfloating; -+ newtags |= r->tags; -+ i = 0; -+ wl_list_for_each(m, &mons, link) { -+ if (r->monitor == i++) -+ mon = m; -+ } -+ } -+ } -+ -+ c->isfloating |= client_is_float_type(c); -+ setmon(c, mon, newtags); -+} -+ -+void -+arrange(Monitor *m) -+{ -+ Client *c; -+ -+ if (!m->wlr_output->enabled) -+ return; -+ -+ wl_list_for_each(c, &clients, link) { -+ if (c->mon == m) { -+ wlr_scene_node_set_enabled(&c->scene->node, VISIBLEON(c, m)); -+ client_set_suspended(c, !VISIBLEON(c, m)); -+ } -+ } -+ -+ wlr_scene_node_set_enabled(&m->fullscreen_bg->node, -+ (c = focustop(m)) && c->isfullscreen); -+ -+ strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, LENGTH(m->ltsymbol)); -+ -+ /* We move all clients (except fullscreen and unmanaged) to LyrTile while -+ * in floating layout to avoid "real" floating clients be always on top */ -+ wl_list_for_each(c, &clients, link) { -+ if (c->mon != m || c->scene->node.parent == layers[LyrFS]) -+ continue; -+ -+ wlr_scene_node_reparent(&c->scene->node, -+ (!m->lt[m->sellt]->arrange && c->isfloating) -+ ? layers[LyrTile] -+ : (m->lt[m->sellt]->arrange && c->isfloating) -+ ? layers[LyrFloat] -+ : c->scene->node.parent); -+ } -+ -+ if (m->lt[m->sellt]->arrange) -+ m->lt[m->sellt]->arrange(m); -+ motionnotify(0, NULL, 0, 0, 0, 0); -+ checkidleinhibitor(NULL); -+} -+ -+void -+arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, int exclusive) -+{ -+ LayerSurface *l; -+ struct wlr_box full_area = m->m; -+ -+ wl_list_for_each(l, list, link) { -+ struct wlr_layer_surface_v1 *layer_surface = l->layer_surface; -+ -+ if (!layer_surface->initialized) -+ continue; -+ -+ if (exclusive != (layer_surface->current.exclusive_zone > 0)) -+ continue; -+ -+ wlr_scene_layer_surface_v1_configure(l->scene_layer, &full_area, usable_area); -+ wlr_scene_node_set_position(&l->popups->node, l->scene->node.x, l->scene->node.y); -+ } -+} -+ -+void -+arrangelayers(Monitor *m) -+{ -+ int i; -+ struct wlr_box usable_area = m->m; -+ LayerSurface *l; -+ uint32_t layers_above_shell[] = { -+ ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, -+ ZWLR_LAYER_SHELL_V1_LAYER_TOP, -+ }; -+ if (!m->wlr_output->enabled) -+ return; -+ -+ /* Arrange exclusive surfaces from top->bottom */ -+ for (i = 3; i >= 0; i--) -+ arrangelayer(m, &m->layers[i], &usable_area, 1); -+ -+ if (!wlr_box_equal(&usable_area, &m->w)) { -+ m->w = usable_area; -+ arrange(m); -+ } -+ -+ /* Arrange non-exlusive surfaces from top->bottom */ -+ for (i = 3; i >= 0; i--) -+ arrangelayer(m, &m->layers[i], &usable_area, 0); -+ -+ /* Find topmost keyboard interactive layer, if such a layer exists */ -+ for (i = 0; i < (int)LENGTH(layers_above_shell); i++) { -+ wl_list_for_each_reverse(l, &m->layers[layers_above_shell[i]], link) { -+ if (locked || !l->layer_surface->current.keyboard_interactive || !l->mapped) -+ continue; -+ /* Deactivate the focused client. */ -+ focusclient(NULL, 0); -+ exclusive_focus = l; -+ client_notify_enter(l->layer_surface->surface, wlr_seat_get_keyboard(seat)); -+ return; -+ } -+ } -+} -+ -+void -+autostartexec(void) { -+ const char *const *p; -+ size_t i = 0; -+ -+ /* count entries */ -+ for (p = autostart; *p; autostart_len++, p++) -+ while (*++p); -+ -+ autostart_pids = calloc(autostart_len, sizeof(pid_t)); -+ for (p = autostart; *p; i++, p++) { -+ if ((autostart_pids[i] = fork()) == 0) { -+ setsid(); -+ execvp(*p, (char *const *)p); -+ die("dwl: execvp %s:", *p); -+ } -+ /* skip arguments */ -+ while (*++p); -+ } -+} -+ -+void -+axisnotify(struct wl_listener *listener, void *data) -+{ -+ /* This event is forwarded by the cursor when a pointer emits an axis event, -+ * for example when you move the scroll wheel. */ -+ struct wlr_pointer_axis_event *event = data; -+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); -+ /* TODO: allow usage of scroll wheel for mousebindings, it can be implemented -+ * by checking the event's orientation and the delta of the event */ -+ /* Notify the client with pointer focus of the axis event. */ -+ wlr_seat_pointer_notify_axis(seat, -+ event->time_msec, event->orientation, event->delta, -+ event->delta_discrete, event->source, event->relative_direction); -+} -+ -+void -+buttonpress(struct wl_listener *listener, void *data) -+{ -+ struct wlr_pointer_button_event *event = data; -+ struct wlr_keyboard *keyboard; -+ uint32_t mods; -+ Client *c; -+ const Button *b; -+ -+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); -+ -+ switch (event->state) { -+ case WL_POINTER_BUTTON_STATE_PRESSED: -+ cursor_mode = CurPressed; -+ selmon = xytomon(cursor->x, cursor->y); -+ if (locked) -+ break; -+ -+ /* Change focus if the button was _pressed_ over a client */ -+ xytonode(cursor->x, cursor->y, NULL, &c, NULL, NULL, NULL); -+ if (c && (!client_is_unmanaged(c) || client_wants_focus(c))) -+ focusclient(c, 1); -+ -+ keyboard = wlr_seat_get_keyboard(seat); -+ mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; -+ for (b = buttons; b < END(buttons); b++) { -+ if (CLEANMASK(mods) == CLEANMASK(b->mod) && -+ event->button == b->button && b->func) { -+ b->func(&b->arg); -+ return; -+ } -+ } -+ break; -+ case WL_POINTER_BUTTON_STATE_RELEASED: -+ /* If you released any buttons, we exit interactive move/resize mode. */ -+ /* TODO: should reset to the pointer focus's current setcursor */ -+ if (!locked && cursor_mode != CurNormal && cursor_mode != CurPressed) { -+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "default"); -+ cursor_mode = CurNormal; -+ /* Drop the window off on its new monitor */ -+ selmon = xytomon(cursor->x, cursor->y); -+ setmon(grabc, selmon, 0); -+ grabc = NULL; -+ return; -+ } -+ cursor_mode = CurNormal; -+ break; -+ } -+ /* If the event wasn't handled by the compositor, notify the client with -+ * pointer focus that a button press has occurred */ -+ wlr_seat_pointer_notify_button(seat, -+ event->time_msec, event->button, event->state); -+} -+ -+void -+chvt(const Arg *arg) -+{ -+ wlr_session_change_vt(session, arg->ui); -+} -+ -+void -+checkidleinhibitor(struct wlr_surface *exclude) -+{ -+ int inhibited = 0, unused_lx, unused_ly; -+ struct wlr_idle_inhibitor_v1 *inhibitor; -+ wl_list_for_each(inhibitor, &idle_inhibit_mgr->inhibitors, link) { -+ struct wlr_surface *surface = wlr_surface_get_root_surface(inhibitor->surface); -+ struct wlr_scene_tree *tree = surface->data; -+ if (exclude != surface && (bypass_surface_visibility || (!tree -+ || wlr_scene_node_coords(&tree->node, &unused_lx, &unused_ly)))) { -+ inhibited = 1; -+ break; -+ } -+ } -+ -+ wlr_idle_notifier_v1_set_inhibited(idle_notifier, inhibited); -+} -+ -+void -+cleanup(void) -+{ -+ size_t i; -+ -+ cleanuplisteners(); -+#ifdef XWAYLAND -+ wlr_xwayland_destroy(xwayland); -+ xwayland = NULL; -+#endif -+ wl_display_destroy_clients(dpy); -+ -+ /* kill child processes */ -+ for (i = 0; i < autostart_len; i++) { -+ if (0 < autostart_pids[i]) { -+ kill(autostart_pids[i], SIGTERM); -+ waitpid(autostart_pids[i], NULL, 0); -+ } -+ } -+ -+ if (child_pid > 0) { -+ kill(-child_pid, SIGTERM); -+ waitpid(child_pid, NULL, 0); -+ } -+ wlr_xcursor_manager_destroy(cursor_mgr); -+ -+ destroykeyboardgroup(&kb_group->destroy, NULL); -+ -+ /* If it's not destroyed manually, it will cause a use-after-free of wlr_seat. -+ * Destroy it until it's fixed on the wlroots side */ -+ wlr_backend_destroy(backend); -+ -+ wl_display_destroy(dpy); -+ /* Destroy after the wayland display (when the monitors are already destroyed) -+ to avoid destroying them with an invalid scene output. */ -+ wlr_scene_node_destroy(&scene->tree.node); -+} -+ -+void -+cleanupmon(struct wl_listener *listener, void *data) -+{ -+ Monitor *m = wl_container_of(listener, m, destroy); -+ LayerSurface *l, *tmp; -+ size_t i; -+ -+ /* m->layers[i] are intentionally not unlinked */ -+ for (i = 0; i < LENGTH(m->layers); i++) { -+ wl_list_for_each_safe(l, tmp, &m->layers[i], link) -+ wlr_layer_surface_v1_destroy(l->layer_surface); -+ } -+ -+ wl_list_remove(&m->destroy.link); -+ wl_list_remove(&m->frame.link); -+ wl_list_remove(&m->link); -+ wl_list_remove(&m->request_state.link); -+ if (m->lock_surface) -+ destroylocksurface(&m->destroy_lock_surface, NULL); -+ m->wlr_output->data = NULL; -+ wlr_output_layout_remove(output_layout, m->wlr_output); -+ wlr_scene_output_destroy(m->scene_output); -+ -+ closemon(m); -+ wlr_scene_node_destroy(&m->fullscreen_bg->node); -+ free(m); -+} -+ -+void -+cleanuplisteners(void) -+{ -+ wl_list_remove(&cursor_axis.link); -+ wl_list_remove(&cursor_button.link); -+ wl_list_remove(&cursor_frame.link); -+ wl_list_remove(&cursor_motion.link); -+ wl_list_remove(&cursor_motion_absolute.link); -+ wl_list_remove(&gpu_reset.link); -+ wl_list_remove(&new_idle_inhibitor.link); -+ wl_list_remove(&layout_change.link); -+ wl_list_remove(&new_input_device.link); -+ wl_list_remove(&new_virtual_keyboard.link); -+ wl_list_remove(&new_virtual_pointer.link); -+ wl_list_remove(&new_pointer_constraint.link); -+ wl_list_remove(&new_output.link); -+ wl_list_remove(&new_xdg_toplevel.link); -+ wl_list_remove(&new_xdg_decoration.link); -+ wl_list_remove(&new_xdg_popup.link); -+ wl_list_remove(&new_layer_surface.link); -+ wl_list_remove(&output_mgr_apply.link); -+ wl_list_remove(&output_mgr_test.link); -+ wl_list_remove(&output_power_mgr_set_mode.link); -+ wl_list_remove(&request_activate.link); -+ wl_list_remove(&request_cursor.link); -+ wl_list_remove(&request_set_psel.link); -+ wl_list_remove(&request_set_sel.link); -+ wl_list_remove(&request_set_cursor_shape.link); -+ wl_list_remove(&request_start_drag.link); -+ wl_list_remove(&start_drag.link); -+ wl_list_remove(&new_session_lock.link); -+#ifdef XWAYLAND -+ wl_list_remove(&new_xwayland_surface.link); -+ wl_list_remove(&xwayland_ready.link); -+#endif -+} -+ -+void -+closemon(Monitor *m) -+{ -+ /* update selmon if needed and -+ * move closed monitor's clients to the focused one */ -+ Client *c; -+ int i = 0, nmons = wl_list_length(&mons); -+ if (!nmons) { -+ selmon = NULL; -+ } else if (m == selmon) { -+ do /* don't switch to disabled mons */ -+ selmon = wl_container_of(mons.next, selmon, link); -+ while (!selmon->wlr_output->enabled && i++ < nmons); -+ -+ if (!selmon->wlr_output->enabled) -+ selmon = NULL; -+ } -+ -+ wl_list_for_each(c, &clients, link) { -+ if (c->isfloating && c->geom.x > m->m.width) -+ resize(c, (struct wlr_box){.x = c->geom.x - m->w.width, .y = c->geom.y, -+ .width = c->geom.width, .height = c->geom.height}, 0); -+ if (c->mon == m) -+ setmon(c, selmon, c->tags); -+ } -+ focusclient(focustop(selmon), 1); -+ printstatus(); -+} -+ -+void -+commitlayersurfacenotify(struct wl_listener *listener, void *data) -+{ -+ LayerSurface *l = wl_container_of(listener, l, surface_commit); -+ struct wlr_layer_surface_v1 *layer_surface = l->layer_surface; -+ struct wlr_scene_tree *scene_layer = layers[layermap[layer_surface->current.layer]]; -+ struct wlr_layer_surface_v1_state old_state; -+ -+ if (l->layer_surface->initial_commit) { -+ client_set_scale(layer_surface->surface, l->mon->wlr_output->scale); -+ -+ /* Temporarily set the layer's current state to pending -+ * so that we can easily arrange it */ -+ old_state = l->layer_surface->current; -+ l->layer_surface->current = l->layer_surface->pending; -+ arrangelayers(l->mon); -+ l->layer_surface->current = old_state; -+ return; -+ } -+ -+ if (layer_surface->current.committed == 0 && l->mapped == layer_surface->surface->mapped) -+ return; -+ l->mapped = layer_surface->surface->mapped; -+ -+ if (scene_layer != l->scene->node.parent) { -+ wlr_scene_node_reparent(&l->scene->node, scene_layer); -+ wl_list_remove(&l->link); -+ wl_list_insert(&l->mon->layers[layer_surface->current.layer], &l->link); -+ wlr_scene_node_reparent(&l->popups->node, (layer_surface->current.layer -+ < ZWLR_LAYER_SHELL_V1_LAYER_TOP ? layers[LyrTop] : scene_layer)); -+ } -+ -+ arrangelayers(l->mon); -+} -+ -+void -+commitnotify(struct wl_listener *listener, void *data) -+{ -+ Client *c = wl_container_of(listener, c, commit); -+ -+ if (c->surface.xdg->initial_commit) { -+ /* -+ * Get the monitor this client will be rendered on -+ * Note that if the user set a rule in which the client is placed on -+ * a different monitor based on its title, this will likely select -+ * a wrong monitor. -+ */ -+ applyrules(c); -+ if (c->mon) { -+ client_set_scale(client_surface(c), c->mon->wlr_output->scale); -+ } -+ setmon(c, NULL, 0); /* Make sure to reapply rules in mapnotify() */ -+ -+ wlr_xdg_toplevel_set_wm_capabilities(c->surface.xdg->toplevel, -+ WLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN); -+ if (c->decoration) -+ requestdecorationmode(&c->set_decoration_mode, c->decoration); -+ wlr_xdg_toplevel_set_size(c->surface.xdg->toplevel, 0, 0); -+ return; -+ } -+ -+ resize(c, c->geom, (c->isfloating && !c->isfullscreen)); -+ -+ /* mark a pending resize as completed */ -+ if (c->resize && c->resize <= c->surface.xdg->current.configure_serial) -+ c->resize = 0; -+} -+ -+void -+commitpopup(struct wl_listener *listener, void *data) -+{ -+ struct wlr_surface *surface = data; -+ struct wlr_xdg_popup *popup = wlr_xdg_popup_try_from_wlr_surface(surface); -+ LayerSurface *l = NULL; -+ Client *c = NULL; -+ struct wlr_box box; -+ int type = -1; -+ -+ if (!popup->base->initial_commit) -+ return; -+ -+ type = toplevel_from_wlr_surface(popup->base->surface, &c, &l); -+ if (!popup->parent || type < 0) -+ return; -+ popup->base->surface->data = wlr_scene_xdg_surface_create( -+ popup->parent->data, popup->base); -+ if ((l && !l->mon) || (c && !c->mon)) { -+ wlr_xdg_popup_destroy(popup); -+ return; -+ } -+ box = type == LayerShell ? l->mon->m : c->mon->w; -+ box.x -= (type == LayerShell ? l->scene->node.x : c->geom.x); -+ box.y -= (type == LayerShell ? l->scene->node.y : c->geom.y); -+ wlr_xdg_popup_unconstrain_from_box(popup, &box); -+ wl_list_remove(&listener->link); -+ free(listener); -+} -+ -+void -+createdecoration(struct wl_listener *listener, void *data) -+{ -+ struct wlr_xdg_toplevel_decoration_v1 *deco = data; -+ Client *c = deco->toplevel->base->data; -+ c->decoration = deco; -+ -+ LISTEN(&deco->events.request_mode, &c->set_decoration_mode, requestdecorationmode); -+ LISTEN(&deco->events.destroy, &c->destroy_decoration, destroydecoration); -+ -+ requestdecorationmode(&c->set_decoration_mode, deco); -+} -+ -+void -+createidleinhibitor(struct wl_listener *listener, void *data) -+{ -+ struct wlr_idle_inhibitor_v1 *idle_inhibitor = data; -+ LISTEN_STATIC(&idle_inhibitor->events.destroy, destroyidleinhibitor); -+ -+ checkidleinhibitor(NULL); -+} -+ -+void -+createkeyboard(struct wlr_keyboard *keyboard) -+{ -+ /* Set the keymap to match the group keymap */ -+ wlr_keyboard_set_keymap(keyboard, kb_group->wlr_group->keyboard.keymap); -+ -+ /* Add the new keyboard to the group */ -+ wlr_keyboard_group_add_keyboard(kb_group->wlr_group, keyboard); -+} -+ -+KeyboardGroup * -+createkeyboardgroup(void) -+{ -+ KeyboardGroup *group = ecalloc(1, sizeof(*group)); -+ struct xkb_context *context; -+ struct xkb_keymap *keymap; -+ -+ group->wlr_group = wlr_keyboard_group_create(); -+ group->wlr_group->data = group; -+ -+ /* Prepare an XKB keymap and assign it to the keyboard group. */ -+ context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); -+ if (!(keymap = xkb_keymap_new_from_names(context, &xkb_rules, -+ XKB_KEYMAP_COMPILE_NO_FLAGS))) -+ die("failed to compile keymap"); -+ -+ wlr_keyboard_set_keymap(&group->wlr_group->keyboard, keymap); -+ xkb_keymap_unref(keymap); -+ xkb_context_unref(context); -+ -+ wlr_keyboard_set_repeat_info(&group->wlr_group->keyboard, repeat_rate, repeat_delay); -+ -+ /* Set up listeners for keyboard events */ -+ LISTEN(&group->wlr_group->keyboard.events.key, &group->key, keypress); -+ LISTEN(&group->wlr_group->keyboard.events.modifiers, &group->modifiers, keypressmod); -+ -+ group->key_repeat_source = wl_event_loop_add_timer(event_loop, keyrepeat, group); -+ -+ /* A seat can only have one keyboard, but this is a limitation of the -+ * Wayland protocol - not wlroots. We assign all connected keyboards to the -+ * same wlr_keyboard_group, which provides a single wlr_keyboard interface for -+ * all of them. Set this combined wlr_keyboard as the seat keyboard. -+ */ -+ wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard); -+ return group; -+} -+ -+void -+createlayersurface(struct wl_listener *listener, void *data) -+{ -+ struct wlr_layer_surface_v1 *layer_surface = data; -+ LayerSurface *l; -+ struct wlr_surface *surface = layer_surface->surface; -+ struct wlr_scene_tree *scene_layer = layers[layermap[layer_surface->pending.layer]]; -+ -+ if (!layer_surface->output -+ && !(layer_surface->output = selmon ? selmon->wlr_output : NULL)) { -+ wlr_layer_surface_v1_destroy(layer_surface); -+ return; -+ } -+ -+ l = layer_surface->data = ecalloc(1, sizeof(*l)); -+ l->type = LayerShell; -+ LISTEN(&surface->events.commit, &l->surface_commit, commitlayersurfacenotify); -+ LISTEN(&surface->events.unmap, &l->unmap, unmaplayersurfacenotify); -+ LISTEN(&layer_surface->events.destroy, &l->destroy, destroylayersurfacenotify); -+ -+ l->layer_surface = layer_surface; -+ l->mon = layer_surface->output->data; -+ l->scene_layer = wlr_scene_layer_surface_v1_create(scene_layer, layer_surface); -+ l->scene = l->scene_layer->tree; -+ l->popups = surface->data = wlr_scene_tree_create(layer_surface->current.layer -+ < ZWLR_LAYER_SHELL_V1_LAYER_TOP ? layers[LyrTop] : scene_layer); -+ l->scene->node.data = l->popups->node.data = l; -+ -+ wl_list_insert(&l->mon->layers[layer_surface->pending.layer],&l->link); -+ wlr_surface_send_enter(surface, layer_surface->output); -+} -+ -+void -+createlocksurface(struct wl_listener *listener, void *data) -+{ -+ SessionLock *lock = wl_container_of(listener, lock, new_surface); -+ struct wlr_session_lock_surface_v1 *lock_surface = data; -+ Monitor *m = lock_surface->output->data; -+ struct wlr_scene_tree *scene_tree = lock_surface->surface->data -+ = wlr_scene_subsurface_tree_create(lock->scene, lock_surface->surface); -+ m->lock_surface = lock_surface; -+ -+ wlr_scene_node_set_position(&scene_tree->node, m->m.x, m->m.y); -+ wlr_session_lock_surface_v1_configure(lock_surface, m->m.width, m->m.height); -+ -+ LISTEN(&lock_surface->events.destroy, &m->destroy_lock_surface, destroylocksurface); -+ -+ if (m == selmon) -+ client_notify_enter(lock_surface->surface, wlr_seat_get_keyboard(seat)); -+} -+ -+void -+createmon(struct wl_listener *listener, void *data) -+{ -+ /* This event is raised by the backend when a new output (aka a display or -+ * monitor) becomes available. */ -+ struct wlr_output *wlr_output = data; -+ const MonitorRule *r; -+ size_t i; -+ struct wlr_output_state state; -+ Monitor *m; -+ -+ if (!wlr_output_init_render(wlr_output, alloc, drw)) -+ return; -+ -+ m = wlr_output->data = ecalloc(1, sizeof(*m)); -+ m->wlr_output = wlr_output; -+ -+ for (i = 0; i < LENGTH(m->layers); i++) -+ wl_list_init(&m->layers[i]); -+ -+ wlr_output_state_init(&state); -+ /* Initialize monitor state using configured rules */ -+ m->tagset[0] = m->tagset[1] = 1; -+ for (r = monrules; r < END(monrules); r++) { -+ if (!r->name || strstr(wlr_output->name, r->name)) { -+ m->m.x = r->x; -+ m->m.y = r->y; -+ m->mfact = r->mfact; -+ m->nmaster = r->nmaster; -+ m->lt[0] = r->lt; -+ m->lt[1] = &layouts[LENGTH(layouts) > 1 && r->lt != &layouts[1]]; -+ strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, LENGTH(m->ltsymbol)); -+ wlr_output_state_set_scale(&state, r->scale); -+ wlr_output_state_set_transform(&state, r->rr); -+ break; -+ } -+ } -+ -+ /* The mode is a tuple of (width, height, refresh rate), and each -+ * monitor supports only a specific set of modes. We just pick the -+ * monitor's preferred mode; a more sophisticated compositor would let -+ * the user configure it. */ -+ wlr_output_state_set_mode(&state, wlr_output_preferred_mode(wlr_output)); -+ -+ /* Set up event listeners */ -+ LISTEN(&wlr_output->events.frame, &m->frame, rendermon); -+ LISTEN(&wlr_output->events.destroy, &m->destroy, cleanupmon); -+ LISTEN(&wlr_output->events.request_state, &m->request_state, requestmonstate); -+ -+ wlr_output_state_set_enabled(&state, 1); -+ wlr_output_commit_state(wlr_output, &state); -+ wlr_output_state_finish(&state); -+ -+ wl_list_insert(&mons, &m->link); -+ printstatus(); -+ -+ /* The xdg-protocol specifies: -+ * -+ * If the fullscreened surface is not opaque, the compositor must make -+ * sure that other screen content not part of the same surface tree (made -+ * up of subsurfaces, popups or similarly coupled surfaces) are not -+ * visible below the fullscreened surface. -+ * -+ */ -+ /* updatemons() will resize and set correct position */ -+ m->fullscreen_bg = wlr_scene_rect_create(layers[LyrFS], 0, 0, fullscreen_bg); -+ wlr_scene_node_set_enabled(&m->fullscreen_bg->node, 0); -+ -+ /* Adds this to the output layout in the order it was configured. -+ * -+ * The output layout utility automatically adds a wl_output global to the -+ * display, which Wayland clients can see to find out information about the -+ * output (such as DPI, scale factor, manufacturer, etc). -+ */ -+ m->scene_output = wlr_scene_output_create(scene, wlr_output); -+ if (m->m.x == -1 && m->m.y == -1) -+ wlr_output_layout_add_auto(output_layout, wlr_output); -+ else -+ wlr_output_layout_add(output_layout, wlr_output, m->m.x, m->m.y); -+} -+ -+void -+createnotify(struct wl_listener *listener, void *data) -+{ -+ /* This event is raised when a client creates a new toplevel (application window). */ -+ struct wlr_xdg_toplevel *toplevel = data; -+ Client *c = NULL; -+ -+ /* Allocate a Client for this surface */ -+ c = toplevel->base->data = ecalloc(1, sizeof(*c)); -+ c->surface.xdg = toplevel->base; -+ c->bw = borderpx; -+ -+ LISTEN(&toplevel->base->surface->events.commit, &c->commit, commitnotify); -+ LISTEN(&toplevel->base->surface->events.map, &c->map, mapnotify); -+ LISTEN(&toplevel->base->surface->events.unmap, &c->unmap, unmapnotify); -+ LISTEN(&toplevel->events.destroy, &c->destroy, destroynotify); -+ LISTEN(&toplevel->events.request_fullscreen, &c->fullscreen, fullscreennotify); -+ LISTEN(&toplevel->events.request_maximize, &c->maximize, maximizenotify); -+ LISTEN(&toplevel->events.set_title, &c->set_title, updatetitle); -+} -+ -+void -+createpointer(struct wlr_pointer *pointer) -+{ -+ struct libinput_device *device; -+ if (wlr_input_device_is_libinput(&pointer->base) -+ && (device = wlr_libinput_get_device_handle(&pointer->base))) { -+ -+ if (libinput_device_config_tap_get_finger_count(device)) { -+ libinput_device_config_tap_set_enabled(device, tap_to_click); -+ libinput_device_config_tap_set_drag_enabled(device, tap_and_drag); -+ libinput_device_config_tap_set_drag_lock_enabled(device, drag_lock); -+ libinput_device_config_tap_set_button_map(device, button_map); -+ } -+ -+ if (libinput_device_config_scroll_has_natural_scroll(device)) -+ libinput_device_config_scroll_set_natural_scroll_enabled(device, natural_scrolling); -+ -+ if (libinput_device_config_dwt_is_available(device)) -+ libinput_device_config_dwt_set_enabled(device, disable_while_typing); -+ -+ if (libinput_device_config_left_handed_is_available(device)) -+ libinput_device_config_left_handed_set(device, left_handed); -+ -+ if (libinput_device_config_middle_emulation_is_available(device)) -+ libinput_device_config_middle_emulation_set_enabled(device, middle_button_emulation); -+ -+ if (libinput_device_config_scroll_get_methods(device) != LIBINPUT_CONFIG_SCROLL_NO_SCROLL) -+ libinput_device_config_scroll_set_method(device, scroll_method); -+ -+ if (libinput_device_config_click_get_methods(device) != LIBINPUT_CONFIG_CLICK_METHOD_NONE) -+ libinput_device_config_click_set_method(device, click_method); -+ -+ if (libinput_device_config_send_events_get_modes(device)) -+ libinput_device_config_send_events_set_mode(device, send_events_mode); -+ -+ if (libinput_device_config_accel_is_available(device)) { -+ libinput_device_config_accel_set_profile(device, accel_profile); -+ libinput_device_config_accel_set_speed(device, accel_speed); -+ } -+ } -+ -+ wlr_cursor_attach_input_device(cursor, &pointer->base); -+} -+ -+void -+createpointerconstraint(struct wl_listener *listener, void *data) -+{ -+ PointerConstraint *pointer_constraint = ecalloc(1, sizeof(*pointer_constraint)); -+ pointer_constraint->constraint = data; -+ LISTEN(&pointer_constraint->constraint->events.destroy, -+ &pointer_constraint->destroy, destroypointerconstraint); -+} -+ -+void -+createpopup(struct wl_listener *listener, void *data) -+{ -+ /* This event is raised when a client (either xdg-shell or layer-shell) -+ * creates a new popup. */ -+ struct wlr_xdg_popup *popup = data; -+ LISTEN_STATIC(&popup->base->surface->events.commit, commitpopup); -+} -+ -+void -+cursorconstrain(struct wlr_pointer_constraint_v1 *constraint) -+{ -+ if (active_constraint == constraint) -+ return; -+ -+ if (active_constraint) -+ wlr_pointer_constraint_v1_send_deactivated(active_constraint); -+ -+ active_constraint = constraint; -+ wlr_pointer_constraint_v1_send_activated(constraint); -+} -+ -+void -+cursorframe(struct wl_listener *listener, void *data) -+{ -+ /* This event is forwarded by the cursor when a pointer emits a frame -+ * event. Frame events are sent after regular pointer events to group -+ * multiple events together. For instance, two axis events may happen at the -+ * same time, in which case a frame event won't be sent in between. */ -+ /* Notify the client with pointer focus of the frame event. */ -+ wlr_seat_pointer_notify_frame(seat); -+} -+ -+void -+cursorwarptohint(void) -+{ -+ Client *c = NULL; -+ double sx = active_constraint->current.cursor_hint.x; -+ double sy = active_constraint->current.cursor_hint.y; -+ -+ toplevel_from_wlr_surface(active_constraint->surface, &c, NULL); -+ if (c && active_constraint->current.cursor_hint.enabled) { -+ wlr_cursor_warp(cursor, NULL, sx + c->geom.x + c->bw, sy + c->geom.y + c->bw); -+ wlr_seat_pointer_warp(active_constraint->seat, sx, sy); -+ } -+} -+ -+void -+destroydecoration(struct wl_listener *listener, void *data) -+{ -+ Client *c = wl_container_of(listener, c, destroy_decoration); -+ -+ wl_list_remove(&c->destroy_decoration.link); -+ wl_list_remove(&c->set_decoration_mode.link); -+} -+ -+void -+destroydragicon(struct wl_listener *listener, void *data) -+{ -+ /* Focus enter isn't sent during drag, so refocus the focused node. */ -+ focusclient(focustop(selmon), 1); -+ motionnotify(0, NULL, 0, 0, 0, 0); -+ wl_list_remove(&listener->link); -+ free(listener); -+} -+ -+void -+destroyidleinhibitor(struct wl_listener *listener, void *data) -+{ -+ /* `data` is the wlr_surface of the idle inhibitor being destroyed, -+ * at this point the idle inhibitor is still in the list of the manager */ -+ checkidleinhibitor(wlr_surface_get_root_surface(data)); -+ wl_list_remove(&listener->link); -+ free(listener); -+} -+ -+void -+destroylayersurfacenotify(struct wl_listener *listener, void *data) -+{ -+ LayerSurface *l = wl_container_of(listener, l, destroy); -+ -+ wl_list_remove(&l->link); -+ wl_list_remove(&l->destroy.link); -+ wl_list_remove(&l->unmap.link); -+ wl_list_remove(&l->surface_commit.link); -+ wlr_scene_node_destroy(&l->scene->node); -+ wlr_scene_node_destroy(&l->popups->node); -+ free(l); -+} -+ -+void -+destroylock(SessionLock *lock, int unlock) -+{ -+ wlr_seat_keyboard_notify_clear_focus(seat); -+ if ((locked = !unlock)) -+ goto destroy; -+ -+ wlr_scene_node_set_enabled(&locked_bg->node, 0); -+ -+ focusclient(focustop(selmon), 0); -+ motionnotify(0, NULL, 0, 0, 0, 0); -+ -+destroy: -+ wl_list_remove(&lock->new_surface.link); -+ wl_list_remove(&lock->unlock.link); -+ wl_list_remove(&lock->destroy.link); -+ -+ wlr_scene_node_destroy(&lock->scene->node); -+ cur_lock = NULL; -+ free(lock); -+} -+ -+void -+destroylocksurface(struct wl_listener *listener, void *data) -+{ -+ Monitor *m = wl_container_of(listener, m, destroy_lock_surface); -+ struct wlr_session_lock_surface_v1 *surface, *lock_surface = m->lock_surface; -+ -+ m->lock_surface = NULL; -+ wl_list_remove(&m->destroy_lock_surface.link); -+ -+ if (lock_surface->surface != seat->keyboard_state.focused_surface) -+ return; -+ -+ if (locked && cur_lock && !wl_list_empty(&cur_lock->surfaces)) { -+ surface = wl_container_of(cur_lock->surfaces.next, surface, link); -+ client_notify_enter(surface->surface, wlr_seat_get_keyboard(seat)); -+ } else if (!locked) { -+ focusclient(focustop(selmon), 1); -+ } else { -+ wlr_seat_keyboard_clear_focus(seat); -+ } -+} -+ -+void -+destroynotify(struct wl_listener *listener, void *data) -+{ -+ /* Called when the xdg_toplevel is destroyed. */ -+ Client *c = wl_container_of(listener, c, destroy); -+ wl_list_remove(&c->destroy.link); -+ wl_list_remove(&c->set_title.link); -+ wl_list_remove(&c->fullscreen.link); -+#ifdef XWAYLAND -+ if (c->type != XDGShell) { -+ wl_list_remove(&c->activate.link); -+ wl_list_remove(&c->associate.link); -+ wl_list_remove(&c->configure.link); -+ wl_list_remove(&c->dissociate.link); -+ wl_list_remove(&c->set_hints.link); -+ } else -+#endif -+ { -+ wl_list_remove(&c->commit.link); -+ wl_list_remove(&c->map.link); -+ wl_list_remove(&c->unmap.link); -+ wl_list_remove(&c->maximize.link); -+ } -+ free(c); -+} -+ -+void -+destroypointerconstraint(struct wl_listener *listener, void *data) -+{ -+ PointerConstraint *pointer_constraint = wl_container_of(listener, pointer_constraint, destroy); -+ -+ if (active_constraint == pointer_constraint->constraint) { -+ cursorwarptohint(); -+ active_constraint = NULL; -+ } -+ -+ wl_list_remove(&pointer_constraint->destroy.link); -+ free(pointer_constraint); -+} -+ -+void -+destroysessionlock(struct wl_listener *listener, void *data) -+{ -+ SessionLock *lock = wl_container_of(listener, lock, destroy); -+ destroylock(lock, 0); -+} -+ -+void -+destroykeyboardgroup(struct wl_listener *listener, void *data) -+{ -+ KeyboardGroup *group = wl_container_of(listener, group, destroy); -+ wl_event_source_remove(group->key_repeat_source); -+ wl_list_remove(&group->key.link); -+ wl_list_remove(&group->modifiers.link); -+ wl_list_remove(&group->destroy.link); -+ wlr_keyboard_group_destroy(group->wlr_group); -+ free(group); -+} -+ -+Monitor * -+dirtomon(enum wlr_direction dir) -+{ -+ struct wlr_output *next; -+ if (!wlr_output_layout_get(output_layout, selmon->wlr_output)) -+ return selmon; -+ if ((next = wlr_output_layout_adjacent_output(output_layout, -+ dir, selmon->wlr_output, selmon->m.x, selmon->m.y))) -+ return next->data; -+ if ((next = wlr_output_layout_farthest_output(output_layout, -+ dir ^ (WLR_DIRECTION_LEFT|WLR_DIRECTION_RIGHT), -+ selmon->wlr_output, selmon->m.x, selmon->m.y))) -+ return next->data; -+ return selmon; -+} -+ -+void -+focusclient(Client *c, int lift) -+{ -+ struct wlr_surface *old = seat->keyboard_state.focused_surface; -+ int unused_lx, unused_ly, old_client_type; -+ Client *old_c = NULL; -+ LayerSurface *old_l = NULL; -+ -+ if (locked) -+ return; -+ -+ /* Raise client in stacking order if requested */ -+ if (c && lift) -+ wlr_scene_node_raise_to_top(&c->scene->node); -+ -+ if (c && client_surface(c) == old) -+ return; -+ -+ if ((old_client_type = toplevel_from_wlr_surface(old, &old_c, &old_l)) == XDGShell) { -+ struct wlr_xdg_popup *popup, *tmp; -+ wl_list_for_each_safe(popup, tmp, &old_c->surface.xdg->popups, link) -+ wlr_xdg_popup_destroy(popup); -+ } -+ -+ /* Put the new client atop the focus stack and select its monitor */ -+ if (c && !client_is_unmanaged(c)) { -+ wl_list_remove(&c->flink); -+ wl_list_insert(&fstack, &c->flink); -+ selmon = c->mon; -+ c->isurgent = 0; -+ -+ /* Don't change border color if there is an exclusive focus or we are -+ * handling a drag operation */ -+ if (!exclusive_focus && !seat->drag) -+ client_set_border_color(c, focuscolor); -+ } -+ -+ /* Deactivate old client if focus is changing */ -+ if (old && (!c || client_surface(c) != old)) { -+ /* If an overlay is focused, don't focus or activate the client, -+ * but only update its position in fstack to render its border with focuscolor -+ * and focus it after the overlay is closed. */ -+ if (old_client_type == LayerShell && wlr_scene_node_coords( -+ &old_l->scene->node, &unused_lx, &unused_ly) -+ && old_l->layer_surface->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) { -+ return; -+ } else if (old_c && old_c == exclusive_focus && client_wants_focus(old_c)) { -+ return; -+ /* Don't deactivate old client if the new one wants focus, as this causes issues with winecfg -+ * and probably other clients */ -+ } else if (old_c && !client_is_unmanaged(old_c) && (!c || !client_wants_focus(c))) { -+ client_set_border_color(old_c, bordercolor); -+ -+ client_activate_surface(old, 0); -+ } -+ } -+ printstatus(); -+ -+ if (!c) { -+ /* With no client, all we have left is to clear focus */ -+ wlr_seat_keyboard_notify_clear_focus(seat); -+ return; -+ } -+ -+ /* Change cursor surface */ -+ motionnotify(0, NULL, 0, 0, 0, 0); -+ -+ /* Have a client, so focus its top-level wlr_surface */ -+ client_notify_enter(client_surface(c), wlr_seat_get_keyboard(seat)); -+ -+ /* Activate the new client */ -+ client_activate_surface(client_surface(c), 1); -+} -+ -+void -+focusmon(const Arg *arg) -+{ -+ int i = 0, nmons = wl_list_length(&mons); -+ if (nmons) { -+ do /* don't switch to disabled mons */ -+ selmon = dirtomon(arg->i); -+ while (!selmon->wlr_output->enabled && i++ < nmons); -+ } -+ focusclient(focustop(selmon), 1); -+} -+ -+void -+focusstack(const Arg *arg) -+{ -+ /* Focus the next or previous client (in tiling order) on selmon */ -+ Client *c, *sel = focustop(selmon); -+ if (!sel || (sel->isfullscreen && !client_has_children(sel))) -+ return; -+ if (arg->i > 0) { -+ wl_list_for_each(c, &sel->link, link) { -+ if (&c->link == &clients) -+ continue; /* wrap past the sentinel node */ -+ if (VISIBLEON(c, selmon)) -+ break; /* found it */ -+ } -+ } else { -+ wl_list_for_each_reverse(c, &sel->link, link) { -+ if (&c->link == &clients) -+ continue; /* wrap past the sentinel node */ -+ if (VISIBLEON(c, selmon)) -+ break; /* found it */ -+ } -+ } -+ /* If only one client is visible on selmon, then c == sel */ -+ focusclient(c, 1); -+} -+ -+/* We probably should change the name of this: it sounds like it -+ * will focus the topmost client of this mon, when actually will -+ * only return that client */ -+Client * -+focustop(Monitor *m) -+{ -+ Client *c; -+ wl_list_for_each(c, &fstack, flink) { -+ if (VISIBLEON(c, m)) -+ return c; -+ } -+ return NULL; -+} -+ -+void -+fullscreennotify(struct wl_listener *listener, void *data) -+{ -+ Client *c = wl_container_of(listener, c, fullscreen); -+ setfullscreen(c, client_wants_fullscreen(c)); -+} -+ -+void -+gpureset(struct wl_listener *listener, void *data) -+{ -+ struct wlr_renderer *old_drw = drw; -+ struct wlr_allocator *old_alloc = alloc; -+ struct Monitor *m; -+ if (!(drw = wlr_renderer_autocreate(backend))) -+ die("couldn't recreate renderer"); -+ -+ if (!(alloc = wlr_allocator_autocreate(backend, drw))) -+ die("couldn't recreate allocator"); -+ -+ wl_list_remove(&gpu_reset.link); -+ wl_signal_add(&drw->events.lost, &gpu_reset); -+ -+ wlr_compositor_set_renderer(compositor, drw); -+ -+ wl_list_for_each(m, &mons, link) { -+ wlr_output_init_render(m->wlr_output, alloc, drw); -+ } -+ -+ wlr_allocator_destroy(old_alloc); -+ wlr_renderer_destroy(old_drw); -+} -+ -+void -+handlesig(int signo) -+{ -+ if (signo == SIGCHLD) { -+ pid_t pid, *p, *lim; -+ while ((pid = waitpid(-1, NULL, WNOHANG)) > 0) { -+ if (pid == child_pid) -+ child_pid = -1; -+ if (!(p = autostart_pids)) -+ continue; -+ lim = &p[autostart_len]; -+ -+ for (; p < lim; p++) { -+ if (*p == pid) { -+ *p = -1; -+ break; -+ } -+ } -+ } -+ } else if (signo == SIGINT || signo == SIGTERM) { -+ quit(NULL); -+ } -+} -+ -+void -+incnmaster(const Arg *arg) -+{ -+ if (!arg || !selmon) -+ return; -+ selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); -+ arrange(selmon); -+} -+ -+void -+inputdevice(struct wl_listener *listener, void *data) -+{ -+ /* This event is raised by the backend when a new input device becomes -+ * available. */ -+ struct wlr_input_device *device = data; -+ uint32_t caps; -+ -+ switch (device->type) { -+ case WLR_INPUT_DEVICE_KEYBOARD: -+ createkeyboard(wlr_keyboard_from_input_device(device)); -+ break; -+ case WLR_INPUT_DEVICE_POINTER: -+ createpointer(wlr_pointer_from_input_device(device)); -+ break; -+ default: -+ /* TODO handle other input device types */ -+ break; -+ } -+ -+ /* We need to let the wlr_seat know what our capabilities are, which is -+ * communiciated to the client. In dwl we always have a cursor, even if -+ * there are no pointer devices, so we always include that capability. */ -+ /* TODO do we actually require a cursor? */ -+ caps = WL_SEAT_CAPABILITY_POINTER; -+ if (!wl_list_empty(&kb_group->wlr_group->devices)) -+ caps |= WL_SEAT_CAPABILITY_KEYBOARD; -+ wlr_seat_set_capabilities(seat, caps); -+} -+ -+int -+keybinding(uint32_t mods, xkb_keysym_t sym) -+{ -+ /* -+ * Here we handle compositor keybindings. This is when the compositor is -+ * processing keys, rather than passing them on to the client for its own -+ * processing. -+ */ -+ const Key *k; -+ for (k = keys; k < END(keys); k++) { -+ if (CLEANMASK(mods) == CLEANMASK(k->mod) -+ && sym == k->keysym && k->func) { -+ k->func(&k->arg); -+ return 1; -+ } -+ } -+ return 0; -+} -+ -+void -+keypress(struct wl_listener *listener, void *data) -+{ -+ int i; -+ /* This event is raised when a key is pressed or released. */ -+ KeyboardGroup *group = wl_container_of(listener, group, key); -+ struct wlr_keyboard_key_event *event = data; -+ -+ /* Translate libinput keycode -> xkbcommon */ -+ uint32_t keycode = event->keycode + 8; -+ /* Get a list of keysyms based on the keymap for this keyboard */ -+ const xkb_keysym_t *syms; -+ int nsyms = xkb_state_key_get_syms( -+ group->wlr_group->keyboard.xkb_state, keycode, &syms); -+ -+ int handled = 0; -+ uint32_t mods = wlr_keyboard_get_modifiers(&group->wlr_group->keyboard); -+ -+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); -+ -+ /* On _press_ if there is no active screen locker, -+ * attempt to process a compositor keybinding. */ -+ if (!locked && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { -+ for (i = 0; i < nsyms; i++) -+ handled = keybinding(mods, syms[i]) || handled; -+ } -+ -+ if (handled && group->wlr_group->keyboard.repeat_info.delay > 0) { -+ group->mods = mods; -+ group->keysyms = syms; -+ group->nsyms = nsyms; -+ wl_event_source_timer_update(group->key_repeat_source, -+ group->wlr_group->keyboard.repeat_info.delay); -+ } else { -+ group->nsyms = 0; -+ wl_event_source_timer_update(group->key_repeat_source, 0); -+ } -+ -+ if (handled) -+ return; -+ -+ wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard); -+ /* Pass unhandled keycodes along to the client. */ -+ wlr_seat_keyboard_notify_key(seat, event->time_msec, -+ event->keycode, event->state); -+} -+ -+void -+keypressmod(struct wl_listener *listener, void *data) -+{ -+ /* This event is raised when a modifier key, such as shift or alt, is -+ * pressed. We simply communicate this to the client. */ -+ KeyboardGroup *group = wl_container_of(listener, group, modifiers); -+ -+ wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard); -+ /* Send modifiers to the client. */ -+ wlr_seat_keyboard_notify_modifiers(seat, -+ &group->wlr_group->keyboard.modifiers); -+} -+ -+int -+keyrepeat(void *data) -+{ -+ KeyboardGroup *group = data; -+ int i; -+ if (!group->nsyms || group->wlr_group->keyboard.repeat_info.rate <= 0) -+ return 0; -+ -+ wl_event_source_timer_update(group->key_repeat_source, -+ 1000 / group->wlr_group->keyboard.repeat_info.rate); -+ -+ for (i = 0; i < group->nsyms; i++) -+ keybinding(group->mods, group->keysyms[i]); -+ -+ return 0; -+} -+ -+void -+killclient(const Arg *arg) -+{ -+ Client *sel = focustop(selmon); -+ if (sel) -+ client_send_close(sel); -+} -+ -+void -+locksession(struct wl_listener *listener, void *data) -+{ -+ struct wlr_session_lock_v1 *session_lock = data; -+ SessionLock *lock; -+ wlr_scene_node_set_enabled(&locked_bg->node, 1); -+ if (cur_lock) { -+ wlr_session_lock_v1_destroy(session_lock); -+ return; -+ } -+ lock = session_lock->data = ecalloc(1, sizeof(*lock)); -+ focusclient(NULL, 0); -+ -+ lock->scene = wlr_scene_tree_create(layers[LyrBlock]); -+ cur_lock = lock->lock = session_lock; -+ locked = 1; -+ -+ LISTEN(&session_lock->events.new_surface, &lock->new_surface, createlocksurface); -+ LISTEN(&session_lock->events.destroy, &lock->destroy, destroysessionlock); -+ LISTEN(&session_lock->events.unlock, &lock->unlock, unlocksession); -+ -+ wlr_session_lock_v1_send_locked(session_lock); -+} -+ -+void -+mapnotify(struct wl_listener *listener, void *data) -+{ -+ /* Called when the surface is mapped, or ready to display on-screen. */ -+ Client *p = NULL; -+ Client *w, *c = wl_container_of(listener, c, map); -+ Monitor *m; -+ int i; -+ -+ /* Create scene tree for this client and its border */ -+ c->scene = client_surface(c)->data = wlr_scene_tree_create(layers[LyrTile]); -+ /* Enabled later by a call to arrange() */ -+ wlr_scene_node_set_enabled(&c->scene->node, client_is_unmanaged(c)); -+ c->scene_surface = c->type == XDGShell -+ ? wlr_scene_xdg_surface_create(c->scene, c->surface.xdg) -+ : wlr_scene_subsurface_tree_create(c->scene, client_surface(c)); -+ c->scene->node.data = c->scene_surface->node.data = c; -+ -+ client_get_geometry(c, &c->geom); -+ -+ /* Handle unmanaged clients first so we can return prior create borders */ -+ if (client_is_unmanaged(c)) { -+ /* Unmanaged clients always are floating */ -+ wlr_scene_node_reparent(&c->scene->node, layers[LyrFloat]); -+ wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y); -+ client_set_size(c, c->geom.width, c->geom.height); -+ if (client_wants_focus(c)) { -+ focusclient(c, 1); -+ exclusive_focus = c; -+ } -+ goto unset_fullscreen; -+ } -+ -+ for (i = 0; i < 4; i++) { -+ c->border[i] = wlr_scene_rect_create(c->scene, 0, 0, -+ c->isurgent ? urgentcolor : bordercolor); -+ c->border[i]->node.data = c; -+ } -+ -+ /* Initialize client geometry with room for border */ -+ client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT); -+ c->geom.width += 2 * c->bw; -+ c->geom.height += 2 * c->bw; -+ -+ /* Insert this client into client lists. */ -+ wl_list_insert(&clients, &c->link); -+ wl_list_insert(&fstack, &c->flink); -+ -+ /* Set initial monitor, tags, floating status, and focus: -+ * we always consider floating, clients that have parent and thus -+ * we set the same tags and monitor as its parent. -+ * If there is no parent, apply rules */ -+ if ((p = client_get_parent(c))) { -+ c->isfloating = 1; -+ setmon(c, p->mon, p->tags); -+ } else { -+ applyrules(c); -+ } -+ printstatus(); -+ -+unset_fullscreen: -+ m = c->mon ? c->mon : xytomon(c->geom.x, c->geom.y); -+ wl_list_for_each(w, &clients, link) { -+ if (w != c && w != p && w->isfullscreen && m == w->mon && (w->tags & c->tags)) -+ setfullscreen(w, 0); -+ } -+} -+ -+void -+maximizenotify(struct wl_listener *listener, void *data) -+{ -+ /* This event is raised when a client would like to maximize itself, -+ * typically because the user clicked on the maximize button on -+ * client-side decorations. dwl doesn't support maximization, but -+ * to conform to xdg-shell protocol we still must send a configure. -+ * Since xdg-shell protocol v5 we should ignore request of unsupported -+ * capabilities, just schedule a empty configure when the client uses <5 -+ * protocol version -+ * wlr_xdg_surface_schedule_configure() is used to send an empty reply. */ -+ Client *c = wl_container_of(listener, c, maximize); -+ if (c->surface.xdg->initialized -+ && wl_resource_get_version(c->surface.xdg->toplevel->resource) -+ < XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION) -+ wlr_xdg_surface_schedule_configure(c->surface.xdg); -+} -+ -+void -+monocle(Monitor *m) -+{ -+ Client *c; -+ int n = 0; -+ -+ wl_list_for_each(c, &clients, link) { -+ if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) -+ continue; -+ resize(c, m->w, 0); -+ n++; -+ } -+ if (n) -+ snprintf(m->ltsymbol, LENGTH(m->ltsymbol), "[%d]", n); -+ if ((c = focustop(m))) -+ wlr_scene_node_raise_to_top(&c->scene->node); -+} -+ -+void -+motionabsolute(struct wl_listener *listener, void *data) -+{ -+ /* This event is forwarded by the cursor when a pointer emits an _absolute_ -+ * motion event, from 0..1 on each axis. This happens, for example, when -+ * wlroots is running under a Wayland window rather than KMS+DRM, and you -+ * move the mouse over the window. You could enter the window from any edge, -+ * so we have to warp the mouse there. Also, some hardware emits these events. */ -+ struct wlr_pointer_motion_absolute_event *event = data; -+ double lx, ly, dx, dy; -+ -+ if (!event->time_msec) /* this is 0 with virtual pointers */ -+ wlr_cursor_warp_absolute(cursor, &event->pointer->base, event->x, event->y); -+ -+ wlr_cursor_absolute_to_layout_coords(cursor, &event->pointer->base, event->x, event->y, &lx, &ly); -+ dx = lx - cursor->x; -+ dy = ly - cursor->y; -+ motionnotify(event->time_msec, &event->pointer->base, dx, dy, dx, dy); -+} -+ -+void -+motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double dy, -+ double dx_unaccel, double dy_unaccel) -+{ -+ double sx = 0, sy = 0, sx_confined, sy_confined; -+ Client *c = NULL, *w = NULL; -+ LayerSurface *l = NULL; -+ struct wlr_surface *surface = NULL; -+ struct wlr_pointer_constraint_v1 *constraint; -+ -+ /* Find the client under the pointer and send the event along. */ -+ xytonode(cursor->x, cursor->y, &surface, &c, NULL, &sx, &sy); -+ -+ if (cursor_mode == CurPressed && !seat->drag -+ && surface != seat->pointer_state.focused_surface -+ && toplevel_from_wlr_surface(seat->pointer_state.focused_surface, &w, &l) >= 0) { -+ c = w; -+ surface = seat->pointer_state.focused_surface; -+ sx = cursor->x - (l ? l->scene->node.x : w->geom.x); -+ sy = cursor->y - (l ? l->scene->node.y : w->geom.y); -+ } -+ -+ /* time is 0 in internal calls meant to restore pointer focus. */ -+ if (time) { -+ wlr_relative_pointer_manager_v1_send_relative_motion( -+ relative_pointer_mgr, seat, (uint64_t)time * 1000, -+ dx, dy, dx_unaccel, dy_unaccel); -+ -+ wl_list_for_each(constraint, &pointer_constraints->constraints, link) -+ cursorconstrain(constraint); -+ -+ if (active_constraint && cursor_mode != CurResize && cursor_mode != CurMove) { -+ toplevel_from_wlr_surface(active_constraint->surface, &c, NULL); -+ if (c && active_constraint->surface == seat->pointer_state.focused_surface) { -+ sx = cursor->x - c->geom.x - c->bw; -+ sy = cursor->y - c->geom.y - c->bw; -+ if (wlr_region_confine(&active_constraint->region, sx, sy, -+ sx + dx, sy + dy, &sx_confined, &sy_confined)) { -+ dx = sx_confined - sx; -+ dy = sy_confined - sy; -+ } -+ -+ if (active_constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED) -+ return; -+ } -+ } -+ -+ wlr_cursor_move(cursor, device, dx, dy); -+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); -+ -+ /* Update selmon (even while dragging a window) */ -+ if (sloppyfocus) -+ selmon = xytomon(cursor->x, cursor->y); -+ } -+ -+ /* Update drag icon's position */ -+ wlr_scene_node_set_position(&drag_icon->node, (int)round(cursor->x), (int)round(cursor->y)); -+ -+ /* If we are currently grabbing the mouse, handle and return */ -+ if (cursor_mode == CurMove) { -+ /* Move the grabbed client to the new position. */ -+ resize(grabc, (struct wlr_box){.x = (int)round(cursor->x) - grabcx, .y = (int)round(cursor->y) - grabcy, -+ .width = grabc->geom.width, .height = grabc->geom.height}, 1); -+ return; -+ } else if (cursor_mode == CurResize) { -+ resize(grabc, (struct wlr_box){.x = grabc->geom.x, .y = grabc->geom.y, -+ .width = (int)round(cursor->x) - grabc->geom.x, .height = (int)round(cursor->y) - grabc->geom.y}, 1); -+ return; -+ } -+ -+ /* If there's no client surface under the cursor, set the cursor image to a -+ * default. This is what makes the cursor image appear when you move it -+ * off of a client or over its border. */ -+ if (!surface && !seat->drag) -+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "default"); -+ -+ pointerfocus(c, surface, sx, sy, time); -+} -+ -+void -+motionrelative(struct wl_listener *listener, void *data) -+{ -+ /* This event is forwarded by the cursor when a pointer emits a _relative_ -+ * pointer motion event (i.e. a delta) */ -+ struct wlr_pointer_motion_event *event = data; -+ /* The cursor doesn't move unless we tell it to. The cursor automatically -+ * handles constraining the motion to the output layout, as well as any -+ * special configuration applied for the specific input device which -+ * generated the event. You can pass NULL for the device if you want to move -+ * the cursor around without any input. */ -+ motionnotify(event->time_msec, &event->pointer->base, event->delta_x, event->delta_y, -+ event->unaccel_dx, event->unaccel_dy); -+} -+ -+void -+moveresize(const Arg *arg) -+{ -+ if (cursor_mode != CurNormal && cursor_mode != CurPressed) -+ return; -+ xytonode(cursor->x, cursor->y, NULL, &grabc, NULL, NULL, NULL); -+ if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen) -+ return; -+ -+ /* Float the window and tell motionnotify to grab it */ -+ setfloating(grabc, 1); -+ switch (cursor_mode = arg->ui) { -+ case CurMove: -+ grabcx = (int)round(cursor->x) - grabc->geom.x; -+ grabcy = (int)round(cursor->y) - grabc->geom.y; -+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "all-scroll"); -+ break; -+ case CurResize: -+ /* Doesn't work for X11 output - the next absolute motion event -+ * returns the cursor to where it started */ -+ wlr_cursor_warp_closest(cursor, NULL, -+ grabc->geom.x + grabc->geom.width, -+ grabc->geom.y + grabc->geom.height); -+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize"); -+ break; -+ } -+} -+ -+void -+outputmgrapply(struct wl_listener *listener, void *data) -+{ -+ struct wlr_output_configuration_v1 *config = data; -+ outputmgrapplyortest(config, 0); -+} -+ -+void -+outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test) -+{ -+ /* -+ * Called when a client such as wlr-randr requests a change in output -+ * configuration. This is only one way that the layout can be changed, -+ * so any Monitor information should be updated by updatemons() after an -+ * output_layout.change event, not here. -+ */ -+ struct wlr_output_configuration_head_v1 *config_head; -+ int ok = 1; -+ -+ wl_list_for_each(config_head, &config->heads, link) { -+ struct wlr_output *wlr_output = config_head->state.output; -+ Monitor *m = wlr_output->data; -+ struct wlr_output_state state; -+ -+ /* Ensure displays previously disabled by wlr-output-power-management-v1 -+ * are properly handled*/ -+ m->asleep = 0; -+ -+ wlr_output_state_init(&state); -+ wlr_output_state_set_enabled(&state, config_head->state.enabled); -+ if (!config_head->state.enabled) -+ goto apply_or_test; -+ -+ if (config_head->state.mode) -+ wlr_output_state_set_mode(&state, config_head->state.mode); -+ else -+ wlr_output_state_set_custom_mode(&state, -+ config_head->state.custom_mode.width, -+ config_head->state.custom_mode.height, -+ config_head->state.custom_mode.refresh); -+ -+ wlr_output_state_set_transform(&state, config_head->state.transform); -+ wlr_output_state_set_scale(&state, config_head->state.scale); -+ wlr_output_state_set_adaptive_sync_enabled(&state, -+ config_head->state.adaptive_sync_enabled); -+ -+apply_or_test: -+ ok &= test ? wlr_output_test_state(wlr_output, &state) -+ : wlr_output_commit_state(wlr_output, &state); -+ -+ /* Don't move monitors if position wouldn't change. This avoids -+ * wlroots marking the output as manually configured. -+ * wlr_output_layout_add does not like disabled outputs */ -+ if (!test && wlr_output->enabled && (m->m.x != config_head->state.x || m->m.y != config_head->state.y)) -+ wlr_output_layout_add(output_layout, wlr_output, -+ config_head->state.x, config_head->state.y); -+ -+ wlr_output_state_finish(&state); -+ } -+ -+ if (ok) -+ wlr_output_configuration_v1_send_succeeded(config); -+ else -+ wlr_output_configuration_v1_send_failed(config); -+ wlr_output_configuration_v1_destroy(config); -+ -+ /* https://codeberg.org/dwl/dwl/issues/577 */ -+ updatemons(NULL, NULL); -+} -+ -+void -+outputmgrtest(struct wl_listener *listener, void *data) -+{ -+ struct wlr_output_configuration_v1 *config = data; -+ outputmgrapplyortest(config, 1); -+} -+ -+void -+pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, -+ uint32_t time) -+{ -+ struct timespec now; -+ -+ if (surface != seat->pointer_state.focused_surface && -+ sloppyfocus && time && c && !client_is_unmanaged(c)) -+ focusclient(c, 0); -+ -+ /* If surface is NULL, clear pointer focus */ -+ if (!surface) { -+ wlr_seat_pointer_notify_clear_focus(seat); -+ return; -+ } -+ -+ if (!time) { -+ clock_gettime(CLOCK_MONOTONIC, &now); -+ time = now.tv_sec * 1000 + now.tv_nsec / 1000000; -+ } -+ -+ /* Let the client know that the mouse cursor has entered one -+ * of its surfaces, and make keyboard focus follow if desired. -+ * wlroots makes this a no-op if surface is already focused */ -+ wlr_seat_pointer_notify_enter(seat, surface, sx, sy); -+ wlr_seat_pointer_notify_motion(seat, time, sx, sy); -+} -+ -+void -+printstatus(void) -+{ -+ Monitor *m = NULL; -+ Client *c; -+ uint32_t occ, urg, sel; -+ -+ wl_list_for_each(m, &mons, link) { -+ occ = urg = 0; -+ wl_list_for_each(c, &clients, link) { -+ if (c->mon != m) -+ continue; -+ occ |= c->tags; -+ if (c->isurgent) -+ urg |= c->tags; -+ } -+ if ((c = focustop(m))) { -+ printf("%s title %s\n", m->wlr_output->name, client_get_title(c)); -+ printf("%s appid %s\n", m->wlr_output->name, client_get_appid(c)); -+ printf("%s fullscreen %d\n", m->wlr_output->name, c->isfullscreen); -+ printf("%s floating %d\n", m->wlr_output->name, c->isfloating); -+ sel = c->tags; -+ } else { -+ printf("%s title \n", m->wlr_output->name); -+ printf("%s appid \n", m->wlr_output->name); -+ printf("%s fullscreen \n", m->wlr_output->name); -+ printf("%s floating \n", m->wlr_output->name); -+ sel = 0; -+ } -+ -+ printf("%s selmon %u\n", m->wlr_output->name, m == selmon); -+ printf("%s tags %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32"\n", -+ m->wlr_output->name, occ, m->tagset[m->seltags], sel, urg); -+ printf("%s layout %s\n", m->wlr_output->name, m->ltsymbol); -+ } -+ fflush(stdout); -+} -+ -+void -+powermgrsetmode(struct wl_listener *listener, void *data) -+{ -+ struct wlr_output_power_v1_set_mode_event *event = data; -+ struct wlr_output_state state = {0}; -+ Monitor *m = event->output->data; -+ -+ if (!m) -+ return; -+ -+ m->gamma_lut_changed = 1; /* Reapply gamma LUT when re-enabling the ouput */ -+ wlr_output_state_set_enabled(&state, event->mode); -+ wlr_output_commit_state(m->wlr_output, &state); -+ -+ m->asleep = !event->mode; -+ updatemons(NULL, NULL); -+} -+ -+void -+quit(const Arg *arg) -+{ -+ wl_display_terminate(dpy); -+} -+ -+void -+rendermon(struct wl_listener *listener, void *data) -+{ -+ /* This function is called every time an output is ready to display a frame, -+ * generally at the output's refresh rate (e.g. 60Hz). */ -+ Monitor *m = wl_container_of(listener, m, frame); -+ Client *c; -+ struct wlr_output_state pending = {0}; -+ struct timespec now; -+ -+ /* Render if no XDG clients have an outstanding resize and are visible on -+ * this monitor. */ -+ wl_list_for_each(c, &clients, link) { -+ if (c->resize && !c->isfloating && client_is_rendered_on_mon(c, m) && !client_is_stopped(c)) -+ goto skip; -+ } -+ -+ wlr_scene_output_commit(m->scene_output, NULL); -+ -+skip: -+ /* Let clients know a frame has been rendered */ -+ clock_gettime(CLOCK_MONOTONIC, &now); -+ wlr_scene_output_send_frame_done(m->scene_output, &now); -+ wlr_output_state_finish(&pending); -+} -+ -+void -+requestdecorationmode(struct wl_listener *listener, void *data) -+{ -+ Client *c = wl_container_of(listener, c, set_decoration_mode); -+ if (c->surface.xdg->initialized) -+ wlr_xdg_toplevel_decoration_v1_set_mode(c->decoration, -+ WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); -+} -+ -+void -+requeststartdrag(struct wl_listener *listener, void *data) -+{ -+ struct wlr_seat_request_start_drag_event *event = data; -+ -+ if (wlr_seat_validate_pointer_grab_serial(seat, event->origin, -+ event->serial)) -+ wlr_seat_start_pointer_drag(seat, event->drag, event->serial); -+ else -+ wlr_data_source_destroy(event->drag->source); -+} -+ -+void -+requestmonstate(struct wl_listener *listener, void *data) -+{ -+ struct wlr_output_event_request_state *event = data; -+ wlr_output_commit_state(event->output, event->state); -+ updatemons(NULL, NULL); -+} -+ -+void -+resize(Client *c, struct wlr_box geo, int interact) -+{ -+ struct wlr_box *bbox; -+ struct wlr_box clip; -+ -+ if (!c->mon || !client_surface(c)->mapped) -+ return; -+ -+ bbox = interact ? &sgeom : &c->mon->w; -+ -+ client_set_bounds(c, geo.width, geo.height); -+ c->geom = geo; -+ applybounds(c, bbox); -+ -+ /* Update scene-graph, including borders */ -+ wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y); -+ wlr_scene_node_set_position(&c->scene_surface->node, c->bw, c->bw); -+ wlr_scene_rect_set_size(c->border[0], c->geom.width, c->bw); -+ wlr_scene_rect_set_size(c->border[1], c->geom.width, c->bw); -+ wlr_scene_rect_set_size(c->border[2], c->bw, c->geom.height - 2 * c->bw); -+ wlr_scene_rect_set_size(c->border[3], c->bw, c->geom.height - 2 * c->bw); -+ wlr_scene_node_set_position(&c->border[1]->node, 0, c->geom.height - c->bw); -+ wlr_scene_node_set_position(&c->border[2]->node, 0, c->bw); -+ wlr_scene_node_set_position(&c->border[3]->node, c->geom.width - c->bw, c->bw); -+ -+ /* this is a no-op if size hasn't changed */ -+ c->resize = client_set_size(c, c->geom.width - 2 * c->bw, -+ c->geom.height - 2 * c->bw); -+ client_get_clip(c, &clip); -+ wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip); -+} -+ -+void -+run(char *startup_cmd) -+{ -+ /* Add a Unix socket to the Wayland display. */ -+ const char *socket = wl_display_add_socket_auto(dpy); -+ if (!socket) -+ die("startup: display_add_socket_auto"); -+ setenv("WAYLAND_DISPLAY", socket, 1); -+ -+ /* Start the backend. This will enumerate outputs and inputs, become the DRM -+ * master, etc */ -+ if (!wlr_backend_start(backend)) -+ die("startup: backend_start"); -+ -+ /* Now that the socket exists and the backend is started, run the startup command */ -+ autostartexec(); -+ if (startup_cmd) { -+ int piperw[2]; -+ if (pipe(piperw) < 0) -+ die("startup: pipe:"); -+ if ((child_pid = fork()) < 0) -+ die("startup: fork:"); -+ if (child_pid == 0) { -+ setsid(); -+ dup2(piperw[0], STDIN_FILENO); -+ close(piperw[0]); -+ close(piperw[1]); -+ execl("/bin/sh", "/bin/sh", "-c", startup_cmd, NULL); -+ die("startup: execl:"); -+ } -+ dup2(piperw[1], STDOUT_FILENO); -+ close(piperw[1]); -+ close(piperw[0]); -+ } -+ -+ /* Mark stdout as non-blocking to avoid the startup script -+ * causing dwl to freeze when a user neither closes stdin -+ * nor consumes standard input in his startup script */ -+ -+ if (fd_set_nonblock(STDOUT_FILENO) < 0) -+ close(STDOUT_FILENO); -+ -+ printstatus(); -+ -+ /* At this point the outputs are initialized, choose initial selmon based on -+ * cursor position, and set default cursor image */ -+ selmon = xytomon(cursor->x, cursor->y); -+ -+ /* TODO hack to get cursor to display in its initial location (100, 100) -+ * instead of (0, 0) and then jumping. Still may not be fully -+ * initialized, as the image/coordinates are not transformed for the -+ * monitor when displayed here */ -+ wlr_cursor_warp_closest(cursor, NULL, cursor->x, cursor->y); -+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "default"); -+ -+ /* Run the Wayland event loop. This does not return until you exit the -+ * compositor. Starting the backend rigged up all of the necessary event -+ * loop configuration to listen to libinput events, DRM events, generate -+ * frame events at the refresh rate, and so on. */ -+ wl_display_run(dpy); -+} -+ -+void -+setcursor(struct wl_listener *listener, void *data) -+{ -+ /* This event is raised by the seat when a client provides a cursor image */ -+ struct wlr_seat_pointer_request_set_cursor_event *event = data; -+ /* If we're "grabbing" the cursor, don't use the client's image, we will -+ * restore it after "grabbing" sending a leave event, followed by a enter -+ * event, which will result in the client requesting set the cursor surface */ -+ if (cursor_mode != CurNormal && cursor_mode != CurPressed) -+ return; -+ /* This can be sent by any client, so we check to make sure this one -+ * actually has pointer focus first. If so, we can tell the cursor to -+ * use the provided surface as the cursor image. It will set the -+ * hardware cursor on the output that it's currently on and continue to -+ * do so as the cursor moves between outputs. */ -+ if (event->seat_client == seat->pointer_state.focused_client) -+ wlr_cursor_set_surface(cursor, event->surface, -+ event->hotspot_x, event->hotspot_y); -+} -+ -+void -+setcursorshape(struct wl_listener *listener, void *data) -+{ -+ struct wlr_cursor_shape_manager_v1_request_set_shape_event *event = data; -+ if (cursor_mode != CurNormal && cursor_mode != CurPressed) -+ return; -+ /* This can be sent by any client, so we check to make sure this one -+ * actually has pointer focus first. If so, we can tell the cursor to -+ * use the provided cursor shape. */ -+ if (event->seat_client == seat->pointer_state.focused_client) -+ wlr_cursor_set_xcursor(cursor, cursor_mgr, -+ wlr_cursor_shape_v1_name(event->shape)); -+} -+ -+void -+setfloating(Client *c, int floating) -+{ -+ Client *p = client_get_parent(c); -+ c->isfloating = floating; -+ /* If in floating layout do not change the client's layer */ -+ if (!c->mon || !client_surface(c)->mapped || !c->mon->lt[c->mon->sellt]->arrange) -+ return; -+ wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen || -+ (p && p->isfullscreen) ? LyrFS -+ : c->isfloating ? LyrFloat : LyrTile]); -+ arrange(c->mon); -+ printstatus(); -+} -+ -+void -+setfullscreen(Client *c, int fullscreen) -+{ -+ c->isfullscreen = fullscreen; -+ if (!c->mon || !client_surface(c)->mapped) -+ return; -+ c->bw = fullscreen ? 0 : borderpx; -+ client_set_fullscreen(c, fullscreen); -+ wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen -+ ? LyrFS : c->isfloating ? LyrFloat : LyrTile]); -+ -+ if (fullscreen) { -+ c->prev = c->geom; -+ resize(c, c->mon->m, 0); -+ } else { -+ /* restore previous size instead of arrange for floating windows since -+ * client positions are set by the user and cannot be recalculated */ -+ resize(c, c->prev, 0); -+ } -+ arrange(c->mon); -+ printstatus(); -+} -+ -+void -+setlayout(const Arg *arg) -+{ -+ if (!selmon) -+ return; -+ if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) -+ selmon->sellt ^= 1; -+ if (arg && arg->v) -+ selmon->lt[selmon->sellt] = (Layout *)arg->v; -+ strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, LENGTH(selmon->ltsymbol)); -+ arrange(selmon); -+ printstatus(); -+} -+ -+/* arg > 1.0 will set mfact absolutely */ -+void -+setmfact(const Arg *arg) -+{ -+ float f; -+ -+ if (!arg || !selmon || !selmon->lt[selmon->sellt]->arrange) -+ return; -+ f = arg->f < 1.0f ? arg->f + selmon->mfact : arg->f - 1.0f; -+ if (f < 0.1 || f > 0.9) -+ return; -+ selmon->mfact = f; -+ arrange(selmon); -+} -+ -+void -+setmon(Client *c, Monitor *m, uint32_t newtags) -+{ -+ Monitor *oldmon = c->mon; -+ -+ if (oldmon == m) -+ return; -+ c->mon = m; -+ c->prev = c->geom; -+ -+ /* Scene graph sends surface leave/enter events on move and resize */ -+ if (oldmon) -+ arrange(oldmon); -+ if (m) { -+ /* Make sure window actually overlaps with the monitor */ -+ resize(c, c->geom, 0); -+ c->tags = newtags ? newtags : m->tagset[m->seltags]; /* assign tags of target monitor */ -+ setfullscreen(c, c->isfullscreen); /* This will call arrange(c->mon) */ -+ setfloating(c, c->isfloating); -+ } -+ focusclient(focustop(selmon), 1); -+} -+ -+void -+setpsel(struct wl_listener *listener, void *data) -+{ -+ /* This event is raised by the seat when a client wants to set the selection, -+ * usually when the user copies something. wlroots allows compositors to -+ * ignore such requests if they so choose, but in dwl we always honor them -+ */ -+ struct wlr_seat_request_set_primary_selection_event *event = data; -+ wlr_seat_set_primary_selection(seat, event->source, event->serial); -+} -+ -+void -+setsel(struct wl_listener *listener, void *data) -+{ -+ /* This event is raised by the seat when a client wants to set the selection, -+ * usually when the user copies something. wlroots allows compositors to -+ * ignore such requests if they so choose, but in dwl we always honor them -+ */ -+ struct wlr_seat_request_set_selection_event *event = data; -+ wlr_seat_set_selection(seat, event->source, event->serial); -+} -+ -+void -+setup(void) -+{ -+ int drm_fd, i, sig[] = {SIGCHLD, SIGINT, SIGTERM, SIGPIPE}; -+ struct sigaction sa = {.sa_flags = SA_RESTART, .sa_handler = handlesig}; -+ sigemptyset(&sa.sa_mask); -+ -+ for (i = 0; i < (int)LENGTH(sig); i++) -+ sigaction(sig[i], &sa, NULL); -+ -+ wlr_log_init(log_level, NULL); -+ -+ /* The Wayland display is managed by libwayland. It handles accepting -+ * clients from the Unix socket, manging Wayland globals, and so on. */ -+ dpy = wl_display_create(); -+ event_loop = wl_display_get_event_loop(dpy); -+ -+ /* The backend is a wlroots feature which abstracts the underlying input and -+ * output hardware. The autocreate option will choose the most suitable -+ * backend based on the current environment, such as opening an X11 window -+ * if an X11 server is running. */ -+ if (!(backend = wlr_backend_autocreate(event_loop, &session))) -+ die("couldn't create backend"); -+ -+ /* Initialize the scene graph used to lay out windows */ -+ scene = wlr_scene_create(); -+ root_bg = wlr_scene_rect_create(&scene->tree, 0, 0, rootcolor); -+ for (i = 0; i < NUM_LAYERS; i++) -+ layers[i] = wlr_scene_tree_create(&scene->tree); -+ drag_icon = wlr_scene_tree_create(&scene->tree); -+ wlr_scene_node_place_below(&drag_icon->node, &layers[LyrBlock]->node); -+ -+ /* Autocreates a renderer, either Pixman, GLES2 or Vulkan for us. The user -+ * can also specify a renderer using the WLR_RENDERER env var. -+ * The renderer is responsible for defining the various pixel formats it -+ * supports for shared memory, this configures that for clients. */ -+ if (!(drw = wlr_renderer_autocreate(backend))) -+ die("couldn't create renderer"); -+ wl_signal_add(&drw->events.lost, &gpu_reset); -+ -+ /* Create shm, drm and linux_dmabuf interfaces by ourselves. -+ * The simplest way is to call: -+ * wlr_renderer_init_wl_display(drw); -+ * but we need to create the linux_dmabuf interface manually to integrate it -+ * with wlr_scene. */ -+ wlr_renderer_init_wl_shm(drw, dpy); -+ -+ if (wlr_renderer_get_texture_formats(drw, WLR_BUFFER_CAP_DMABUF)) { -+ wlr_drm_create(dpy, drw); -+ wlr_scene_set_linux_dmabuf_v1(scene, -+ wlr_linux_dmabuf_v1_create_with_renderer(dpy, 5, drw)); -+ } -+ -+ if ((drm_fd = wlr_renderer_get_drm_fd(drw)) >= 0 && drw->features.timeline -+ && backend->features.timeline) -+ wlr_linux_drm_syncobj_manager_v1_create(dpy, 1, drm_fd); -+ -+ /* Autocreates an allocator for us. -+ * The allocator is the bridge between the renderer and the backend. It -+ * handles the buffer creation, allowing wlroots to render onto the -+ * screen */ -+ if (!(alloc = wlr_allocator_autocreate(backend, drw))) -+ die("couldn't create allocator"); -+ -+ /* This creates some hands-off wlroots interfaces. The compositor is -+ * necessary for clients to allocate surfaces and the data device manager -+ * handles the clipboard. Each of these wlroots interfaces has room for you -+ * to dig your fingers in and play with their behavior if you want. Note that -+ * the clients cannot set the selection directly without compositor approval, -+ * see the setsel() function. */ -+ compositor = wlr_compositor_create(dpy, 6, drw); -+ wlr_subcompositor_create(dpy); -+ wlr_data_device_manager_create(dpy); -+ wlr_export_dmabuf_manager_v1_create(dpy); -+ wlr_screencopy_manager_v1_create(dpy); -+ wlr_data_control_manager_v1_create(dpy); -+ wlr_primary_selection_v1_device_manager_create(dpy); -+ wlr_viewporter_create(dpy); -+ wlr_single_pixel_buffer_manager_v1_create(dpy); -+ wlr_fractional_scale_manager_v1_create(dpy, 1); -+ wlr_presentation_create(dpy, backend, 2); -+ wlr_alpha_modifier_v1_create(dpy); -+ -+ /* Initializes the interface used to implement urgency hints */ -+ activation = wlr_xdg_activation_v1_create(dpy); -+ wl_signal_add(&activation->events.request_activate, &request_activate); -+ -+ wlr_scene_set_gamma_control_manager_v1(scene, wlr_gamma_control_manager_v1_create(dpy)); -+ -+ power_mgr = wlr_output_power_manager_v1_create(dpy); -+ wl_signal_add(&power_mgr->events.set_mode, &output_power_mgr_set_mode); -+ -+ /* Creates an output layout, which is a wlroots utility for working with an -+ * arrangement of screens in a physical layout. */ -+ output_layout = wlr_output_layout_create(dpy); -+ wl_signal_add(&output_layout->events.change, &layout_change); -+ -+ wlr_xdg_output_manager_v1_create(dpy, output_layout); -+ -+ /* Configure a listener to be notified when new outputs are available on the -+ * backend. */ -+ wl_list_init(&mons); -+ wl_signal_add(&backend->events.new_output, &new_output); -+ -+ /* Set up our client lists, the xdg-shell and the layer-shell. The xdg-shell is a -+ * Wayland protocol which is used for application windows. For more -+ * detail on shells, refer to the article: -+ * -+ * https://drewdevault.com/2018/07/29/Wayland-shells.html -+ */ -+ wl_list_init(&clients); -+ wl_list_init(&fstack); -+ -+ xdg_shell = wlr_xdg_shell_create(dpy, 6); -+ wl_signal_add(&xdg_shell->events.new_toplevel, &new_xdg_toplevel); -+ wl_signal_add(&xdg_shell->events.new_popup, &new_xdg_popup); -+ -+ layer_shell = wlr_layer_shell_v1_create(dpy, 3); -+ wl_signal_add(&layer_shell->events.new_surface, &new_layer_surface); -+ -+ idle_notifier = wlr_idle_notifier_v1_create(dpy); -+ -+ idle_inhibit_mgr = wlr_idle_inhibit_v1_create(dpy); -+ wl_signal_add(&idle_inhibit_mgr->events.new_inhibitor, &new_idle_inhibitor); -+ -+ session_lock_mgr = wlr_session_lock_manager_v1_create(dpy); -+ wl_signal_add(&session_lock_mgr->events.new_lock, &new_session_lock); -+ locked_bg = wlr_scene_rect_create(layers[LyrBlock], sgeom.width, sgeom.height, -+ (float [4]){0.1f, 0.1f, 0.1f, 1.0f}); -+ wlr_scene_node_set_enabled(&locked_bg->node, 0); -+ -+ /* Use decoration protocols to negotiate server-side decorations */ -+ wlr_server_decoration_manager_set_default_mode( -+ wlr_server_decoration_manager_create(dpy), -+ WLR_SERVER_DECORATION_MANAGER_MODE_SERVER); -+ xdg_decoration_mgr = wlr_xdg_decoration_manager_v1_create(dpy); -+ wl_signal_add(&xdg_decoration_mgr->events.new_toplevel_decoration, &new_xdg_decoration); -+ -+ pointer_constraints = wlr_pointer_constraints_v1_create(dpy); -+ wl_signal_add(&pointer_constraints->events.new_constraint, &new_pointer_constraint); -+ -+ relative_pointer_mgr = wlr_relative_pointer_manager_v1_create(dpy); -+ -+ /* -+ * Creates a cursor, which is a wlroots utility for tracking the cursor -+ * image shown on screen. -+ */ -+ cursor = wlr_cursor_create(); -+ wlr_cursor_attach_output_layout(cursor, output_layout); -+ -+ /* Creates an xcursor manager, another wlroots utility which loads up -+ * Xcursor themes to source cursor images from and makes sure that cursor -+ * images are available at all scale factors on the screen (necessary for -+ * HiDPI support). Scaled cursors will be loaded with each output. */ -+ cursor_mgr = wlr_xcursor_manager_create(NULL, 24); -+ setenv("XCURSOR_SIZE", "24", 1); -+ -+ /* -+ * wlr_cursor *only* displays an image on screen. It does not move around -+ * when the pointer moves. However, we can attach input devices to it, and -+ * it will generate aggregate events for all of them. In these events, we -+ * can choose how we want to process them, forwarding them to clients and -+ * moving the cursor around. More detail on this process is described in -+ * https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html -+ * -+ * And more comments are sprinkled throughout the notify functions above. -+ */ -+ wl_signal_add(&cursor->events.motion, &cursor_motion); -+ wl_signal_add(&cursor->events.motion_absolute, &cursor_motion_absolute); -+ wl_signal_add(&cursor->events.button, &cursor_button); -+ wl_signal_add(&cursor->events.axis, &cursor_axis); -+ wl_signal_add(&cursor->events.frame, &cursor_frame); -+ -+ cursor_shape_mgr = wlr_cursor_shape_manager_v1_create(dpy, 1); -+ wl_signal_add(&cursor_shape_mgr->events.request_set_shape, &request_set_cursor_shape); -+ -+ /* -+ * Configures a seat, which is a single "seat" at which a user sits and -+ * operates the computer. This conceptually includes up to one keyboard, -+ * pointer, touch, and drawing tablet device. We also rig up a listener to -+ * let us know when new input devices are available on the backend. -+ */ -+ wl_signal_add(&backend->events.new_input, &new_input_device); -+ virtual_keyboard_mgr = wlr_virtual_keyboard_manager_v1_create(dpy); -+ wl_signal_add(&virtual_keyboard_mgr->events.new_virtual_keyboard, -+ &new_virtual_keyboard); -+ virtual_pointer_mgr = wlr_virtual_pointer_manager_v1_create(dpy); -+ wl_signal_add(&virtual_pointer_mgr->events.new_virtual_pointer, -+ &new_virtual_pointer); -+ -+ seat = wlr_seat_create(dpy, "seat0"); -+ wl_signal_add(&seat->events.request_set_cursor, &request_cursor); -+ wl_signal_add(&seat->events.request_set_selection, &request_set_sel); -+ wl_signal_add(&seat->events.request_set_primary_selection, &request_set_psel); -+ wl_signal_add(&seat->events.request_start_drag, &request_start_drag); -+ wl_signal_add(&seat->events.start_drag, &start_drag); -+ -+ kb_group = createkeyboardgroup(); -+ wl_list_init(&kb_group->destroy.link); -+ -+ output_mgr = wlr_output_manager_v1_create(dpy); -+ wl_signal_add(&output_mgr->events.apply, &output_mgr_apply); -+ wl_signal_add(&output_mgr->events.test, &output_mgr_test); -+ -+ /* Make sure XWayland clients don't connect to the parent X server, -+ * e.g when running in the x11 backend or the wayland backend and the -+ * compositor has Xwayland support */ -+ unsetenv("DISPLAY"); -+#ifdef XWAYLAND -+ /* -+ * Initialise the XWayland X server. -+ * It will be started when the first X client is started. -+ */ -+ if ((xwayland = wlr_xwayland_create(dpy, compositor, 1))) { -+ wl_signal_add(&xwayland->events.ready, &xwayland_ready); -+ wl_signal_add(&xwayland->events.new_surface, &new_xwayland_surface); -+ -+ setenv("DISPLAY", xwayland->display_name, 1); -+ } else { -+ fprintf(stderr, "failed to setup XWayland X server, continuing without it\n"); -+ } -+#endif -+} -+ -+void -+spawn(const Arg *arg) -+{ -+ if (fork() == 0) { -+ dup2(STDERR_FILENO, STDOUT_FILENO); -+ setsid(); -+ execvp(((char **)arg->v)[0], (char **)arg->v); -+ die("dwl: execvp %s failed:", ((char **)arg->v)[0]); -+ } -+} -+ -+void -+startdrag(struct wl_listener *listener, void *data) -+{ -+ struct wlr_drag *drag = data; -+ if (!drag->icon) -+ return; -+ -+ drag->icon->data = &wlr_scene_drag_icon_create(drag_icon, drag->icon)->node; -+ LISTEN_STATIC(&drag->icon->events.destroy, destroydragicon); -+} -+ -+void -+tag(const Arg *arg) -+{ -+ Client *sel = focustop(selmon); -+ if (!sel || (arg->ui & TAGMASK) == 0) -+ return; -+ -+ sel->tags = arg->ui & TAGMASK; -+ focusclient(focustop(selmon), 1); -+ arrange(selmon); -+ printstatus(); -+} -+ -+void -+tagmon(const Arg *arg) -+{ -+ Client *sel = focustop(selmon); -+ if (sel) -+ setmon(sel, dirtomon(arg->i), 0); -+} -+ -+void -+tile(Monitor *m) -+{ -+ unsigned int mw, my, ty; -+ int i, n = 0; -+ Client *c; -+ -+ wl_list_for_each(c, &clients, link) -+ if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen) -+ n++; -+ if (n == 0) -+ return; -+ -+ if (n > m->nmaster) -+ mw = m->nmaster ? (int)roundf(m->w.width * m->mfact) : 0; -+ else -+ mw = m->w.width; -+ i = my = ty = 0; -+ wl_list_for_each(c, &clients, link) { -+ if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) -+ continue; -+ if (i < m->nmaster) { -+ resize(c, (struct wlr_box){.x = m->w.x, .y = m->w.y + my, .width = mw, -+ .height = (m->w.height - my) / (MIN(n, m->nmaster) - i)}, 0); -+ my += c->geom.height; -+ } else { -+ resize(c, (struct wlr_box){.x = m->w.x + mw, .y = m->w.y + ty, -+ .width = m->w.width - mw, .height = (m->w.height - ty) / (n - i)}, 0); -+ ty += c->geom.height; -+ } -+ i++; -+ } -+} -+ -+void -+togglefloating(const Arg *arg) -+{ -+ Client *sel = focustop(selmon); -+ /* return if fullscreen */ -+ if (sel && !sel->isfullscreen) -+ setfloating(sel, !sel->isfloating); -+} -+ -+void -+togglefullscreen(const Arg *arg) -+{ -+ Client *sel = focustop(selmon); -+ if (sel) -+ setfullscreen(sel, !sel->isfullscreen); -+} -+ -+void -+toggletag(const Arg *arg) -+{ -+ uint32_t newtags; -+ Client *sel = focustop(selmon); -+ if (!sel || !(newtags = sel->tags ^ (arg->ui & TAGMASK))) -+ return; -+ -+ sel->tags = newtags; -+ focusclient(focustop(selmon), 1); -+ arrange(selmon); -+ printstatus(); -+} -+ -+void -+toggleview(const Arg *arg) -+{ -+ uint32_t newtagset; -+ if (!(newtagset = selmon ? selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK) : 0)) -+ return; -+ -+ selmon->tagset[selmon->seltags] = newtagset; -+ focusclient(focustop(selmon), 1); -+ arrange(selmon); -+ printstatus(); -+} -+ -+void -+unlocksession(struct wl_listener *listener, void *data) -+{ -+ SessionLock *lock = wl_container_of(listener, lock, unlock); -+ destroylock(lock, 1); -+} -+ -+void -+unmaplayersurfacenotify(struct wl_listener *listener, void *data) -+{ -+ LayerSurface *l = wl_container_of(listener, l, unmap); -+ -+ l->mapped = 0; -+ wlr_scene_node_set_enabled(&l->scene->node, 0); -+ if (l == exclusive_focus) -+ exclusive_focus = NULL; -+ if (l->layer_surface->output && (l->mon = l->layer_surface->output->data)) -+ arrangelayers(l->mon); -+ if (l->layer_surface->surface == seat->keyboard_state.focused_surface) -+ focusclient(focustop(selmon), 1); -+ motionnotify(0, NULL, 0, 0, 0, 0); -+} -+ -+void -+unmapnotify(struct wl_listener *listener, void *data) -+{ -+ /* Called when the surface is unmapped, and should no longer be shown. */ -+ Client *c = wl_container_of(listener, c, unmap); -+ if (c == grabc) { -+ cursor_mode = CurNormal; -+ grabc = NULL; -+ } -+ -+ if (client_is_unmanaged(c)) { -+ if (c == exclusive_focus) { -+ exclusive_focus = NULL; -+ focusclient(focustop(selmon), 1); -+ } -+ } else { -+ wl_list_remove(&c->link); -+ setmon(c, NULL, 0); -+ wl_list_remove(&c->flink); -+ } -+ -+ wlr_scene_node_destroy(&c->scene->node); -+ printstatus(); -+ motionnotify(0, NULL, 0, 0, 0, 0); -+} -+ -+void -+updatemons(struct wl_listener *listener, void *data) -+{ -+ /* -+ * Called whenever the output layout changes: adding or removing a -+ * monitor, changing an output's mode or position, etc. This is where -+ * the change officially happens and we update geometry, window -+ * positions, focus, and the stored configuration in wlroots' -+ * output-manager implementation. -+ */ -+ struct wlr_output_configuration_v1 *config -+ = wlr_output_configuration_v1_create(); -+ Client *c; -+ struct wlr_output_configuration_head_v1 *config_head; -+ Monitor *m; -+ -+ /* First remove from the layout the disabled monitors */ -+ wl_list_for_each(m, &mons, link) { -+ if (m->wlr_output->enabled || m->asleep) -+ continue; -+ config_head = wlr_output_configuration_head_v1_create(config, m->wlr_output); -+ config_head->state.enabled = 0; -+ /* Remove this output from the layout to avoid cursor enter inside it */ -+ wlr_output_layout_remove(output_layout, m->wlr_output); -+ closemon(m); -+ m->m = m->w = (struct wlr_box){0}; -+ } -+ /* Insert outputs that need to */ -+ wl_list_for_each(m, &mons, link) { -+ if (m->wlr_output->enabled -+ && !wlr_output_layout_get(output_layout, m->wlr_output)) -+ wlr_output_layout_add_auto(output_layout, m->wlr_output); -+ } -+ -+ /* Now that we update the output layout we can get its box */ -+ wlr_output_layout_get_box(output_layout, NULL, &sgeom); -+ -+ wlr_scene_node_set_position(&root_bg->node, sgeom.x, sgeom.y); -+ wlr_scene_rect_set_size(root_bg, sgeom.width, sgeom.height); -+ -+ /* Make sure the clients are hidden when dwl is locked */ -+ wlr_scene_node_set_position(&locked_bg->node, sgeom.x, sgeom.y); -+ wlr_scene_rect_set_size(locked_bg, sgeom.width, sgeom.height); -+ -+ wl_list_for_each(m, &mons, link) { -+ if (!m->wlr_output->enabled) -+ continue; -+ config_head = wlr_output_configuration_head_v1_create(config, m->wlr_output); -+ -+ /* Get the effective monitor geometry to use for surfaces */ -+ wlr_output_layout_get_box(output_layout, m->wlr_output, &m->m); -+ m->w = m->m; -+ wlr_scene_output_set_position(m->scene_output, m->m.x, m->m.y); -+ -+ wlr_scene_node_set_position(&m->fullscreen_bg->node, m->m.x, m->m.y); -+ wlr_scene_rect_set_size(m->fullscreen_bg, m->m.width, m->m.height); -+ -+ if (m->lock_surface) { -+ struct wlr_scene_tree *scene_tree = m->lock_surface->surface->data; -+ wlr_scene_node_set_position(&scene_tree->node, m->m.x, m->m.y); -+ wlr_session_lock_surface_v1_configure(m->lock_surface, m->m.width, m->m.height); -+ } -+ -+ /* Calculate the effective monitor geometry to use for clients */ -+ arrangelayers(m); -+ /* Don't move clients to the left output when plugging monitors */ -+ arrange(m); -+ /* make sure fullscreen clients have the right size */ -+ if ((c = focustop(m)) && c->isfullscreen) -+ resize(c, m->m, 0); -+ -+ /* Try to re-set the gamma LUT when updating monitors, -+ * it's only really needed when enabling a disabled output, but meh. */ -+ m->gamma_lut_changed = 1; -+ -+ config_head->state.x = m->m.x; -+ config_head->state.y = m->m.y; -+ -+ if (!selmon) { -+ selmon = m; -+ } -+ } -+ -+ if (selmon && selmon->wlr_output->enabled) { -+ wl_list_for_each(c, &clients, link) { -+ if (!c->mon && client_surface(c)->mapped) -+ setmon(c, selmon, c->tags); -+ } -+ focusclient(focustop(selmon), 1); -+ if (selmon->lock_surface) { -+ client_notify_enter(selmon->lock_surface->surface, -+ wlr_seat_get_keyboard(seat)); -+ client_activate_surface(selmon->lock_surface->surface, 1); -+ } -+ } -+ -+ /* FIXME: figure out why the cursor image is at 0,0 after turning all -+ * the monitors on. -+ * Move the cursor image where it used to be. It does not generate a -+ * wl_pointer.motion event for the clients, it's only the image what it's -+ * at the wrong position after all. */ -+ wlr_cursor_move(cursor, NULL, 0, 0); -+ -+ wlr_output_manager_v1_set_configuration(output_mgr, config); -+} -+ -+void -+updatetitle(struct wl_listener *listener, void *data) -+{ -+ Client *c = wl_container_of(listener, c, set_title); -+ if (c == focustop(c->mon)) -+ printstatus(); -+} -+ -+void -+urgent(struct wl_listener *listener, void *data) -+{ -+ struct wlr_xdg_activation_v1_request_activate_event *event = data; -+ Client *c = NULL; -+ toplevel_from_wlr_surface(event->surface, &c, NULL); -+ if (!c || c == focustop(selmon)) -+ return; -+ -+ c->isurgent = 1; -+ printstatus(); -+ -+ if (client_surface(c)->mapped) -+ client_set_border_color(c, urgentcolor); -+} -+ -+void -+view(const Arg *arg) -+{ -+ if (!selmon || (arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) -+ return; -+ selmon->seltags ^= 1; /* toggle sel tagset */ -+ if (arg->ui & TAGMASK) -+ selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; -+ focusclient(focustop(selmon), 1); -+ arrange(selmon); -+ printstatus(); -+} -+ -+void -+virtualkeyboard(struct wl_listener *listener, void *data) -+{ -+ struct wlr_virtual_keyboard_v1 *kb = data; -+ /* virtual keyboards shouldn't share keyboard group */ -+ KeyboardGroup *group = createkeyboardgroup(); -+ /* Set the keymap to match the group keymap */ -+ wlr_keyboard_set_keymap(&kb->keyboard, group->wlr_group->keyboard.keymap); -+ LISTEN(&kb->keyboard.base.events.destroy, &group->destroy, destroykeyboardgroup); -+ -+ /* Add the new keyboard to the group */ -+ wlr_keyboard_group_add_keyboard(group->wlr_group, &kb->keyboard); -+} -+ -+void -+virtualpointer(struct wl_listener *listener, void *data) -+{ -+ struct wlr_virtual_pointer_v1_new_pointer_event *event = data; -+ struct wlr_input_device *device = &event->new_pointer->pointer.base; -+ -+ wlr_cursor_attach_input_device(cursor, device); -+ if (event->suggested_output) -+ wlr_cursor_map_input_to_output(cursor, device, event->suggested_output); -+} -+ -+Monitor * -+xytomon(double x, double y) -+{ -+ struct wlr_output *o = wlr_output_layout_output_at(output_layout, x, y); -+ return o ? o->data : NULL; -+} -+ -+void -+xytonode(double x, double y, struct wlr_surface **psurface, -+ Client **pc, LayerSurface **pl, double *nx, double *ny) -+{ -+ struct wlr_scene_node *node, *pnode; -+ struct wlr_surface *surface = NULL; -+ Client *c = NULL; -+ LayerSurface *l = NULL; -+ int layer; -+ -+ for (layer = NUM_LAYERS - 1; !surface && layer >= 0; layer--) { -+ if (!(node = wlr_scene_node_at(&layers[layer]->node, x, y, nx, ny))) -+ continue; -+ -+ if (node->type == WLR_SCENE_NODE_BUFFER) -+ surface = wlr_scene_surface_try_from_buffer( -+ wlr_scene_buffer_from_node(node))->surface; -+ /* Walk the tree to find a node that knows the client */ -+ for (pnode = node; pnode && !c; pnode = &pnode->parent->node) -+ c = pnode->data; -+ if (c && c->type == LayerShell) { -+ c = NULL; -+ l = pnode->data; -+ } -+ } -+ -+ if (psurface) *psurface = surface; -+ if (pc) *pc = c; -+ if (pl) *pl = l; -+} -+ -+void -+zoom(const Arg *arg) -+{ -+ Client *c, *sel = focustop(selmon); -+ -+ if (!sel || !selmon || !selmon->lt[selmon->sellt]->arrange || sel->isfloating) -+ return; -+ -+ /* Search for the first tiled window that is not sel, marking sel as -+ * NULL if we pass it along the way */ -+ wl_list_for_each(c, &clients, link) { -+ if (VISIBLEON(c, selmon) && !c->isfloating) { -+ if (c != sel) -+ break; -+ sel = NULL; -+ } -+ } -+ -+ /* Return if no other tiled window was found */ -+ if (&c->link == &clients) -+ return; -+ -+ /* If we passed sel, move c to the front; otherwise, move sel to the -+ * front */ -+ if (!sel) -+ sel = c; -+ wl_list_remove(&sel->link); -+ wl_list_insert(&clients, &sel->link); -+ -+ focusclient(sel, 1); -+ arrange(selmon); -+} -+ -+#ifdef XWAYLAND -+void -+activatex11(struct wl_listener *listener, void *data) -+{ -+ Client *c = wl_container_of(listener, c, activate); -+ -+ /* Only "managed" windows can be activated */ -+ if (!client_is_unmanaged(c)) -+ wlr_xwayland_surface_activate(c->surface.xwayland, 1); -+} -+ -+void -+associatex11(struct wl_listener *listener, void *data) -+{ -+ Client *c = wl_container_of(listener, c, associate); -+ -+ LISTEN(&client_surface(c)->events.map, &c->map, mapnotify); -+ LISTEN(&client_surface(c)->events.unmap, &c->unmap, unmapnotify); -+} -+ -+void -+configurex11(struct wl_listener *listener, void *data) -+{ -+ Client *c = wl_container_of(listener, c, configure); -+ struct wlr_xwayland_surface_configure_event *event = data; -+ if (!client_surface(c) || !client_surface(c)->mapped) { -+ wlr_xwayland_surface_configure(c->surface.xwayland, -+ event->x, event->y, event->width, event->height); -+ return; -+ } -+ if (client_is_unmanaged(c)) { -+ wlr_scene_node_set_position(&c->scene->node, event->x, event->y); -+ wlr_xwayland_surface_configure(c->surface.xwayland, -+ event->x, event->y, event->width, event->height); -+ return; -+ } -+ if ((c->isfloating && c != grabc) || !c->mon->lt[c->mon->sellt]->arrange) { -+ resize(c, (struct wlr_box){.x = event->x - c->bw, -+ .y = event->y - c->bw, .width = event->width + c->bw * 2, -+ .height = event->height + c->bw * 2}, 0); -+ } else { -+ arrange(c->mon); -+ } -+} -+ -+void -+createnotifyx11(struct wl_listener *listener, void *data) -+{ -+ struct wlr_xwayland_surface *xsurface = data; -+ Client *c; -+ -+ /* Allocate a Client for this surface */ -+ c = xsurface->data = ecalloc(1, sizeof(*c)); -+ c->surface.xwayland = xsurface; -+ c->type = X11; -+ c->bw = client_is_unmanaged(c) ? 0 : borderpx; -+ -+ /* Listen to the various events it can emit */ -+ LISTEN(&xsurface->events.associate, &c->associate, associatex11); -+ LISTEN(&xsurface->events.destroy, &c->destroy, destroynotify); -+ LISTEN(&xsurface->events.dissociate, &c->dissociate, dissociatex11); -+ LISTEN(&xsurface->events.request_activate, &c->activate, activatex11); -+ LISTEN(&xsurface->events.request_configure, &c->configure, configurex11); -+ LISTEN(&xsurface->events.request_fullscreen, &c->fullscreen, fullscreennotify); -+ LISTEN(&xsurface->events.set_hints, &c->set_hints, sethints); -+ LISTEN(&xsurface->events.set_title, &c->set_title, updatetitle); -+} -+ -+void -+dissociatex11(struct wl_listener *listener, void *data) -+{ -+ Client *c = wl_container_of(listener, c, dissociate); -+ wl_list_remove(&c->map.link); -+ wl_list_remove(&c->unmap.link); -+} -+ -+void -+sethints(struct wl_listener *listener, void *data) -+{ -+ Client *c = wl_container_of(listener, c, set_hints); -+ struct wlr_surface *surface = client_surface(c); -+ if (c == focustop(selmon) || !c->surface.xwayland->hints) -+ return; -+ -+ c->isurgent = xcb_icccm_wm_hints_get_urgency(c->surface.xwayland->hints); -+ printstatus(); -+ -+ if (c->isurgent && surface && surface->mapped) -+ client_set_border_color(c, urgentcolor); -+} -+ -+void -+xwaylandready(struct wl_listener *listener, void *data) -+{ -+ struct wlr_xcursor *xcursor; -+ -+ /* assign the one and only seat */ -+ wlr_xwayland_set_seat(xwayland, seat); -+ -+ /* Set the default XWayland cursor to match the rest of dwl. */ -+ if ((xcursor = wlr_xcursor_manager_get_xcursor(cursor_mgr, "default", 1))) -+ wlr_xwayland_set_cursor(xwayland, -+ xcursor->images[0]->buffer, xcursor->images[0]->width * 4, -+ xcursor->images[0]->width, xcursor->images[0]->height, -+ xcursor->images[0]->hotspot_x, xcursor->images[0]->hotspot_y); -+} -+#endif -+ -+int -+main(int argc, char *argv[]) -+{ -+ char *startup_cmd = NULL; -+ int c; -+ -+ while ((c = getopt(argc, argv, "s:hdv")) != -1) { -+ if (c == 's') -+ startup_cmd = optarg; -+ else if (c == 'd') -+ log_level = WLR_DEBUG; -+ else if (c == 'v') -+ die("dwl " VERSION); -+ else -+ goto usage; -+ } -+ if (optind < argc) -+ goto usage; -+ -+ /* Wayland requires XDG_RUNTIME_DIR for creating its communications socket */ -+ if (!getenv("XDG_RUNTIME_DIR")) -+ die("XDG_RUNTIME_DIR must be set"); -+ setup(); -+ run(startup_cmd); -+ cleanup(); -+ return EXIT_SUCCESS; -+ -+usage: -+ die("Usage: %s [-v] [-d] [-s startup command]", argv[0]); -+} diff --git a/patches/swallow.patch b/patches/swallow.patch deleted file mode 100644 index faee376..0000000 --- a/patches/swallow.patch +++ /dev/null @@ -1,350 +0,0 @@ -From a220e1ed4b04a66c837dfc8e3363d3e696cbf541 Mon Sep 17 00:00:00 2001 -From: Nikita Ivanov -Date: Wed, 5 Feb 2025 02:34:39 +0100 -Subject: [PATCH] Swallow: hide the terminal when it spawns a client - ---- - client.h | 12 ++++ - config.def.h | 11 +++- - dwl.c | 152 +++++++++++++++++++++++++++++++++++++++++++++++++-- - 3 files changed, 168 insertions(+), 7 deletions(-) - -diff --git a/client.h b/client.h -index 42f225f..bc9cad2 100644 ---- a/client.h -+++ b/client.h -@@ -131,6 +131,18 @@ client_get_appid(Client *c) - return c->surface.xdg->toplevel->app_id; - } - -+static inline int -+client_get_pid(Client *c) -+{ -+ pid_t pid; -+#ifdef XWAYLAND -+ if (client_is_x11(c)) -+ return c->surface.xwayland->pid; -+#endif -+ wl_client_get_credentials(c->surface.xdg->client->client, &pid, NULL, NULL); -+ return pid; -+} -+ - static inline void - client_get_clip(Client *c, struct wlr_box *clip) - { -diff --git a/config.def.h b/config.def.h -index 22d2171..42342f1 100644 ---- a/config.def.h -+++ b/config.def.h -@@ -13,6 +13,8 @@ static const float focuscolor[] = COLOR(0x005577ff); - static const float urgentcolor[] = COLOR(0xff0000ff); - /* This conforms to the xdg-protocol. Set the alpha to zero to restore the old behavior */ - static const float fullscreen_bg[] = {0.1f, 0.1f, 0.1f, 1.0f}; /* You can also use glsl colors */ -+static int enableautoswallow = 1; /* enables autoswallowing newly spawned clients */ -+static float swallowborder = 1.0f; /* add this multiplied by borderpx to border when a client is swallowed */ - - /* tagging - TAGCOUNT must be no greater than 31 */ - #define TAGCOUNT (9) -@@ -22,10 +24,11 @@ static int log_level = WLR_ERROR; - - /* NOTE: ALWAYS keep a rule declared even if you don't use rules (e.g leave at least one example) */ - static const Rule rules[] = { -- /* app_id title tags mask isfloating monitor */ -+ /* app_id title tags mask isfloating isterm noswallow monitor */ - /* examples: */ -- { "Gimp_EXAMPLE", NULL, 0, 1, -1 }, /* Start on currently visible tags floating, not tiled */ -- { "firefox_EXAMPLE", NULL, 1 << 8, 0, -1 }, /* Start on ONLY tag "9" */ -+ { "foot", NULL, 0, 0, 1, 1, -1 }, -+ { "Gimp_EXAMPLE", NULL, 0, 1, 0, 0, -1 }, /* Start on currently visible tags floating, not tiled */ -+ { "firefox_EXAMPLE", NULL, 1 << 8, 0, 0, 0, -1 }, /* Start on ONLY tag "9" */ - }; - - /* layout(s) */ -@@ -142,6 +145,8 @@ static const Key keys[] = { - { MODKEY, XKB_KEY_space, setlayout, {0} }, - { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} }, - { MODKEY, XKB_KEY_e, togglefullscreen, {0} }, -+ { MODKEY, XKB_KEY_a, toggleswallow, {0} }, -+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_A, toggleautoswallow,{0} }, - { MODKEY, XKB_KEY_0, view, {.ui = ~0} }, - { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} }, - { MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} }, -diff --git a/dwl.c b/dwl.c -index def2562..71d500a 100644 ---- a/dwl.c -+++ b/dwl.c -@@ -73,12 +73,13 @@ - #define MAX(A, B) ((A) > (B) ? (A) : (B)) - #define MIN(A, B) ((A) < (B) ? (A) : (B)) - #define CLEANMASK(mask) (mask & ~WLR_MODIFIER_CAPS) --#define VISIBLEON(C, M) ((M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags])) -+#define VISIBLEON(C, M) ((M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags]) && !(C)->swallowedby) - #define LENGTH(X) (sizeof X / sizeof X[0]) - #define END(A) ((A) + LENGTH(A)) - #define TAGMASK ((1u << TAGCOUNT) - 1) - #define LISTEN(E, L, H) wl_signal_add((E), ((L)->notify = (H), (L))) - #define LISTEN_STATIC(E, H) do { static struct wl_listener _l = {.notify = (H)}; wl_signal_add((E), &_l); } while (0) -+#define BORDERPX(C) (borderpx + ((C)->swallowing ? (int)ceilf(swallowborder * (C)->swallowing->bw) : 0)) - - /* enums */ - enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */ -@@ -104,7 +105,8 @@ typedef struct { - } Button; - - typedef struct Monitor Monitor; --typedef struct { -+typedef struct Client Client; -+struct Client { - /* Must keep these three elements in this order */ - unsigned int type; /* XDGShell or X11* */ - struct wlr_box geom; /* layout-relative, includes border */ -@@ -140,8 +142,12 @@ typedef struct { - unsigned int bw; - uint32_t tags; - int isfloating, isurgent, isfullscreen; -+ int isterm, noswallow; - uint32_t resize; /* configure serial of a pending resize */ --} Client; -+ pid_t pid; -+ Client *swallowing; /* client being hidden */ -+ Client *swallowedby; -+}; - - typedef struct { - uint32_t mod; -@@ -230,6 +236,8 @@ typedef struct { - const char *title; - uint32_t tags; - int isfloating; -+ int isterm; -+ int noswallow; - int monitor; - } Rule; - -@@ -311,6 +319,7 @@ static void moveresize(const Arg *arg); - static void outputmgrapply(struct wl_listener *listener, void *data); - static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test); - static void outputmgrtest(struct wl_listener *listener, void *data); -+static pid_t parentpid(pid_t pid); - static void pointerfocus(Client *c, struct wlr_surface *surface, - double sx, double sy, uint32_t time); - static void printstatus(void); -@@ -335,11 +344,15 @@ static void setsel(struct wl_listener *listener, void *data); - static void setup(void); - static void spawn(const Arg *arg); - static void startdrag(struct wl_listener *listener, void *data); -+static void swallow(Client *c, Client *toswallow); - static void tag(const Arg *arg); - static void tagmon(const Arg *arg); -+static Client *termforwin(Client *c); - static void tile(Monitor *m); - static void togglefloating(const Arg *arg); - static void togglefullscreen(const Arg *arg); -+static void toggleswallow(const Arg *arg); -+static void toggleautoswallow(const Arg *arg); - static void toggletag(const Arg *arg); - static void toggleview(const Arg *arg); - static void unlocksession(struct wl_listener *listener, void *data); -@@ -466,11 +479,15 @@ applyrules(Client *c) - if (!(title = client_get_title(c))) - title = broken; - -+ c->pid = client_get_pid(c); -+ - for (r = rules; r < END(rules); r++) { - if ((!r->title || strstr(title, r->title)) - && (!r->id || strstr(appid, r->id))) { - c->isfloating = r->isfloating; - newtags |= r->tags; -+ c->isterm = r->isterm; -+ c->noswallow = r->noswallow; - i = 0; - wl_list_for_each(m, &mons, link) { - if (r->monitor == i++) -@@ -478,6 +495,12 @@ applyrules(Client *c) - } - } - } -+ if (enableautoswallow && !c->noswallow && !c->isfloating && -+ !c->surface.xdg->initial_commit) { -+ Client *p = termforwin(c); -+ if (p) -+ swallow(c, p); -+ } - setmon(c, mon, newtags); - } - -@@ -2006,6 +2029,20 @@ outputmgrtest(struct wl_listener *listener, void *data) - outputmgrapplyortest(config, 1); - } - -+pid_t -+parentpid(pid_t pid) -+{ -+ unsigned int v = 0; -+ FILE *f; -+ char buf[256]; -+ snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)pid); -+ if (!(f = fopen(buf, "r"))) -+ return 0; -+ fscanf(f, "%*u %*s %*c %u", &v); -+ fclose(f); -+ return (pid_t)v; -+} -+ - void - pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, - uint32_t time) -@@ -2326,7 +2363,7 @@ setfullscreen(Client *c, int fullscreen) - c->isfullscreen = fullscreen; - if (!c->mon || !client_surface(c)->mapped) - return; -- c->bw = fullscreen ? 0 : borderpx; -+ c->bw = fullscreen ? 0 : BORDERPX(c); - client_set_fullscreen(c, fullscreen); - wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen - ? LyrFS : c->isfloating ? LyrFloat : LyrTile]); -@@ -2404,6 +2441,9 @@ setmon(Client *c, Monitor *m, uint32_t newtags) - setfloating(c, c->isfloating); - } - focusclient(focustop(selmon), 1); -+ -+ if (c->swallowing) -+ setmon(c->swallowing, m, newtags); - } - - void -@@ -2669,6 +2709,44 @@ startdrag(struct wl_listener *listener, void *data) - LISTEN_STATIC(&drag->icon->events.destroy, destroydragicon); - } - -+void -+swallow(Client *c, Client *toswallow) -+{ -+ /* Do not allow a client to swallow itself */ -+ if (c == toswallow) -+ return; -+ -+ /* Swallow */ -+ if (toswallow && !c->swallowing) { -+ c->swallowing = toswallow; -+ toswallow->swallowedby = c; -+ toswallow->mon = c->mon; -+ toswallow->mon = c->mon; -+ wl_list_remove(&c->link); -+ wl_list_insert(&c->swallowing->link, &c->link); -+ wl_list_remove(&c->flink); -+ wl_list_insert(&c->swallowing->flink, &c->flink); -+ c->bw = BORDERPX(c); -+ c->tags = toswallow->tags; -+ c->isfloating = toswallow->isfloating; -+ c->geom = toswallow->geom; -+ setfullscreen(toswallow, 0); -+ } -+ -+ /* Unswallow */ -+ else if (c->swallowing) { -+ wl_list_remove(&c->swallowing->link); -+ wl_list_insert(&c->link, &c->swallowing->link); -+ wl_list_remove(&c->swallowing->flink); -+ wl_list_insert(&c->flink, &c->swallowing->flink); -+ c->swallowing->tags = c->tags; -+ c->swallowing->swallowedby = NULL; -+ c->swallowing = NULL; -+ c->bw = BORDERPX(c); -+ setfullscreen(c, 0); -+ } -+} -+ - void - tag(const Arg *arg) - { -@@ -2690,6 +2768,40 @@ tagmon(const Arg *arg) - setmon(sel, dirtomon(arg->i), 0); - } - -+Client * -+termforwin(Client *c) -+{ -+ Client *p; -+ pid_t pid; -+ pid_t pids[32]; -+ size_t i, pids_len; -+ -+ if (!c->pid || c->isterm) -+ return NULL; -+ -+ /* Get all parent pids */ -+ pids_len = 0; -+ pid = c->pid; -+ while (pids_len < LENGTH(pids)) { -+ pid = parentpid(pid); -+ if (!pid) -+ break; -+ pids[pids_len++] = pid; -+ } -+ -+ /* Find closest parent */ -+ for (i = 0; i < pids_len; i++) { -+ wl_list_for_each(p, &clients, link) { -+ if (!p->pid || !p->isterm || p->swallowedby) -+ continue; -+ if (pids[i] == p->pid) -+ return p; -+ } -+ } -+ -+ return NULL; -+} -+ - void - tile(Monitor *m) - { -@@ -2741,6 +2853,32 @@ togglefullscreen(const Arg *arg) - setfullscreen(sel, !sel->isfullscreen); - } - -+void -+toggleswallow(const Arg *arg) -+{ -+ Client *c, *sel = focustop(selmon); -+ if (!sel) -+ return; -+ -+ if (sel->swallowing) { -+ swallow(sel, NULL); -+ } else { -+ wl_list_for_each(c, &sel->flink, flink) { -+ if (&c->flink == &fstack) -+ continue; /* wrap past the sentinel node */ -+ if (VISIBLEON(c, selmon)) -+ break; /* found it */ -+ } -+ swallow(sel, c); -+ } -+} -+ -+void -+toggleautoswallow(const Arg *arg) -+{ -+ enableautoswallow = !enableautoswallow; -+} -+ - void - toggletag(const Arg *arg) - { -@@ -2801,6 +2939,12 @@ unmapnotify(struct wl_listener *listener, void *data) - grabc = NULL; - } - -+ if (c->swallowing) { -+ swallow(c, NULL); -+ } else if (c->swallowedby) { -+ swallow(c->swallowedby, NULL); -+ } -+ - if (client_is_unmanaged(c)) { - if (c == exclusive_focus) { - exclusive_focus = NULL; --- -2.49.0