diff options
-rw-r--r-- | .gitmodules | 3 | ||||
-rw-r--r-- | Makefile | 19 | ||||
-rw-r--r-- | client.h | 52 | ||||
-rw-r--r-- | config.def.h | 157 | ||||
-rw-r--r-- | config.mk | 10 | ||||
-rw-r--r-- | dwl.c | 1044 | ||||
-rw-r--r-- | patches/borders.patch | 213 | ||||
-rw-r--r-- | patches/gestures.patch | 169 | ||||
-rw-r--r-- | patches/ipc.patch | 597 | ||||
-rw-r--r-- | patches/lockedkeys.patch | 79 | ||||
-rw-r--r-- | patches/main...serenevoid:uselessgaps.patch | 137 | ||||
-rw-r--r-- | patches/passthrough.patch | 82 | ||||
-rw-r--r-- | patches/pointer-gestures-unstable-v1.patch | 186 | ||||
-rw-r--r-- | patches/regexrules.patch | 76 | ||||
-rw-r--r-- | patches/swallow.patch | 240 | ||||
-rw-r--r-- | patches/tearing.patch | 356 | ||||
-rw-r--r-- | patches/toggle_constraints.patch | 86 | ||||
-rw-r--r-- | patches/xwayland-handle-minimize.patch | 91 | ||||
-rwxr-xr-x | pre_build.sh | 2 | ||||
-rw-r--r-- | protocols/dwl-ipc-unstable-v2.xml | 181 | ||||
m--------- | wlroots | 0 |
21 files changed, 3638 insertions, 142 deletions
diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..b0169c1 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "wlroots"] + path = wlroots + url = https://gitlab.freedesktop.org/wlroots/wlroots @@ -17,12 +17,14 @@ DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(WLR_INCS) $(DWLCPPFLAGS) $(DWLDEV LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` $(WLR_LIBS) -lm $(LIBS) all: dwl -dwl: dwl.o util.o - $(CC) dwl.o util.o $(DWLCFLAGS) $(LDFLAGS) $(LDLIBS) -o $@ +dwl: dwl.o util.o dwl-ipc-unstable-v2-protocol.o + $(CC) dwl.o util.o dwl-ipc-unstable-v2-protocol.o $(DWLCFLAGS) $(LDFLAGS) $(LDLIBS) -o $@ dwl.o: dwl.c client.h config.h config.mk cursor-shape-v1-protocol.h \ pointer-constraints-unstable-v1-protocol.h wlr-layer-shell-unstable-v1-protocol.h \ - wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h + wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h \ + dwl-ipc-unstable-v2-protocol.h tearing-control-v1-protocol.h util.o: util.c util.h +dwl-ipc-unstable-v2-protocol.o: dwl-ipc-unstable-v2-protocol.c dwl-ipc-unstable-v2-protocol.h # wayland-scanner is a tool which generates C headers and rigging for Wayland # protocols, which are specified in XML. wlroots requires you to rig these up @@ -45,11 +47,20 @@ wlr-output-power-management-unstable-v1-protocol.h: xdg-shell-protocol.h: $(WAYLAND_SCANNER) server-header \ $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@ +dwl-ipc-unstable-v2-protocol.h: + $(WAYLAND_SCANNER) server-header \ + protocols/dwl-ipc-unstable-v2.xml $@ +dwl-ipc-unstable-v2-protocol.c: + $(WAYLAND_SCANNER) private-code \ + protocols/dwl-ipc-unstable-v2.xml $@ +tearing-control-v1-protocol.h: + $(WAYLAND_SCANNER) server-header \ + $(WAYLAND_PROTOCOLS)/staging/tearing-control/tearing-control-v1.xml $@ config.h: cp config.def.h $@ clean: - rm -f dwl *.o *-protocol.h + rm -f dwl *.o *-protocol.* dist: clean mkdir -p dwl-$(VERSION) @@ -94,9 +94,12 @@ 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); + struct wlr_xwayland_surface *surface; + if ((surface = wlr_xwayland_surface_try_from_wlr_surface(s))) { + if (activated && surface->minimized) + wlr_xwayland_surface_set_minimized(surface, false); + + wlr_xwayland_surface_activate(surface, activated); return; } #endif @@ -131,6 +134,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) { @@ -314,8 +329,35 @@ 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); + + if (!c) + return; + + if (c == focustop(selmon)) { + if (selmon->lt[selmon->sellt] == (Layout*)&layouts[1] || c->isfloating) + color = floatcolor; + else if (selmon->lt[selmon->sellt] == (Layout*)&layouts[2]) + color = monoclecolor; + else if (c->isurgent) + color = urgentcolor; + else if (!exclusive_focus && !seat->drag) + color = focuscolor; + } else + color = color; + + + for (i = 0; i < 4; i++) { + if (border_color_type == BrdOriginal) { + wlr_scene_rect_set_color(c->border[i], color); + } else if (border_color_type == BrdStart) { + wlr_scene_rect_set_color(c->borders[i], color); + } else if (border_color_type == BrdEnd) { + wlr_scene_rect_set_color(c->bordere[i], color); + } else if (border_color_type == BrdStartEnd) { + wlr_scene_rect_set_color(c->borders[i], color); + wlr_scene_rect_set_color(c->bordere[i], color); + } + } } static inline void diff --git a/config.def.h b/config.def.h index 22d2171..d6cdf3a 100644 --- a/config.def.h +++ b/config.def.h @@ -5,14 +5,29 @@ (hex & 0xFF) / 255.0f } /* appearance */ static const int sloppyfocus = 1; /* focus follows mouse */ +static const int warpcursor = 1; /* weather the cursor should move with client focus */ 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.1f, 0.1f, 0.1f, 1.0f}; /* You can also use glsl colors */ +static const int smartgaps = 0; /* 1 means no outer gap when there is only one window */ +static const unsigned int borderpx = 3; /* border pixel of windows */ +static const unsigned int borderspx = 3; /* width of the border that start from outside the windows */ +static const unsigned int borderepx = 3; /* width of the border that start from inside the windows */ +static const unsigned int borderspx_offset = 3; /* offset of the border that start from outside the windows */ +static const unsigned int borderepx_negative_offset = 3; /* offset of the border that start from inside the windows */ +static const unsigned int gappx = 10; /* horiz inner gap between windows */ +static const unsigned int swipe_min_threshold = 0; +static const float bordercolor[] = COLOR(0x161617ff); +static const float focuscolor[] = COLOR(0xea83a5ff); +static const float urgentcolor[] = COLOR(0xe29ecaff); +static const float floatcolor[] = COLOR(0xaca1cfff); +static const float monoclecolor[] = COLOR(0xe6b99dff); + +static const float rootcolor[] = COLOR(0x161617ff); +static const float fullscreen_bg[] = COLOR(0x00000000); + +static const float *borderscolor = bordercolor; /* color of the border that start from outside the windows */ +static const float *borderecolor = bordercolor; /* color of the border that start from inside the windows */ +static const int border_color_type = BrdOriginal; /* borders to be colored (focuscolor, urgentcolor) */ +static const int borders_only_floating = 0; /* tagging - TAGCOUNT must be no greater than 31 */ #define TAGCOUNT (9) @@ -20,12 +35,40 @@ static const float fullscreen_bg[] = {0.1f, 0.1f, 0.1f, 1.0f}; /* You ca /* logging */ static int log_level = WLR_ERROR; +/* passthrough */ +static int passthrough = 0; + /* 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" */ + /* app_id title tags mask isfloating isterm noswallow monitor */ + { "foot", NULL, 0, 0, 1, 0, -1 }, + { "wev", NULL, 0, 0, 0, 1, -1 }, + + /* steam settings */ + { NULL, "Steam Settings", 0, 1, 0, 0, -1 }, + { NULL, "Friends List", 0, 1, 0, 0, -1 }, + { NULL, "About Steam", 0, 1, 0, 0, -1 }, + { NULL, "Special Offers", 0, 1, 0, 0, -1 }, + { NULL, "Steam", 1 << 5, 0, 0, 0, -1 }, + + /* discord (firefox with different app_id) */ + { "discord", NULL, 1 << 7, 0, 0, 1, 0 }, + { "vesktop", NULL, 1 << 7, 0, 0, 1, 0 }, + + /* firefox settings */ + { "Firefox", "Library", 0, 1, 0, 0, -1 }, + { "Firefox", "File Upload", 0, 1, 0, 0, -1 }, + { "Firefox", "About Mozilla Firefox", 0, 1, 0, 0, -1 }, + { "Firefox", "Picture-in-Picture", 0, 1, 0, 0, -1 }, + { "Firefox", "*— Mozilla Firefox$", 1 << 1, 0, 0, 0, 1 } +}; + +/* tearing */ +static int tearing_allowed = 1; +static const ForceTearingRule force_tearing[] = { + {.title = "", .appid = "hl_linux"}, + {.title = "Warcraft III", .appid = ""}, + {.title = "", .appid = "gamescope"}, }; /* layout(s) */ @@ -44,9 +87,7 @@ static const Layout layouts[] = { /* 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 }, - */ + { "eDP-1", 0.5f, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 }, /* defaults */ { NULL, 0.55f, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 }, }; @@ -58,10 +99,11 @@ static const struct xkb_rule_names xkb_rules = { .options = "ctrl:nocaps", */ .options = NULL, + .layout = "us" }; -static const int repeat_rate = 25; -static const int repeat_delay = 600; +static const int repeat_rate = 30; +static const int repeat_delay = 400; /* Trackpad */ static const int tap_to_click = 1; @@ -107,47 +149,65 @@ 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 MODKEY WLR_MODIFIER_LOGO +#define ALTKEY WLR_MODIFIER_ALT +#define SHIFTKEY WLR_MODIFIER_SHIFT +#define CTRLKEY WLR_MODIFIER_CTRL #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} } + { MODKEY|CTRLKEY, KEY, toggleview, {.ui = 1 << TAG} }, \ + { MODKEY|SHIFTKEY, SKEY, tag, {.ui = 1 << TAG} }, \ + { MODKEY|CTRLKEY|SHIFTKEY, 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 }; +#define CMD(...) {.v = (const char*[]){ __VA_ARGS__, 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_d, spawn, CMD("wmenu-run") }, + { MODKEY, XKB_KEY_Return, spawn, CMD("foot") }, + { MODKEY, XKB_KEY_a, spawn, CMD("moosic") }, + { MODKEY, XKB_KEY_s, spawn, CMD("shooter") }, + { 0, XKB_KEY_XF86AudioRaiseVolume, spawn, CMD("volume", "+5") }, + { 0, XKB_KEY_XF86AudioLowerVolume, spawn, CMD("volume", "-5") }, + { 0, XKB_KEY_XF86AudioMute, spawn, CMD("volume", "m") }, + { 0, XKB_KEY_XF86AudioPlay, spawn, CMD("moosic", "p") }, + { 0, XKB_KEY_XF86AudioStop, spawn, CMD("moosic", "p") }, + { 0, XKB_KEY_XF86AudioPrev, spawn, CMD("mpc", "next") }, + { 0, XKB_KEY_XF86AudioNext, spawn, CMD("mpc", "prev") }, + { 0, XKB_KEY_XF86MonBrightnessUp, spawn, CMD("brightness", "+2%") }, + { 0, XKB_KEY_XF86MonBrightnessDown, spawn, CMD("brightness", "-2%") }, + { MODKEY, XKB_KEY_b, togglebar, {0} }, { 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_o, incnmaster, {.i = -1} }, + { MODKEY, XKB_KEY_u, incnmaster, {.i = 0} }, { 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_semicolon, setmfact, {.f = 0} }, + { MODKEY|SHIFTKEY, XKB_KEY_Return, zoom, {0} }, { MODKEY, XKB_KEY_Tab, view, {0} }, - { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_C, killclient, {0} }, + { MODKEY, XKB_KEY_g, togglegaps, {0} }, + { MODKEY|SHIFTKEY, 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|SHIFTKEY, XKB_KEY_space, togglefloating, {0} }, + { MODKEY, XKB_KEY_e, togglefullscreen, {0} }, + { MODKEY, XKB_KEY_c, togglepointerconstraints, {0}}, { MODKEY, XKB_KEY_0, view, {.ui = ~0} }, - { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} }, + { MODKEY|SHIFTKEY, 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} }, + { MODKEY|SHIFTKEY, XKB_KEY_less, tagmon, {.i = WLR_DIRECTION_LEFT} }, + { MODKEY|SHIFTKEY, 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), @@ -157,14 +217,35 @@ static const Key keys[] = { 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} }, + { ALTKEY|MODKEY|CTRLKEY|SHIFTKEY, XKB_KEY_Escape, togglepassthrough, {0} }, + { MODKEY|SHIFTKEY, 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} }, + { CTRLKEY|ALTKEY,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)} } +#define CHVT(n) { CTRLKEY|ALTKEY,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 Key lockedkeys[] = { + /* Note that Shift changes certain key codes: c -> C, 2 -> at, etc. */ + /* modifier key function argument */ + { 0, XKB_KEY_XF86AudioRaiseVolume, spawn, CMD("volume", "+5") }, + { 0, XKB_KEY_XF86AudioLowerVolume, spawn, CMD("volume", "-5") }, + { 0, XKB_KEY_XF86AudioMute, spawn, CMD("volume", "m") }, + { 0, XKB_KEY_XF86AudioPlay, spawn, CMD("moosic", "p") }, + { 0, XKB_KEY_XF86AudioStop, spawn, CMD("moosic", "p") }, + { 0, XKB_KEY_XF86AudioPrev, spawn, CMD("mpc", "next") }, + { 0, XKB_KEY_XF86AudioNext, spawn, CMD("mpc", "prev") }, + { 0, XKB_KEY_XF86MonBrightnessUp, spawn, CMD("brightness", "+2%") }, + { 0, XKB_KEY_XF86MonBrightnessDown, spawn, CMD("brightness", "-2%") }, + + /* Ctrl-Alt-Backspace and Ctrl-Alt-Fx used to be handled by X server */ + { CTRLKEY|ALTKEY,XKB_KEY_Terminate_Server, quit, {0} }, +#define CHVT(n) { CTRLKEY|ALTKEY,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), }; @@ -174,3 +255,9 @@ static const Button buttons[] = { { MODKEY, BTN_MIDDLE, togglefloating, {0} }, { MODKEY, BTN_RIGHT, moveresize, {.ui = CurResize} }, }; + +static const Gesture gestures[] = { + /* modifier gesture fingers_count function argument */ + { 0, SWIPE_DOWN, 3, spawn, CMD("quickmenu") }, + { 0, SWIPE_UP, 3, spawn, CMD("killall", "wmenu") }, +}; @@ -14,10 +14,10 @@ WLR_LIBS = `$(PKG_CONFIG) --libs wlroots-0.19` # Allow using an alternative wlroots installations # This has to have all the includes required by wlroots, e.g: # Assuming wlroots git repo is "${PWD}/wlroots" and you only ran "meson setup build && ninja -C build" -#WLR_INCS = -I/usr/include/pixman-1 -I/usr/include/elogind -I/usr/include/libdrm \ -# -I$(PWD)/wlroots/include +WLR_INCS = -I/usr/include/pixman-1 -I/usr/include/elogind -I/usr/include/libdrm \ +-I$(PWD)/wlroots/include # Set -rpath to avoid using the wrong library. -#WLR_LIBS = -Wl,-rpath,$(PWD)/wlroots/build -L$(PWD)/wlroots/build -lwlroots-0.19 +WLR_LIBS = -Wl,-rpath,$(PWD)/wlroots/build -L$(PWD)/wlroots/build -lwlroots-0.19 # Assuming you ran "meson setup --prefix ${PWD}/0.19 build && ninja -C build install" #WLR_INCS = -I/usr/include/pixman-1 -I/usr/include/elogind -I/usr/include/libdrm \ @@ -27,8 +27,8 @@ WLR_LIBS = `$(PKG_CONFIG) --libs wlroots-0.19` XWAYLAND = XLIBS = # Uncomment to build XWayland support -#XWAYLAND = -DXWAYLAND -#XLIBS = xcb xcb-icccm +XWAYLAND = -DXWAYLAND +XLIBS = xcb xcb-icccm # dwl itself only uses C99 features, but wlroots' headers use anonymous unions (C11). # To avoid warnings about them, we do not use -std=c99 and instead of using the @@ -11,6 +11,7 @@ #include <sys/wait.h> #include <time.h> #include <unistd.h> +#include <regex.h> #include <wayland-server-core.h> #include <wlr/backend.h> #include <wlr/backend/libinput.h> @@ -40,6 +41,7 @@ #include <wlr/types/wlr_output_power_management_v1.h> #include <wlr/types/wlr_pointer.h> #include <wlr/types/wlr_pointer_constraints_v1.h> +#include <wlr/types/wlr_pointer_gestures_v1.h> #include <wlr/types/wlr_presentation_time.h> #include <wlr/types/wlr_primary_selection.h> #include <wlr/types/wlr_primary_selection_v1.h> @@ -51,6 +53,7 @@ #include <wlr/types/wlr_session_lock_v1.h> #include <wlr/types/wlr_single_pixel_buffer_v1.h> #include <wlr/types/wlr_subcompositor.h> +#include <wlr/types/wlr_tearing_control_v1.h> #include <wlr/types/wlr_viewporter.h> #include <wlr/types/wlr_virtual_keyboard_v1.h> #include <wlr/types/wlr_virtual_pointer_v1.h> @@ -68,6 +71,7 @@ #include <xcb/xcb_icccm.h> #endif +#include "dwl-ipc-unstable-v2-protocol.h" #include "util.h" /* macros */ @@ -85,6 +89,13 @@ 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 */ +enum { SWIPE_LEFT, SWIPE_RIGHT, SWIPE_DOWN, SWIPE_UP }; +enum { BrdOriginal, BrdStart, BrdEnd, BrdStartEnd }; + +typedef struct ForceTearingRule { + const char* title; + const char* appid; +} ForceTearingRule; typedef union { int i; @@ -100,14 +111,25 @@ typedef struct { const Arg arg; } Button; -typedef struct Monitor Monitor; typedef struct { - /* Must keep this field first */ + unsigned int mod; + unsigned int motion; + unsigned int fingers_count; + void (*func)(const Arg *); + const Arg arg; +} Gesture; + +typedef struct Monitor Monitor; +typedef struct Client Client; +struct Client { + /* Must keep these three elements in this order */ 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_rect *borders[4]; /* top, bottom, left, right */ + struct wlr_scene_rect *bordere[4]; /* top, bottom, left, right */ struct wlr_scene_tree *scene_surface; struct wl_list link; struct wl_list flink; @@ -131,15 +153,27 @@ typedef struct { #ifdef XWAYLAND struct wl_listener activate; struct wl_listener associate; + struct wl_listener minimize; struct wl_listener dissociate; struct wl_listener configure; struct wl_listener set_hints; #endif unsigned int bw; + unsigned int bws; + unsigned int bwe; uint32_t tags; - int isfloating, isurgent, isfullscreen; + int isfloating, isurgent, isfullscreen, isterm, noswallow; uint32_t resize; /* configure serial of a pending resize */ -} Client; + enum wp_tearing_control_v1_presentation_hint tearing_hint; + pid_t pid; + Client *swallowing, *swallowedby; +}; + +typedef struct { + struct wl_list link; + struct wl_resource *resource; + Monitor *mon; +} DwlIpcOutput; typedef struct { uint32_t mod; @@ -185,6 +219,7 @@ typedef struct { struct Monitor { struct wl_list link; + struct wl_list dwl_ipc_outputs; struct wlr_output *wlr_output; struct wlr_scene_output *scene_output; struct wlr_scene_rect *fullscreen_bg; /* See createmon() for info */ @@ -197,6 +232,7 @@ struct Monitor { struct wlr_box w; /* window area, layout-relative */ struct wl_list layers[4]; /* LayerSurface.link */ const Layout *lt[2]; + int gappx; /* horizontal outer gaps */ unsigned int seltags; unsigned int sellt; uint32_t tagset[2]; @@ -204,6 +240,7 @@ struct Monitor { int gamma_lut_changed; int nmaster; char ltsymbol[16]; + char *name; int asleep; }; @@ -227,6 +264,8 @@ typedef struct { const char *title; uint32_t tags; int isfloating; + int isterm; + int noswallow; int monitor; } Rule; @@ -239,6 +278,19 @@ typedef struct { struct wl_listener destroy; } SessionLock; +typedef struct TearingController { + struct wlr_tearing_control_v1 *tearing_control; + struct wl_listener set_hint; + struct wl_listener destroy; + + struct wl_list link; /* tearing_controllers */ +} TearingController; + +typedef struct SendFrameDoneData { + struct timespec when; + struct Monitor *mon; +} SendFrameDoneData; + /* function declarations */ static void applybounds(Client *c, struct wlr_box *bbox); static void applyrules(Client *c); @@ -248,6 +300,15 @@ static void arrangelayer(Monitor *m, struct wl_list *list, static void arrangelayers(Monitor *m); static void axisnotify(struct wl_listener *listener, void *data); static void buttonpress(struct wl_listener *listener, void *data); +static int ongesture(struct wlr_pointer_swipe_end_event *event); +static void swipe_begin(struct wl_listener *listener, void *data); +static void swipe_update(struct wl_listener *listener, void *data); +static void swipe_end(struct wl_listener *listener, void *data); +static void pinch_begin(struct wl_listener *listener, void *data); +static void pinch_update(struct wl_listener *listener, void *data); +static void pinch_end(struct wl_listener *listener, void *data); +static void hold_begin(struct wl_listener *listener, void *data); +static void hold_end(struct wl_listener *listener, void *data); static void chvt(const Arg *arg); static void checkidleinhibitor(struct wlr_surface *exclude); static void cleanup(void); @@ -282,6 +343,17 @@ 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 dwl_ipc_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id); +static void dwl_ipc_manager_destroy(struct wl_resource *resource); +static void dwl_ipc_manager_get_output(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *output); +static void dwl_ipc_manager_release(struct wl_client *client, struct wl_resource *resource); +static void dwl_ipc_output_destroy(struct wl_resource *resource); +static void dwl_ipc_output_printstatus(Monitor *monitor); +static void dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output); +static void dwl_ipc_output_set_client_tags(struct wl_client *client, struct wl_resource *resource, uint32_t and_tags, uint32_t xor_tags); +static void dwl_ipc_output_set_layout(struct wl_client *client, struct wl_resource *resource, uint32_t index); +static void dwl_ipc_output_set_tags(struct wl_client *client, struct wl_resource *resource, uint32_t tagmask, uint32_t toggle_tagset); +static void dwl_ipc_output_release(struct wl_client *client, struct wl_resource *resource); static void focusclient(Client *c, int lift); static void focusmon(const Arg *arg); static void focusstack(const Arg *arg); @@ -289,9 +361,13 @@ 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 handletearingcontrollersethint(struct wl_listener *listener, void *data); +static void handletearingcontrollerdestroy(struct wl_listener *listener, void *data); +static void handlenewtearinghint(struct wl_listener *listener, void *data); 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 int lockedkeybinding(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); @@ -305,6 +381,7 @@ static void motionnotify(uint32_t time, struct wlr_input_device *device, double double sy, double sx_unaccel, double sy_unaccel); static void motionrelative(struct wl_listener *listener, void *data); static void moveresize(const Arg *arg); +static int moncantear(Monitor* m); 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); @@ -319,6 +396,7 @@ 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 sendframedoneiterator(struct wlr_scene_buffer *buffer, int x, int y, void *user_data); 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); @@ -334,8 +412,12 @@ 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 togglebar(const Arg *arg); static void togglefloating(const Arg *arg); static void togglefullscreen(const Arg *arg); +static void togglegaps(const Arg *arg); +static void togglepointerconstraints(const Arg *arg); +static void togglepassthrough(const Arg *arg); static void toggletag(const Arg *arg); static void toggleview(const Arg *arg); static void unlocksession(struct wl_listener *listener, void *data); @@ -351,6 +433,11 @@ 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); +static int regex_match(const char *pattern, const char *str); +static pid_t getparentprocess(pid_t p); +static int isdescprocess(pid_t p, pid_t c); +static Client *termforwin(Client *w); +static void swallow(Client *c, Client *w); /* variables */ static pid_t child_pid = -1; @@ -382,6 +469,7 @@ 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_gestures_v1 *pointer_gestures; static struct wlr_pointer_constraints_v1 *pointer_constraints; static struct wlr_relative_pointer_manager_v1 *relative_pointer_mgr; @@ -395,17 +483,29 @@ 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; +struct wlr_tearing_control_manager_v1 *tearing_control_v1; +struct wl_listener tearing_control_new_object; +struct wl_list tearing_controllers; + static struct wlr_seat *seat; static KeyboardGroup *kb_group; static unsigned int cursor_mode; static Client *grabc; static int grabcx, grabcy; /* client-relative */ +static int enable_constraints = 1; + static struct wlr_output_layout *output_layout; static struct wlr_box sgeom; static struct wl_list mons; static Monitor *selmon; +static uint32_t swipe_fingers = 0; +static double swipe_dx = 0; +static double swipe_dy = 0; + +static int enablegaps = 1; /* enables gaps, used by togglegaps */ + /* global event handlers */ static struct wl_listener cursor_axis = {.notify = axisnotify}; static struct wl_listener cursor_button = {.notify = buttonpress}; @@ -435,6 +535,8 @@ 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}; +static struct zdwl_ipc_manager_v2_interface dwl_manager_implementation = {.release = dwl_ipc_manager_release, .get_output = dwl_ipc_manager_get_output}; +static struct zdwl_ipc_output_v2_interface dwl_output_implementation = {.release = dwl_ipc_output_release, .set_tags = dwl_ipc_output_set_tags, .set_layout = dwl_ipc_output_set_layout, .set_client_tags = dwl_ipc_output_set_client_tags}; #ifdef XWAYLAND static void activatex11(struct wl_listener *listener, void *data); @@ -442,6 +544,8 @@ 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 xcb_atom_t getatom(xcb_connection_t *xc, const char *name); +static void minimizenotify(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}; @@ -455,6 +559,8 @@ static struct wlr_xwayland *xwayland; /* attempt to encapsulate suck into one file */ #include "client.h" +static const unsigned int abzsquare = swipe_min_threshold * swipe_min_threshold; + /* function implementations */ void applybounds(Client *c, struct wlr_box *bbox) @@ -487,10 +593,14 @@ 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))) { + if ((!r->title || regex_match(r->title, title)) + && (!r->id || regex_match(r->id, appid))) { c->isfloating = r->isfloating; + c->isterm = r->isterm; + c->noswallow = r->noswallow; newtags |= r->tags; i = 0; wl_list_for_each(m, &mons, link) { @@ -499,6 +609,21 @@ applyrules(Client *c) } } } + if (!c->noswallow && !client_is_float_type(c) + && !c->surface.xdg->initial_commit) { + Client *p = termforwin(c); + if (p) { + c->swallowedby = p; + p->swallowing = c; + wl_list_remove(&c->link); + wl_list_remove(&c->flink); + swallow(c, p); + wl_list_remove(&p->link); + wl_list_remove(&p->flink); + mon = p->mon; + newtags = p->tags; + } + } setmon(c, mon, newtags); } @@ -645,6 +770,11 @@ buttonpress(struct wl_listener *listener, void *data) for (b = buttons; b < END(buttons); b++) { if (CLEANMASK(mods) == CLEANMASK(b->mod) && event->button == b->button && b->func) { + if (passthrough) { + if (b->func != togglepassthrough) continue; + b->func(&b->arg); + break; + } b->func(&b->arg); return; } @@ -672,6 +802,174 @@ buttonpress(struct wl_listener *listener, void *data) } void +swipe_begin(struct wl_listener *listener, void *data) +{ + struct wlr_pointer_swipe_begin_event *event = data; + + swipe_fingers = event->fingers; + // Reset swipe distance at the beginning of a swipe + swipe_dx = 0; + swipe_dy = 0; + + // Forward swipe begin event to client + wlr_pointer_gestures_v1_send_swipe_begin( + pointer_gestures, + seat, + event->time_msec, + event->fingers + ); +} + +void +swipe_update(struct wl_listener *listener, void *data) +{ + struct wlr_pointer_swipe_update_event *event = data; + + swipe_fingers = event->fingers; + // Accumulate swipe distance + swipe_dx += event->dx; + swipe_dy += event->dy; + + // Forward swipe update event to client + wlr_pointer_gestures_v1_send_swipe_update( + pointer_gestures, + seat, + event->time_msec, + event->dx, + event->dy + ); +} + +int +ongesture(struct wlr_pointer_swipe_end_event *event) +{ + struct wlr_keyboard *keyboard; + uint32_t mods; + const Gesture *g; + unsigned int motion; + unsigned int adx = (int)round(fabs(swipe_dx)); + unsigned int ady = (int)round(fabs(swipe_dy)); + int handled = 0; + + if (event->cancelled) { + return handled; + } + + // Require absolute distance movement beyond a small thresh-hold + if (adx * adx + ady * ady < abzsquare) { + return handled; + } + + if (adx > ady) { + motion = swipe_dx < 0 ? SWIPE_LEFT : SWIPE_RIGHT; + } else { + motion = swipe_dy < 0 ? SWIPE_UP : SWIPE_DOWN; + } + + keyboard = wlr_seat_get_keyboard(seat); + mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; + for (g = gestures; g < END(gestures); g++) { + if (CLEANMASK(mods) == CLEANMASK(g->mod) && + swipe_fingers == g->fingers_count && + motion == g->motion && g->func) { + g->func(&g->arg); + handled = 1; + } + } + return handled; +} + +void +swipe_end(struct wl_listener *listener, void *data) +{ + struct wlr_pointer_swipe_end_event *event = data; + + // TODO: should we stop here if the event has been handled? + ongesture(event); + + // Forward swipe end event to client + wlr_pointer_gestures_v1_send_swipe_end( + pointer_gestures, + seat, + event->time_msec, + event->cancelled + ); +} + +void +pinch_begin(struct wl_listener *listener, void *data) +{ + struct wlr_pointer_pinch_begin_event *event = data; + + // Forward pinch begin event to client + wlr_pointer_gestures_v1_send_pinch_begin( + pointer_gestures, + seat, + event->time_msec, + event->fingers + ); +} + +void +pinch_update(struct wl_listener *listener, void *data) +{ + struct wlr_pointer_pinch_update_event *event = data; + + // Forward pinch update event to client + wlr_pointer_gestures_v1_send_pinch_update( + pointer_gestures, + seat, + event->time_msec, + event->dx, + event->dy, + event->scale, + event->rotation + ); +} + +void +pinch_end(struct wl_listener *listener, void *data) +{ + struct wlr_pointer_pinch_end_event *event = data; + + // Forward pinch end event to client + wlr_pointer_gestures_v1_send_pinch_end( + pointer_gestures, + seat, + event->time_msec, + event->cancelled + ); +} + +void +hold_begin(struct wl_listener *listener, void *data) +{ + struct wlr_pointer_hold_begin_event *event = data; + + // Forward hold begin event to client + wlr_pointer_gestures_v1_send_hold_begin( + pointer_gestures, + seat, + event->time_msec, + event->fingers + ); +} + +void +hold_end(struct wl_listener *listener, void *data) +{ + struct wlr_pointer_hold_end_event *event = data; + + // Forward hold end event to client + wlr_pointer_gestures_v1_send_hold_end( + pointer_gestures, + seat, + event->time_msec, + event->cancelled + ); +} + +void chvt(const Arg *arg) { wlr_session_change_vt(session, arg->ui); @@ -729,6 +1027,10 @@ cleanupmon(struct wl_listener *listener, void *data) LayerSurface *l, *tmp; size_t i; + DwlIpcOutput *ipc_output, *ipc_output_tmp; + wl_list_for_each_safe(ipc_output, ipc_output_tmp, &m->dwl_ipc_outputs, link) + wl_resource_destroy(ipc_output->resource); + /* 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) @@ -1049,13 +1351,16 @@ createmon(struct wl_listener *listener, void *data) m = wlr_output->data = ecalloc(1, sizeof(*m)); m->wlr_output = wlr_output; + wl_list_init(&m->dwl_ipc_outputs); + 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->gappx = gappx; m->tagset[0] = m->tagset[1] = 1; for (r = monrules; r < END(monrules); r++) { + m->name = wlr_output->name; if (!r->name || strstr(wlr_output->name, r->name)) { m->m.x = r->x; m->m.y = r->y; @@ -1124,6 +1429,8 @@ createnotify(struct wl_listener *listener, void *data) c = toplevel->base->data = ecalloc(1, sizeof(*c)); c->surface.xdg = toplevel->base; c->bw = borderpx; + c->bws = borders_only_floating ? 0 : borderspx; + c->bwe = borders_only_floating ? 0 : borderepx; LISTEN(&toplevel->base->surface->events.commit, &c->commit, commitnotify); LISTEN(&toplevel->base->surface->events.map, &c->map, mapnotify); @@ -1334,6 +1641,7 @@ destroynotify(struct wl_listener *listener, void *data) wl_list_remove(&c->activate.link); wl_list_remove(&c->associate.link); wl_list_remove(&c->configure.link); + wl_list_remove(&c->minimize.link); wl_list_remove(&c->dissociate.link); wl_list_remove(&c->set_hints.link); } else @@ -1397,6 +1705,192 @@ dirtomon(enum wlr_direction dir) } void +dwl_ipc_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) +{ + struct wl_resource *manager_resource = wl_resource_create(client, &zdwl_ipc_manager_v2_interface, version, id); + if (!manager_resource) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(manager_resource, &dwl_manager_implementation, NULL, dwl_ipc_manager_destroy); + + zdwl_ipc_manager_v2_send_tags(manager_resource, TAGCOUNT); + + for (unsigned int i = 0; i < LENGTH(layouts); i++) + zdwl_ipc_manager_v2_send_layout(manager_resource, layouts[i].symbol); +} + +void +dwl_ipc_manager_destroy(struct wl_resource *resource) +{ + /* No state to destroy */ +} + +void +dwl_ipc_manager_get_output(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *output) +{ + DwlIpcOutput *ipc_output; + Monitor *monitor = wlr_output_from_resource(output)->data; + struct wl_resource *output_resource = wl_resource_create(client, &zdwl_ipc_output_v2_interface, wl_resource_get_version(resource), id); + if (!output_resource) + return; + + ipc_output = ecalloc(1, sizeof(*ipc_output)); + ipc_output->resource = output_resource; + ipc_output->mon = monitor; + wl_resource_set_implementation(output_resource, &dwl_output_implementation, ipc_output, dwl_ipc_output_destroy); + wl_list_insert(&monitor->dwl_ipc_outputs, &ipc_output->link); + dwl_ipc_output_printstatus_to(ipc_output); +} + +void +dwl_ipc_manager_release(struct wl_client *client, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static void +dwl_ipc_output_destroy(struct wl_resource *resource) +{ + DwlIpcOutput *ipc_output = wl_resource_get_user_data(resource); + wl_list_remove(&ipc_output->link); + free(ipc_output); +} + +void +dwl_ipc_output_printstatus(Monitor *monitor) +{ + DwlIpcOutput *ipc_output; + wl_list_for_each(ipc_output, &monitor->dwl_ipc_outputs, link) + dwl_ipc_output_printstatus_to(ipc_output); +} + +void +dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output) +{ + Monitor *monitor = ipc_output->mon; + Client *c, *focused; + int tagmask, state, numclients, focused_client, tag; + const char *title, *appid; + focused = focustop(monitor); + zdwl_ipc_output_v2_send_active(ipc_output->resource, monitor == selmon); + + for (tag = 0 ; tag < TAGCOUNT; tag++) { + numclients = state = focused_client = 0; + tagmask = 1 << tag; + if ((tagmask & monitor->tagset[monitor->seltags]) != 0) + state |= ZDWL_IPC_OUTPUT_V2_TAG_STATE_ACTIVE; + + wl_list_for_each(c, &clients, link) { + if (c->mon != monitor) + continue; + if (!(c->tags & tagmask)) + continue; + if (c == focused) + focused_client = 1; + if (c->isurgent) + state |= ZDWL_IPC_OUTPUT_V2_TAG_STATE_URGENT; + + numclients++; + } + zdwl_ipc_output_v2_send_tag(ipc_output->resource, tag, state, numclients, focused_client); + } + title = focused ? client_get_title(focused) : ""; + appid = focused ? client_get_appid(focused) : ""; + + zdwl_ipc_output_v2_send_layout(ipc_output->resource, monitor->lt[monitor->sellt] - layouts); + zdwl_ipc_output_v2_send_title(ipc_output->resource, title); + zdwl_ipc_output_v2_send_appid(ipc_output->resource, appid); + zdwl_ipc_output_v2_send_layout_symbol(ipc_output->resource, monitor->ltsymbol); + if (wl_resource_get_version(ipc_output->resource) >= ZDWL_IPC_OUTPUT_V2_FULLSCREEN_SINCE_VERSION) { + zdwl_ipc_output_v2_send_fullscreen(ipc_output->resource, focused ? focused->isfullscreen : 0); + } + if (wl_resource_get_version(ipc_output->resource) >= ZDWL_IPC_OUTPUT_V2_FLOATING_SINCE_VERSION) { + zdwl_ipc_output_v2_send_floating(ipc_output->resource, focused ? focused->isfloating : 0); + } + zdwl_ipc_output_v2_send_frame(ipc_output->resource); +} + +void +dwl_ipc_output_set_client_tags(struct wl_client *client, struct wl_resource *resource, uint32_t and_tags, uint32_t xor_tags) +{ + DwlIpcOutput *ipc_output; + Monitor *monitor; + Client *selected_client; + unsigned int newtags = 0; + + ipc_output = wl_resource_get_user_data(resource); + if (!ipc_output) + return; + + monitor = ipc_output->mon; + selected_client = focustop(monitor); + if (!selected_client) + return; + + newtags = (selected_client->tags & and_tags) ^ xor_tags; + if (!newtags) + return; + + selected_client->tags = newtags; + if (selmon == monitor) + focusclient(focustop(monitor), 1); + arrange(selmon); + printstatus(); +} + +void +dwl_ipc_output_set_layout(struct wl_client *client, struct wl_resource *resource, uint32_t index) +{ + DwlIpcOutput *ipc_output; + Monitor *monitor; + + ipc_output = wl_resource_get_user_data(resource); + if (!ipc_output) + return; + + monitor = ipc_output->mon; + if (index >= LENGTH(layouts)) + return; + if (index != monitor->lt[monitor->sellt] - layouts) + monitor->sellt ^= 1; + + monitor->lt[monitor->sellt] = &layouts[index]; + arrange(monitor); + printstatus(); +} + +void +dwl_ipc_output_set_tags(struct wl_client *client, struct wl_resource *resource, uint32_t tagmask, uint32_t toggle_tagset) +{ + DwlIpcOutput *ipc_output; + Monitor *monitor; + unsigned int newtags = tagmask & TAGMASK; + + ipc_output = wl_resource_get_user_data(resource); + if (!ipc_output) + return; + monitor = ipc_output->mon; + + if (!newtags || newtags == monitor->tagset[monitor->seltags]) + return; + if (toggle_tagset) + monitor->seltags ^= 1; + + monitor->tagset[monitor->seltags] = newtags; + if (selmon == monitor) + focusclient(focustop(monitor), 1); + arrange(monitor); + printstatus(); +} + +void +dwl_ipc_output_release(struct wl_client *client, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +void focusclient(Client *c, int lift) { struct wlr_surface *old = seat->keyboard_state.focused_surface; @@ -1427,10 +1921,7 @@ focusclient(Client *c, int lift) 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); + client_set_border_color(c, focuscolor); } /* Deactivate old client if focus is changing */ @@ -1447,7 +1938,7 @@ focusclient(Client *c, int lift) /* 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_set_border_color(old_c, bordercolor); client_activate_surface(old, 0); } @@ -1473,6 +1964,8 @@ focusclient(Client *c, int lift) void focusmon(const Arg *arg) { + Client *c; + int i = 0, nmons = wl_list_length(&mons); if (nmons) { do /* don't switch to disabled mons */ @@ -1480,6 +1973,17 @@ focusmon(const Arg *arg) while (!selmon->wlr_output->enabled && i++ < nmons); } focusclient(focustop(selmon), 1); + + if (warpcursor) { + wlr_cursor_warp_closest(cursor, NULL, selmon->w.x + selmon->w.width / 2.0, + selmon->w.y + selmon->w.height / 2.0); + c = focustop(selmon); + if (c) { + wlr_cursor_warp_closest(cursor, NULL, c->geom.x + c->geom.width / 2.0, + c->geom.y + c->geom.height / 2.0); + } + motionnotify(0, NULL, 0, 0, 0, 0); + } } void @@ -1506,6 +2010,12 @@ focusstack(const Arg *arg) } /* If only one client is visible on selmon, then c == sel */ focusclient(c, 1); + + if (warpcursor) { + wlr_cursor_warp_closest( cursor, NULL, c->geom.x + c->geom.width / 2.0, + c->geom.y + c->geom.height / 2.0); + motionnotify(0, NULL, 0, 0, 0, 0); + } } /* We probably should change the name of this, it sounds like @@ -1564,12 +2074,150 @@ handlesig(int signo) } void +handletearingcontrollersethint(struct wl_listener *listener, void *data) +{ + Client *c = NULL, *i = NULL; + struct TearingController *controller = wl_container_of(listener, controller, set_hint); + + struct wlr_xdg_surface *surface = wlr_xdg_surface_try_from_wlr_surface(controller->tearing_control->surface); +#ifdef XWAYLAND + struct wlr_xwayland_surface *xsurface = wlr_xwayland_surface_try_from_wlr_surface(controller->tearing_control->surface); +#endif + + wl_list_for_each(i, &fstack, flink) { + if (i->surface.xdg == surface +#ifdef XWAYLAND + || i->surface.xwayland == xsurface +#endif + ) { + c = i; + break; + } + } + + if (c) { + enum wp_tearing_control_v1_presentation_hint hint = controller->tearing_control->current; + fprintf( + stderr, "TEARING: found surface: %p(appid: '%s', title: '%s'), hint: %d(%s)\n", + (void*)c, client_get_appid(c), client_get_title(c), hint, hint ? "ASYNC" : "VSYNC" + ); + c->tearing_hint = controller->tearing_control->current; + } +} + +void +handletearingcontrollerdestroy(struct wl_listener *listener, void *data) +{ + struct TearingController *controller = wl_container_of(listener, controller, destroy); + + wl_list_remove(&controller->set_hint.link); + wl_list_remove(&controller->destroy.link); + wl_list_remove(&controller->link); + free(controller); +} + +void +handlenewtearinghint(struct wl_listener *listener, void *data) +{ + struct wlr_tearing_control_v1 *tearing_control = data; + struct TearingController *controller = calloc(1, sizeof(struct TearingController)); + + if (!controller) + return; + + controller->tearing_control = tearing_control; + controller->set_hint.notify = handletearingcontrollersethint; + wl_signal_add(&tearing_control->events.set_hint, &controller->set_hint); + + controller->destroy.notify = handletearingcontrollerdestroy; + wl_signal_add(&tearing_control->events.destroy, &controller->destroy); + + wl_list_init(&controller->link); + wl_list_insert(&tearing_controllers, &controller->link); +} + +pid_t +getparentprocess(pid_t p) +{ + unsigned int v = 0; + + FILE *f; + char buf[256]; + snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)p); + + if (!(f = fopen(buf, "r"))) + return 0; + + fscanf(f, "%*u %*s %*c %u", &v); + fclose(f); + + return (pid_t)v; +} + +int +isdescprocess(pid_t p, pid_t c) +{ + while (p != c && c != 0) + c = getparentprocess(c); + + return (int)c; +} + +Client * +termforwin(Client *w) +{ + Client *c; + + if (!w->pid || w->isterm || w->noswallow) + return NULL; + + wl_list_for_each(c, &fstack, flink) + if (c->isterm && !c->swallowing && c->pid && isdescprocess(c->pid, w->pid)) + return c; + + return NULL; +} + +void +swallow(Client *c, Client *w) +{ + c->bw = w->bw; + c->isfloating = w->isfloating; + c->isurgent = w->isurgent; + c->isfullscreen = w->isfullscreen; + c->tags = w->tags; + c->geom = w->geom; + wl_list_insert(&w->link, &c->link); + wl_list_insert(&w->flink, &c->flink); + wlr_scene_node_set_enabled(&w->scene->node, 0); + wlr_scene_node_set_enabled(&c->scene->node, 1); +} + +void incnmaster(const Arg *arg) { + Client *c; + const MonitorRule *r; + if (!arg || !selmon) return; + if (arg->i == 0) { + for (r = monrules; r < END(monrules); r++) + if (!r->name || strstr(selmon->name, r->name)) + selmon->nmaster = r->nmaster; + } selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); arrange(selmon); + + + if (warpcursor) { + c = focustop(selmon); + if (c) { + wlr_cursor_warp_closest(cursor, NULL, c->geom.x + c->geom.width / 2.0, + c->geom.y + c->geom.height / 2.0); + motionnotify(0, NULL, 0, 0, 0, 0); + } + } } void @@ -1614,6 +2262,8 @@ keybinding(uint32_t mods, xkb_keysym_t sym) for (k = keys; k < END(keys); k++) { if (CLEANMASK(mods) == CLEANMASK(k->mod) && sym == k->keysym && k->func) { + if (passthrough && k->func != togglepassthrough) + continue; k->func(&k->arg); return 1; } @@ -1621,6 +2271,21 @@ keybinding(uint32_t mods, xkb_keysym_t sym) return 0; } +int +lockedkeybinding(uint32_t mods, xkb_keysym_t sym) +{ + int handled = 0; + const Key *k; + for (k = lockedkeys; k < END(lockedkeys); k++) { + if (CLEANMASK(mods) == CLEANMASK(k->mod) && + sym == k->keysym && k->func) { + k->func(&k->arg); + handled = 1; + } + } + return handled; +} + void keypress(struct wl_listener *listener, void *data) { @@ -1648,6 +2313,10 @@ keypress(struct wl_listener *listener, void *data) handled = keybinding(mods, syms[i]) || handled; } + if (locked && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) + for (i = 0; i < nsyms; i++) + handled = lockedkeybinding(mods, syms[i]) || handled; + if (handled && group->wlr_group->keyboard.repeat_info.delay > 0) { group->mods = mods; group->keysyms = syms; @@ -1730,6 +2399,33 @@ locksession(struct wl_listener *listener, void *data) wlr_session_lock_v1_send_locked(session_lock); } +static inline void +forcetearingrule(Client *c) +{ + int success = 0; + const char* appid = client_get_appid(c); + const char* title = client_get_title(c); + + for (unsigned i = 0; i < LENGTH(force_tearing); i++) { + if (appid) + if (strcmp(force_tearing[i].appid, appid) == 0) { + success = 1; + break; + } + + if (title) + if (strcmp(force_tearing[i].title, title) == 0) { + success = 1; + break; + } + } + + if (success) { + c->tearing_hint = WP_TEARING_CONTROL_V1_PRESENTATION_HINT_ASYNC; + fprintf(stderr, "tearing forced for: appid: '%s', title: '%s'\n", appid, title); + } +} + void mapnotify(struct wl_listener *listener, void *data) { @@ -1739,6 +2435,8 @@ mapnotify(struct wl_listener *listener, void *data) Monitor *m; int i; + forcetearingrule(c); + /* 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() */ @@ -1767,6 +2465,12 @@ mapnotify(struct wl_listener *listener, void *data) c->border[i] = wlr_scene_rect_create(c->scene, 0, 0, c->isurgent ? urgentcolor : bordercolor); c->border[i]->node.data = c; + + c->borders[i] = wlr_scene_rect_create(c->scene, 0, 0, borderscolor); + c->borders[i]->node.data = c; + + c->bordere[i] = wlr_scene_rect_create(c->scene, 0, 0, borderecolor); + c->bordere[i]->node.data = c; } /* Initialize client geometry with room for border */ @@ -1788,6 +2492,12 @@ mapnotify(struct wl_listener *listener, void *data) } else { applyrules(c); } + + if (borders_only_floating) { + c->bws = c->isfloating ? borderspx : 0; + c->bwe = c->isfloating ? borderepx : 0; + } + printstatus(); unset_fullscreen: @@ -1820,12 +2530,16 @@ void monocle(Monitor *m) { Client *c; - int n = 0; + int n = 0, e = enablegaps; + + if (smartgaps == 1) { + e = 0; // outer gaps disabled + } wl_list_for_each(c, &clients, link) { if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) continue; - resize(c, m->w, 0); + resize(c, (struct wlr_box){ .x = m->w.x + m->gappx*e, .y = m->w.y + m->gappx*e, .width = m->w.width - 2*m->gappx*e, .height = m->w.height - 2*m->gappx*e }, 0); n++; } if (n) @@ -1883,22 +2597,24 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d 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 (enable_constraints){ + 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; } - - if (active_constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED) - return; } } @@ -1909,6 +2625,7 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d if (sloppyfocus) selmon = xytomon(cursor->x, cursor->y); } + client_set_border_color(focustop(selmon), focuscolor); /* Update drag icon's position */ wlr_scene_node_set_position(&drag_icon->node, (int)round(cursor->x), (int)round(cursor->y)); @@ -1977,6 +2694,13 @@ moveresize(const Arg *arg) } } +int +moncantear(Monitor* m) +{ + Client *c = focustop(m); + return (c && c->isfullscreen && c->tearing_hint); /* 1 == ASYNC */ +} + void outputmgrapply(struct wl_listener *listener, void *data) { @@ -2060,7 +2784,7 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, { struct timespec now; - if (surface != seat->pointer_state.focused_surface && + if ((!active_constraint || active_constraint->surface != surface) && sloppyfocus && time && c && !client_is_unmanaged(c)) focusclient(c, 0); @@ -2086,38 +2810,9 @@ 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); + wl_list_for_each(m, &mons, link) + dwl_ipc_output_printstatus(m); } void @@ -2147,27 +2842,40 @@ quit(const Arg *arg) 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_scene_output *scene_output = m->scene_output; struct wlr_output_state pending = {0}; - struct timespec now; + SendFrameDoneData frame_done_data = {0}; - /* 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; + m->wlr_output->frame_pending = false; + + if (!wlr_scene_output_needs_frame(scene_output)) { + goto skip; } - wlr_scene_output_commit(m->scene_output, NULL); + wlr_output_state_init(&pending); + if (!wlr_scene_output_build_state(m->scene_output, &pending, NULL)) { + goto skip; + } + + if (tearing_allowed && moncantear(m)) { + pending.tearing_page_flip = true; + + if (!wlr_output_test_state(m->wlr_output, &pending)) { + fprintf(stderr, "Output test failed on '%s', retrying without tearing page-flip\n", m->wlr_output->name); + pending.tearing_page_flip = false; + } + } + + if (!wlr_output_commit_state(m->wlr_output, &pending)) + fprintf(stderr, "Page-flip failed on output %s", m->wlr_output->name); -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); + +skip: + clock_gettime(CLOCK_MONOTONIC, &frame_done_data.when); + frame_done_data.mon = m; + wlr_scene_output_for_each_buffer(m->scene_output, sendframedoneiterator, &frame_done_data); } void @@ -2225,6 +2933,24 @@ resize(Client *c, struct wlr_box geo, int interact) 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); + wlr_scene_rect_set_size(c->borders[0], c->geom.width - 2 * borderspx_offset, c->bws); + wlr_scene_rect_set_size(c->borders[1], c->geom.width - 2 * borderspx_offset, c->bws); + wlr_scene_rect_set_size(c->borders[2], c->bws, c->geom.height - 2 * c->bws - 2 * borderspx_offset); + wlr_scene_rect_set_size(c->borders[3], c->bws, c->geom.height - 2 * c->bws - 2 * borderspx_offset); + wlr_scene_node_set_position(&c->borders[0]->node, borderspx_offset, borderspx_offset); + wlr_scene_node_set_position(&c->borders[1]->node, borderspx_offset, c->geom.height - c->bws - borderspx_offset); + wlr_scene_node_set_position(&c->borders[2]->node, borderspx_offset, c->bws + borderspx_offset); + wlr_scene_node_set_position(&c->borders[3]->node, c->geom.width - c->bws - borderspx_offset, c->bws + borderspx_offset); + + wlr_scene_rect_set_size(c->bordere[0], c->geom.width - (c->bw - c->bwe) * 2 + borderepx_negative_offset * 2, c->bwe); + wlr_scene_rect_set_size(c->bordere[1], c->geom.width - (c->bw - c->bwe) * 2 + borderepx_negative_offset * 2, c->bwe); + wlr_scene_rect_set_size(c->bordere[2], c->bwe, c->geom.height - 2 * c->bw + 2 * borderepx_negative_offset); + wlr_scene_rect_set_size(c->bordere[3], c->bwe, c->geom.height - 2 * c->bw + 2 * borderepx_negative_offset); + wlr_scene_node_set_position(&c->bordere[0]->node, c->bw - c->bwe - borderepx_negative_offset, c->bw - c->bwe - borderepx_negative_offset); + wlr_scene_node_set_position(&c->bordere[1]->node, c->bw - c->bwe - borderepx_negative_offset, c->geom.height - c->bw + borderepx_negative_offset); + wlr_scene_node_set_position(&c->bordere[2]->node, c->bw - c->bwe - borderepx_negative_offset, c->bw - borderepx_negative_offset); + wlr_scene_node_set_position(&c->bordere[3]->node, c->geom.width - c->bw + borderepx_negative_offset, c->bw - borderepx_negative_offset); + /* 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); @@ -2292,6 +3018,16 @@ run(char *startup_cmd) } void +sendframedoneiterator(struct wlr_scene_buffer *buffer, int x, int y, void *user_data) +{ + SendFrameDoneData *data = user_data; + if (buffer->primary_output != data->mon->scene_output) + return; + + wlr_scene_buffer_send_frame_done(buffer, &data->when); +} + +void setcursor(struct wl_listener *listener, void *data) { /* This event is raised by the seat when a client provides a cursor image */ @@ -2333,6 +3069,12 @@ setfloating(Client *c, int 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; + + if (borders_only_floating) { + c->bws = c->isfloating ? borderspx : 0; + c->bwe = c->isfloating ? borderepx : 0; + } + wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen || (p && p->isfullscreen) ? LyrFS : c->isfloating ? LyrFloat : LyrTile]); @@ -2347,6 +3089,8 @@ setfullscreen(Client *c, int fullscreen) if (!c->mon || !client_surface(c)->mapped) return; c->bw = fullscreen ? 0 : borderpx; + c->bws = fullscreen ? 0 : borderspx; + c->bwe = fullscreen ? 0 : borderepx; client_set_fullscreen(c, fullscreen); wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen ? LyrFS : c->isfloating ? LyrFloat : LyrTile]); @@ -2382,14 +3126,30 @@ void setmfact(const Arg *arg) { float f; + const MonitorRule *r; + Client *c; 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; + if (arg->f == 0) { + for (r = monrules; r < END(monrules); r++) + if (!r->name || strstr(selmon->name, r->name)) + selmon->mfact = r->mfact; + } else if (f > 0.1 || f < 0.9) + selmon->mfact = f; + else + return; arrange(selmon); + + if (warpcursor) { + c = focustop(selmon); + if (c) { + wlr_cursor_warp_closest(cursor, NULL, c->geom.x + c->geom.width / 2.0, + c->geom.y + c->geom.height / 2.0); + motionnotify(0, NULL, 0, 0, 0, 0); + } + } } void @@ -2627,6 +3387,16 @@ setup(void) wl_signal_add(&virtual_pointer_mgr->events.new_virtual_pointer, &new_virtual_pointer); + pointer_gestures = wlr_pointer_gestures_v1_create(dpy); + LISTEN_STATIC(&cursor->events.swipe_begin, swipe_begin); + LISTEN_STATIC(&cursor->events.swipe_update, swipe_update); + LISTEN_STATIC(&cursor->events.swipe_end, swipe_end); + LISTEN_STATIC(&cursor->events.pinch_begin, pinch_begin); + LISTEN_STATIC(&cursor->events.pinch_update, pinch_update); + LISTEN_STATIC(&cursor->events.pinch_end, pinch_end); + LISTEN_STATIC(&cursor->events.hold_begin, hold_begin); + LISTEN_STATIC(&cursor->events.hold_end, hold_end); + 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); @@ -2641,6 +3411,13 @@ setup(void) wl_signal_add(&output_mgr->events.apply, &output_mgr_apply); wl_signal_add(&output_mgr->events.test, &output_mgr_test); + wl_global_create(dpy, &zdwl_ipc_manager_v2_interface, 2, NULL, dwl_ipc_manager_bind); + + tearing_control_v1 = wlr_tearing_control_manager_v1_create(dpy, 1); + tearing_control_new_object.notify = handlenewtearinghint; + wl_signal_add(&tearing_control_v1->events.new_object, &tearing_control_new_object); + wl_list_init(&tearing_controllers); + /* 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 */ @@ -2707,8 +3484,8 @@ tagmon(const Arg *arg) void tile(Monitor *m) { - unsigned int mw, my, ty; - int i, n = 0; + unsigned int h, r, e = enablegaps, mw, my, ty; + int i, n = 0; Client *c; wl_list_for_each(c, &clients, link) @@ -2717,28 +3494,44 @@ tile(Monitor *m) if (n == 0) return; + if (smartgaps == n) { + e = 0; // outer gaps disabled + } + if (n > m->nmaster) - mw = m->nmaster ? (int)roundf(m->w.width * m->mfact) : 0; + mw = m->nmaster ? (int)roundf((m->w.width + m->gappx*e) * m->mfact) : 0; else - mw = m->w.width; - i = my = ty = 0; + mw = m->w.width - 2*m->gappx*e + m->gappx*e; + i = 0; + my = ty = m->gappx*e; 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; + r = MIN(n, m->nmaster) - i; + h = (m->w.height - my - m->gappx*e - m->gappx*e * (r - 1)) / r; + resize(c, (struct wlr_box){.x = m->w.x + m->gappx*e, .y = m->w.y + my, + .width = mw - m->gappx*e, .height = h}, 0); + my += c->geom.height + m->gappx*e; } 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; + r = n - i; + h = (m->w.height - ty - m->gappx*e - m->gappx*e * (r - 1)) / r; + resize(c, (struct wlr_box){.x = m->w.x + mw + m->gappx*e, .y = m->w.y + ty, + .width = m->w.width - mw - 2*m->gappx*e, .height = h}, 0); + ty += c->geom.height + m->gappx*e; } i++; } } void +togglebar(const Arg *arg) { + DwlIpcOutput *ipc_output; + wl_list_for_each(ipc_output, &selmon->dwl_ipc_outputs, link) + zdwl_ipc_output_v2_send_toggle_visibility(ipc_output->resource); +} + +void togglefloating(const Arg *arg) { Client *sel = focustop(selmon); @@ -2756,6 +3549,25 @@ togglefullscreen(const Arg *arg) } void +togglegaps(const Arg *arg) +{ + enablegaps = !enablegaps; + arrange(selmon); +} + +void +togglepointerconstraints(const Arg *arg) +{ + enable_constraints = !enable_constraints; +} + +void +togglepassthrough(const Arg *arg) +{ + passthrough = !passthrough; +} + +void toggletag(const Arg *arg) { uint32_t newtags; @@ -2815,15 +3627,32 @@ unmapnotify(struct wl_listener *listener, void *data) grabc = NULL; } + if (c->swallowedby) + swallow(c->swallowedby, c); + if (client_is_unmanaged(c)) { if (c == exclusive_focus) { exclusive_focus = NULL; focusclient(focustop(selmon), 1); } } else { - wl_list_remove(&c->link); + if (!c->swallowing) + wl_list_remove(&c->link); setmon(c, NULL, 0); - wl_list_remove(&c->flink); + if (!c->swallowing) + wl_list_remove(&c->flink); + } + + if (c->swallowedby) { + c->swallowedby->prev = c->geom; + setfullscreen(c->swallowedby, c->isfullscreen); + c->swallowedby->swallowing = NULL; + c->swallowedby = NULL; + } + + if (c->swallowing) { + c->swallowing->swallowedby = NULL; + c->swallowing = NULL; } wlr_scene_node_destroy(&c->scene->node); @@ -2958,7 +3787,7 @@ urgent(struct wl_listener *listener, void *data) printstatus(); if (client_surface(c)->mapped) - client_set_border_color(c, urgentcolor); + client_set_border_color(c, urgentcolor); } void @@ -3070,6 +3899,19 @@ zoom(const Arg *arg) arrange(selmon); } +int +regex_match(const char *pattern, const char *str) { + regex_t regex; + int reti; + if (regcomp(®ex, pattern, REG_EXTENDED) != 0) + return 0; + reti = regexec(®ex, str, (size_t)0, NULL, 0); + regfree(®ex); + if (reti == 0) + return 1; + return 0; +} + #ifdef XWAYLAND void activatex11(struct wl_listener *listener, void *data) @@ -3132,6 +3974,7 @@ createnotifyx11(struct wl_listener *listener, void *data) 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_minimize, &c->minimize, minimizenotify); 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); @@ -3147,6 +3990,21 @@ dissociatex11(struct wl_listener *listener, void *data) } void +minimizenotify(struct wl_listener *listener, void *data) +{ + Client *c = wl_container_of(listener, c, minimize); + struct wlr_xwayland_surface *xsurface = c->surface.xwayland; + struct wlr_xwayland_minimize_event *e = data; + int focused; + + if (xsurface->surface == NULL || !xsurface->surface->mapped) + return; + + focused = seat->keyboard_state.focused_surface == xsurface->surface; + wlr_xwayland_surface_set_minimized(xsurface, !focused && e->minimize); +} + +void sethints(struct wl_listener *listener, void *data) { Client *c = wl_container_of(listener, c, set_hints); @@ -3158,7 +4016,7 @@ sethints(struct wl_listener *listener, void *data) printstatus(); if (c->isurgent && surface && surface->mapped) - client_set_border_color(c, urgentcolor); + client_set_border_color(c, urgentcolor); } void diff --git a/patches/borders.patch b/patches/borders.patch new file mode 100644 index 0000000..0fb91c0 --- /dev/null +++ b/patches/borders.patch @@ -0,0 +1,213 @@ +From b12cfff672f0705d8259cf26b3a574faa5ca43ae Mon Sep 17 00:00:00 2001 +From: wochap <gean.marroquin@gmail.com> +Date: Tue, 4 Jun 2024 16:02:25 -0500 +Subject: [PATCH] implement borders patch + +tihs patch adds 2 extra borders relative to the client, they don't +change the size of the client +--- + client.h | 16 +++++++++++++--- + config.def.h | 8 ++++++++ + dwl.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++---- + 3 files changed, 70 insertions(+), 7 deletions(-) + +diff --git a/client.h b/client.h +index 800b867..33fd579 100644 +--- a/client.h ++++ b/client.h +@@ -325,11 +325,21 @@ client_send_close(Client *c) + } + + static inline void +-client_set_border_color(Client *c, const float color[static 4]) ++client_set_border_color(Client *c, const float color[static 4], const float colors[static 4], const float colore[static 4]) + { + int i; +- for (i = 0; i < 4; i++) +- wlr_scene_rect_set_color(c->border[i], color); ++ for (i = 0; i < 4; i++) { ++ if (border_color_type == BrdOriginal) { ++ wlr_scene_rect_set_color(c->border[i], color); ++ } else if (border_color_type == BrdStart) { ++ wlr_scene_rect_set_color(c->borders[i], colors); ++ } else if (border_color_type == BrdEnd) { ++ wlr_scene_rect_set_color(c->bordere[i], colore); ++ } else if (border_color_type == BrdStartEnd) { ++ wlr_scene_rect_set_color(c->borders[i], colors); ++ wlr_scene_rect_set_color(c->bordere[i], colore); ++ } ++ } + } + + static inline void +diff --git a/config.def.h b/config.def.h +index 8847e58..2d6bbe5 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -7,8 +7,16 @@ + 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 unsigned int borderspx = 0; /* width of the border that start from outside the windows */ ++static const unsigned int borderepx = 0; /* width of the border that start from inside the windows */ ++static const unsigned int borderspx_offset = 0; /* offset of the border that start from outside the windows */ ++static const unsigned int borderepx_negative_offset = 0; /* offset of the border that start from inside the windows */ + static const float rootcolor[] = COLOR(0x222222ff); + static const float bordercolor[] = COLOR(0x444444ff); ++static const float borderscolor[] = COLOR(0x444444ff); /* color of the border that start from outside the windows */ ++static const float borderecolor[] = COLOR(0x444444ff); /* color of the border that start from inside the windows */ ++static const int border_color_type = BrdOriginal; /* borders to be colored (focuscolor, urgentcolor) */ ++static const int borders_only_floating = 0; + 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 */ +diff --git a/dwl.c b/dwl.c +index bf763df..303832a 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -86,6 +86,7 @@ enum { LyrBg, LyrBottom, LyrTile, LyrFloat, LyrTop, LyrFS, LyrOverlay, LyrBlock, + enum { NetWMWindowTypeDialog, NetWMWindowTypeSplash, NetWMWindowTypeToolbar, + NetWMWindowTypeUtility, NetLast }; /* EWMH atoms */ + #endif ++enum { BrdOriginal, BrdStart, BrdEnd, BrdStartEnd }; + + typedef union { + int i; +@@ -109,6 +110,8 @@ typedef struct { + Monitor *mon; + struct wlr_scene_tree *scene; + struct wlr_scene_rect *border[4]; /* top, bottom, left, right */ ++ struct wlr_scene_rect *borders[4]; /* top, bottom, left, right */ ++ struct wlr_scene_rect *bordere[4]; /* top, bottom, left, right */ + struct wlr_scene_tree *scene_surface; + struct wl_list link; + struct wl_list flink; +@@ -136,6 +139,8 @@ typedef struct { + struct wl_listener set_hints; + #endif + unsigned int bw; ++ unsigned int bws; ++ unsigned int bwe; + uint32_t tags; + int isfloating, isurgent, isfullscreen; + uint32_t resize; /* configure serial of a pending resize */ +@@ -973,6 +978,8 @@ createnotify(struct wl_listener *listener, void *data) + c = xdg_surface->data = ecalloc(1, sizeof(*c)); + c->surface.xdg = xdg_surface; + c->bw = borderpx; ++ c->bws = borders_only_floating ? 0 : borderspx; ++ c->bwe = borders_only_floating ? 0 : borderepx; + + wlr_xdg_toplevel_set_wm_capabilities(xdg_surface->toplevel, + WLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN); +@@ -1268,7 +1275,7 @@ focusclient(Client *c, int lift) + /* 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); ++ client_set_border_color(c, focuscolor, focuscolor, focuscolor); + } + + /* Deactivate old client if focus is changing */ +@@ -1285,7 +1292,7 @@ focusclient(Client *c, int lift) + /* 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_set_border_color(old_c, bordercolor, borderscolor, borderecolor); + + client_activate_surface(old, 0); + } +@@ -1597,6 +1604,12 @@ mapnotify(struct wl_listener *listener, void *data) + c->border[i] = wlr_scene_rect_create(c->scene, 0, 0, + c->isurgent ? urgentcolor : bordercolor); + c->border[i]->node.data = c; ++ ++ c->borders[i] = wlr_scene_rect_create(c->scene, 0, 0, borderscolor); ++ c->borders[i]->node.data = c; ++ ++ c->bordere[i] = wlr_scene_rect_create(c->scene, 0, 0, borderecolor); ++ c->bordere[i]->node.data = c; + } + + /* Initialize client geometry with room for border */ +@@ -1618,6 +1631,12 @@ mapnotify(struct wl_listener *listener, void *data) + } else { + applyrules(c); + } ++ ++ if (borders_only_floating) { ++ c->bws = c->isfloating ? borderspx : 0; ++ c->bwe = c->isfloating ? borderepx : 0; ++ } ++ + printstatus(); + + unset_fullscreen: +@@ -2051,6 +2070,24 @@ resize(Client *c, struct wlr_box geo, int interact) + 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); + ++ wlr_scene_rect_set_size(c->borders[0], c->geom.width - 2 * borderspx_offset, c->bws); ++ wlr_scene_rect_set_size(c->borders[1], c->geom.width - 2 * borderspx_offset, c->bws); ++ wlr_scene_rect_set_size(c->borders[2], c->bws, c->geom.height - 2 * c->bws - 2 * borderspx_offset); ++ wlr_scene_rect_set_size(c->borders[3], c->bws, c->geom.height - 2 * c->bws - 2 * borderspx_offset); ++ wlr_scene_node_set_position(&c->borders[0]->node, borderspx_offset, borderspx_offset); ++ wlr_scene_node_set_position(&c->borders[1]->node, borderspx_offset, c->geom.height - c->bws - borderspx_offset); ++ wlr_scene_node_set_position(&c->borders[2]->node, borderspx_offset, c->bws + borderspx_offset); ++ wlr_scene_node_set_position(&c->borders[3]->node, c->geom.width - c->bws - borderspx_offset, c->bws + borderspx_offset); ++ ++ wlr_scene_rect_set_size(c->bordere[0], c->geom.width - (c->bw - c->bwe) * 2 + borderepx_negative_offset * 2, c->bwe); ++ wlr_scene_rect_set_size(c->bordere[1], c->geom.width - (c->bw - c->bwe) * 2 + borderepx_negative_offset * 2, c->bwe); ++ wlr_scene_rect_set_size(c->bordere[2], c->bwe, c->geom.height - 2 * c->bw + 2 * borderepx_negative_offset); ++ wlr_scene_rect_set_size(c->bordere[3], c->bwe, c->geom.height - 2 * c->bw + 2 * borderepx_negative_offset); ++ wlr_scene_node_set_position(&c->bordere[0]->node, c->bw - c->bwe - borderepx_negative_offset, c->bw - c->bwe - borderepx_negative_offset); ++ wlr_scene_node_set_position(&c->bordere[1]->node, c->bw - c->bwe - borderepx_negative_offset, c->geom.height - c->bw + borderepx_negative_offset); ++ wlr_scene_node_set_position(&c->bordere[2]->node, c->bw - c->bwe - borderepx_negative_offset, c->bw - borderepx_negative_offset); ++ wlr_scene_node_set_position(&c->bordere[3]->node, c->geom.width - c->bw + borderepx_negative_offset, c->bw - borderepx_negative_offset); ++ + /* 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); +@@ -2151,6 +2188,12 @@ setfloating(Client *c, int floating) + c->isfloating = floating; + if (!c->mon) + return; ++ ++ if (borders_only_floating) { ++ c->bws = c->isfloating ? borderspx : 0; ++ c->bwe = c->isfloating ? borderepx : 0; ++ } ++ + wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen || + (p && p->isfullscreen) ? LyrFS + : c->isfloating ? LyrFloat : LyrTile]); +@@ -2165,6 +2208,8 @@ setfullscreen(Client *c, int fullscreen) + if (!c->mon) + return; + c->bw = fullscreen ? 0 : borderpx; ++ c->bws = fullscreen ? 0 : borderspx; ++ c->bwe = fullscreen ? 0 : borderepx; + client_set_fullscreen(c, fullscreen); + wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen + ? LyrFS : c->isfloating ? LyrFloat : LyrTile]); +@@ -2819,7 +2864,7 @@ urgent(struct wl_listener *listener, void *data) + printstatus(); + + if (client_surface(c)->mapped) +- client_set_border_color(c, urgentcolor); ++ client_set_border_color(c, urgentcolor, urgentcolor, urgentcolor); + } + + void +@@ -3023,7 +3068,7 @@ sethints(struct wl_listener *listener, void *data) + printstatus(); + + if (c->isurgent && surface && surface->mapped) +- client_set_border_color(c, urgentcolor); ++ client_set_border_color(c, urgentcolor, urgentcolor, urgentcolor); + } + + void +-- +2.44.1 diff --git a/patches/gestures.patch b/patches/gestures.patch new file mode 100644 index 0000000..4f74325 --- /dev/null +++ b/patches/gestures.patch @@ -0,0 +1,169 @@ +From 42f97e88bd901d81b81da61c44a790b583706308 Mon Sep 17 00:00:00 2001 +From: wochap <gean.marroquin@gmail.com> +Date: Fri, 5 Jul 2024 11:18:49 -0500 +Subject: [PATCH] implement gestures + +--- + config.def.h | 9 +++++++ + dwl.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 77 insertions(+) + +diff --git a/config.def.h b/config.def.h +index 22d2171..8b75564 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -14,6 +14,8 @@ 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 const unsigned int swipe_min_threshold = 0; ++ + /* tagging - TAGCOUNT must be no greater than 31 */ + #define TAGCOUNT (9) + +@@ -174,3 +176,10 @@ static const Button buttons[] = { + { MODKEY, BTN_MIDDLE, togglefloating, {0} }, + { MODKEY, BTN_RIGHT, moveresize, {.ui = CurResize} }, + }; ++ ++static const Gesture gestures[] = { ++ // { MODKEY, SWIPE_LEFT, 4, shiftview, { .i = 1 } }, ++ // { MODKEY, SWIPE_RIGHT, 4, shiftview, { .i = -1 } }, ++ { MODKEY, SWIPE_UP, 3, focusstack, {.i = 1} }, ++ { MODKEY, SWIPE_DOWN, 3, focusstack, {.i = -1} }, ++}; +diff --git a/dwl.c b/dwl.c +index ded83e2..5d861e8 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -88,6 +88,7 @@ enum { LyrBg, LyrBottom, LyrTile, LyrFloat, LyrTop, LyrFS, LyrOverlay, LyrBlock, + enum { NetWMWindowTypeDialog, NetWMWindowTypeSplash, NetWMWindowTypeToolbar, + NetWMWindowTypeUtility, NetLast }; /* EWMH atoms */ + #endif ++enum { SWIPE_LEFT, SWIPE_RIGHT, SWIPE_DOWN, SWIPE_UP }; + + typedef union { + int i; +@@ -103,6 +104,14 @@ typedef struct { + const Arg arg; + } Button; + ++typedef struct { ++ unsigned int mod; ++ unsigned int motion; ++ unsigned int fingers_count; ++ void (*func)(const Arg *); ++ const Arg arg; ++} Gesture; ++ + typedef struct Monitor Monitor; + typedef struct { + /* Must keep these three elements in this order */ +@@ -251,6 +260,7 @@ static void arrangelayer(Monitor *m, struct wl_list *list, + static void arrangelayers(Monitor *m); + static void axisnotify(struct wl_listener *listener, void *data); + static void buttonpress(struct wl_listener *listener, void *data); ++static int ongesture(struct wlr_pointer_swipe_end_event *event); + static void swipe_begin(struct wl_listener *listener, void *data); + static void swipe_update(struct wl_listener *listener, void *data); + static void swipe_end(struct wl_listener *listener, void *data); +@@ -416,6 +426,10 @@ static struct wlr_box sgeom; + static struct wl_list mons; + static Monitor *selmon; + ++static uint32_t swipe_fingers = 0; ++static double swipe_dx = 0; ++static double swipe_dy = 0; ++ + #ifdef XWAYLAND + static void activatex11(struct wl_listener *listener, void *data); + static void associatex11(struct wl_listener *listener, void *data); +@@ -435,6 +449,8 @@ static xcb_atom_t netatom[NetLast]; + /* attempt to encapsulate suck into one file */ + #include "client.h" + ++static const unsigned int abzsquare = swipe_min_threshold * swipe_min_threshold; ++ + /* function implementations */ + void + applybounds(Client *c, struct wlr_box *bbox) +@@ -657,6 +673,11 @@ swipe_begin(struct wl_listener *listener, void *data) + { + struct wlr_pointer_swipe_begin_event *event = data; + ++ swipe_fingers = event->fingers; ++ // Reset swipe distance at the beginning of a swipe ++ swipe_dx = 0; ++ swipe_dy = 0; ++ + // Forward swipe begin event to client + wlr_pointer_gestures_v1_send_swipe_begin( + pointer_gestures, +@@ -671,6 +692,11 @@ swipe_update(struct wl_listener *listener, void *data) + { + struct wlr_pointer_swipe_update_event *event = data; + ++ swipe_fingers = event->fingers; ++ // Accumulate swipe distance ++ swipe_dx += event->dx; ++ swipe_dy += event->dy; ++ + // Forward swipe update event to client + wlr_pointer_gestures_v1_send_swipe_update( + pointer_gestures, +@@ -681,11 +707,53 @@ swipe_update(struct wl_listener *listener, void *data) + ); + } + ++int ++ongesture(struct wlr_pointer_swipe_end_event *event) ++{ ++ struct wlr_keyboard *keyboard; ++ uint32_t mods; ++ const Gesture *g; ++ unsigned int motion; ++ unsigned int adx = (int)round(fabs(swipe_dx)); ++ unsigned int ady = (int)round(fabs(swipe_dy)); ++ int handled = 0; ++ ++ if (event->cancelled) { ++ return handled; ++ } ++ ++ // Require absolute distance movement beyond a small thresh-hold ++ if (adx * adx + ady * ady < abzsquare) { ++ return handled; ++ } ++ ++ if (adx > ady) { ++ motion = swipe_dx < 0 ? SWIPE_LEFT : SWIPE_RIGHT; ++ } else { ++ motion = swipe_dy < 0 ? SWIPE_UP : SWIPE_DOWN; ++ } ++ ++ keyboard = wlr_seat_get_keyboard(seat); ++ mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; ++ for (g = gestures; g < END(gestures); g++) { ++ if (CLEANMASK(mods) == CLEANMASK(g->mod) && ++ swipe_fingers == g->fingers_count && ++ motion == g->motion && g->func) { ++ g->func(&g->arg); ++ handled = 1; ++ } ++ } ++ return handled; ++} ++ + void + swipe_end(struct wl_listener *listener, void *data) + { + struct wlr_pointer_swipe_end_event *event = data; + ++ // TODO: should we stop here if the event has been handled? ++ ongesture(event); ++ + // Forward swipe end event to client + wlr_pointer_gestures_v1_send_swipe_end( + pointer_gestures, +-- +2.45.1 diff --git a/patches/ipc.patch b/patches/ipc.patch new file mode 100644 index 0000000..9f989e7 --- /dev/null +++ b/patches/ipc.patch @@ -0,0 +1,597 @@ +From 6c6d655b68770ce82a24fde9b58c4d97b672553a Mon Sep 17 00:00:00 2001 +From: choc <notchoc@proton.me> +Date: Mon, 23 Oct 2023 10:35:17 +0800 +Subject: [PATCH] implement dwl-ipc-unstable-v2 + https://codeberg.org/dwl/dwl-patches/wiki/ipc + +--- + Makefile | 14 +- + config.def.h | 1 + + dwl.c | 257 ++++++++++++++++++++++++++---- + protocols/dwl-ipc-unstable-v2.xml | 181 +++++++++++++++++++++ + 4 files changed, 419 insertions(+), 34 deletions(-) + create mode 100644 protocols/dwl-ipc-unstable-v2.xml + +diff --git a/Makefile b/Makefile +index 8db7409..a79a080 100644 +--- a/Makefile ++++ b/Makefile +@@ -17,12 +17,14 @@ DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(WLR_INCS) $(DWLCPPFLAGS) $(DWLDEV + LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` $(WLR_LIBS) -lm $(LIBS) + + all: dwl +-dwl: dwl.o util.o +- $(CC) dwl.o util.o $(DWLCFLAGS) $(LDFLAGS) $(LDLIBS) -o $@ ++dwl: dwl.o util.o dwl-ipc-unstable-v2-protocol.o ++ $(CC) dwl.o util.o dwl-ipc-unstable-v2-protocol.o $(DWLCFLAGS) $(LDFLAGS) $(LDLIBS) -o $@ + dwl.o: dwl.c client.h config.h config.mk cursor-shape-v1-protocol.h \ + pointer-constraints-unstable-v1-protocol.h wlr-layer-shell-unstable-v1-protocol.h \ +- wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h ++ wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h \ ++ dwl-ipc-unstable-v2-protocol.h + util.o: util.c util.h ++dwl-ipc-unstable-v2-protocol.o: dwl-ipc-unstable-v2-protocol.c dwl-ipc-unstable-v2-protocol.h + + # wayland-scanner is a tool which generates C headers and rigging for Wayland + # protocols, which are specified in XML. wlroots requires you to rig these up +@@ -45,6 +47,12 @@ wlr-output-power-management-unstable-v1-protocol.h: + xdg-shell-protocol.h: + $(WAYLAND_SCANNER) server-header \ + $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@ ++dwl-ipc-unstable-v2-protocol.h: ++ $(WAYLAND_SCANNER) server-header \ ++ protocols/dwl-ipc-unstable-v2.xml $@ ++dwl-ipc-unstable-v2-protocol.c: ++ $(WAYLAND_SCANNER) private-code \ ++ protocols/dwl-ipc-unstable-v2.xml $@ + + config.h: + cp config.def.h $@ +diff --git a/config.def.h b/config.def.h +index 22d2171..1593033 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -127,6 +127,7 @@ static const Key keys[] = { + /* modifier key function argument */ + { MODKEY, XKB_KEY_p, spawn, {.v = menucmd} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, spawn, {.v = termcmd} }, ++ { MODKEY, XKB_KEY_b, togglebar, {0} }, + { MODKEY, XKB_KEY_j, focusstack, {.i = +1} }, + { MODKEY, XKB_KEY_k, focusstack, {.i = -1} }, + { MODKEY, XKB_KEY_i, incnmaster, {.i = +1} }, +diff --git a/dwl.c b/dwl.c +index 8a587d1..7a4949b 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -68,6 +68,7 @@ + #include <xcb/xcb_icccm.h> + #endif + ++#include "dwl-ipc-unstable-v2-protocol.h" + #include "util.h" + + /* macros */ +@@ -144,6 +145,12 @@ typedef struct { + uint32_t resize; /* configure serial of a pending resize */ + } Client; + ++typedef struct { ++ struct wl_list link; ++ struct wl_resource *resource; ++ Monitor *mon; ++} DwlIpcOutput; ++ + typedef struct { + uint32_t mod; + xkb_keysym_t keysym; +@@ -189,6 +196,7 @@ typedef struct { + + struct Monitor { + struct wl_list link; ++ struct wl_list dwl_ipc_outputs; + struct wlr_output *wlr_output; + struct wlr_scene_output *scene_output; + struct wlr_scene_rect *fullscreen_bg; /* See createmon() for info */ +@@ -286,6 +294,17 @@ static void destroysessionlock(struct wl_listener *listener, void *data); + static void destroysessionmgr(struct wl_listener *listener, void *data); + static void destroykeyboardgroup(struct wl_listener *listener, void *data); + static Monitor *dirtomon(enum wlr_direction dir); ++static void dwl_ipc_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id); ++static void dwl_ipc_manager_destroy(struct wl_resource *resource); ++static void dwl_ipc_manager_get_output(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *output); ++static void dwl_ipc_manager_release(struct wl_client *client, struct wl_resource *resource); ++static void dwl_ipc_output_destroy(struct wl_resource *resource); ++static void dwl_ipc_output_printstatus(Monitor *monitor); ++static void dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output); ++static void dwl_ipc_output_set_client_tags(struct wl_client *client, struct wl_resource *resource, uint32_t and_tags, uint32_t xor_tags); ++static void dwl_ipc_output_set_layout(struct wl_client *client, struct wl_resource *resource, uint32_t index); ++static void dwl_ipc_output_set_tags(struct wl_client *client, struct wl_resource *resource, uint32_t tagmask, uint32_t toggle_tagset); ++static void dwl_ipc_output_release(struct wl_client *client, struct wl_resource *resource); + static void focusclient(Client *c, int lift); + static void focusmon(const Arg *arg); + static void focusstack(const Arg *arg); +@@ -338,6 +357,7 @@ 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 togglebar(const Arg *arg); + static void togglefloating(const Arg *arg); + static void togglefullscreen(const Arg *arg); + static void toggletag(const Arg *arg); +@@ -411,6 +431,9 @@ static struct wlr_box sgeom; + static struct wl_list mons; + static Monitor *selmon; + ++static struct zdwl_ipc_manager_v2_interface dwl_manager_implementation = {.release = dwl_ipc_manager_release, .get_output = dwl_ipc_manager_get_output}; ++static struct zdwl_ipc_output_v2_interface dwl_output_implementation = {.release = dwl_ipc_output_release, .set_tags = dwl_ipc_output_set_tags, .set_layout = dwl_ipc_output_set_layout, .set_client_tags = dwl_ipc_output_set_client_tags}; ++ + #ifdef XWAYLAND + static void activatex11(struct wl_listener *listener, void *data); + static void associatex11(struct wl_listener *listener, void *data); +@@ -703,6 +726,10 @@ cleanupmon(struct wl_listener *listener, void *data) + LayerSurface *l, *tmp; + size_t i; + ++ DwlIpcOutput *ipc_output, *ipc_output_tmp; ++ wl_list_for_each_safe(ipc_output, ipc_output_tmp, &m->dwl_ipc_outputs, link) ++ wl_resource_destroy(ipc_output->resource); ++ + /* 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) +@@ -983,6 +1010,8 @@ createmon(struct wl_listener *listener, void *data) + m = wlr_output->data = ecalloc(1, sizeof(*m)); + m->wlr_output = wlr_output; + ++ wl_list_init(&m->dwl_ipc_outputs); ++ + for (i = 0; i < LENGTH(m->layers); i++) + wl_list_init(&m->layers[i]); + +@@ -1334,6 +1363,192 @@ dirtomon(enum wlr_direction dir) + return selmon; + } + ++void ++dwl_ipc_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) ++{ ++ struct wl_resource *manager_resource = wl_resource_create(client, &zdwl_ipc_manager_v2_interface, version, id); ++ if (!manager_resource) { ++ wl_client_post_no_memory(client); ++ return; ++ } ++ wl_resource_set_implementation(manager_resource, &dwl_manager_implementation, NULL, dwl_ipc_manager_destroy); ++ ++ zdwl_ipc_manager_v2_send_tags(manager_resource, TAGCOUNT); ++ ++ for (unsigned int i = 0; i < LENGTH(layouts); i++) ++ zdwl_ipc_manager_v2_send_layout(manager_resource, layouts[i].symbol); ++} ++ ++void ++dwl_ipc_manager_destroy(struct wl_resource *resource) ++{ ++ /* No state to destroy */ ++} ++ ++void ++dwl_ipc_manager_get_output(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *output) ++{ ++ DwlIpcOutput *ipc_output; ++ Monitor *monitor = wlr_output_from_resource(output)->data; ++ struct wl_resource *output_resource = wl_resource_create(client, &zdwl_ipc_output_v2_interface, wl_resource_get_version(resource), id); ++ if (!output_resource) ++ return; ++ ++ ipc_output = ecalloc(1, sizeof(*ipc_output)); ++ ipc_output->resource = output_resource; ++ ipc_output->mon = monitor; ++ wl_resource_set_implementation(output_resource, &dwl_output_implementation, ipc_output, dwl_ipc_output_destroy); ++ wl_list_insert(&monitor->dwl_ipc_outputs, &ipc_output->link); ++ dwl_ipc_output_printstatus_to(ipc_output); ++} ++ ++void ++dwl_ipc_manager_release(struct wl_client *client, struct wl_resource *resource) ++{ ++ wl_resource_destroy(resource); ++} ++ ++static void ++dwl_ipc_output_destroy(struct wl_resource *resource) ++{ ++ DwlIpcOutput *ipc_output = wl_resource_get_user_data(resource); ++ wl_list_remove(&ipc_output->link); ++ free(ipc_output); ++} ++ ++void ++dwl_ipc_output_printstatus(Monitor *monitor) ++{ ++ DwlIpcOutput *ipc_output; ++ wl_list_for_each(ipc_output, &monitor->dwl_ipc_outputs, link) ++ dwl_ipc_output_printstatus_to(ipc_output); ++} ++ ++void ++dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output) ++{ ++ Monitor *monitor = ipc_output->mon; ++ Client *c, *focused; ++ int tagmask, state, numclients, focused_client, tag; ++ const char *title, *appid; ++ focused = focustop(monitor); ++ zdwl_ipc_output_v2_send_active(ipc_output->resource, monitor == selmon); ++ ++ for (tag = 0 ; tag < TAGCOUNT; tag++) { ++ numclients = state = focused_client = 0; ++ tagmask = 1 << tag; ++ if ((tagmask & monitor->tagset[monitor->seltags]) != 0) ++ state |= ZDWL_IPC_OUTPUT_V2_TAG_STATE_ACTIVE; ++ ++ wl_list_for_each(c, &clients, link) { ++ if (c->mon != monitor) ++ continue; ++ if (!(c->tags & tagmask)) ++ continue; ++ if (c == focused) ++ focused_client = 1; ++ if (c->isurgent) ++ state |= ZDWL_IPC_OUTPUT_V2_TAG_STATE_URGENT; ++ ++ numclients++; ++ } ++ zdwl_ipc_output_v2_send_tag(ipc_output->resource, tag, state, numclients, focused_client); ++ } ++ title = focused ? client_get_title(focused) : ""; ++ appid = focused ? client_get_appid(focused) : ""; ++ ++ zdwl_ipc_output_v2_send_layout(ipc_output->resource, monitor->lt[monitor->sellt] - layouts); ++ zdwl_ipc_output_v2_send_title(ipc_output->resource, title); ++ zdwl_ipc_output_v2_send_appid(ipc_output->resource, appid); ++ zdwl_ipc_output_v2_send_layout_symbol(ipc_output->resource, monitor->ltsymbol); ++ if (wl_resource_get_version(ipc_output->resource) >= ZDWL_IPC_OUTPUT_V2_FULLSCREEN_SINCE_VERSION) { ++ zdwl_ipc_output_v2_send_fullscreen(ipc_output->resource, focused ? focused->isfullscreen : 0); ++ } ++ if (wl_resource_get_version(ipc_output->resource) >= ZDWL_IPC_OUTPUT_V2_FLOATING_SINCE_VERSION) { ++ zdwl_ipc_output_v2_send_floating(ipc_output->resource, focused ? focused->isfloating : 0); ++ } ++ zdwl_ipc_output_v2_send_frame(ipc_output->resource); ++} ++ ++void ++dwl_ipc_output_set_client_tags(struct wl_client *client, struct wl_resource *resource, uint32_t and_tags, uint32_t xor_tags) ++{ ++ DwlIpcOutput *ipc_output; ++ Monitor *monitor; ++ Client *selected_client; ++ unsigned int newtags = 0; ++ ++ ipc_output = wl_resource_get_user_data(resource); ++ if (!ipc_output) ++ return; ++ ++ monitor = ipc_output->mon; ++ selected_client = focustop(monitor); ++ if (!selected_client) ++ return; ++ ++ newtags = (selected_client->tags & and_tags) ^ xor_tags; ++ if (!newtags) ++ return; ++ ++ selected_client->tags = newtags; ++ if (selmon == monitor) ++ focusclient(focustop(monitor), 1); ++ arrange(selmon); ++ printstatus(); ++} ++ ++void ++dwl_ipc_output_set_layout(struct wl_client *client, struct wl_resource *resource, uint32_t index) ++{ ++ DwlIpcOutput *ipc_output; ++ Monitor *monitor; ++ ++ ipc_output = wl_resource_get_user_data(resource); ++ if (!ipc_output) ++ return; ++ ++ monitor = ipc_output->mon; ++ if (index >= LENGTH(layouts)) ++ return; ++ if (index != monitor->lt[monitor->sellt] - layouts) ++ monitor->sellt ^= 1; ++ ++ monitor->lt[monitor->sellt] = &layouts[index]; ++ arrange(monitor); ++ printstatus(); ++} ++ ++void ++dwl_ipc_output_set_tags(struct wl_client *client, struct wl_resource *resource, uint32_t tagmask, uint32_t toggle_tagset) ++{ ++ DwlIpcOutput *ipc_output; ++ Monitor *monitor; ++ unsigned int newtags = tagmask & TAGMASK; ++ ++ ipc_output = wl_resource_get_user_data(resource); ++ if (!ipc_output) ++ return; ++ monitor = ipc_output->mon; ++ ++ if (!newtags || newtags == monitor->tagset[monitor->seltags]) ++ return; ++ if (toggle_tagset) ++ monitor->seltags ^= 1; ++ ++ monitor->tagset[monitor->seltags] = newtags; ++ if (selmon == monitor) ++ focusclient(focustop(monitor), 1); ++ arrange(monitor); ++ printstatus(); ++} ++ ++void ++dwl_ipc_output_release(struct wl_client *client, struct wl_resource *resource) ++{ ++ wl_resource_destroy(resource); ++} ++ + void + focusclient(Client *c, int lift) + { +@@ -2033,38 +2248,9 @@ 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); ++ wl_list_for_each(m, &mons, link) ++ dwl_ipc_output_printstatus(m); + } + + void +@@ -2584,6 +2770,8 @@ setup(void) + LISTEN_STATIC(&output_mgr->events.apply, outputmgrapply); + LISTEN_STATIC(&output_mgr->events.test, outputmgrtest); + ++ wl_global_create(dpy, &zdwl_ipc_manager_v2_interface, 2, NULL, dwl_ipc_manager_bind); ++ + /* 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 */ +@@ -2681,6 +2869,13 @@ tile(Monitor *m) + } + } + ++void ++togglebar(const Arg *arg) { ++ DwlIpcOutput *ipc_output; ++ wl_list_for_each(ipc_output, &selmon->dwl_ipc_outputs, link) ++ zdwl_ipc_output_v2_send_toggle_visibility(ipc_output->resource); ++} ++ + void + togglefloating(const Arg *arg) + { +diff --git a/protocols/dwl-ipc-unstable-v2.xml b/protocols/dwl-ipc-unstable-v2.xml +new file mode 100644 +index 0000000..0a6e7e5 +--- /dev/null ++++ b/protocols/dwl-ipc-unstable-v2.xml +@@ -0,0 +1,181 @@ ++<?xml version="1.0" encoding="utf-8"?> ++<!-- ++This is largely ripped from somebar's ipc patchset; just with some personal modifications. ++I would probably just submit raphi's patchset but I don't think that would be polite. ++--> ++<protocol name="dwl_ipc_unstable_v2"> ++ <description summary="inter-proccess-communication about dwl's state"> ++ This protocol allows clients to update and get updates from dwl. ++ ++ Warning! The protocol described in this file is experimental and ++ backward incompatible changes may be made. Backward compatible ++ changes may be added together with the corresponding interface ++ version bump. ++ Backward incompatible changes are done by bumping the version ++ number in the protocol and interface names and resetting the ++ interface version. Once the protocol is to be declared stable, ++ the 'z' prefix and the version number in the protocol and ++ interface names are removed and the interface version number is ++ reset. ++ </description> ++ ++ <interface name="zdwl_ipc_manager_v2" version="2"> ++ <description summary="manage dwl state"> ++ This interface is exposed as a global in wl_registry. ++ ++ Clients can use this interface to get a dwl_ipc_output. ++ After binding the client will recieve the dwl_ipc_manager.tags and dwl_ipc_manager.layout events. ++ The dwl_ipc_manager.tags and dwl_ipc_manager.layout events expose tags and layouts to the client. ++ </description> ++ ++ <request name="release" type="destructor"> ++ <description summary="release dwl_ipc_manager"> ++ Indicates that the client will not the dwl_ipc_manager object anymore. ++ Objects created through this instance are not affected. ++ </description> ++ </request> ++ ++ <request name="get_output"> ++ <description summary="get a dwl_ipc_outout for a wl_output"> ++ Get a dwl_ipc_outout for the specified wl_output. ++ </description> ++ <arg name="id" type="new_id" interface="zdwl_ipc_output_v2"/> ++ <arg name="output" type="object" interface="wl_output"/> ++ </request> ++ ++ <event name="tags"> ++ <description summary="Announces tag amount"> ++ This event is sent after binding. ++ A roundtrip after binding guarantees the client recieved all tags. ++ </description> ++ <arg name="amount" type="uint"/> ++ </event> ++ ++ <event name="layout"> ++ <description summary="Announces a layout"> ++ This event is sent after binding. ++ A roundtrip after binding guarantees the client recieved all layouts. ++ </description> ++ <arg name="name" type="string"/> ++ </event> ++ </interface> ++ ++ <interface name="zdwl_ipc_output_v2" version="2"> ++ <description summary="control dwl output"> ++ Observe and control a dwl output. ++ ++ Events are double-buffered: ++ Clients should cache events and redraw when a dwl_ipc_output.frame event is sent. ++ ++ Request are not double-buffered: ++ The compositor will update immediately upon request. ++ </description> ++ ++ <enum name="tag_state"> ++ <entry name="none" value="0" summary="no state"/> ++ <entry name="active" value="1" summary="tag is active"/> ++ <entry name="urgent" value="2" summary="tag has at least one urgent client"/> ++ </enum> ++ ++ <request name="release" type="destructor"> ++ <description summary="release dwl_ipc_outout"> ++ Indicates to that the client no longer needs this dwl_ipc_output. ++ </description> ++ </request> ++ ++ <event name="toggle_visibility"> ++ <description summary="Toggle client visibilty"> ++ Indicates the client should hide or show themselves. ++ If the client is visible then hide, if hidden then show. ++ </description> ++ </event> ++ ++ <event name="active"> ++ <description summary="Update the selected output."> ++ Indicates if the output is active. Zero is invalid, nonzero is valid. ++ </description> ++ <arg name="active" type="uint"/> ++ </event> ++ ++ <event name="tag"> ++ <description summary="Update the state of a tag."> ++ Indicates that a tag has been updated. ++ </description> ++ <arg name="tag" type="uint" summary="Index of the tag"/> ++ <arg name="state" type="uint" enum="tag_state" summary="The state of the tag."/> ++ <arg name="clients" type="uint" summary="The number of clients in the tag."/> ++ <arg name="focused" type="uint" summary="If there is a focused client. Nonzero being valid, zero being invalid."/> ++ </event> ++ ++ <event name="layout"> ++ <description summary="Update the layout."> ++ Indicates a new layout is selected. ++ </description> ++ <arg name="layout" type="uint" summary="Index of the layout."/> ++ </event> ++ ++ <event name="title"> ++ <description summary="Update the title."> ++ Indicates the title has changed. ++ </description> ++ <arg name="title" type="string" summary="The new title name."/> ++ </event> ++ ++ <event name="appid" since="1"> ++ <description summary="Update the appid."> ++ Indicates the appid has changed. ++ </description> ++ <arg name="appid" type="string" summary="The new appid."/> ++ </event> ++ ++ <event name="layout_symbol" since="1"> ++ <description summary="Update the current layout symbol"> ++ Indicates the layout has changed. Since layout symbols are dynamic. ++ As opposed to the zdwl_ipc_manager.layout event, this should take precendence when displaying. ++ You can ignore the zdwl_ipc_output.layout event. ++ </description> ++ <arg name="layout" type="string" summary="The new layout"/> ++ </event> ++ ++ <event name="frame"> ++ <description summary="The update sequence is done."> ++ Indicates that a sequence of status updates have finished and the client should redraw. ++ </description> ++ </event> ++ ++ <request name="set_tags"> ++ <description summary="Set the active tags of this output"/> ++ <arg name="tagmask" type="uint" summary="bitmask of the tags that should be set."/> ++ <arg name="toggle_tagset" type="uint" summary="toggle the selected tagset, zero for invalid, nonzero for valid."/> ++ </request> ++ ++ <request name="set_client_tags"> ++ <description summary="Set the tags of the focused client."> ++ The tags are updated as follows: ++ new_tags = (current_tags AND and_tags) XOR xor_tags ++ </description> ++ <arg name="and_tags" type="uint"/> ++ <arg name="xor_tags" type="uint"/> ++ </request> ++ ++ <request name="set_layout"> ++ <description summary="Set the layout of this output"/> ++ <arg name="index" type="uint" summary="index of a layout recieved by dwl_ipc_manager.layout"/> ++ </request> ++ ++ <!-- Version 2 --> ++ <event name="fullscreen" since="2"> ++ <description summary="Update fullscreen status"> ++ Indicates if the selected client on this output is fullscreen. ++ </description> ++ <arg name="is_fullscreen" type="uint" summary="If the selected client is fullscreen. Nonzero is valid, zero invalid"/> ++ </event> ++ ++ <event name="floating" since="2"> ++ <description summary="Update the floating status"> ++ Indicates if the selected client on this output is floating. ++ </description> ++ <arg name="is_floating" type="uint" summary="If the selected client is floating. Nonzero is valid, zero invalid"/> ++ </event> ++ </interface> ++</protocol> +-- +2.43.0 + diff --git a/patches/lockedkeys.patch b/patches/lockedkeys.patch new file mode 100644 index 0000000..f683536 --- /dev/null +++ b/patches/lockedkeys.patch @@ -0,0 +1,79 @@ +From 70dc03a3817b8fd933244c2db1bb849d9626b12b Mon Sep 17 00:00:00 2001 +From: wochap <gean.marroquin@gmail.com> +Date: Thu, 11 Apr 2024 13:16:40 -0500 +Subject: [PATCH] allow to add keybindings in lockscreen + +--- + config.def.h | 11 +++++++++++ + dwl.c | 20 ++++++++++++++++++++ + 2 files changed, 31 insertions(+) + +diff --git a/config.def.h b/config.def.h +index 8847e58..0d4a4f8 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -164,6 +164,17 @@ static const Key keys[] = { + CHVT(7), CHVT(8), CHVT(9), CHVT(10), CHVT(11), CHVT(12), + }; + ++static const Key lockedkeys[] = { ++ /* Note that Shift changes certain key codes: c -> C, 2 -> at, etc. */ ++ /* modifier key function argument */ ++ ++ /* 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} }, ++#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} }, +diff --git a/dwl.c b/dwl.c +index bf763df..db4bb2b 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -287,6 +287,7 @@ 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 int lockedkeybinding(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); +@@ -1446,6 +1447,21 @@ keybinding(uint32_t mods, xkb_keysym_t sym) + return 0; + } + ++int ++lockedkeybinding(uint32_t mods, xkb_keysym_t sym) ++{ ++ int handled = 0; ++ const Key *k; ++ for (k = lockedkeys; k < END(lockedkeys); k++) { ++ if (CLEANMASK(mods) == CLEANMASK(k->mod) && ++ sym == k->keysym && k->func) { ++ k->func(&k->arg); ++ handled = 1; ++ } ++ } ++ return handled; ++} ++ + void + keypress(struct wl_listener *listener, void *data) + { +@@ -1473,6 +1489,10 @@ keypress(struct wl_listener *listener, void *data) + handled = keybinding(mods, syms[i]) || handled; + } + ++ if (locked && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) ++ for (i = 0; i < nsyms; i++) ++ handled = lockedkeybinding(mods, syms[i]) || handled; ++ + if (handled && group->wlr_group->keyboard.repeat_info.delay > 0) { + group->mods = mods; + group->keysyms = syms; +-- +2.43.2 diff --git a/patches/main...serenevoid:uselessgaps.patch b/patches/main...serenevoid:uselessgaps.patch new file mode 100644 index 0000000..4c0944f --- /dev/null +++ b/patches/main...serenevoid:uselessgaps.patch @@ -0,0 +1,137 @@ +From 366a810531ce3425ea5eca4e32eba431dc26570e Mon Sep 17 00:00:00 2001 +From: serenevoid <ajuph9224@gmail.com> +Date: Sun, 16 Jul 2023 22:07:02 +0530 +Subject: [PATCH] feat(gaps): Useless Gaps + +- KISS (Keep it simple and stupid) +- Use common gap value instead of separate inner and outer gaps +- Set gap value in config.h and toggle gaps on or off +- minimal and efficient +--- + config.def.h | 3 +++ + dwl.c | 41 +++++++++++++++++++++++++++++++---------- + 2 files changed, 34 insertions(+), 10 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 447ba0051..16ad12ce8 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -1,7 +1,9 @@ + /* 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 int smartgaps = 0; /* 1 means no outer gap when there is only one window */ + static const unsigned int borderpx = 1; /* border pixel of windows */ ++static const unsigned int gappx = 10; /* horiz inner gap between windows */ + static const float bordercolor[] = {0.5, 0.5, 0.5, 1.0}; + static const float focuscolor[] = {1.0, 0.0, 0.0, 1.0}; + /* To conform the xdg-protocol, set the alpha to zero to restore the old behavior */ +@@ -119,6 +121,7 @@ static const Key keys[] = { + { MODKEY, XKB_KEY_l, setmfact, {.f = +0.05} }, + { MODKEY, XKB_KEY_Return, zoom, {0} }, + { MODKEY, XKB_KEY_Tab, view, {0} }, +++ { MODKEY, XKB_KEY_g, togglegaps, {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]} }, +diff --git a/dwl.c b/dwl.c +index 93f66efe6..aea07d68c 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -185,6 +185,7 @@ struct Monitor { + struct wlr_box w; /* window area, layout-relative */ + struct wl_list layers[4]; /* LayerSurface::link */ + const Layout *lt[2]; ++ int gappx; /* horizontal outer gaps */ + unsigned int seltags; + unsigned int sellt; + uint32_t tagset[2]; +@@ -304,6 +305,7 @@ 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 togglegaps(const Arg *arg); + static void toggletag(const Arg *arg); + static void toggleview(const Arg *arg); + static void unlocksession(struct wl_listener *listener, void *data); +@@ -367,6 +369,8 @@ static struct wlr_box sgeom; + static struct wl_list mons; + static Monitor *selmon; + ++static int enablegaps = 1; /* enables gaps, used by togglegaps */ ++ + /* global event handlers */ + static struct wl_listener cursor_axis = {.notify = axisnotify}; + static struct wl_listener cursor_button = {.notify = buttonpress}; +@@ -913,6 +917,7 @@ createmon(struct wl_listener *listener, void *data) + /* Initialize monitor state using configured rules */ + for (i = 0; i < LENGTH(m->layers); i++) + wl_list_init(&m->layers[i]); ++ m->gappx = gappx; + m->tagset[0] = m->tagset[1] = 1; + for (r = monrules; r < END(monrules); r++) { + if (!r->name || strstr(wlr_output->name, r->name)) { +@@ -2361,7 +2366,7 @@ tagmon(const Arg *arg) + void + tile(Monitor *m) + { +- unsigned int i, n = 0, mw, my, ty; ++ unsigned int i, n = 0, h, r, e = enablegaps, mw, my, ty; + Client *c; + + wl_list_for_each(c, &clients, link) +@@ -2370,22 +2375,31 @@ tile(Monitor *m) + if (n == 0) + return; + ++ if (smartgaps == n) { ++ e = 0; // outer gaps disabled ++ } ++ + if (n > m->nmaster) +- mw = m->nmaster ? m->w.width * m->mfact : 0; ++ mw = m->nmaster ? (m->w.width + m->gappx*e) * m->mfact : 0; + else +- mw = m->w.width; +- i = my = ty = 0; ++ mw = m->w.width - 2*m->gappx*e + m->gappx*e; ++ i = 0; ++ my = ty = m->gappx*e; + 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; ++ r = MIN(n, m->nmaster) - i; ++ h = (m->w.height - my - m->gappx*e - m->gappx*e * (r - 1)) / r; ++ resize(c, (struct wlr_box){.x = m->w.x + m->gappx*e, .y = m->w.y + my, ++ .width = mw - m->gappx*e, .height = h}, 0); ++ my += c->geom.height + m->gappx*e; + } 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; ++ r = n - i; ++ h = (m->w.height - ty - m->gappx*e - m->gappx*e * (r - 1)) / r; ++ resize(c, (struct wlr_box){.x = m->w.x + mw + m->gappx*e, .y = m->w.y + ty, ++ .width = m->w.width - mw - 2*m->gappx*e, .height = h}, 0); ++ ty += c->geom.height + m->gappx*e; + } + i++; + } +@@ -2408,6 +2422,13 @@ togglefullscreen(const Arg *arg) + setfullscreen(sel, !sel->isfullscreen); + } + ++void ++togglegaps(const Arg *arg) ++{ ++ enablegaps = !enablegaps; ++ arrange(selmon); ++} ++ + void + toggletag(const Arg *arg) + { diff --git a/patches/passthrough.patch b/patches/passthrough.patch new file mode 100644 index 0000000..d87f00e --- /dev/null +++ b/patches/passthrough.patch @@ -0,0 +1,82 @@ +From cd67c8386b0188daa15348c1d0d99187a556e461 Mon Sep 17 00:00:00 2001 +From: choc <notchoc@proton.me> +Date: Mon, 2 Jan 2023 13:00:29 +0800 +Subject: [PATCH] passthrough: allow pausing keybind handling + +also allows for bitcarrying-esque control of nested instances +--- + config.def.h | 4 ++++ + dwl.c | 14 ++++++++++++++ + 2 files changed, 18 insertions(+) + +diff --git a/config.def.h b/config.def.h +index 646a3d6..2d14e2a 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -20,6 +20,9 @@ static const float fullscreen_bg[] = {0.1f, 0.1f, 0.1f, 1.0f}; /* You ca + /* logging */ + static int log_level = WLR_ERROR; + ++/* passthrough */ ++static int passthrough = 0; ++ + static const Rule rules[] = { + /* app_id title tags mask isfloating monitor */ + /* examples: */ +@@ -156,6 +159,7 @@ static const Key keys[] = { + TAGKEYS( XKB_KEY_7, XKB_KEY_ampersand, 6), + TAGKEYS( XKB_KEY_8, XKB_KEY_asterisk, 7), + TAGKEYS( XKB_KEY_9, XKB_KEY_parenleft, 8), ++ { WLR_MODIFIER_ALT|WLR_MODIFIER_LOGO|WLR_MODIFIER_CTRL|WLR_MODIFIER_SHIFT, XKB_KEY_Escape, togglepassthrough, {0} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Q, quit, {0} }, + + /* Ctrl-Alt-Backspace and Ctrl-Alt-Fx used to be handled by X server */ +diff --git a/dwl.c b/dwl.c +index 9fb50a7..a1c65b4 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -339,6 +339,7 @@ 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 togglepassthrough(const Arg *arg); + static void toggletag(const Arg *arg); + static void toggleview(const Arg *arg); + static void unlocksession(struct wl_listener *listener, void *data); +@@ -620,6 +621,11 @@ buttonpress(struct wl_listener *listener, void *data) + for (b = buttons; b < END(buttons); b++) { + if (CLEANMASK(mods) == CLEANMASK(b->mod) && + event->button == b->button && b->func) { ++ if (passthrough) { ++ if (b->func != togglepassthrough) continue; ++ b->func(&b->arg); ++ break; ++ } + b->func(&b->arg); + return; + } +@@ -1509,6 +1515,8 @@ keybinding(uint32_t mods, xkb_keysym_t sym) + for (k = keys; k < END(keys); k++) { + if (CLEANMASK(mods) == CLEANMASK(k->mod) + && sym == k->keysym && k->func) { ++ if (passthrough && k->func != togglepassthrough) ++ continue; + k->func(&k->arg); + return 1; + } +@@ -2677,6 +2685,12 @@ togglefullscreen(const Arg *arg) + setfullscreen(sel, !sel->isfullscreen); + } + ++void ++togglepassthrough(const Arg *arg) ++{ ++ passthrough = !passthrough; ++} ++ + void + toggletag(const Arg *arg) + { +-- +2.43.0 + diff --git a/patches/pointer-gestures-unstable-v1.patch b/patches/pointer-gestures-unstable-v1.patch new file mode 100644 index 0000000..f76648a --- /dev/null +++ b/patches/pointer-gestures-unstable-v1.patch @@ -0,0 +1,186 @@ +From be7e98d28fc59aab67026e7d5efdcaeb26029713 Mon Sep 17 00:00:00 2001 +From: wochap <gean.marroquin@gmail.com> +Date: Fri, 12 Jul 2024 11:30:17 -0500 +Subject: [PATCH] implement pointer-gestures-unstable-v1 + +--- + dwl.c | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 136 insertions(+) + +diff --git a/dwl.c b/dwl.c +index dc0437e..e5805b1 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -38,6 +38,7 @@ + #include <wlr/types/wlr_output_power_management_v1.h> + #include <wlr/types/wlr_pointer.h> + #include <wlr/types/wlr_pointer_constraints_v1.h> ++#include <wlr/types/wlr_pointer_gestures_v1.h> + #include <wlr/types/wlr_presentation_time.h> + #include <wlr/types/wlr_primary_selection.h> + #include <wlr/types/wlr_primary_selection_v1.h> +@@ -250,6 +251,14 @@ static void arrangelayer(Monitor *m, struct wl_list *list, + static void arrangelayers(Monitor *m); + static void axisnotify(struct wl_listener *listener, void *data); + static void buttonpress(struct wl_listener *listener, void *data); ++static void swipe_begin(struct wl_listener *listener, void *data); ++static void swipe_update(struct wl_listener *listener, void *data); ++static void swipe_end(struct wl_listener *listener, void *data); ++static void pinch_begin(struct wl_listener *listener, void *data); ++static void pinch_update(struct wl_listener *listener, void *data); ++static void pinch_end(struct wl_listener *listener, void *data); ++static void hold_begin(struct wl_listener *listener, void *data); ++static void hold_end(struct wl_listener *listener, void *data); + static void chvt(const Arg *arg); + static void checkidleinhibitor(struct wlr_surface *exclude); + static void cleanup(void); +@@ -383,6 +392,7 @@ 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_gestures_v1 *pointer_gestures; + + static struct wlr_pointer_constraints_v1 *pointer_constraints; + static struct wlr_relative_pointer_manager_v1 *relative_pointer_mgr; +@@ -644,6 +654,122 @@ buttonpress(struct wl_listener *listener, void *data) + event->time_msec, event->button, event->state); + } + ++void ++swipe_begin(struct wl_listener *listener, void *data) ++{ ++ struct wlr_pointer_swipe_begin_event *event = data; ++ ++ // Forward swipe begin event to client ++ wlr_pointer_gestures_v1_send_swipe_begin( ++ pointer_gestures, ++ seat, ++ event->time_msec, ++ event->fingers ++ ); ++} ++ ++void ++swipe_update(struct wl_listener *listener, void *data) ++{ ++ struct wlr_pointer_swipe_update_event *event = data; ++ ++ // Forward swipe update event to client ++ wlr_pointer_gestures_v1_send_swipe_update( ++ pointer_gestures, ++ seat, ++ event->time_msec, ++ event->dx, ++ event->dy ++ ); ++} ++ ++void ++swipe_end(struct wl_listener *listener, void *data) ++{ ++ struct wlr_pointer_swipe_end_event *event = data; ++ ++ // Forward swipe end event to client ++ wlr_pointer_gestures_v1_send_swipe_end( ++ pointer_gestures, ++ seat, ++ event->time_msec, ++ event->cancelled ++ ); ++} ++ ++void ++pinch_begin(struct wl_listener *listener, void *data) ++{ ++ struct wlr_pointer_pinch_begin_event *event = data; ++ ++ // Forward pinch begin event to client ++ wlr_pointer_gestures_v1_send_pinch_begin( ++ pointer_gestures, ++ seat, ++ event->time_msec, ++ event->fingers ++ ); ++} ++ ++void ++pinch_update(struct wl_listener *listener, void *data) ++{ ++ struct wlr_pointer_pinch_update_event *event = data; ++ ++ // Forward pinch update event to client ++ wlr_pointer_gestures_v1_send_pinch_update( ++ pointer_gestures, ++ seat, ++ event->time_msec, ++ event->dx, ++ event->dy, ++ event->scale, ++ event->rotation ++ ); ++} ++ ++void ++pinch_end(struct wl_listener *listener, void *data) ++{ ++ struct wlr_pointer_pinch_end_event *event = data; ++ ++ // Forward pinch end event to client ++ wlr_pointer_gestures_v1_send_pinch_end( ++ pointer_gestures, ++ seat, ++ event->time_msec, ++ event->cancelled ++ ); ++} ++ ++void ++hold_begin(struct wl_listener *listener, void *data) ++{ ++ struct wlr_pointer_hold_begin_event *event = data; ++ ++ // Forward hold begin event to client ++ wlr_pointer_gestures_v1_send_hold_begin( ++ pointer_gestures, ++ seat, ++ event->time_msec, ++ event->fingers ++ ); ++} ++ ++void ++hold_end(struct wl_listener *listener, void *data) ++{ ++ struct wlr_pointer_hold_end_event *event = data; ++ ++ // Forward hold end event to client ++ wlr_pointer_gestures_v1_send_hold_end( ++ pointer_gestures, ++ seat, ++ event->time_msec, ++ event->cancelled ++ ); ++} ++ + void + chvt(const Arg *arg) + { +@@ -2556,6 +2682,16 @@ setup(void) + virtual_pointer_mgr = wlr_virtual_pointer_manager_v1_create(dpy); + LISTEN_STATIC(&virtual_pointer_mgr->events.new_virtual_pointer, virtualpointer); + ++ pointer_gestures = wlr_pointer_gestures_v1_create(dpy); ++ LISTEN_STATIC(&cursor->events.swipe_begin, swipe_begin); ++ LISTEN_STATIC(&cursor->events.swipe_update, swipe_update); ++ LISTEN_STATIC(&cursor->events.swipe_end, swipe_end); ++ LISTEN_STATIC(&cursor->events.pinch_begin, pinch_begin); ++ LISTEN_STATIC(&cursor->events.pinch_update, pinch_update); ++ LISTEN_STATIC(&cursor->events.pinch_end, pinch_end); ++ LISTEN_STATIC(&cursor->events.hold_begin, hold_begin); ++ LISTEN_STATIC(&cursor->events.hold_end, hold_end); ++ + seat = wlr_seat_create(dpy, "seat0"); + LISTEN_STATIC(&seat->events.request_set_cursor, setcursor); + LISTEN_STATIC(&seat->events.request_set_selection, setsel); +-- +2.45.1 diff --git a/patches/regexrules.patch b/patches/regexrules.patch new file mode 100644 index 0000000..f7207d3 --- /dev/null +++ b/patches/regexrules.patch @@ -0,0 +1,76 @@ +From 7fed9449575b1e4f58d519d2f87b7e66e2056125 Mon Sep 17 00:00:00 2001 +From: wochap <gean.marroquin@gmail.com> +Date: Thu, 11 Apr 2024 12:45:47 -0500 +Subject: [PATCH] implement regex support in rules for app_id and title Enables + the use of regular expressions for window rules "app_id" and "title" + +--- + config.def.h | 1 + + dwl.c | 19 +++++++++++++++++-- + 2 files changed, 18 insertions(+), 2 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 8847e58..89f5b60 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -25,6 +25,7 @@ static const Rule rules[] = { + /* 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" */ ++ { "^kitty_EXAMPLE$", NULL, 0, 0, -1 }, + }; + + /* layout(s) */ +diff --git a/dwl.c b/dwl.c +index bf763df..fc185af 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -10,6 +10,7 @@ + #include <sys/wait.h> + #include <time.h> + #include <unistd.h> ++#include <regex.h> + #include <wayland-server-core.h> + #include <wlr/backend.h> + #include <wlr/backend/libinput.h> +@@ -347,6 +348,7 @@ 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); ++static int regex_match(const char *pattern, const char *str); + + /* variables */ + static const char broken[] = "broken"; +@@ -459,8 +461,8 @@ applyrules(Client *c) + title = broken; + + for (r = rules; r < END(rules); r++) { +- if ((!r->title || strstr(title, r->title)) +- && (!r->id || strstr(appid, r->id))) { ++ if ((!r->title || regex_match(r->title, title)) ++ && (!r->id || regex_match(r->id, appid))) { + c->isfloating = r->isfloating; + newtags |= r->tags; + i = 0; +@@ -2929,6 +2931,19 @@ zoom(const Arg *arg) + arrange(selmon); + } + ++int ++regex_match(const char *pattern, const char *str) { ++ regex_t regex; ++ int reti; ++ if (regcomp(®ex, pattern, REG_EXTENDED) != 0) ++ return 0; ++ reti = regexec(®ex, str, (size_t)0, NULL, 0); ++ regfree(®ex); ++ if (reti == 0) ++ return 1; ++ return 0; ++} ++ + #ifdef XWAYLAND + void + activatex11(struct wl_listener *listener, void *data) +-- +2.43.2 diff --git a/patches/swallow.patch b/patches/swallow.patch new file mode 100644 index 0000000..b4581c4 --- /dev/null +++ b/patches/swallow.patch @@ -0,0 +1,240 @@ +From 4b80c425c9f414bc079a0e61f5a3ef42eea85476 Mon Sep 17 00:00:00 2001 +From: choc <notchoc@proton.me> +Date: Fri, 15 Sep 2023 10:36:21 +0800 +Subject: [PATCH] implement swallow + +--- + client.h | 12 ++++++ + config.def.h | 7 ++-- + dwl.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++++--- + 3 files changed, 123 insertions(+), 8 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..7e5fef1 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -22,10 +22,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" */ ++ { "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" */ ++ { "foot", NULL, 0, 0, 1, 1, -1 }, /* make foot swallow clients that are not foot */ + }; + + /* layout(s) */ +diff --git a/dwl.c b/dwl.c +index dc0437e..c6a5e9d 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -103,7 +103,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 */ +@@ -138,9 +139,11 @@ typedef struct { + #endif + unsigned int bw; + uint32_t tags; +- int isfloating, isurgent, isfullscreen; ++ int isfloating, isurgent, isfullscreen, isterm, noswallow; + uint32_t resize; /* configure serial of a pending resize */ +-} Client; ++ pid_t pid; ++ Client *swallowing, *swallowedby; ++}; + + typedef struct { + uint32_t mod; +@@ -229,6 +232,8 @@ typedef struct { + const char *title; + uint32_t tags; + int isfloating; ++ int isterm; ++ int noswallow; + int monitor; + } Rule; + +@@ -351,6 +356,10 @@ 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); ++static pid_t getparentprocess(pid_t p); ++static int isdescprocess(pid_t p, pid_t c); ++static Client *termforwin(Client *w); ++static void swallow(Client *c, Client *w); + + /* variables */ + static const char broken[] = "broken"; +@@ -461,10 +470,14 @@ 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; ++ c->isterm = r->isterm; ++ c->noswallow = r->noswallow; + newtags |= r->tags; + i = 0; + wl_list_for_each(m, &mons, link) { +@@ -473,6 +486,21 @@ applyrules(Client *c) + } + } + } ++ if (!c->noswallow && !client_is_float_type(c) ++ && !c->surface.xdg->initial_commit) { ++ Client *p = termforwin(c); ++ if (p) { ++ c->swallowedby = p; ++ p->swallowing = c; ++ wl_list_remove(&c->link); ++ wl_list_remove(&c->flink); ++ swallow(c, p); ++ wl_list_remove(&p->link); ++ wl_list_remove(&p->flink); ++ mon = p->mon; ++ newtags = p->tags; ++ } ++ } + setmon(c, mon, newtags); + } + +@@ -1467,6 +1495,63 @@ handlesig(int signo) + } + } + ++pid_t ++getparentprocess(pid_t p) ++{ ++ unsigned int v = 0; ++ ++ FILE *f; ++ char buf[256]; ++ snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)p); ++ ++ if (!(f = fopen(buf, "r"))) ++ return 0; ++ ++ fscanf(f, "%*u %*s %*c %u", &v); ++ fclose(f); ++ ++ return (pid_t)v; ++} ++ ++int ++isdescprocess(pid_t p, pid_t c) ++{ ++ while (p != c && c != 0) ++ c = getparentprocess(c); ++ ++ return (int)c; ++} ++ ++Client * ++termforwin(Client *w) ++{ ++ Client *c; ++ ++ if (!w->pid || w->isterm || w->noswallow) ++ return NULL; ++ ++ wl_list_for_each(c, &fstack, flink) ++ if (c->isterm && !c->swallowing && c->pid && isdescprocess(c->pid, w->pid)) ++ return c; ++ ++ return NULL; ++} ++ ++void ++swallow(Client *c, Client *w) ++{ ++ c->bw = w->bw; ++ c->isfloating = w->isfloating; ++ c->isurgent = w->isurgent; ++ c->isfullscreen = w->isfullscreen; ++ c->tags = w->tags; ++ c->geom = w->geom; ++ wl_list_insert(&w->link, &c->link); ++ wl_list_insert(&w->flink, &c->flink); ++ wlr_scene_node_set_enabled(&w->scene->node, 0); ++ wlr_scene_node_set_enabled(&c->scene->node, 1); ++} ++ + void + incnmaster(const Arg *arg) + { +@@ -2746,15 +2831,32 @@ unmapnotify(struct wl_listener *listener, void *data) + grabc = NULL; + } + ++ if (c->swallowedby) ++ swallow(c->swallowedby, c); ++ + if (client_is_unmanaged(c)) { + if (c == exclusive_focus) { + exclusive_focus = NULL; + focusclient(focustop(selmon), 1); + } + } else { +- wl_list_remove(&c->link); ++ if (!c->swallowing) ++ wl_list_remove(&c->link); + setmon(c, NULL, 0); +- wl_list_remove(&c->flink); ++ if (!c->swallowing) ++ wl_list_remove(&c->flink); ++ } ++ ++ if (c->swallowedby) { ++ c->swallowedby->prev = c->geom; ++ setfullscreen(c->swallowedby, c->isfullscreen); ++ c->swallowedby->swallowing = NULL; ++ c->swallowedby = NULL; ++ } ++ ++ if (c->swallowing) { ++ c->swallowing->swallowedby = NULL; ++ c->swallowing = NULL; + } + + wlr_scene_node_destroy(&c->scene->node); +-- +2.43.0 + diff --git a/patches/tearing.patch b/patches/tearing.patch new file mode 100644 index 0000000..951d25c --- /dev/null +++ b/patches/tearing.patch @@ -0,0 +1,356 @@ +From 66b2e1646bee8502a3715403c165015bd019438c Mon Sep 17 00:00:00 2001 +From: korei999 <ju7t1xe@gmail.com> +Date: Wed, 18 Sep 2024 20:11:22 +0300 +Subject: [PATCH] implement tearing protocol + +--- + Makefile | 5 +- + config.def.h | 8 +++ + dwl.c | 184 +++++++++++++++++++++++++++++++++++++++++++++++---- + 3 files changed, 182 insertions(+), 15 deletions(-) + +diff --git a/Makefile b/Makefile +index 8db7409..6edc7d7 100644 +--- a/Makefile ++++ b/Makefile +@@ -21,7 +21,7 @@ dwl: dwl.o util.o + $(CC) dwl.o util.o $(DWLCFLAGS) $(LDFLAGS) $(LDLIBS) -o $@ + dwl.o: dwl.c client.h config.h config.mk cursor-shape-v1-protocol.h \ + pointer-constraints-unstable-v1-protocol.h wlr-layer-shell-unstable-v1-protocol.h \ +- wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h ++ wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h tearing-control-v1-protocol.h + util.o: util.c util.h + + # wayland-scanner is a tool which generates C headers and rigging for Wayland +@@ -45,6 +45,9 @@ wlr-output-power-management-unstable-v1-protocol.h: + xdg-shell-protocol.h: + $(WAYLAND_SCANNER) server-header \ + $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@ ++tearing-control-v1-protocol.h: ++ $(WAYLAND_SCANNER) server-header \ ++ $(WAYLAND_PROTOCOLS)/staging/tearing-control/tearing-control-v1.xml $@ + + config.h: + cp config.def.h $@ +diff --git a/config.def.h b/config.def.h +index 22d2171..52d38d3 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -28,6 +28,14 @@ static const Rule rules[] = { + { "firefox_EXAMPLE", NULL, 1 << 8, 0, -1 }, /* Start on ONLY tag "9" */ + }; + ++/* tearing */ ++static int tearing_allowed = 1; ++static const ForceTearingRule force_tearing[] = { ++ {.title = "", .appid = "hl_linux"}, ++ {.title = "Warcraft III", .appid = ""}, ++ {.title = "", .appid = "gamescope"}, ++}; ++ + /* layout(s) */ + static const Layout layouts[] = { + /* symbol arrange function */ +diff --git a/dwl.c b/dwl.c +index dc0c861..44be1bf 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -51,6 +51,7 @@ + #include <wlr/types/wlr_session_lock_v1.h> + #include <wlr/types/wlr_single_pixel_buffer_v1.h> + #include <wlr/types/wlr_subcompositor.h> ++#include <wlr/types/wlr_tearing_control_v1.h> + #include <wlr/types/wlr_viewporter.h> + #include <wlr/types/wlr_virtual_keyboard_v1.h> + #include <wlr/types/wlr_virtual_pointer_v1.h> +@@ -90,6 +91,11 @@ enum { NetWMWindowTypeDialog, NetWMWindowTypeSplash, NetWMWindowTypeToolbar, + NetWMWindowTypeUtility, NetLast }; /* EWMH atoms */ + #endif + ++typedef struct ForceTearingRule { ++ const char* title; ++ const char* appid; ++} ForceTearingRule; ++ + typedef union { + int i; + uint32_t ui; +@@ -143,6 +149,7 @@ typedef struct { + uint32_t tags; + int isfloating, isurgent, isfullscreen; + uint32_t resize; /* configure serial of a pending resize */ ++ enum wp_tearing_control_v1_presentation_hint tearing_hint; + } Client; + + typedef struct { +@@ -243,6 +250,19 @@ typedef struct { + struct wl_listener destroy; + } SessionLock; + ++typedef struct TearingController { ++ struct wlr_tearing_control_v1 *tearing_control; ++ struct wl_listener set_hint; ++ struct wl_listener destroy; ++ ++ struct wl_list link; /* tearing_controllers */ ++} TearingController; ++ ++typedef struct SendFrameDoneData { ++ struct timespec when; ++ struct Monitor *mon; ++} SendFrameDoneData; ++ + /* function declarations */ + static void applybounds(Client *c, struct wlr_box *bbox); + static void applyrules(Client *c); +@@ -293,6 +313,9 @@ 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 handletearingcontrollersethint(struct wl_listener *listener, void *data); ++static void handletearingcontrollerdestroy(struct wl_listener *listener, void *data); ++static void handlenewtearinghint(struct wl_listener *listener, void *data); + 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); +@@ -309,6 +332,7 @@ static void motionnotify(uint32_t time, struct wlr_input_device *device, double + double sy, double sx_unaccel, double sy_unaccel); + static void motionrelative(struct wl_listener *listener, void *data); + static void moveresize(const Arg *arg); ++static int moncantear(Monitor* m); + 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); +@@ -323,6 +347,7 @@ 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 sendframedoneiterator(struct wlr_scene_buffer *buffer, int x, int y, void *user_data); + 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); +@@ -400,6 +425,10 @@ static struct wlr_scene_rect *locked_bg; + static struct wlr_session_lock_v1 *cur_lock; + static struct wl_listener lock_listener = {.notify = locksession}; + ++struct wlr_tearing_control_manager_v1 *tearing_control_v1; ++struct wl_listener tearing_control_new_object; ++struct wl_list tearing_controllers; ++ + static struct wlr_seat *seat; + static KeyboardGroup *kb_group; + static unsigned int cursor_mode; +@@ -1510,6 +1539,69 @@ handlesig(int signo) + } + } + ++void ++handletearingcontrollersethint(struct wl_listener *listener, void *data) ++{ ++ Client *c = NULL, *i = NULL; ++ struct TearingController *controller = wl_container_of(listener, controller, set_hint); ++ ++ struct wlr_xdg_surface *surface = wlr_xdg_surface_try_from_wlr_surface(controller->tearing_control->surface); ++#ifdef XWAYLAND ++ struct wlr_xwayland_surface *xsurface = wlr_xwayland_surface_try_from_wlr_surface(controller->tearing_control->surface); ++#endif ++ ++ wl_list_for_each(i, &fstack, flink) { ++ if (i->surface.xdg == surface ++#ifdef XWAYLAND ++ || i->surface.xwayland == xsurface ++#endif ++ ) { ++ c = i; ++ break; ++ } ++ } ++ ++ if (c) { ++ enum wp_tearing_control_v1_presentation_hint hint = controller->tearing_control->current; ++ fprintf( ++ stderr, "TEARING: found surface: %p(appid: '%s', title: '%s'), hint: %d(%s)\n", ++ (void*)c, client_get_appid(c), client_get_title(c), hint, hint ? "ASYNC" : "VSYNC" ++ ); ++ c->tearing_hint = controller->tearing_control->current; ++ } ++} ++ ++void ++handletearingcontrollerdestroy(struct wl_listener *listener, void *data) ++{ ++ struct TearingController *controller = wl_container_of(listener, controller, destroy); ++ ++ wl_list_remove(&controller->set_hint.link); ++ wl_list_remove(&controller->destroy.link); ++ wl_list_remove(&controller->link); ++ free(controller); ++} ++ ++void ++handlenewtearinghint(struct wl_listener *listener, void *data) ++{ ++ struct wlr_tearing_control_v1 *tearing_control = data; ++ struct TearingController *controller = calloc(1, sizeof(struct TearingController)); ++ ++ if (!controller) ++ return; ++ ++ controller->tearing_control = tearing_control; ++ controller->set_hint.notify = handletearingcontrollersethint; ++ wl_signal_add(&tearing_control->events.set_hint, &controller->set_hint); ++ ++ controller->destroy.notify = handletearingcontrollerdestroy; ++ wl_signal_add(&tearing_control->events.destroy, &controller->destroy); ++ ++ wl_list_init(&controller->link); ++ wl_list_insert(&tearing_controllers, &controller->link); ++} ++ + void + incnmaster(const Arg *arg) + { +@@ -1677,6 +1769,33 @@ locksession(struct wl_listener *listener, void *data) + wlr_session_lock_v1_send_locked(session_lock); + } + ++static inline void ++forcetearingrule(Client *c) ++{ ++ int success = 0; ++ const char* appid = client_get_appid(c); ++ const char* title = client_get_title(c); ++ ++ for (unsigned i = 0; i < LENGTH(force_tearing); i++) { ++ if (appid) ++ if (strcmp(force_tearing[i].appid, appid) == 0) { ++ success = 1; ++ break; ++ } ++ ++ if (title) ++ if (strcmp(force_tearing[i].title, title) == 0) { ++ success = 1; ++ break; ++ } ++ } ++ ++ if (success) { ++ c->tearing_hint = WP_TEARING_CONTROL_V1_PRESENTATION_HINT_ASYNC; ++ fprintf(stderr, "tearing forced for: appid: '%s', title: '%s'\n", appid, title); ++ } ++} ++ + void + mapnotify(struct wl_listener *listener, void *data) + { +@@ -1686,6 +1805,8 @@ mapnotify(struct wl_listener *listener, void *data) + Monitor *m; + int i; + ++ forcetearingrule(c); ++ + /* 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() */ +@@ -1924,6 +2045,13 @@ moveresize(const Arg *arg) + } + } + ++int ++moncantear(Monitor* m) ++{ ++ Client *c = focustop(m); ++ return (c && c->isfullscreen && c->tearing_hint); /* 1 == ASYNC */ ++} ++ + void + outputmgrapply(struct wl_listener *listener, void *data) + { +@@ -2093,27 +2221,40 @@ quit(const Arg *arg) + 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_scene_output *scene_output = m->scene_output; + struct wlr_output_state pending = {0}; +- struct timespec now; ++ SendFrameDoneData frame_done_data = {0}; + +- /* 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; ++ m->wlr_output->frame_pending = false; ++ ++ if (!wlr_scene_output_needs_frame(scene_output)) { ++ goto skip; + } + +- wlr_scene_output_commit(m->scene_output, NULL); ++ wlr_output_state_init(&pending); ++ if (!wlr_scene_output_build_state(m->scene_output, &pending, NULL)) { ++ goto skip; ++ } ++ ++ if (tearing_allowed && moncantear(m)) { ++ pending.tearing_page_flip = true; ++ ++ if (!wlr_output_test_state(m->wlr_output, &pending)) { ++ fprintf(stderr, "Output test failed on '%s', retrying without tearing page-flip\n", m->wlr_output->name); ++ pending.tearing_page_flip = false; ++ } ++ } ++ ++ if (!wlr_output_commit_state(m->wlr_output, &pending)) ++ fprintf(stderr, "Page-flip failed on output %s", m->wlr_output->name); + +-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); ++ ++skip: ++ clock_gettime(CLOCK_MONOTONIC, &frame_done_data.when); ++ frame_done_data.mon = m; ++ wlr_scene_output_for_each_buffer(m->scene_output, sendframedoneiterator, &frame_done_data); + } + + void +@@ -2237,6 +2378,16 @@ run(char *startup_cmd) + wl_display_run(dpy); + } + ++void ++sendframedoneiterator(struct wlr_scene_buffer *buffer, int x, int y, void *user_data) ++{ ++ SendFrameDoneData *data = user_data; ++ if (buffer->primary_output != data->mon->scene_output) ++ return; ++ ++ wlr_scene_buffer_send_frame_done(buffer, &data->when); ++} ++ + void + setcursor(struct wl_listener *listener, void *data) + { +@@ -2584,6 +2735,11 @@ setup(void) + LISTEN_STATIC(&output_mgr->events.apply, outputmgrapply); + LISTEN_STATIC(&output_mgr->events.test, outputmgrtest); + ++ tearing_control_v1 = wlr_tearing_control_manager_v1_create(dpy, 1); ++ tearing_control_new_object.notify = handlenewtearinghint; ++ wl_signal_add(&tearing_control_v1->events.new_object, &tearing_control_new_object); ++ wl_list_init(&tearing_controllers); ++ + /* 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 */ +-- +2.46.0 + diff --git a/patches/toggle_constraints.patch b/patches/toggle_constraints.patch new file mode 100644 index 0000000..e823377 --- /dev/null +++ b/patches/toggle_constraints.patch @@ -0,0 +1,86 @@ +From 13bb9d86a0da2ebc82c20ee721b3eeb28592b0a0 Mon Sep 17 00:00:00 2001 +From: David Donahue <david.donahue2996@gmail.com> +Date: Tue, 26 Mar 2024 17:45:26 -0600 +Subject: [PATCH] Added function to toggle pointer constraints via a keybind + +--- + dwl.c | 41 ++++++++++++++++++++++++++--------------- + 1 file changed, 26 insertions(+), 15 deletions(-) + +diff --git a/dwl.c b/dwl.c +index 5867b0c..04d377b 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -332,6 +332,7 @@ 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 togglepointerconstraints(const Arg *arg); + static void toggletag(const Arg *arg); + static void toggleview(const Arg *arg); + static void unlocksession(struct wl_listener *listener, void *data); +@@ -400,6 +401,8 @@ static unsigned int cursor_mode; + static Client *grabc; + static int grabcx, grabcy; /* client-relative */ + ++static int enable_constraints = 1; ++ + static struct wlr_output_layout *output_layout; + static struct wlr_box sgeom; + static struct wl_list mons; +@@ -1700,22 +1703,24 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d + 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 (enable_constraints){ ++ 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; + } +- +- if (active_constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED) +- return; + } + } + +@@ -2620,6 +2625,12 @@ togglefullscreen(const Arg *arg) + setfullscreen(sel, !sel->isfullscreen); + } + ++void ++togglepointerconstraints(const Arg *arg) ++{ ++ enable_constraints = !enable_constraints; ++} ++ + void + toggletag(const Arg *arg) + { +-- +2.43.2 + diff --git a/patches/xwayland-handle-minimize.patch b/patches/xwayland-handle-minimize.patch new file mode 100644 index 0000000..d6d1f8f --- /dev/null +++ b/patches/xwayland-handle-minimize.patch @@ -0,0 +1,91 @@ +From 7277f668f19f5a7fcfbbc96e80cb2829487848ca Mon Sep 17 00:00:00 2001 +From: korei999 <ju7t1xe@gmail.com> +Date: Mon, 1 Apr 2024 15:13:11 +0300 +Subject: [PATCH] handle minimize request for xwayland clients + +--- + client.h | 9 ++++++--- + dwl.c | 19 +++++++++++++++++++ + 2 files changed, 25 insertions(+), 3 deletions(-) + +diff --git a/client.h b/client.h +index 800b867..c46cfb2 100644 +--- a/client.h ++++ b/client.h +@@ -94,9 +94,12 @@ 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); ++ struct wlr_xwayland_surface *surface; ++ if ((surface = wlr_xwayland_surface_try_from_wlr_surface(s))) { ++ if (activated && surface->minimized) ++ wlr_xwayland_surface_set_minimized(surface, false); ++ ++ wlr_xwayland_surface_activate(surface, activated); + return; + } + #endif +diff --git a/dwl.c b/dwl.c +index 39ce68c..b49f57b 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -131,6 +131,7 @@ typedef struct { + #ifdef XWAYLAND + struct wl_listener activate; + struct wl_listener associate; ++ struct wl_listener minimize; + struct wl_listener dissociate; + struct wl_listener configure; + struct wl_listener set_hints; +@@ -412,6 +413,7 @@ 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 xcb_atom_t getatom(xcb_connection_t *xc, const char *name); ++static void minimizenotify(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 wlr_xwayland *xwayland; +@@ -1177,6 +1179,7 @@ destroynotify(struct wl_listener *listener, void *data) + wl_list_remove(&c->activate.link); + wl_list_remove(&c->associate.link); + wl_list_remove(&c->configure.link); ++ wl_list_remove(&c->minimize.link); + wl_list_remove(&c->dissociate.link); + wl_list_remove(&c->set_hints.link); + } else +@@ -2984,6 +2987,7 @@ createnotifyx11(struct wl_listener *listener, void *data) + 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_minimize, &c->minimize, minimizenotify); + 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); +@@ -3011,6 +3015,21 @@ getatom(xcb_connection_t *xc, const char *name) + return atom; + } + ++void ++minimizenotify(struct wl_listener *listener, void *data) ++{ ++ Client *c = wl_container_of(listener, c, minimize); ++ struct wlr_xwayland_surface *xsurface = c->surface.xwayland; ++ struct wlr_xwayland_minimize_event *e = data; ++ int focused; ++ ++ if (xsurface->surface == NULL || !xsurface->surface->mapped) ++ return; ++ ++ focused = seat->keyboard_state.focused_surface == xsurface->surface; ++ wlr_xwayland_surface_set_minimized(xsurface, !focused && e->minimize); ++} ++ + void + sethints(struct wl_listener *listener, void *data) + { +-- +2.44.0 + diff --git a/pre_build.sh b/pre_build.sh new file mode 100755 index 0000000..226007b --- /dev/null +++ b/pre_build.sh @@ -0,0 +1,2 @@ +#!/bin/sh +(cd wlroots && meson setup build -Dwrap_mode=default && ninja -C build) diff --git a/protocols/dwl-ipc-unstable-v2.xml b/protocols/dwl-ipc-unstable-v2.xml new file mode 100644 index 0000000..0a6e7e5 --- /dev/null +++ b/protocols/dwl-ipc-unstable-v2.xml @@ -0,0 +1,181 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +This is largely ripped from somebar's ipc patchset; just with some personal modifications. +I would probably just submit raphi's patchset but I don't think that would be polite. +--> +<protocol name="dwl_ipc_unstable_v2"> + <description summary="inter-proccess-communication about dwl's state"> + This protocol allows clients to update and get updates from dwl. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible + changes may be added together with the corresponding interface + version bump. + Backward incompatible changes are done by bumping the version + number in the protocol and interface names and resetting the + interface version. Once the protocol is to be declared stable, + the 'z' prefix and the version number in the protocol and + interface names are removed and the interface version number is + reset. + </description> + + <interface name="zdwl_ipc_manager_v2" version="2"> + <description summary="manage dwl state"> + This interface is exposed as a global in wl_registry. + + Clients can use this interface to get a dwl_ipc_output. + After binding the client will recieve the dwl_ipc_manager.tags and dwl_ipc_manager.layout events. + The dwl_ipc_manager.tags and dwl_ipc_manager.layout events expose tags and layouts to the client. + </description> + + <request name="release" type="destructor"> + <description summary="release dwl_ipc_manager"> + Indicates that the client will not the dwl_ipc_manager object anymore. + Objects created through this instance are not affected. + </description> + </request> + + <request name="get_output"> + <description summary="get a dwl_ipc_outout for a wl_output"> + Get a dwl_ipc_outout for the specified wl_output. + </description> + <arg name="id" type="new_id" interface="zdwl_ipc_output_v2"/> + <arg name="output" type="object" interface="wl_output"/> + </request> + + <event name="tags"> + <description summary="Announces tag amount"> + This event is sent after binding. + A roundtrip after binding guarantees the client recieved all tags. + </description> + <arg name="amount" type="uint"/> + </event> + + <event name="layout"> + <description summary="Announces a layout"> + This event is sent after binding. + A roundtrip after binding guarantees the client recieved all layouts. + </description> + <arg name="name" type="string"/> + </event> + </interface> + + <interface name="zdwl_ipc_output_v2" version="2"> + <description summary="control dwl output"> + Observe and control a dwl output. + + Events are double-buffered: + Clients should cache events and redraw when a dwl_ipc_output.frame event is sent. + + Request are not double-buffered: + The compositor will update immediately upon request. + </description> + + <enum name="tag_state"> + <entry name="none" value="0" summary="no state"/> + <entry name="active" value="1" summary="tag is active"/> + <entry name="urgent" value="2" summary="tag has at least one urgent client"/> + </enum> + + <request name="release" type="destructor"> + <description summary="release dwl_ipc_outout"> + Indicates to that the client no longer needs this dwl_ipc_output. + </description> + </request> + + <event name="toggle_visibility"> + <description summary="Toggle client visibilty"> + Indicates the client should hide or show themselves. + If the client is visible then hide, if hidden then show. + </description> + </event> + + <event name="active"> + <description summary="Update the selected output."> + Indicates if the output is active. Zero is invalid, nonzero is valid. + </description> + <arg name="active" type="uint"/> + </event> + + <event name="tag"> + <description summary="Update the state of a tag."> + Indicates that a tag has been updated. + </description> + <arg name="tag" type="uint" summary="Index of the tag"/> + <arg name="state" type="uint" enum="tag_state" summary="The state of the tag."/> + <arg name="clients" type="uint" summary="The number of clients in the tag."/> + <arg name="focused" type="uint" summary="If there is a focused client. Nonzero being valid, zero being invalid."/> + </event> + + <event name="layout"> + <description summary="Update the layout."> + Indicates a new layout is selected. + </description> + <arg name="layout" type="uint" summary="Index of the layout."/> + </event> + + <event name="title"> + <description summary="Update the title."> + Indicates the title has changed. + </description> + <arg name="title" type="string" summary="The new title name."/> + </event> + + <event name="appid" since="1"> + <description summary="Update the appid."> + Indicates the appid has changed. + </description> + <arg name="appid" type="string" summary="The new appid."/> + </event> + + <event name="layout_symbol" since="1"> + <description summary="Update the current layout symbol"> + Indicates the layout has changed. Since layout symbols are dynamic. + As opposed to the zdwl_ipc_manager.layout event, this should take precendence when displaying. + You can ignore the zdwl_ipc_output.layout event. + </description> + <arg name="layout" type="string" summary="The new layout"/> + </event> + + <event name="frame"> + <description summary="The update sequence is done."> + Indicates that a sequence of status updates have finished and the client should redraw. + </description> + </event> + + <request name="set_tags"> + <description summary="Set the active tags of this output"/> + <arg name="tagmask" type="uint" summary="bitmask of the tags that should be set."/> + <arg name="toggle_tagset" type="uint" summary="toggle the selected tagset, zero for invalid, nonzero for valid."/> + </request> + + <request name="set_client_tags"> + <description summary="Set the tags of the focused client."> + The tags are updated as follows: + new_tags = (current_tags AND and_tags) XOR xor_tags + </description> + <arg name="and_tags" type="uint"/> + <arg name="xor_tags" type="uint"/> + </request> + + <request name="set_layout"> + <description summary="Set the layout of this output"/> + <arg name="index" type="uint" summary="index of a layout recieved by dwl_ipc_manager.layout"/> + </request> + + <!-- Version 2 --> + <event name="fullscreen" since="2"> + <description summary="Update fullscreen status"> + Indicates if the selected client on this output is fullscreen. + </description> + <arg name="is_fullscreen" type="uint" summary="If the selected client is fullscreen. Nonzero is valid, zero invalid"/> + </event> + + <event name="floating" since="2"> + <description summary="Update the floating status"> + Indicates if the selected client on this output is floating. + </description> + <arg name="is_floating" type="uint" summary="If the selected client is floating. Nonzero is valid, zero invalid"/> + </event> + </interface> +</protocol> diff --git a/wlroots b/wlroots new file mode 160000 +Subproject d305934ebe6852785a1f425ee96861f0b7280d7 |