From 97b9dbc1e667dc5b99544fa7148c0b6e0cf21623 Mon Sep 17 00:00:00 2001 From: A Frederick Christensen Date: Sun, 8 Mar 2026 23:30:05 -0500 Subject: [PATCH 01/36] tablet-and-touch: removed This coupled patch was likely only of use to me (@fauxmight) At present, it does not cleanly apply with other patches I am using and is thus no longer likely of use to anyone. --- patches/tablet-and-touch/README.md | 13 - .../tablet-and-touch-0.8.patch | 584 ------------------ ...ablet-and-touch-wlroots-next-f4249db.patch | 584 ------------------ 3 files changed, 1181 deletions(-) delete mode 100644 patches/tablet-and-touch/README.md delete mode 100644 patches/tablet-and-touch/tablet-and-touch-0.8.patch delete mode 100644 patches/tablet-and-touch/tablet-and-touch-wlroots-next-f4249db.patch diff --git a/patches/tablet-and-touch/README.md b/patches/tablet-and-touch/README.md deleted file mode 100644 index 426ae81..0000000 --- a/patches/tablet-and-touch/README.md +++ /dev/null @@ -1,13 +0,0 @@ -### Description -This combines -[tablet-input](../tablet-input) and -[touch-input](../touch-input) -into a single appliable patch. -The two patches modify similar code areas in `dwl.c` and will conflict if one attempts to direcly apply both. - - -### Download -- [tablet-and-touch-wlroots-next-f4249db.patch](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/tablet-and-touch/tablet-and-touch-wlroots-next-f4249db.patch) -- [tablet-and-touch-0.8.patch](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/tablet-and-touch/tablet-and-touch-0.8.patch) -### Authors -- [fauxmight](https://codeberg.org/fauxmight) diff --git a/patches/tablet-and-touch/tablet-and-touch-0.8.patch b/patches/tablet-and-touch/tablet-and-touch-0.8.patch deleted file mode 100644 index 16ece16..0000000 --- a/patches/tablet-and-touch/tablet-and-touch-0.8.patch +++ /dev/null @@ -1,584 +0,0 @@ -From f0f797f8abebc4624c7e6bdfbb1ec2a3122f3914 Mon Sep 17 00:00:00 2001 -From: A Frederick Christensen -Date: Thu, 26 Feb 2026 23:20:03 -0600 -Subject: [PATCH] Apply tablet-and-touch patch - ---- - Makefile | 6 +- - config.def.h | 1 + - dwl.c | 398 +++++++++++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 404 insertions(+), 1 deletion(-) - -diff --git a/Makefile b/Makefile -index 578194f..e0d1835 100644 ---- a/Makefile -+++ b/Makefile -@@ -21,7 +21,8 @@ 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 \ -+ tablet-v2-protocol.h - util.o: util.c util.h - - # wayland-scanner is a tool which generates C headers and rigging for Wayland -@@ -45,6 +46,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 $@ -+tablet-v2-protocol.h: -+ $(WAYLAND_SCANNER) server-header \ -+ $(WAYLAND_PROTOCOLS)/unstable/tablet/tablet-unstable-v2.xml $@ - - config.h: - cp config.def.h $@ -diff --git a/config.def.h b/config.def.h -index 8a6eda0..1f20dfd 100644 ---- a/config.def.h -+++ b/config.def.h -@@ -4,6 +4,7 @@ - ((hex >> 8) & 0xFF) / 255.0f, \ - (hex & 0xFF) / 255.0f } - /* appearance */ -+static const int tabletmaptosurface = 0; /* map tablet input to surface(1) or monitor(0) */ - 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 */ -diff --git a/dwl.c b/dwl.c -index 44f3ad9..5ed467d 100644 ---- a/dwl.c -+++ b/dwl.c -@@ -51,6 +51,10 @@ - #include - #include - #include -+#include -+#include -+#include -+#include - #include - #include - #include -@@ -161,6 +165,12 @@ typedef struct { - struct wl_listener destroy; - } KeyboardGroup; - -+typedef struct TouchGroup { -+ struct wl_list link; -+ struct wlr_touch *touch; -+ Monitor *m; -+} TouchGroup; -+ - typedef struct { - /* Must keep this field first */ - unsigned int type; /* LayerShell */ -@@ -268,7 +278,10 @@ static void createnotify(struct wl_listener *listener, void *data); - static void createpointer(struct wlr_pointer *pointer); - static void createpointerconstraint(struct wl_listener *listener, void *data); - static void createpopup(struct wl_listener *listener, void *data); -+static void createtablet(struct wlr_input_device *device); -+static void createtouch(struct wlr_touch *touch); - static void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint); -+static void createtouch(struct wlr_touch *touch); - static void cursorframe(struct wl_listener *listener, void *data); - static void cursorwarptohint(void); - static void destroydecoration(struct wl_listener *listener, void *data); -@@ -281,6 +294,9 @@ static void destroynotify(struct wl_listener *listener, void *data); - static void destroypointerconstraint(struct wl_listener *listener, void *data); - static void destroysessionlock(struct wl_listener *listener, void *data); - static void destroykeyboardgroup(struct wl_listener *listener, void *data); -+static void destroytablet(struct wl_listener *listener, void *data); -+static void destroytabletsurfacenotify(struct wl_listener *listener, void *data); -+static void destroytablettool(struct wl_listener *listener, void *data); - static Monitor *dirtomon(enum wlr_direction dir); - static void focusclient(Client *c, int lift); - static void focusmon(const Arg *arg); -@@ -333,11 +349,20 @@ static void spawn(const Arg *arg); - static void startdrag(struct wl_listener *listener, void *data); - static void tag(const Arg *arg); - static void tagmon(const Arg *arg); -+static void tablettoolmotion(struct wlr_tablet_v2_tablet_tool *tool, bool change_x, bool change_y, double x, double y, double dx, double dy); -+static void tablettoolproximity(struct wl_listener *listener, void *data); -+static void tablettoolaxis(struct wl_listener *listener, void *data); -+static void tablettoolbutton(struct wl_listener *listener, void *data); -+static void tablettooltip(struct wl_listener *listener, void *data); - static void tile(Monitor *m); - static void togglefloating(const Arg *arg); - static void togglefullscreen(const Arg *arg); - static void toggletag(const Arg *arg); - static void toggleview(const Arg *arg); -+static void touchdown(struct wl_listener *listener, void *data); -+static void touchup(struct wl_listener *listener, void *data); -+static void touchframe(struct wl_listener *listener, void *data); -+static void touchmotion(struct wl_listener *listener, void *data); - static void unlocksession(struct wl_listener *listener, void *data); - static void unmaplayersurfacenotify(struct wl_listener *listener, void *data); - static void unmapnotify(struct wl_listener *listener, void *data); -@@ -390,6 +415,13 @@ static struct wlr_pointer_constraint_v1 *active_constraint; - static struct wlr_cursor *cursor; - static struct wlr_xcursor_manager *cursor_mgr; - -+static struct wlr_tablet_manager_v2 *tablet_mgr; -+static struct wlr_tablet_v2_tablet *tablet = NULL; -+static struct wlr_tablet_v2_tablet_tool *tablet_tool = NULL; -+static struct wlr_tablet_v2_tablet_pad *tablet_pad = NULL; -+static struct wlr_surface *tablet_curr_surface = NULL; -+static struct wl_listener destroy_tablet_surface_listener = {.notify = destroytabletsurfacenotify}; -+ - static struct wlr_scene_rect *root_bg; - static struct wlr_session_lock_manager_v1 *session_lock_mgr; - static struct wlr_scene_rect *locked_bg; -@@ -405,6 +437,7 @@ static struct wlr_output_layout *output_layout; - static struct wlr_box sgeom; - static struct wl_list mons; - static Monitor *selmon; -+static struct wl_list touches; - - /* global event handlers */ - static struct wl_listener cursor_axis = {.notify = axisnotify}; -@@ -412,6 +445,12 @@ static struct wl_listener cursor_button = {.notify = buttonpress}; - static struct wl_listener cursor_frame = {.notify = cursorframe}; - static struct wl_listener cursor_motion = {.notify = motionrelative}; - static struct wl_listener cursor_motion_absolute = {.notify = motionabsolute}; -+static struct wl_listener tablet_device_destroy = {.notify = destroytablet}; -+static struct wl_listener tablet_tool_axis = {.notify = tablettoolaxis}; -+static struct wl_listener tablet_tool_button = {.notify = tablettoolbutton}; -+static struct wl_listener tablet_tool_destroy = {.notify = destroytablettool}; -+static struct wl_listener tablet_tool_proximity = {.notify = tablettoolproximity}; -+static struct wl_listener tablet_tool_tip = {.notify = tablettooltip}; - static struct wl_listener gpu_reset = {.notify = gpureset}; - static struct wl_listener layout_change = {.notify = updatemons}; - static struct wl_listener new_idle_inhibitor = {.notify = createidleinhibitor}; -@@ -434,6 +473,10 @@ static struct wl_listener request_set_sel = {.notify = setsel}; - static struct wl_listener request_set_cursor_shape = {.notify = setcursorshape}; - static struct wl_listener request_start_drag = {.notify = requeststartdrag}; - static struct wl_listener start_drag = {.notify = startdrag}; -+static struct wl_listener touch_down = {.notify = touchdown}; -+static struct wl_listener touch_frame = {.notify = touchframe}; -+static struct wl_listener touch_motion = {.notify = touchmotion}; -+static struct wl_listener touch_up = {.notify = touchup}; - static struct wl_listener new_session_lock = {.notify = locksession}; - - #ifdef XWAYLAND -@@ -781,6 +824,10 @@ cleanuplisteners(void) - wl_list_remove(&request_set_cursor_shape.link); - wl_list_remove(&request_start_drag.link); - wl_list_remove(&start_drag.link); -+ wl_list_remove(&touch_down.link); -+ wl_list_remove(&touch_frame.link); -+ wl_list_remove(&touch_motion.link); -+ wl_list_remove(&touch_up.link); - wl_list_remove(&new_session_lock.link); - #ifdef XWAYLAND - wl_list_remove(&new_xwayland_surface.link); -@@ -1199,6 +1246,38 @@ createpopup(struct wl_listener *listener, void *data) - LISTEN_STATIC(&popup->base->surface->events.commit, commitpopup); - } - -+void -+createtablet(struct wlr_input_device *device) -+{ -+ if (!tablet) { -+ struct libinput_device *device_handle = NULL; -+ if (!wlr_input_device_is_libinput(device) || -+ !(device_handle = wlr_libinput_get_device_handle(device))) -+ return; -+ -+ tablet = wlr_tablet_create(tablet_mgr, seat, device); -+ wl_signal_add(&tablet->wlr_device->events.destroy, &tablet_device_destroy); -+ if (libinput_device_config_send_events_get_modes(device_handle)) { -+ libinput_device_config_send_events_set_mode(device_handle, send_events_mode); -+ wlr_cursor_attach_input_device(cursor, device); -+ } -+ } else if (device == tablet->wlr_device) { -+ wlr_log(WLR_ERROR, "createtablet: duplicate device"); -+ } else { -+ wlr_log(WLR_ERROR, "createtablet: already have one tablet"); -+ } -+} -+ -+void -+createtouch(struct wlr_touch *wlr_touch) -+{ -+ TouchGroup *touch = ecalloc(1, sizeof(TouchGroup)); -+ -+ touch->touch = wlr_touch; -+ wl_list_insert(&touches, &touch->link); -+ wlr_cursor_attach_input_device(cursor, &wlr_touch->base); -+} -+ - void - cursorconstrain(struct wlr_pointer_constraint_v1 *constraint) - { -@@ -1383,6 +1462,29 @@ destroykeyboardgroup(struct wl_listener *listener, void *data) - free(group); - } - -+void -+destroytablet(struct wl_listener *listener, void *data) -+{ -+ wl_list_remove(&tablet_device_destroy.link); -+ wlr_cursor_detach_input_device(cursor, tablet->wlr_device); -+ tablet = NULL; -+} -+ -+void -+destroytabletsurfacenotify(struct wl_listener *listener, void *data) -+{ -+ if (tablet_curr_surface) -+ wl_list_remove(&destroy_tablet_surface_listener.link); -+ tablet_curr_surface = NULL; -+} -+ -+void -+destroytablettool(struct wl_listener *listener, void *data) -+{ -+ destroytabletsurfacenotify(NULL, NULL); -+ tablet_tool = NULL; -+} -+ - Monitor * - dirtomon(enum wlr_direction dir) - { -@@ -1590,6 +1692,15 @@ inputdevice(struct wl_listener *listener, void *data) - case WLR_INPUT_DEVICE_POINTER: - createpointer(wlr_pointer_from_input_device(device)); - break; -+ case WLR_INPUT_DEVICE_TABLET: -+ createtablet(device); -+ break; -+ case WLR_INPUT_DEVICE_TABLET_PAD: -+ tablet_pad = wlr_tablet_pad_create(tablet_mgr, seat, device); -+ break; -+ case WLR_INPUT_DEVICE_TOUCH: -+ createtouch(wlr_touch_from_input_device(device)); -+ break; - default: - /* TODO handle other input device types */ - break; -@@ -1602,6 +1713,8 @@ inputdevice(struct wl_listener *listener, void *data) - caps = WL_SEAT_CAPABILITY_POINTER; - if (!wl_list_empty(&kb_group->wlr_group->devices)) - caps |= WL_SEAT_CAPABILITY_KEYBOARD; -+ if (!wl_list_empty(&touches)) -+ caps |= WL_SEAT_CAPABILITY_TOUCH; - wlr_seat_set_capabilities(seat, caps); - } - -@@ -2585,6 +2698,8 @@ setup(void) - - relative_pointer_mgr = wlr_relative_pointer_manager_v1_create(dpy); - -+ tablet_mgr = wlr_tablet_v2_create(dpy); -+ - /* - * Creates a cursor, which is a wlroots utility for tracking the cursor - * image shown on screen. -@@ -2614,6 +2729,18 @@ setup(void) - wl_signal_add(&cursor->events.button, &cursor_button); - wl_signal_add(&cursor->events.axis, &cursor_axis); - wl_signal_add(&cursor->events.frame, &cursor_frame); -+ wl_signal_add(&cursor->events.tablet_tool_proximity, &tablet_tool_proximity); -+ wl_signal_add(&cursor->events.tablet_tool_axis, &tablet_tool_axis); -+ wl_signal_add(&cursor->events.tablet_tool_button, &tablet_tool_button); -+ wl_signal_add(&cursor->events.tablet_tool_tip, &tablet_tool_tip); -+ -+ -+ wl_list_init(&touches); -+ -+ wl_signal_add(&cursor->events.touch_down, &touch_down); -+ wl_signal_add(&cursor->events.touch_frame, &touch_frame); -+ wl_signal_add(&cursor->events.touch_motion, &touch_motion); -+ wl_signal_add(&cursor->events.touch_up, &touch_up); - - cursor_shape_mgr = wlr_cursor_shape_manager_v1_create(dpy, 1); - wl_signal_add(&cursor_shape_mgr->events.request_set_shape, &request_set_cursor_shape); -@@ -2709,6 +2836,163 @@ tagmon(const Arg *arg) - setmon(sel, dirtomon(arg->i), 0); - } - -+void -+tabletapplymap(double x, double y, struct wlr_input_device *dev) -+{ -+ Client *p; -+ struct wlr_box geom = {0}; -+ if (tabletmaptosurface && tablet_curr_surface) { -+ toplevel_from_wlr_surface(tablet_curr_surface, &p, NULL); -+ if (p) { -+ for (; client_get_parent(p); p = client_get_parent(p)); -+ geom.x = p->geom.x + p->bw; -+ geom.y = p->geom.y + p->bw; -+ geom.width = p->geom.width - 2 * p->bw; -+ geom.height = p->geom.height - 2 * p->bw; -+ } -+ } -+ wlr_cursor_map_input_to_region(cursor, dev, &geom); -+ wlr_cursor_map_input_to_output(cursor, dev, selmon->wlr_output); -+} -+ -+void -+tablettoolmotion(struct wlr_tablet_v2_tablet_tool *tool, bool change_x, bool change_y, -+ double x, double y, double dx, double dy) -+{ -+ struct wlr_surface *surface = NULL; -+ double sx, sy; -+ -+ if (!change_x && !change_y) -+ return; -+ -+ tabletapplymap(x, y, tablet->wlr_device); -+ -+ // TODO: apply constraints -+ switch (tablet_tool->wlr_tool->type) { -+ case WLR_TABLET_TOOL_TYPE_LENS: -+ case WLR_TABLET_TOOL_TYPE_MOUSE: -+ wlr_cursor_move(cursor, tablet->wlr_device, dx, dy); -+ break; -+ default: -+ wlr_cursor_warp_absolute(cursor, tablet->wlr_device, change_x ? x : NAN, change_y ? y : NAN); -+ break; -+ } -+ -+ motionnotify(0, NULL, 0, 0, 0, 0); -+ -+ xytonode(cursor->x, cursor->y, &surface, NULL, NULL, &sx, &sy); -+ if (surface && !wlr_surface_accepts_tablet_v2(surface, tablet)) -+ surface = NULL; -+ -+ if (surface != tablet_curr_surface) { -+ if (tablet_curr_surface) { -+ // TODO: wait until all buttons released before leaving -+ if (tablet_tool) -+ wlr_tablet_v2_tablet_tool_notify_proximity_out(tablet_tool); -+ if (tablet_pad) -+ wlr_tablet_v2_tablet_pad_notify_leave(tablet_pad, tablet_curr_surface); -+ wl_list_remove(&destroy_tablet_surface_listener.link); -+ } -+ if (surface) { -+ if (tablet_pad) -+ wlr_tablet_v2_tablet_pad_notify_enter(tablet_pad, tablet, surface); -+ if (tablet_tool) -+ wlr_tablet_v2_tablet_tool_notify_proximity_in(tablet_tool, tablet, surface); -+ wl_signal_add(&surface->events.destroy, &destroy_tablet_surface_listener); -+ } -+ tablet_curr_surface = surface; -+ } -+ -+ if (surface) -+ wlr_tablet_v2_tablet_tool_notify_motion(tablet_tool, sx, sy); -+} -+ -+void -+tablettoolproximity(struct wl_listener *listener, void *data) -+{ -+ struct wlr_tablet_tool_proximity_event *event = data; -+ struct wlr_tablet_tool *tool = event->tool; -+ -+ if (!tablet_tool) { -+ tablet_tool = wlr_tablet_tool_create(tablet_mgr, seat, tool); -+ wl_signal_add(&tablet_tool->wlr_tool->events.destroy, &tablet_tool_destroy); -+ wl_signal_add(&tablet_tool->events.set_cursor, &request_cursor); -+ } -+ -+ switch (event->state) { -+ case WLR_TABLET_TOOL_PROXIMITY_OUT: -+ wlr_tablet_v2_tablet_tool_notify_proximity_out(tablet_tool); -+ destroytabletsurfacenotify(NULL, NULL); -+ break; -+ case WLR_TABLET_TOOL_PROXIMITY_IN: -+ tablettoolmotion(tablet_tool, true, true, event->x, event->y, 0, 0); -+ break; -+ } -+} -+ -+double tilt_x = 0; -+double tilt_y = 0; -+ -+void -+tablettoolaxis(struct wl_listener *listener, void *data) -+{ -+ struct wlr_tablet_tool_axis_event *event = data; -+ -+ tablettoolmotion(tablet_tool, -+ event->updated_axes & WLR_TABLET_TOOL_AXIS_X, -+ event->updated_axes & WLR_TABLET_TOOL_AXIS_Y, -+ event->x, event->y, event->dx, event->dy); -+ -+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_PRESSURE) -+ wlr_tablet_v2_tablet_tool_notify_pressure(tablet_tool, event->pressure); -+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_DISTANCE) -+ wlr_tablet_v2_tablet_tool_notify_distance(tablet_tool, event->distance); -+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_X) -+ tilt_x = event->tilt_x; -+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_Y) -+ tilt_y = event->tilt_y; -+ if (event->updated_axes & (WLR_TABLET_TOOL_AXIS_TILT_X | WLR_TABLET_TOOL_AXIS_TILT_Y)) -+ wlr_tablet_v2_tablet_tool_notify_tilt(tablet_tool, tilt_x, tilt_y); -+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_ROTATION) -+ wlr_tablet_v2_tablet_tool_notify_rotation(tablet_tool, event->rotation); -+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_SLIDER) -+ wlr_tablet_v2_tablet_tool_notify_slider(tablet_tool, event->slider); -+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_WHEEL) -+ wlr_tablet_v2_tablet_tool_notify_wheel(tablet_tool, event->wheel_delta, 0); -+} -+ -+void -+tablettoolbutton(struct wl_listener *listener, void *data) -+{ -+ struct wlr_tablet_tool_button_event *event = data; -+ wlr_tablet_v2_tablet_tool_notify_button(tablet_tool, event->button, -+ (enum zwp_tablet_pad_v2_button_state)event->state); -+} -+ -+void -+tablettooltip(struct wl_listener *listener, void *data) -+{ -+ struct wlr_tablet_tool_tip_event *event = data; -+ -+ if (!tablet_curr_surface) { -+ struct wlr_pointer_button_event fakeptrbtnevent = { -+ .button = BTN_LEFT, -+ .state = event->state == WLR_TABLET_TOOL_TIP_UP ? -+ WL_POINTER_BUTTON_STATE_RELEASED : WL_POINTER_BUTTON_STATE_PRESSED, -+ .time_msec = event->time_msec, -+ }; -+ buttonpress(NULL, (void *)&fakeptrbtnevent); -+ } -+ -+ if (event->state == WLR_TABLET_TOOL_TIP_UP) { -+ wlr_tablet_v2_tablet_tool_notify_up(tablet_tool); -+ return; -+ } -+ -+ wlr_tablet_v2_tablet_tool_notify_down(tablet_tool); -+ wlr_tablet_tool_v2_start_implicit_grab(tablet_tool); -+} -+ - void - tile(Monitor *m) - { -@@ -2787,6 +3071,120 @@ toggleview(const Arg *arg) - printstatus(); - } - -+void -+touchdown(struct wl_listener *listener, void *data) -+{ -+ struct wlr_touch_down_event *event = data; -+ double lx, ly; -+ double sx, sy; -+ struct wlr_surface *surface; -+ Client *c = NULL; -+ uint32_t serial = 0; -+ Monitor *m; -+ -+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); -+ -+ // Map the input to the appropriate output, to ensure that rotation is -+ // handled. -+ wl_list_for_each(m, &mons, link) { -+ if (m == NULL || m->wlr_output == NULL) { -+ continue; -+ } -+ if (event->touch->output_name != NULL && 0 != strcmp(event->touch->output_name, m->wlr_output->name)) { -+ continue; -+ } -+ -+ wlr_cursor_map_input_to_output(cursor, &event->touch->base, m->wlr_output); -+ } -+ -+ wlr_cursor_absolute_to_layout_coords(cursor, &event->touch->base, event->x, event->y, &lx, &ly); -+ -+ /* Find the client under the pointer and send the event along. */ -+ xytonode(lx, ly, &surface, &c, NULL, &sx, &sy); -+ if (sloppyfocus) -+ focusclient(c, 0); -+ -+ if (surface != NULL) { -+ serial = wlr_seat_touch_notify_down(seat, surface, event->time_msec, event->touch_id, sx, sy); -+ } -+ -+ if (serial && wlr_seat_touch_num_points(seat) == 1) { -+ /* Emulate a mouse click if the touch event wasn't handled */ -+ struct wlr_pointer_button_event *button_event = data; -+ struct wlr_pointer_motion_absolute_event *motion_event = data; -+ double dx, dy; -+ -+ wlr_cursor_absolute_to_layout_coords(cursor, &motion_event->pointer->base, motion_event->x, motion_event->y, &lx, &ly); -+ wlr_cursor_warp_closest(cursor, &motion_event->pointer->base, lx, ly); -+ dx = lx - cursor->x; -+ dy = ly - cursor->y; -+ motionnotify(motion_event->time_msec, &motion_event->pointer->base, dx, dy, dx, dy); -+ -+ button_event->button = BTN_LEFT; -+ button_event->state = WL_POINTER_BUTTON_STATE_PRESSED; -+ buttonpress(listener, button_event); -+ } -+} -+ -+void -+touchup(struct wl_listener *listener, void *data) -+{ -+ struct wlr_touch_up_event *event = data; -+ -+ if (!wlr_seat_touch_get_point(seat, event->touch_id)) { -+ return; -+ } -+ -+ if (wlr_seat_touch_num_points(seat) == 1) { -+ struct wlr_pointer_button_event *button_event = data; -+ -+ button_event->button = BTN_LEFT; -+ button_event->state = WL_POINTER_BUTTON_STATE_RELEASED; -+ buttonpress(listener, button_event); -+ } -+ -+ wlr_seat_touch_notify_up(seat, event->time_msec, event->touch_id); -+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); -+} -+ -+void -+touchframe(struct wl_listener *listener, void *data) -+{ -+ wlr_seat_touch_notify_frame(seat); -+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); -+} -+ -+void -+touchmotion(struct wl_listener *listener, void *data) -+{ -+ struct wlr_touch_motion_event *event = data; -+ double lx, ly; -+ double sx, sy; -+ struct wlr_surface *surface; -+ Client *c = NULL; -+ -+ if (!wlr_seat_touch_get_point(seat, event->touch_id)) { -+ return; -+ } -+ -+ wlr_cursor_absolute_to_layout_coords(cursor, &event->touch->base, event->x, event->y, &lx, &ly); -+ xytonode(lx, ly, &surface, &c, NULL, &sx, &sy); -+ -+ if (c != NULL && surface != NULL) { -+ if (sloppyfocus) -+ focusclient(c, 0); -+ wlr_seat_touch_point_focus(seat, surface, event->time_msec, event->touch_id, sx, sy); -+ } else { -+ if (sloppyfocus) -+ focusclient(NULL, 0); -+ wlr_seat_touch_point_clear_focus(seat, event->time_msec, event->touch_id); -+ } -+ wlr_seat_touch_notify_motion(seat, event->time_msec, event->touch_id, sx, sy); -+ -+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); -+} -+ -+ - void - unlocksession(struct wl_listener *listener, void *data) - { --- -2.52.0 - diff --git a/patches/tablet-and-touch/tablet-and-touch-wlroots-next-f4249db.patch b/patches/tablet-and-touch/tablet-and-touch-wlroots-next-f4249db.patch deleted file mode 100644 index bf577d4..0000000 --- a/patches/tablet-and-touch/tablet-and-touch-wlroots-next-f4249db.patch +++ /dev/null @@ -1,584 +0,0 @@ -From 78eda59b86ca01188fd645fe4062dda75d60acb1 Mon Sep 17 00:00:00 2001 -From: A Frederick Christensen -Date: Thu, 26 Feb 2026 23:23:49 -0600 -Subject: [PATCH] Apply tablet-and-touch patch - ---- - Makefile | 6 +- - config.def.h | 1 + - dwl.c | 398 +++++++++++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 404 insertions(+), 1 deletion(-) - -diff --git a/Makefile b/Makefile -index 578194f..e0d1835 100644 ---- a/Makefile -+++ b/Makefile -@@ -21,7 +21,8 @@ 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 \ -+ tablet-v2-protocol.h - util.o: util.c util.h - - # wayland-scanner is a tool which generates C headers and rigging for Wayland -@@ -45,6 +46,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 $@ -+tablet-v2-protocol.h: -+ $(WAYLAND_SCANNER) server-header \ -+ $(WAYLAND_PROTOCOLS)/unstable/tablet/tablet-unstable-v2.xml $@ - - config.h: - cp config.def.h $@ -diff --git a/config.def.h b/config.def.h -index 8a6eda0..1f20dfd 100644 ---- a/config.def.h -+++ b/config.def.h -@@ -4,6 +4,7 @@ - ((hex >> 8) & 0xFF) / 255.0f, \ - (hex & 0xFF) / 255.0f } - /* appearance */ -+static const int tabletmaptosurface = 0; /* map tablet input to surface(1) or monitor(0) */ - 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 */ -diff --git a/dwl.c b/dwl.c -index 8a9715d..6dc462a 100644 ---- a/dwl.c -+++ b/dwl.c -@@ -52,6 +52,10 @@ - #include - #include - #include -+#include -+#include -+#include -+#include - #include - #include - #include -@@ -163,6 +167,12 @@ typedef struct { - struct wl_listener destroy; - } KeyboardGroup; - -+typedef struct TouchGroup { -+ struct wl_list link; -+ struct wlr_touch *touch; -+ Monitor *m; -+} TouchGroup; -+ - typedef struct { - /* Must keep this field first */ - unsigned int type; /* LayerShell */ -@@ -270,7 +280,10 @@ static void createnotify(struct wl_listener *listener, void *data); - static void createpointer(struct wlr_pointer *pointer); - static void createpointerconstraint(struct wl_listener *listener, void *data); - static void createpopup(struct wl_listener *listener, void *data); -+static void createtablet(struct wlr_input_device *device); -+static void createtouch(struct wlr_touch *touch); - static void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint); -+static void createtouch(struct wlr_touch *touch); - static void cursorframe(struct wl_listener *listener, void *data); - static void cursorwarptohint(void); - static void destroydecoration(struct wl_listener *listener, void *data); -@@ -283,6 +296,9 @@ static void destroynotify(struct wl_listener *listener, void *data); - static void destroypointerconstraint(struct wl_listener *listener, void *data); - static void destroysessionlock(struct wl_listener *listener, void *data); - static void destroykeyboardgroup(struct wl_listener *listener, void *data); -+static void destroytablet(struct wl_listener *listener, void *data); -+static void destroytabletsurfacenotify(struct wl_listener *listener, void *data); -+static void destroytablettool(struct wl_listener *listener, void *data); - static Monitor *dirtomon(enum wlr_direction dir); - static void focusclient(Client *c, int lift); - static void focusmon(const Arg *arg); -@@ -335,11 +351,20 @@ static void spawn(const Arg *arg); - static void startdrag(struct wl_listener *listener, void *data); - static void tag(const Arg *arg); - static void tagmon(const Arg *arg); -+static void tablettoolmotion(struct wlr_tablet_v2_tablet_tool *tool, bool change_x, bool change_y, double x, double y, double dx, double dy); -+static void tablettoolproximity(struct wl_listener *listener, void *data); -+static void tablettoolaxis(struct wl_listener *listener, void *data); -+static void tablettoolbutton(struct wl_listener *listener, void *data); -+static void tablettooltip(struct wl_listener *listener, void *data); - static void tile(Monitor *m); - static void togglefloating(const Arg *arg); - static void togglefullscreen(const Arg *arg); - static void toggletag(const Arg *arg); - static void toggleview(const Arg *arg); -+static void touchdown(struct wl_listener *listener, void *data); -+static void touchup(struct wl_listener *listener, void *data); -+static void touchframe(struct wl_listener *listener, void *data); -+static void touchmotion(struct wl_listener *listener, void *data); - static void unlocksession(struct wl_listener *listener, void *data); - static void unmaplayersurfacenotify(struct wl_listener *listener, void *data); - static void unmapnotify(struct wl_listener *listener, void *data); -@@ -392,6 +417,13 @@ static struct wlr_pointer_constraint_v1 *active_constraint; - static struct wlr_cursor *cursor; - static struct wlr_xcursor_manager *cursor_mgr; - -+static struct wlr_tablet_manager_v2 *tablet_mgr; -+static struct wlr_tablet_v2_tablet *tablet = NULL; -+static struct wlr_tablet_v2_tablet_tool *tablet_tool = NULL; -+static struct wlr_tablet_v2_tablet_pad *tablet_pad = NULL; -+static struct wlr_surface *tablet_curr_surface = NULL; -+static struct wl_listener destroy_tablet_surface_listener = {.notify = destroytabletsurfacenotify}; -+ - static struct wlr_scene_rect *root_bg; - static struct wlr_session_lock_manager_v1 *session_lock_mgr; - static struct wlr_scene_rect *locked_bg; -@@ -407,6 +439,7 @@ static struct wlr_output_layout *output_layout; - static struct wlr_box sgeom; - static struct wl_list mons; - static Monitor *selmon; -+static struct wl_list touches; - - /* global event handlers */ - static struct wl_listener cursor_axis = {.notify = axisnotify}; -@@ -414,6 +447,12 @@ static struct wl_listener cursor_button = {.notify = buttonpress}; - static struct wl_listener cursor_frame = {.notify = cursorframe}; - static struct wl_listener cursor_motion = {.notify = motionrelative}; - static struct wl_listener cursor_motion_absolute = {.notify = motionabsolute}; -+static struct wl_listener tablet_device_destroy = {.notify = destroytablet}; -+static struct wl_listener tablet_tool_axis = {.notify = tablettoolaxis}; -+static struct wl_listener tablet_tool_button = {.notify = tablettoolbutton}; -+static struct wl_listener tablet_tool_destroy = {.notify = destroytablettool}; -+static struct wl_listener tablet_tool_proximity = {.notify = tablettoolproximity}; -+static struct wl_listener tablet_tool_tip = {.notify = tablettooltip}; - static struct wl_listener gpu_reset = {.notify = gpureset}; - static struct wl_listener layout_change = {.notify = updatemons}; - static struct wl_listener new_idle_inhibitor = {.notify = createidleinhibitor}; -@@ -436,6 +475,10 @@ static struct wl_listener request_set_sel = {.notify = setsel}; - static struct wl_listener request_set_cursor_shape = {.notify = setcursorshape}; - static struct wl_listener request_start_drag = {.notify = requeststartdrag}; - static struct wl_listener start_drag = {.notify = startdrag}; -+static struct wl_listener touch_down = {.notify = touchdown}; -+static struct wl_listener touch_frame = {.notify = touchframe}; -+static struct wl_listener touch_motion = {.notify = touchmotion}; -+static struct wl_listener touch_up = {.notify = touchup}; - static struct wl_listener new_session_lock = {.notify = locksession}; - - #ifdef XWAYLAND -@@ -783,6 +826,10 @@ cleanuplisteners(void) - wl_list_remove(&request_set_cursor_shape.link); - wl_list_remove(&request_start_drag.link); - wl_list_remove(&start_drag.link); -+ wl_list_remove(&touch_down.link); -+ wl_list_remove(&touch_frame.link); -+ wl_list_remove(&touch_motion.link); -+ wl_list_remove(&touch_up.link); - wl_list_remove(&new_session_lock.link); - #ifdef XWAYLAND - wl_list_remove(&new_xwayland_surface.link); -@@ -1201,6 +1248,38 @@ createpopup(struct wl_listener *listener, void *data) - LISTEN_STATIC(&popup->base->surface->events.commit, commitpopup); - } - -+void -+createtablet(struct wlr_input_device *device) -+{ -+ if (!tablet) { -+ struct libinput_device *device_handle = NULL; -+ if (!wlr_input_device_is_libinput(device) || -+ !(device_handle = wlr_libinput_get_device_handle(device))) -+ return; -+ -+ tablet = wlr_tablet_create(tablet_mgr, seat, device); -+ wl_signal_add(&tablet->wlr_device->events.destroy, &tablet_device_destroy); -+ if (libinput_device_config_send_events_get_modes(device_handle)) { -+ libinput_device_config_send_events_set_mode(device_handle, send_events_mode); -+ wlr_cursor_attach_input_device(cursor, device); -+ } -+ } else if (device == tablet->wlr_device) { -+ wlr_log(WLR_ERROR, "createtablet: duplicate device"); -+ } else { -+ wlr_log(WLR_ERROR, "createtablet: already have one tablet"); -+ } -+} -+ -+void -+createtouch(struct wlr_touch *wlr_touch) -+{ -+ TouchGroup *touch = ecalloc(1, sizeof(TouchGroup)); -+ -+ touch->touch = wlr_touch; -+ wl_list_insert(&touches, &touch->link); -+ wlr_cursor_attach_input_device(cursor, &wlr_touch->base); -+} -+ - void - cursorconstrain(struct wlr_pointer_constraint_v1 *constraint) - { -@@ -1385,6 +1464,29 @@ destroykeyboardgroup(struct wl_listener *listener, void *data) - free(group); - } - -+void -+destroytablet(struct wl_listener *listener, void *data) -+{ -+ wl_list_remove(&tablet_device_destroy.link); -+ wlr_cursor_detach_input_device(cursor, tablet->wlr_device); -+ tablet = NULL; -+} -+ -+void -+destroytabletsurfacenotify(struct wl_listener *listener, void *data) -+{ -+ if (tablet_curr_surface) -+ wl_list_remove(&destroy_tablet_surface_listener.link); -+ tablet_curr_surface = NULL; -+} -+ -+void -+destroytablettool(struct wl_listener *listener, void *data) -+{ -+ destroytabletsurfacenotify(NULL, NULL); -+ tablet_tool = NULL; -+} -+ - Monitor * - dirtomon(enum wlr_direction dir) - { -@@ -1592,6 +1694,15 @@ inputdevice(struct wl_listener *listener, void *data) - case WLR_INPUT_DEVICE_POINTER: - createpointer(wlr_pointer_from_input_device(device)); - break; -+ case WLR_INPUT_DEVICE_TABLET: -+ createtablet(device); -+ break; -+ case WLR_INPUT_DEVICE_TABLET_PAD: -+ tablet_pad = wlr_tablet_pad_create(tablet_mgr, seat, device); -+ break; -+ case WLR_INPUT_DEVICE_TOUCH: -+ createtouch(wlr_touch_from_input_device(device)); -+ break; - default: - /* TODO handle other input device types */ - break; -@@ -1604,6 +1715,8 @@ inputdevice(struct wl_listener *listener, void *data) - caps = WL_SEAT_CAPABILITY_POINTER; - if (!wl_list_empty(&kb_group->wlr_group->devices)) - caps |= WL_SEAT_CAPABILITY_KEYBOARD; -+ if (!wl_list_empty(&touches)) -+ caps |= WL_SEAT_CAPABILITY_TOUCH; - wlr_seat_set_capabilities(seat, caps); - } - -@@ -2588,6 +2701,8 @@ setup(void) - - relative_pointer_mgr = wlr_relative_pointer_manager_v1_create(dpy); - -+ tablet_mgr = wlr_tablet_v2_create(dpy); -+ - /* - * Creates a cursor, which is a wlroots utility for tracking the cursor - * image shown on screen. -@@ -2617,6 +2732,18 @@ setup(void) - wl_signal_add(&cursor->events.button, &cursor_button); - wl_signal_add(&cursor->events.axis, &cursor_axis); - wl_signal_add(&cursor->events.frame, &cursor_frame); -+ wl_signal_add(&cursor->events.tablet_tool_proximity, &tablet_tool_proximity); -+ wl_signal_add(&cursor->events.tablet_tool_axis, &tablet_tool_axis); -+ wl_signal_add(&cursor->events.tablet_tool_button, &tablet_tool_button); -+ wl_signal_add(&cursor->events.tablet_tool_tip, &tablet_tool_tip); -+ -+ -+ wl_list_init(&touches); -+ -+ wl_signal_add(&cursor->events.touch_down, &touch_down); -+ wl_signal_add(&cursor->events.touch_frame, &touch_frame); -+ wl_signal_add(&cursor->events.touch_motion, &touch_motion); -+ wl_signal_add(&cursor->events.touch_up, &touch_up); - - cursor_shape_mgr = wlr_cursor_shape_manager_v1_create(dpy, 1); - wl_signal_add(&cursor_shape_mgr->events.request_set_shape, &request_set_cursor_shape); -@@ -2712,6 +2839,163 @@ tagmon(const Arg *arg) - setmon(sel, dirtomon(arg->i), 0); - } - -+void -+tabletapplymap(double x, double y, struct wlr_input_device *dev) -+{ -+ Client *p; -+ struct wlr_box geom = {0}; -+ if (tabletmaptosurface && tablet_curr_surface) { -+ toplevel_from_wlr_surface(tablet_curr_surface, &p, NULL); -+ if (p) { -+ for (; client_get_parent(p); p = client_get_parent(p)); -+ geom.x = p->geom.x + p->bw; -+ geom.y = p->geom.y + p->bw; -+ geom.width = p->geom.width - 2 * p->bw; -+ geom.height = p->geom.height - 2 * p->bw; -+ } -+ } -+ wlr_cursor_map_input_to_region(cursor, dev, &geom); -+ wlr_cursor_map_input_to_output(cursor, dev, selmon->wlr_output); -+} -+ -+void -+tablettoolmotion(struct wlr_tablet_v2_tablet_tool *tool, bool change_x, bool change_y, -+ double x, double y, double dx, double dy) -+{ -+ struct wlr_surface *surface = NULL; -+ double sx, sy; -+ -+ if (!change_x && !change_y) -+ return; -+ -+ tabletapplymap(x, y, tablet->wlr_device); -+ -+ // TODO: apply constraints -+ switch (tablet_tool->wlr_tool->type) { -+ case WLR_TABLET_TOOL_TYPE_LENS: -+ case WLR_TABLET_TOOL_TYPE_MOUSE: -+ wlr_cursor_move(cursor, tablet->wlr_device, dx, dy); -+ break; -+ default: -+ wlr_cursor_warp_absolute(cursor, tablet->wlr_device, change_x ? x : NAN, change_y ? y : NAN); -+ break; -+ } -+ -+ motionnotify(0, NULL, 0, 0, 0, 0); -+ -+ xytonode(cursor->x, cursor->y, &surface, NULL, NULL, &sx, &sy); -+ if (surface && !wlr_surface_accepts_tablet_v2(surface, tablet)) -+ surface = NULL; -+ -+ if (surface != tablet_curr_surface) { -+ if (tablet_curr_surface) { -+ // TODO: wait until all buttons released before leaving -+ if (tablet_tool) -+ wlr_tablet_v2_tablet_tool_notify_proximity_out(tablet_tool); -+ if (tablet_pad) -+ wlr_tablet_v2_tablet_pad_notify_leave(tablet_pad, tablet_curr_surface); -+ wl_list_remove(&destroy_tablet_surface_listener.link); -+ } -+ if (surface) { -+ if (tablet_pad) -+ wlr_tablet_v2_tablet_pad_notify_enter(tablet_pad, tablet, surface); -+ if (tablet_tool) -+ wlr_tablet_v2_tablet_tool_notify_proximity_in(tablet_tool, tablet, surface); -+ wl_signal_add(&surface->events.destroy, &destroy_tablet_surface_listener); -+ } -+ tablet_curr_surface = surface; -+ } -+ -+ if (surface) -+ wlr_tablet_v2_tablet_tool_notify_motion(tablet_tool, sx, sy); -+} -+ -+void -+tablettoolproximity(struct wl_listener *listener, void *data) -+{ -+ struct wlr_tablet_tool_proximity_event *event = data; -+ struct wlr_tablet_tool *tool = event->tool; -+ -+ if (!tablet_tool) { -+ tablet_tool = wlr_tablet_tool_create(tablet_mgr, seat, tool); -+ wl_signal_add(&tablet_tool->wlr_tool->events.destroy, &tablet_tool_destroy); -+ wl_signal_add(&tablet_tool->events.set_cursor, &request_cursor); -+ } -+ -+ switch (event->state) { -+ case WLR_TABLET_TOOL_PROXIMITY_OUT: -+ wlr_tablet_v2_tablet_tool_notify_proximity_out(tablet_tool); -+ destroytabletsurfacenotify(NULL, NULL); -+ break; -+ case WLR_TABLET_TOOL_PROXIMITY_IN: -+ tablettoolmotion(tablet_tool, true, true, event->x, event->y, 0, 0); -+ break; -+ } -+} -+ -+double tilt_x = 0; -+double tilt_y = 0; -+ -+void -+tablettoolaxis(struct wl_listener *listener, void *data) -+{ -+ struct wlr_tablet_tool_axis_event *event = data; -+ -+ tablettoolmotion(tablet_tool, -+ event->updated_axes & WLR_TABLET_TOOL_AXIS_X, -+ event->updated_axes & WLR_TABLET_TOOL_AXIS_Y, -+ event->x, event->y, event->dx, event->dy); -+ -+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_PRESSURE) -+ wlr_tablet_v2_tablet_tool_notify_pressure(tablet_tool, event->pressure); -+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_DISTANCE) -+ wlr_tablet_v2_tablet_tool_notify_distance(tablet_tool, event->distance); -+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_X) -+ tilt_x = event->tilt_x; -+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_Y) -+ tilt_y = event->tilt_y; -+ if (event->updated_axes & (WLR_TABLET_TOOL_AXIS_TILT_X | WLR_TABLET_TOOL_AXIS_TILT_Y)) -+ wlr_tablet_v2_tablet_tool_notify_tilt(tablet_tool, tilt_x, tilt_y); -+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_ROTATION) -+ wlr_tablet_v2_tablet_tool_notify_rotation(tablet_tool, event->rotation); -+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_SLIDER) -+ wlr_tablet_v2_tablet_tool_notify_slider(tablet_tool, event->slider); -+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_WHEEL) -+ wlr_tablet_v2_tablet_tool_notify_wheel(tablet_tool, event->wheel_delta, 0); -+} -+ -+void -+tablettoolbutton(struct wl_listener *listener, void *data) -+{ -+ struct wlr_tablet_tool_button_event *event = data; -+ wlr_tablet_v2_tablet_tool_notify_button(tablet_tool, event->button, -+ (enum zwp_tablet_pad_v2_button_state)event->state); -+} -+ -+void -+tablettooltip(struct wl_listener *listener, void *data) -+{ -+ struct wlr_tablet_tool_tip_event *event = data; -+ -+ if (!tablet_curr_surface) { -+ struct wlr_pointer_button_event fakeptrbtnevent = { -+ .button = BTN_LEFT, -+ .state = event->state == WLR_TABLET_TOOL_TIP_UP ? -+ WL_POINTER_BUTTON_STATE_RELEASED : WL_POINTER_BUTTON_STATE_PRESSED, -+ .time_msec = event->time_msec, -+ }; -+ buttonpress(NULL, (void *)&fakeptrbtnevent); -+ } -+ -+ if (event->state == WLR_TABLET_TOOL_TIP_UP) { -+ wlr_tablet_v2_tablet_tool_notify_up(tablet_tool); -+ return; -+ } -+ -+ wlr_tablet_v2_tablet_tool_notify_down(tablet_tool); -+ wlr_tablet_tool_v2_start_implicit_grab(tablet_tool); -+} -+ - void - tile(Monitor *m) - { -@@ -2790,6 +3074,120 @@ toggleview(const Arg *arg) - printstatus(); - } - -+void -+touchdown(struct wl_listener *listener, void *data) -+{ -+ struct wlr_touch_down_event *event = data; -+ double lx, ly; -+ double sx, sy; -+ struct wlr_surface *surface; -+ Client *c = NULL; -+ uint32_t serial = 0; -+ Monitor *m; -+ -+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); -+ -+ // Map the input to the appropriate output, to ensure that rotation is -+ // handled. -+ wl_list_for_each(m, &mons, link) { -+ if (m == NULL || m->wlr_output == NULL) { -+ continue; -+ } -+ if (event->touch->output_name != NULL && 0 != strcmp(event->touch->output_name, m->wlr_output->name)) { -+ continue; -+ } -+ -+ wlr_cursor_map_input_to_output(cursor, &event->touch->base, m->wlr_output); -+ } -+ -+ wlr_cursor_absolute_to_layout_coords(cursor, &event->touch->base, event->x, event->y, &lx, &ly); -+ -+ /* Find the client under the pointer and send the event along. */ -+ xytonode(lx, ly, &surface, &c, NULL, &sx, &sy); -+ if (sloppyfocus) -+ focusclient(c, 0); -+ -+ if (surface != NULL) { -+ serial = wlr_seat_touch_notify_down(seat, surface, event->time_msec, event->touch_id, sx, sy); -+ } -+ -+ if (serial && wlr_seat_touch_num_points(seat) == 1) { -+ /* Emulate a mouse click if the touch event wasn't handled */ -+ struct wlr_pointer_button_event *button_event = data; -+ struct wlr_pointer_motion_absolute_event *motion_event = data; -+ double dx, dy; -+ -+ wlr_cursor_absolute_to_layout_coords(cursor, &motion_event->pointer->base, motion_event->x, motion_event->y, &lx, &ly); -+ wlr_cursor_warp_closest(cursor, &motion_event->pointer->base, lx, ly); -+ dx = lx - cursor->x; -+ dy = ly - cursor->y; -+ motionnotify(motion_event->time_msec, &motion_event->pointer->base, dx, dy, dx, dy); -+ -+ button_event->button = BTN_LEFT; -+ button_event->state = WL_POINTER_BUTTON_STATE_PRESSED; -+ buttonpress(listener, button_event); -+ } -+} -+ -+void -+touchup(struct wl_listener *listener, void *data) -+{ -+ struct wlr_touch_up_event *event = data; -+ -+ if (!wlr_seat_touch_get_point(seat, event->touch_id)) { -+ return; -+ } -+ -+ if (wlr_seat_touch_num_points(seat) == 1) { -+ struct wlr_pointer_button_event *button_event = data; -+ -+ button_event->button = BTN_LEFT; -+ button_event->state = WL_POINTER_BUTTON_STATE_RELEASED; -+ buttonpress(listener, button_event); -+ } -+ -+ wlr_seat_touch_notify_up(seat, event->time_msec, event->touch_id); -+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); -+} -+ -+void -+touchframe(struct wl_listener *listener, void *data) -+{ -+ wlr_seat_touch_notify_frame(seat); -+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); -+} -+ -+void -+touchmotion(struct wl_listener *listener, void *data) -+{ -+ struct wlr_touch_motion_event *event = data; -+ double lx, ly; -+ double sx, sy; -+ struct wlr_surface *surface; -+ Client *c = NULL; -+ -+ if (!wlr_seat_touch_get_point(seat, event->touch_id)) { -+ return; -+ } -+ -+ wlr_cursor_absolute_to_layout_coords(cursor, &event->touch->base, event->x, event->y, &lx, &ly); -+ xytonode(lx, ly, &surface, &c, NULL, &sx, &sy); -+ -+ if (c != NULL && surface != NULL) { -+ if (sloppyfocus) -+ focusclient(c, 0); -+ wlr_seat_touch_point_focus(seat, surface, event->time_msec, event->touch_id, sx, sy); -+ } else { -+ if (sloppyfocus) -+ focusclient(NULL, 0); -+ wlr_seat_touch_point_clear_focus(seat, event->time_msec, event->touch_id); -+ } -+ wlr_seat_touch_notify_motion(seat, event->time_msec, event->touch_id, sx, sy); -+ -+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); -+} -+ -+ - void - unlocksession(struct wl_listener *listener, void *data) - { --- -2.52.0 - From 507f76f981aa9dacd35e817c9f7050e200e20f3d Mon Sep 17 00:00:00 2001 From: nate zhou Date: Sun, 8 Mar 2026 11:48:37 +0800 Subject: [PATCH 02/36] Add: bar-modes patch Add modes_labels indicator to bar, which behaves like river-classic's dam bar. This patch has to be applied after the bar and modes patch. --- patches/bar-modes/README.md | 31 +++++++ patches/bar-modes/bar-modes.patch | 131 ++++++++++++++++++++++++++++++ patches/bar-modes/example.png | Bin 0 -> 6555 bytes 3 files changed, 162 insertions(+) create mode 100644 patches/bar-modes/README.md create mode 100644 patches/bar-modes/bar-modes.patch create mode 100644 patches/bar-modes/example.png diff --git a/patches/bar-modes/README.md b/patches/bar-modes/README.md new file mode 100644 index 0000000..e7dc12d --- /dev/null +++ b/patches/bar-modes/README.md @@ -0,0 +1,31 @@ +![dwl bar-modes patch example](./example.png) + +### Description + +Add a mode indicator to bar that tells which mode you are in, just like +river-classic's [dam](https://codeberg.org/sewn/dam) bar. + +The string from `modes_labels` defined in `config.h` is used, while normal mode +is ignored. + +Another usage is to serve as a hint for each modes keybindings: + +```c +enum { + BROWSER, +}; +const char *modes_lablels[] = { + "[f]irefox [b]rave [c]hromium [q]utebrowser", +}; +``` + +### Dependencies +this patch depends on: +- [bar](https://codeberg.org/dwl/dwl-patches/src/branch/main/patches/bar/) patch +- [modes](https://codeberg.org/dwl/dwl-patches/src/branch/main/patches/modes/) patch + +### Download +- [v0.8](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/bar-modes/bar-modes.patch) + +### Authors +- [unixchad](https://codeberg.org/unixchad/) diff --git a/patches/bar-modes/bar-modes.patch b/patches/bar-modes/bar-modes.patch new file mode 100644 index 0000000..8e239a2 --- /dev/null +++ b/patches/bar-modes/bar-modes.patch @@ -0,0 +1,131 @@ +From 4f2c8a99720d90a551bf38f2c8d25ad239346eef Mon Sep 17 00:00:00 2001 +From: nate zhou +Date: Mon, 2 Mar 2026 21:54:03 +0800 +Subject: [PATCH] Patch: bar-modes for 0.8 + +Add modes_labels indicator to bar, which behaves like river-classic's +dam bar. This patch has to be applied after the bar and modes patch. +--- + dwl.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++---------- + 1 file changed, 59 insertions(+), 11 deletions(-) + +diff --git a/dwl.c b/dwl.c +index ae290ad..df525d3 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -1585,19 +1585,26 @@ drawbar(Monitor *m) + uint32_t i, occ = 0, urg = 0; + Client *c; + Buffer *buf; ++ char mode_text[256] = ""; ++ int mode_width = 0; + + if (!m->scene_buffer->node.enabled) + return; + if (!(buf = bufmon(m))) + return; + +- /* draw status first so it can be overdrawn by tags later */ +- if (m == selmon) { /* status is only drawn on selected monitor */ +- drwl_setscheme(m->drw, colors[SchemeNorm]); +- tw = TEXTW(m, stext) - m->lrpad + 2; /* 2px right padding */ +- drwl_text(m->drw, m->b.width - tw, 0, tw, m->b.height, 0, stext, 0); ++ /* get current mode text if in a mode (not normal) */ ++ if (active_mode_index >= 0 && active_mode_index < LENGTH(modes_labels) && ++ modes_labels[active_mode_index]) { ++ strncpy(mode_text, modes_labels[active_mode_index], sizeof(mode_text) - 1); ++ mode_text[sizeof(mode_text) - 1] = '\0'; ++ mode_width = TEXTW(m, mode_text); + } + ++ /* calculate status text width */ ++ drwl_setscheme(m->drw, colors[SchemeNorm]); ++ tw = TEXTW(m, stext) - m->lrpad + 2; ++ + wl_list_for_each(c, &clients, link) { + if (c->mon != m) + continue; +@@ -1607,6 +1614,8 @@ drawbar(Monitor *m) + } + x = 0; + c = focustop(m); ++ ++ /* draw tags (always shown) */ + for (i = 0; i < LENGTH(tags); i++) { + w = TEXTW(m, tags[i]); + drwl_setscheme(m->drw, colors[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); +@@ -1617,19 +1626,57 @@ drawbar(Monitor *m) + urg & 1 << i); + x += w; + } ++ ++ /* draw mode indicator after tags if in a mode */ ++ if (mode_text[0]) { ++ drwl_setscheme(m->drw, colors[SchemeSel]); ++ drwl_text(m->drw, x, 0, mode_width, m->b.height, m->lrpad / 2, mode_text, 0); ++ x += mode_width; ++ } ++ ++ /* draw layout symbol after mode */ + w = TEXTW(m, m->ltsymbol); + drwl_setscheme(m->drw, colors[SchemeNorm]); +- x = drwl_text(m->drw, x, 0, w, m->b.height, m->lrpad / 2, m->ltsymbol, 0); ++ drwl_text(m->drw, x, 0, w, m->b.height, m->lrpad / 2, m->ltsymbol, 0); ++ x += w; ++ ++ int remaining = m->b.width - x; + +- if ((w = m->b.width - tw - x) > m->b.height) { +- if (c) { ++ if (mode_text[0]) { ++ int title_width = remaining; ++ ++ if (title_width > m->b.height && c) { + drwl_setscheme(m->drw, colors[m == selmon ? SchemeSel : SchemeNorm]); +- drwl_text(m->drw, x, 0, w, m->b.height, m->lrpad / 2, client_get_title(c), 0); ++ drwl_text(m->drw, x, 0, title_width, m->b.height, m->lrpad / 2, ++ client_get_title(c), 0); + if (c && c->isfloating) + drwl_rect(m->drw, x + boxs, boxs, boxw, boxw, 0, 0); +- } else { ++ } else if (title_width > 0) { ++ drwl_setscheme(m->drw, colors[SchemeNorm]); ++ drwl_rect(m->drw, x, 0, title_width, m->b.height, 1, 1); ++ } ++ /* no status text when in a mode - completely omitted */ ++ } else { ++ /* not in a mode - normal behavior with status */ ++ if (remaining >= tw) { ++ drwl_setscheme(m->drw, colors[SchemeNorm]); ++ drwl_text(m->drw, m->b.width - tw, 0, tw, m->b.height, 0, stext, 0); ++ ++ int title_space = remaining - tw; ++ if (title_space > m->b.height && c) { ++ drwl_setscheme(m->drw, colors[m == selmon ? SchemeSel : SchemeNorm]); ++ drwl_text(m->drw, x, 0, title_space, m->b.height, m->lrpad / 2, ++ client_get_title(c), 0); ++ if (c && c->isfloating) ++ drwl_rect(m->drw, x + boxs, boxs, boxw, boxw, 0, 0); ++ } else if (title_space > 0) { ++ drwl_setscheme(m->drw, colors[SchemeNorm]); ++ drwl_rect(m->drw, x, 0, title_space, m->b.height, 1, 1); ++ } ++ } else if (remaining > 0) { + drwl_setscheme(m->drw, colors[SchemeNorm]); +- drwl_rect(m->drw, x, 0, w, m->b.height, 1, 1); ++ drwl_text(m->drw, m->b.width - remaining, 0, remaining, m->b.height, 0, ++ stext, 0); + } + } + +@@ -3437,6 +3484,7 @@ entermode(const Arg *arg) + { + active_mode_index = arg->i; + printstatus(); ++ drawbars(); + } + + #ifdef XWAYLAND +-- +2.53.0 + diff --git a/patches/bar-modes/example.png b/patches/bar-modes/example.png new file mode 100644 index 0000000000000000000000000000000000000000..185b4fe161d2788e5271392774fd4381deef1e7d GIT binary patch literal 6555 zcmZ{J1yB{?_x92uoq`gggtWj#I+VCH(xF^Y8ZO-_Eh&w(fV6;sbf==!rMvSIm&Qwe z>u-Ml|IGKzd}n8Nch2s+JA2;qKIb{lMrf)l5);xB0sug)1eMbQ04zW7{60P=xbGCj zN(T?j@1a_10N~9A03SjC;1YCw*aiSMegHte1pu+n06^=Q+4M>Ryny>oMNtm8`?q~* zElLDEc&P2rCJ?-*e*$bVmQ zuFvbMVkXA=uf1RI?3;P^ zzxQBrAM|wm*X|P}L53=(F`Vo?8Qv4+DQhm!wo(UuY4{^e>?G=#mwJ=L$MQ~NzSXmS zHMhkJ@qI+xtV+Xkej|r71NN!TR3Qj4s^531+n_+ewfZf3V5QyG*cxUcB9dTqck!j= zl3@yZu1|Pj5t*FXVDpJE`}?iVwUwd69w4iENYyT??W@$J5M5D+Ou5uVE6;_jo0j#wUD#98as7EOYYoIOiQ<+ z2R?l6_|%tL-e*zL$M$|wFX1NnxMjwN^;2b?Y_4W3qvY=m=aoZVY3aM=&R_r_CH>Uu zbI~L`6lN8s{S;Bgcv~>T7EsrEH>5Ob;LKEiW@u5!%|?^1<3( z>*<9p2F1t6S5;LJ;lA&Ur2ZJ1DIJh4#}ZNWwWx@lmGu$L_UdZ2-Hd>b&sA=2E)t1! z*&3Rdn0OTDa=fl&YrE?)Ru7-kd-W<%=1WDz!S7_Q)|M7mSJ!RV%Uav1y z6J`3^97%0$ZKI>3iR>EZ=jSXbZ12X>9A8z>t!&rIe(r-p#r~|TvY9BY&&yg}T^$+G{Oce>!GM9uHn@K8@7>iLpI$yK$_yFyQ@ps zVez$rf#=RBD+7YR*P-vEGuc>J+m~+X=)bB*eu&r%uqylawAQ0*T1PI?^ zV!M;&Tu+}yL`AI~c>*6!wuZ}!iR~NV~hcC&tI6?yg_)w`Ze*%1b85$2>Sevh-qvKp1+;JYJqvN{T69I-kN6d||m97`<-%o-CU#Mkw zZ%vk(8suZ+_g48`e>y}%Fq3Z)rZ9+qWBfap7&CCWm9~sR=U>8gcAS=thxCzzz4jvS zSaIqq!nstMy=(7pJ}5+f8A8%ou*>IEyDmMXD{imQrzk#m8uwx=303OgusN zdqxgJgk7oSz}`-au}Pfvxs2<1qNvJKO1tMdGrM3gA%f9%Lb)*$PD1Pj*)vJsSz^E1 zepS}Q0Ipj$A0UR|(8aOYg;*09BnWxATyP*jbh!^*`l;6qGAZysGPiG?tdv-ET*>cDWvDpK)dE^;R$`t!a5rSc;qH-^Y zStyh|T)BWEqGx^GOk_J`#Tg+L2>>)SG$8Wz_xJw_+rt2|va*7MgN22K^Qa(@+RaT1 zvr#)Z9MZ6dAiYLni;+sEbKO6Y^LCF2B`m<^2Y=fmH^~Sj2o}wLe zpKaDC9-JNp?*1&;3}WW6Unv@*A@LfHvQOO*|7=+Kl~;%bnd29S3-C_}-hvr!r062L zuPeJD=zw=If$_>WaM>LVbD*U!MVg#z*M*gP&FEY$ZzK}n79MsOUM3osV(Ut?Yv`9n z&@x%L)+`OAzdhr6dx;G!M}3?|rZg>aF*X^Ag{c#dAV$;&)Z^1tRMggrHkw_9ez@e& zo}FF{Ij5iRD;krUv(oA%LnXNy{oRCqTyS67eH3>s=g9c86(2MBa=s+^wR)eRp}qpk z7}N`D|G-Mdnu|K$MgpgWM)w8_)!~RlYNaw-S;E5g_dYj&@z3%xh1Ly19*oijojOBx;-VyB)Kvbx+O~sXKHLX;VD$hzy)N4i!`y*RNWmN zKZ`iM)6^WMQ z$LOz?YV?_$%N)kgm;AKBwRA0&moE@HNp;fE%K29DkmV1hH&=EQnEqc0# zPb|&NF0`GjlGc_B{atO|z$u#aW7^wfzs;N+8%Mhy9Aix-{hAcK8QRp=A?2 zU8NMEr%sWH=TF#g`r$waHU2DZE1ahHWHeuutxVgwqPK>HDB@DuEb{}qngz`k+qaKV z1kt<|K2kj!P$@9i%GJ1kOh9Us4EuRZvxjHN-G_tzwH$MC#nN3M z*f2l(XR@zGLH;t}V}cJD$iDf7M1|g^AlMRySxye~YXXXrlXXENWN**Anl1c!d3l*Z z+2;sK6Wz9_Qf9xq(AtNH}gnicfVpLLj z<=F-Y2BL?zn8aK^_M5S>u~}GH3}s7-OGz=BJKC9=$`)vijgD%%XGwXV7FSgvhKB6# zZf{glxEtN~jeNwrrdNlu#Gg<+<>j^Si>5O*H3c6&IXOv&9iai$#dm|p0D#I%!8dAo zCD}(zF}g_vb)e(1ppp3O3jh6wA;z?)RrSGh8(kTMzSqzV3#V+W;;Oco*zOfqNQ9P4 zZWOj)uS=;dP@!nDzuIaZ?*!4A5Xi#({}%elTDE#%DeOA_t&GgYy&c8|cR z%;_(mS{deRCm?u@FP&Q*|AR8{2Onh}xEe^0QFQ{T1p}jVC;yrGBqzQv-*7x%k@#2v zK*#nwXS4)KPMI<@Fk^J}w`*ebXt>Q@gb@>P^qiL+f&~Ve`7OX-JBrK~gttd5@`t`K z#X}w-pOJV6aPZ}iELmGwHF%&**b=?=rk^F{sb`5Lr>EPPnv&Ddq$DIbgHG&LkHW%2 znD3GNn>P%pe+slp006`VVq#*>WaWGWa(8!wdU|Z^WFr+8Nx~%I8JC&)keuAoFsnH!2ad_Qf@H0+Qh-_Kxaq~JO-Wio7wj&zsuo>BJ z7DiQR;i=o%l1s4G1 zh~fRIvSem?wgzLP%-Q&)$TTFK{xjs0OzJ@!`U-y^ylHX1Te2MnfrU;Ut7Gm!8BK)2 z*>14VtB|imF-ei*b%FOcW(fU!7%TkEe}r|Ra9AgT)_qJR4zP)2$&{vwn+p6JY7)*M zQ|uUk(O0!vdJHP@cJ1okk*k+pbUO`W)6;){G6nRk9U0fz$+pYK$}qpJGT^4!1xccr zfY)F;1cc^e5=(6kDjpslaq;GbW{;>M)MD#ZSLi+b)uH-j=SCrV_~q#ih_v|LW^_cj zfim0M+hheJJFdWo_;~X4hOzGM;NEu;!wvEskM(bCa z$m`Wb|1%h&Ke#x9qKUF9~EXgof`8(%jeJ zXYS`^8Vdcnl3W`445Zm3qdXgFV1JSJO&tp^c_jBTj}iD1Gw}%hjYIy$jeud+Pludv z=3kRN&gl#5_`_c$ql7EP>B8T7jrc$)2t$u!-41auB|?Ik@jJ_(G{V2gDb>AL$__7s z6&St5RK9#eXM82{f`jUuq&ob;dVHzFgE9s{h9ZMh%!tF?zsM{q_agk*`!6|zCD?VU zsj9A@p#RN;1pPfhuy9G$4|i{_E`o2>y}U$&JMjq#m6eoa^@J%C z+ZEuTAd#7sWi3QsZ4H3kEM_3Hgv9IDZJHc*R#q~0lK4R!RD_pt^x@sIWFeHhuijS~ z0DuIeX^1TI^%=`L3gzJ7Kw6okFX9Gwb@8K^na(S$&21_$3H`EaDoy~%*eW(~AbISQ zB^wMA^woXbidx=Q%cQ^) z->gjj(rgbhxLVfb&YoX+6jSKjys`Wqj%&74<*!~TjDWb-JE1j9l4pj-u5f40;I1W> zreS3;KOTmLKXM#YZT7aRJT2(2g{u0A#reFuQjfJD@6ImhDb5@PT;E4+TU;zVil}ry zCmHK@PpBC1D^8fhr5~A7-jX(IG3Pqn_iDJ*&4wtH(U@89uif*;>sDRriIoc@C62fh zdsqIZx^3f!GSI<-BE!6`WJeUMibA1Kd~du^ysYYJxfB`}P&gQ0vaZm`Q~izwbSznB zSlJ79q!9od6<1iD(astg!wn505{zJzy0W{=)nq9kD7ZoViy1#MBBH9cmWP}BTX{J| zK;ZcJSO5Z%bXpCe;FOnQ#y4tqFE1!CfA#7W9L^IDB_bgKi_BuZMj?;NVyHs?LW9%$ z{sdNUZ|_QzR%ds25&{Ax6_wqc_^UBKmeaE{0&L97qg`;lG&D4z+OBxn*^4SGhY^Tp zVq#4{eo!66C#QmHl7i`}?u6v4Mes)YQ}<_;K7rn;<{C!q_!1vmKiyaweAk zn7LhQN__qndz*_9`ir)=vRw+A^TK_W-vd2ab4Ar=o1WL8d)_hE31@X%k#otArZ3Yu z#I{tybE9_i*ILO4tU(*r_NZ7rRzB>%CPu{4x^ts7wfi@{b z%vj{AOF#Qs_pL5@5-}$~3Q1qtHkni2O@cq?G8E!045r#QgCr7&=*Bjds)PZyhPQ zmFAs87ng?cEo(f|&g6M0oQ1aFCTim%IK9<0A?15|@KyD@G1K*w&F3^qNelH;0O)yh zOCUSnXfa3+ssezFs%o^Sx4nHui&K#%$9x^!*vxD_>8W;JULG@k9>NOjw!r1AT)~|7 zyUuNQ64be7z@nB8u?5wM>gr<b*-GKeQ2j)Mxmf@xwl2BIRi#wR(qj&u&&_eTYkF>@qWV^-rT@=;aEbb7xVvUI zx09OB>KIQgVz^5tX5X&U_@38Wwu`HMcd2)(dR|*$U!u~GX7}E%YE)Xw9i7$sW~uO| zW!%9(#F7lgY_(%yQ)0{6Q}@$8D~jOyV{ygAbiYVKV#3{V2{#=G3v&TC_7Nyw2RsiR zw?SOCo87W-dE-+hsVjTBp55o7C}rw=`@($1Ln@dg&%)4RMdm2pj)uci&3urb$`SLB z&MBQ2PJ+S)2=OHvJ$cB4GInS(=+bMI5a4eErCwp zSCxOwBQk!PuLEimG*=<0rqlL3hS+uUAG0UF=9FLgQaAQn2Q94j3_ku=xxU1p4#Q-u zEH7kT+v-9vLpB53hud6tvLR!JRT_Klk1~XJzy8a}=mV-&gF&5vB{q4}BrO z|3X^f@0Rt~Cyr#t1hMbyBtNM+ie!f7TKk}&pelRaEFl$u9i&cV_ST8LPnzx#nKQcO zaans9>AkYZPn(&^>9zuC)h?ah2vcp#U8~(AjY<45{a4ZdqeP~cPvY@y9D5CBl{300 z68$*SI>^6)CP!Z?-!3>!35z@&+8J|TGu+tP0+}Egu)MNT1+q&7@=wkMSMUNyl;&$aSca;r2Uy$XjDOdO1d`~&Q{t_HOj|HLM#5<*SrFK>H~tY z*||_?g1;^RC3$tZ JiWhH#{twG5q=Wzf literal 0 HcmV?d00001 From 898bc7a946c7be14034a665bd7a7b042632465ea Mon Sep 17 00:00:00 2001 From: nate zhou Date: Sun, 8 Mar 2026 11:51:28 +0800 Subject: [PATCH 03/36] Add: hide-cursor-when-typing Hide the cursor when start typing, and restore it when start moving cursor again, just like xbanish. --- patches/hide-cursor-when-typing/README.md | 15 ++ .../hide-cursor-when-typing.patch | 170 ++++++++++++++++++ 2 files changed, 185 insertions(+) create mode 100644 patches/hide-cursor-when-typing/README.md create mode 100644 patches/hide-cursor-when-typing/hide-cursor-when-typing.patch diff --git a/patches/hide-cursor-when-typing/README.md b/patches/hide-cursor-when-typing/README.md new file mode 100644 index 0000000..95b4961 --- /dev/null +++ b/patches/hide-cursor-when-typing/README.md @@ -0,0 +1,15 @@ +### Description + +Hide the mouse cursor when you start typing, and restore it again when the +mouse cursor moves or a mouse button is pressed, just like +[xbanish](https://github.com/jcs/xbanish). + +### credits +Some code is taken from the +[unclutter](https://codeberg.org/dwl/dwl-patches/src/branch/main/patches/unclutter) patch. + +### Download +[v0.8](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/hide-cursor-when-typing/hide-cursor-when-typing.patch) + +### Authors +- [unixchad](https://codeberg.org/unixchad/) diff --git a/patches/hide-cursor-when-typing/hide-cursor-when-typing.patch b/patches/hide-cursor-when-typing/hide-cursor-when-typing.patch new file mode 100644 index 0000000..fe71534 --- /dev/null +++ b/patches/hide-cursor-when-typing/hide-cursor-when-typing.patch @@ -0,0 +1,170 @@ +From c72de058e6635c5bb3d973995ccbdb6e028f354d Mon Sep 17 00:00:00 2001 +From: nate zhou +Date: Tue, 3 Mar 2026 16:48:46 +0800 +Subject: [PATCH] Patch: hide-cursor-when-typing-0.8.patch + +Hide the cursor when start typing, and restore it when start moving +cursor again, just like xbanish. +--- + config.def.h | 2 ++ + dwl.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++------ + 2 files changed, 63 insertions(+), 7 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 8a6eda0..6205c0a 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -102,6 +102,8 @@ 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; + ++static const int hide_cursor_when_typing = 1; ++ + /* If you want to use the windows key for MODKEY, use WLR_MODIFIER_LOGO */ + #define MODKEY WLR_MODIFIER_ALT + +diff --git a/dwl.c b/dwl.c +index 44f3ad9..9a076b6 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -289,6 +289,8 @@ 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 handlecursoractivity(void); ++static int hidecursor(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); +@@ -389,6 +391,14 @@ static struct wlr_pointer_constraint_v1 *active_constraint; + + static struct wlr_cursor *cursor; + static struct wlr_xcursor_manager *cursor_mgr; ++static struct wl_event_source *hide_source; ++static bool cursor_hidden = false; ++static struct { ++ enum wp_cursor_shape_device_v1_shape shape; ++ struct wlr_surface *surface; ++ int hotspot_x; ++ int hotspot_y; ++} last_cursor; + + static struct wlr_scene_rect *root_bg; + static struct wlr_session_lock_manager_v1 *session_lock_mgr; +@@ -610,6 +620,7 @@ axisnotify(struct wl_listener *listener, void *data) + * for example when you move the scroll wheel. */ + struct wlr_pointer_axis_event *event = data; + wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); ++ handlecursoractivity(); + /* TODO: allow usage of scroll wheel for mousebindings, it can be implemented + * by checking the event's orientation and the delta of the event */ + /* Notify the client with pointer focus of the axis event. */ +@@ -628,6 +639,7 @@ buttonpress(struct wl_listener *listener, void *data) + const Button *b; + + wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); ++ handlecursoractivity(); + + switch (event->state) { + case WL_POINTER_BUTTON_STATE_PRESSED: +@@ -1566,6 +1578,30 @@ handlesig(int signo) + quit(NULL); + } + ++void ++handlecursoractivity(void) ++{ ++ if (!cursor_hidden) ++ return; ++ ++ cursor_hidden = false; ++ ++ if (last_cursor.shape) ++ wlr_cursor_set_xcursor(cursor, cursor_mgr, ++ wlr_cursor_shape_v1_name(last_cursor.shape)); ++ else ++ wlr_cursor_set_surface(cursor, last_cursor.surface, ++ last_cursor.hotspot_x, last_cursor.hotspot_y); ++} ++ ++int ++hidecursor(void *data) ++{ ++ wlr_cursor_unset_image(cursor); ++ cursor_hidden = true; ++ return 1; ++} ++ + void + incnmaster(const Arg *arg) + { +@@ -1645,6 +1681,11 @@ keypress(struct wl_listener *listener, void *data) + + wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); + ++ /* hide cursor when typing starts */ ++ if (hide_cursor_when_typing && !cursor_hidden && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { ++ hidecursor(NULL); ++ } ++ + /* On _press_ if there is no active screen locker, + * attempt to process a compositor keybinding. */ + if (!locked && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { +@@ -1907,6 +1948,7 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d + + wlr_cursor_move(cursor, device, dx, dy); + wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); ++ handlecursoractivity(); + + /* Update selmon (even while dragging a window) */ + if (sloppyfocus) +@@ -1931,7 +1973,7 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d + /* If there's no client surface under the cursor, set the cursor image to a + * default. This is what makes the cursor image appear when you move it + * off of a client or over its border. */ +- if (!surface && !seat->drag) ++ if (!surface && !seat->drag && !cursor_hidden) + wlr_cursor_set_xcursor(cursor, cursor_mgr, "default"); + + pointerfocus(c, surface, sx, sy, time); +@@ -2311,9 +2353,16 @@ setcursor(struct wl_listener *listener, void *data) + * use the provided surface as the cursor image. It will set the + * hardware cursor on the output that it's currently on and continue to + * do so as the cursor moves between outputs. */ +- if (event->seat_client == seat->pointer_state.focused_client) +- wlr_cursor_set_surface(cursor, event->surface, +- event->hotspot_x, event->hotspot_y); ++ if (event->seat_client == seat->pointer_state.focused_client) { ++ last_cursor.shape = 0; ++ last_cursor.surface = event->surface; ++ last_cursor.hotspot_x = event->hotspot_x; ++ last_cursor.hotspot_y = event->hotspot_y; ++ ++ if (!cursor_hidden) ++ wlr_cursor_set_surface(cursor, event->surface, ++ event->hotspot_x, event->hotspot_y); ++ } + } + + void +@@ -2325,9 +2374,14 @@ setcursorshape(struct wl_listener *listener, void *data) + /* This can be sent by any client, so we check to make sure this one + * actually has pointer focus first. If so, we can tell the cursor to + * use the provided cursor shape. */ +- if (event->seat_client == seat->pointer_state.focused_client) +- wlr_cursor_set_xcursor(cursor, cursor_mgr, +- wlr_cursor_shape_v1_name(event->shape)); ++ if (event->seat_client == seat->pointer_state.focused_client) { ++ last_cursor.shape = event->shape; ++ last_cursor.surface = NULL; ++ ++ if (!cursor_hidden) ++ wlr_cursor_set_xcursor(cursor, cursor_mgr, ++ wlr_cursor_shape_v1_name(event->shape)); ++ } + } + + void +-- +2.53.0 + From 58e371fcb31bde1e55f99498a77afd9b567a30c8 Mon Sep 17 00:00:00 2001 From: nate zhou Date: Sun, 8 Mar 2026 11:23:08 +0800 Subject: [PATCH 04/36] swapandfocusdir: update for 0.8 and abandoned patch adopted by unixchad --- patches/swapandfocusdir/README.md | 2 + patches/swapandfocusdir/swapandfocusdir.patch | 52 +++++++++---------- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/patches/swapandfocusdir/README.md b/patches/swapandfocusdir/README.md index 2861a07..076912c 100644 --- a/patches/swapandfocusdir/README.md +++ b/patches/swapandfocusdir/README.md @@ -6,9 +6,11 @@ Swap the focused window with the window (no floating) to the left, right, above, **NOTE:** this patch uses the same algorithm that River uses to select the window in the given direction. ### Download +- [v0.8](https://codeberg.org/dwl/dwl-patches/raw/commit/a5f66f3d9cdbfc1a3fd7ae9c0288c35c8ec19479/patches/swapandfocusdir/swapandfocusdir.patch) - [git branch](https://codeberg.org/wochap/dwl/src/branch/v0.5/swapandfocusdir) - [2024-07-09](https://codeberg.org/dwl/dwl-patches/raw/commit/13d96b51b54500dd24544cf3a73c61b7a1414bc6/patches/swapandfocusdir/swapandfocusdir.patch) - [v0.5](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/swapandfocusdir/swapandfocusdir.patch) ### Authors +- [unixchad](https://codeberg.org/unixchad) - [wochap](https://codeberg.org/wochap) diff --git a/patches/swapandfocusdir/swapandfocusdir.patch b/patches/swapandfocusdir/swapandfocusdir.patch index 3636387..7aedf22 100644 --- a/patches/swapandfocusdir/swapandfocusdir.patch +++ b/patches/swapandfocusdir/swapandfocusdir.patch @@ -1,7 +1,7 @@ -From 285470897406b653e77d732a77356aaf9a70b799 Mon Sep 17 00:00:00 2001 -From: wochap -Date: Fri, 5 Jul 2024 12:37:39 -0500 -Subject: [PATCH] implement swapandfocusdir +From 6972f383921a892e30ac10b42a3d3ce47b2f93e5 Mon Sep 17 00:00:00 2001 +From: nate zhou +Date: Sat, 28 Feb 2026 20:48:27 +0800 +Subject: [PATCH] Patch: swapandfocusdir.patch --- config.def.h | 8 +++ @@ -9,29 +9,29 @@ Subject: [PATCH] implement swapandfocusdir 2 files changed, 172 insertions(+) diff --git a/config.def.h b/config.def.h -index 22d2171..724e15e 100644 +index 8a6eda0..6e7fc39 100644 --- a/config.def.h +++ b/config.def.h -@@ -129,6 +129,14 @@ static const Key keys[] = { - { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, spawn, {.v = termcmd} }, - { MODKEY, XKB_KEY_j, focusstack, {.i = +1} }, - { MODKEY, XKB_KEY_k, focusstack, {.i = -1} }, -+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Left, focusdir, {.ui = 0} }, -+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Right, focusdir, {.ui = 1} }, -+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Up, focusdir, {.ui = 2} }, -+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Down, focusdir, {.ui = 3} }, -+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Left, swapdir, {.ui = 0} }, -+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Right, swapdir, {.ui = 1} }, -+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Up, swapdir, {.ui = 2} }, -+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Down, swapdir, {.ui = 3} }, - { MODKEY, XKB_KEY_i, incnmaster, {.i = +1} }, - { MODKEY, XKB_KEY_d, incnmaster, {.i = -1} }, - { MODKEY, XKB_KEY_h, setmfact, {.f = -0.05f} }, +@@ -125,6 +125,14 @@ static const Key keys[] = { + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, spawn, {.v = termcmd} }, + { MODKEY, XKB_KEY_j, focusstack, {.i = +1} }, + { MODKEY, XKB_KEY_k, focusstack, {.i = -1} }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Left, focusdir, {.ui = 0} }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Right, focusdir, {.ui = 1} }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Up, focusdir, {.ui = 2} }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Down, focusdir, {.ui = 3} }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Left, swapdir, {.ui = 0} }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Right, swapdir, {.ui = 1} }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Up, swapdir, {.ui = 2} }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Down, swapdir, {.ui = 3} }, + { MODKEY, XKB_KEY_i, incnmaster, {.i = +1} }, + { MODKEY, XKB_KEY_d, incnmaster, {.i = -1} }, + { MODKEY, XKB_KEY_h, setmfact, {.f = -0.05f} }, diff --git a/dwl.c b/dwl.c -index dc0437e..844c1f5 100644 +index 44f3ad9..1893fbd 100644 --- a/dwl.c +++ b/dwl.c -@@ -241,6 +241,11 @@ typedef struct { +@@ -239,6 +239,11 @@ typedef struct { struct wl_listener destroy; } SessionLock; @@ -51,8 +51,8 @@ index dc0437e..844c1f5 100644 +static void swapdir(const Arg *arg); static Client *focustop(Monitor *m); static void fullscreennotify(struct wl_listener *listener, void *data); - static void handlesig(int signo); -@@ -1425,6 +1432,163 @@ focusstack(const Arg *arg) + static void gpureset(struct wl_listener *listener, void *data); +@@ -1511,6 +1518,163 @@ focusstack(const Arg *arg) focusclient(c, 1); } @@ -213,9 +213,9 @@ index dc0437e..844c1f5 100644 + arrange(selmon); +} + - /* We probably should change the name of this, it sounds like + /* We probably should change the name of this: it sounds like it * will focus the topmost client of this mon, when actually will * only return that client */ -- -2.45.1 +2.53.0 From 9481ea7ea3442ec7843bdbcf4324ee3c29f69ebe Mon Sep 17 00:00:00 2001 From: nate zhou Date: Sun, 8 Mar 2026 11:36:25 +0800 Subject: [PATCH 05/36] bottomstack: update for 0.8 and abandoned patch adopted by unixchad --- patches/bottomstack/README.md | 2 ++ patches/bottomstack/bottomstack.patch | 41 ++++++++++++++------------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/patches/bottomstack/README.md b/patches/bottomstack/README.md index fa9ede6..63e6895 100644 --- a/patches/bottomstack/README.md +++ b/patches/bottomstack/README.md @@ -15,12 +15,14 @@ bstack (TTT) bstackhoriz (===) ### Download +- [v0.8](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/bottomstack/bottomstack.patch) - [git branch](https://codeberg.org/wochap/dwl/src/branch/v0.6-b/bottomstack) - [2024-07-09](https://codeberg.org/dwl/dwl-patches/raw/commit/20de07dc8759200c8a4c9651475acb331d245890/patches/bottomstack/bottomstack.patch) - [2024-04-11](https://codeberg.org/dwl/dwl-patches/raw/commit/0f4e40fee49d1b8b430778e241b29496ae3b3b70/bottomstack/bottomstack.patch) - [v0.5](https://codeberg.org/dwl/dwl-patches/raw/commit/5368aa392c7ebf8d7d24c232b80cfae1be457d41/bottomstack/bottomstack.patch) ### Authors +- [unixchad](https://codeberg.org/unixchad) - [wochap](https://codeberg.org/wochap) - [DanielMowitz](https://github.com/DanielMowitz) - [Abanoub8](https://github.com/Abanoub8) diff --git a/patches/bottomstack/bottomstack.patch b/patches/bottomstack/bottomstack.patch index c2b9331..e0dba2f 100644 --- a/patches/bottomstack/bottomstack.patch +++ b/patches/bottomstack/bottomstack.patch @@ -1,7 +1,7 @@ -From b352fb08f40b1ee2d8c4748be4922df711e3aaa9 Mon Sep 17 00:00:00 2001 -From: wochap -Date: Fri, 5 Jul 2024 10:44:29 -0500 -Subject: [PATCH] implement bottomstack +From 86e379b187bcc9f8f5896a87309474fcf3736de9 Mon Sep 17 00:00:00 2001 +From: nate zhou +Date: Sat, 28 Feb 2026 21:29:53 +0800 +Subject: [PATCH] Patch: bottomstack-0.8.patch --- config.def.h | 4 +++ @@ -9,10 +9,10 @@ Subject: [PATCH] implement bottomstack 2 files changed, 88 insertions(+) diff --git a/config.def.h b/config.def.h -index 22d2171..5aac3e9 100644 +index 8a6eda0..ae87518 100644 --- a/config.def.h +++ b/config.def.h -@@ -34,6 +34,8 @@ static const Layout layouts[] = { +@@ -33,6 +33,8 @@ static const Layout layouts[] = { { "[]=", tile }, { "><>", NULL }, /* no layout function means floating behavior */ { "[M]", monocle }, @@ -21,20 +21,20 @@ index 22d2171..5aac3e9 100644 }; /* monitors */ -@@ -139,6 +141,8 @@ static const Key keys[] = { - { 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_u, setlayout, {.v = &layouts[3]} }, -+ { MODKEY, XKB_KEY_o, setlayout, {.v = &layouts[4]} }, - { MODKEY, XKB_KEY_space, setlayout, {0} }, - { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} }, - { MODKEY, XKB_KEY_e, togglefullscreen, {0} }, +@@ -135,6 +137,8 @@ static const Key keys[] = { + { 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_u, setlayout, {.v = &layouts[3]} }, ++ { MODKEY, XKB_KEY_o, setlayout, {.v = &layouts[4]} }, + { MODKEY, XKB_KEY_space, setlayout, {0} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} }, + { MODKEY, XKB_KEY_e, togglefullscreen, {0} }, diff --git a/dwl.c b/dwl.c -index dc0437e..5648d5f 100644 +index 44f3ad9..68c25a5 100644 --- a/dwl.c +++ b/dwl.c -@@ -57,6 +57,7 @@ +@@ -59,6 +59,7 @@ #include #include #include @@ -50,8 +50,8 @@ index dc0437e..5648d5f 100644 +static void bstackhoriz(Monitor *m); /* variables */ - static const char broken[] = "broken"; -@@ -3160,3 +3163,84 @@ main(int argc, char *argv[]) + static pid_t child_pid = -1; +@@ -3213,3 +3216,84 @@ main(int argc, char *argv[]) usage: die("Usage: %s [-v] [-d] [-s startup command]", argv[0]); } @@ -137,4 +137,5 @@ index dc0437e..5648d5f 100644 + } +} -- -2.45.1 +2.53.0 + From 99c3aeb1ed087e0d664ce905531b9b47c2242a20 Mon Sep 17 00:00:00 2001 From: nate zhou Date: Sun, 8 Mar 2026 11:46:46 +0800 Subject: [PATCH 06/36] modes: update for 0.8 and abandoned patch adopted by unixchad --- patches/modes/README.md | 4 +- patches/modes/modes-0.5.patch | 165 ++++++++++++++++++++++++++++++++++ patches/modes/modes.patch | 74 +++++++-------- 3 files changed, 205 insertions(+), 38 deletions(-) create mode 100644 patches/modes/modes-0.5.patch diff --git a/patches/modes/README.md b/patches/modes/README.md index c855af9..49af3e8 100644 --- a/patches/modes/README.md +++ b/patches/modes/README.md @@ -28,8 +28,10 @@ static const Modekey modekeys[] = { ``` ### Download +- [v0.8](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/modes/modes.patch) - [git branch](https://codeberg.org/wochap/dwl/src/branch/v0.5/modes) -- [v0.5](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/modes/modes.patch) +- [v0.5](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/modes/modes-0.5.patch) ### Authors +- [unixchad](https://codeberg.org/unixchad) - [wochap](https://codeberg.org/wochap) diff --git a/patches/modes/modes-0.5.patch b/patches/modes/modes-0.5.patch new file mode 100644 index 0000000..5ecf846 --- /dev/null +++ b/patches/modes/modes-0.5.patch @@ -0,0 +1,165 @@ +From a32b85018ff2cea0fc9f9137789860a4aadc3b3a Mon Sep 17 00:00:00 2001 +From: wochap +Date: Wed, 6 Mar 2024 07:31:17 -0500 +Subject: [PATCH] implement modes + +like sway/river modes +--- + config.def.h | 20 ++++++++++++++++++++ + dwl.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 66 insertions(+) + +diff --git a/config.def.h b/config.def.h +index db0babc..1616136 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -13,6 +13,13 @@ static const float urgentcolor[] = COLOR(0xff0000ff); + /* To conform the xdg-protocol, set the alpha to zero to restore the old behavior */ + static const float fullscreen_bg[] = {0.1, 0.1, 0.1, 1.0}; /* You can also use glsl colors */ + ++enum { ++ BROWSER, ++}; ++const char *modes_labels[] = { ++ "browser", ++}; ++ + /* tagging - TAGCOUNT must be no greater than 31 */ + #define TAGCOUNT (9) + +@@ -152,6 +159,8 @@ static const Key keys[] = { + TAGKEYS( XKB_KEY_9, XKB_KEY_parenleft, 8), + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Q, quit, {0} }, + ++ { MODKEY, XKB_KEY_b, entermode, {.i = BROWSER} }, ++ + /* Ctrl-Alt-Backspace and Ctrl-Alt-Fx used to be handled by X server */ + { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_Terminate_Server, quit, {0} }, + /* Ctrl-Alt-Fx is used to switch to another VT, if you don't know what a VT is +@@ -162,6 +171,17 @@ static const Key keys[] = { + CHVT(7), CHVT(8), CHVT(9), CHVT(10), CHVT(11), CHVT(12), + }; + ++static const Modekey modekeys[] = { ++ /* mode modifier key function argument */ ++ { BROWSER, { 0, XKB_KEY_f, spawn, SHCMD("firefox") } }, ++ { BROWSER, { 0, XKB_KEY_f, entermode, {.i = NORMAL} } }, ++ { BROWSER, { 0, XKB_KEY_b, spawn, SHCMD("brave") } }, ++ { BROWSER, { 0, XKB_KEY_b, entermode, {.i = NORMAL} } }, ++ { BROWSER, { 0, XKB_KEY_g, spawn, SHCMD("google-chrome-stable") } }, ++ { BROWSER, { 0, XKB_KEY_g, entermode, {.i = NORMAL} } }, ++ { BROWSER, { 0, XKB_KEY_Escape, entermode, {.i = NORMAL} } }, ++}; ++ + static const Button buttons[] = { + { MODKEY, BTN_LEFT, moveresize, {.ui = CurMove} }, + { MODKEY, BTN_MIDDLE, togglefloating, {0} }, +diff --git a/dwl.c b/dwl.c +index ef27a1d..1ada006 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -139,6 +139,11 @@ typedef struct { + const Arg arg; + } Key; + ++typedef struct { ++ int mode_index; ++ Key key; ++} Modekey; ++ + typedef struct { + struct wl_list link; + struct wlr_keyboard *wlr_keyboard; +@@ -270,6 +275,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 modekeybinding(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); +@@ -327,6 +333,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 void entermode(const Arg *arg); + + /* variables */ + static const char broken[] = "broken"; +@@ -377,6 +384,9 @@ static struct wlr_box sgeom; + static struct wl_list mons; + static Monitor *selmon; + ++static const int NORMAL = -1; ++static int active_mode_index = NORMAL; ++ + #ifdef XWAYLAND + static void activatex11(struct wl_listener *listener, void *data); + static void associatex11(struct wl_listener *listener, void *data); +@@ -1372,6 +1382,11 @@ keybinding(uint32_t mods, xkb_keysym_t sym) + */ + int handled = 0; + const Key *k; ++ ++ if (active_mode_index >= 0) { ++ return modekeybinding(mods, sym); ++ } ++ + for (k = keys; k < END(keys); k++) { + if (CLEANMASK(mods) == CLEANMASK(k->mod) && + sym == k->keysym && k->func) { +@@ -1382,6 +1397,29 @@ keybinding(uint32_t mods, xkb_keysym_t sym) + return handled; + } + ++int ++modekeybinding(uint32_t mods, xkb_keysym_t sym) ++{ ++ int handled = 0; ++ const Modekey *mk; ++ const Key *k; ++ ++ for (mk = modekeys; mk < END(modekeys); mk++) { ++ if (active_mode_index != mk->mode_index) { ++ continue; ++ } ++ ++ k = &mk->key; ++ 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) + { +@@ -1851,6 +1889,7 @@ printstatus(void) + printf("%s tags %u %u %u %u\n", m->wlr_output->name, occ, m->tagset[m->seltags], + sel, urg); + printf("%s layout %s\n", m->wlr_output->name, m->ltsymbol); ++ printf("%s mode %s\n", m->wlr_output->name, modes_labels[active_mode_index] ? modes_labels[active_mode_index] : ""); + } + fflush(stdout); + } +@@ -2746,6 +2785,13 @@ zoom(const Arg *arg) + arrange(selmon); + } + ++void ++entermode(const Arg *arg) ++{ ++ active_mode_index = arg->i; ++ printstatus(); ++} ++ + #ifdef XWAYLAND + void + activatex11(struct wl_listener *listener, void *data) +-- +2.42.0 + diff --git a/patches/modes/modes.patch b/patches/modes/modes.patch index 5ecf846..c171eb2 100644 --- a/patches/modes/modes.patch +++ b/patches/modes/modes.patch @@ -1,21 +1,20 @@ -From a32b85018ff2cea0fc9f9137789860a4aadc3b3a Mon Sep 17 00:00:00 2001 -From: wochap -Date: Wed, 6 Mar 2024 07:31:17 -0500 -Subject: [PATCH] implement modes +From 915115151a4429bab38dd8cff2268f34ee23984f Mon Sep 17 00:00:00 2001 +From: nate zhou +Date: Mon, 2 Mar 2026 19:23:59 +0800 +Subject: [PATCH] Patch: modes-0.8.patch -like sway/river modes --- config.def.h | 20 ++++++++++++++++++++ - dwl.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 66 insertions(+) + dwl.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 67 insertions(+) diff --git a/config.def.h b/config.def.h -index db0babc..1616136 100644 +index 8a6eda0..bc88ea5 100644 --- a/config.def.h +++ b/config.def.h -@@ -13,6 +13,13 @@ static const float urgentcolor[] = COLOR(0xff0000ff); - /* To conform the xdg-protocol, set the alpha to zero to restore the old behavior */ - static const float fullscreen_bg[] = {0.1, 0.1, 0.1, 1.0}; /* You can also use glsl colors */ +@@ -14,6 +14,13 @@ static const float urgentcolor[] = COLOR(0xff0000ff); + /* This conforms to the xdg-protocol. Set the alpha to zero to restore the old behavior */ + static const float fullscreen_bg[] = {0.0f, 0.0f, 0.0f, 1.0f}; /* You can also use glsl colors */ +enum { + BROWSER, @@ -27,16 +26,16 @@ index db0babc..1616136 100644 /* tagging - TAGCOUNT must be no greater than 31 */ #define TAGCOUNT (9) -@@ -152,6 +159,8 @@ static const Key keys[] = { - TAGKEYS( XKB_KEY_9, XKB_KEY_parenleft, 8), - { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Q, quit, {0} }, +@@ -155,6 +162,8 @@ static const Key keys[] = { + TAGKEYS( XKB_KEY_9, XKB_KEY_parenleft, 8), + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_q, quit, {0} }, + { MODKEY, XKB_KEY_b, entermode, {.i = BROWSER} }, + /* Ctrl-Alt-Backspace and Ctrl-Alt-Fx used to be handled by X server */ { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_Terminate_Server, quit, {0} }, /* Ctrl-Alt-Fx is used to switch to another VT, if you don't know what a VT is -@@ -162,6 +171,17 @@ static const Key keys[] = { +@@ -165,6 +174,17 @@ static const Key keys[] = { CHVT(7), CHVT(8), CHVT(9), CHVT(10), CHVT(11), CHVT(12), }; @@ -55,10 +54,10 @@ index db0babc..1616136 100644 { MODKEY, BTN_LEFT, moveresize, {.ui = CurMove} }, { MODKEY, BTN_MIDDLE, togglefloating, {0} }, diff --git a/dwl.c b/dwl.c -index ef27a1d..1ada006 100644 +index 44f3ad9..9247541 100644 --- a/dwl.c +++ b/dwl.c -@@ -139,6 +139,11 @@ typedef struct { +@@ -148,6 +148,11 @@ typedef struct { const Arg arg; } Key; @@ -68,9 +67,9 @@ index ef27a1d..1ada006 100644 +} Modekey; + typedef struct { - struct wl_list link; - struct wlr_keyboard *wlr_keyboard; -@@ -270,6 +275,7 @@ static void handlesig(int signo); + struct wlr_keyboard_group *wlr_group; + +@@ -292,6 +297,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); @@ -78,27 +77,27 @@ index ef27a1d..1ada006 100644 static void keypress(struct wl_listener *listener, void *data); static void keypressmod(struct wl_listener *listener, void *data); static int keyrepeat(void *data); -@@ -327,6 +333,7 @@ static Monitor *xytomon(double x, double y); +@@ -351,6 +357,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 void entermode(const Arg *arg); /* variables */ - static const char broken[] = "broken"; -@@ -377,6 +384,9 @@ static struct wlr_box sgeom; + static pid_t child_pid = -1; +@@ -406,6 +413,9 @@ static struct wlr_box sgeom; static struct wl_list mons; static Monitor *selmon; +static const int NORMAL = -1; +static int active_mode_index = NORMAL; + - #ifdef XWAYLAND - static void activatex11(struct wl_listener *listener, void *data); - static void associatex11(struct wl_listener *listener, void *data); -@@ -1372,6 +1382,11 @@ keybinding(uint32_t mods, xkb_keysym_t sym) + /* global event handlers */ + static struct wl_listener cursor_axis = {.notify = axisnotify}; + static struct wl_listener cursor_button = {.notify = buttonpress}; +@@ -1614,6 +1624,11 @@ keybinding(uint32_t mods, xkb_keysym_t sym) + * processing. */ - int handled = 0; const Key *k; + + if (active_mode_index >= 0) { @@ -106,10 +105,10 @@ index ef27a1d..1ada006 100644 + } + for (k = keys; k < END(keys); k++) { - if (CLEANMASK(mods) == CLEANMASK(k->mod) && - sym == k->keysym && k->func) { -@@ -1382,6 +1397,29 @@ keybinding(uint32_t mods, xkb_keysym_t sym) - return handled; + if (CLEANMASK(mods) == CLEANMASK(k->mod) + && xkb_keysym_to_lower(sym) == xkb_keysym_to_lower(k->keysym) +@@ -1625,6 +1640,30 @@ keybinding(uint32_t mods, xkb_keysym_t sym) + return 0; } +int @@ -134,19 +133,20 @@ index ef27a1d..1ada006 100644 + + return handled; +} ++ + void keypress(struct wl_listener *listener, void *data) { -@@ -1851,6 +1889,7 @@ printstatus(void) - printf("%s tags %u %u %u %u\n", m->wlr_output->name, occ, m->tagset[m->seltags], - sel, urg); +@@ -2119,6 +2158,7 @@ printstatus(void) + 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); + printf("%s mode %s\n", m->wlr_output->name, modes_labels[active_mode_index] ? modes_labels[active_mode_index] : ""); } fflush(stdout); } -@@ -2746,6 +2785,13 @@ zoom(const Arg *arg) +@@ -3075,6 +3115,13 @@ zoom(const Arg *arg) arrange(selmon); } @@ -161,5 +161,5 @@ index ef27a1d..1ada006 100644 void activatex11(struct wl_listener *listener, void *data) -- -2.42.0 +2.53.0 From 0f2cd0555a7cb7ce22715ea3c285f0c950486fb2 Mon Sep 17 00:00:00 2001 From: Dhruva Sambrani <44899822+DhruvaSambrani@users.noreply.github.com> Date: Tue, 10 Mar 2026 16:42:02 +0100 Subject: [PATCH 07/36] ignore dimmer rects in xytonode --- patches/dim-unfocused/README.md | 3 +- .../dim-unfocused-20240918.patch | 216 +++++++++++++++++ patches/dim-unfocused/dim-unfocused.patch | 217 +----------------- 3 files changed, 219 insertions(+), 217 deletions(-) create mode 100644 patches/dim-unfocused/dim-unfocused-20240918.patch diff --git a/patches/dim-unfocused/README.md b/patches/dim-unfocused/README.md index dad3ef0..6a8bc4d 100644 --- a/patches/dim-unfocused/README.md +++ b/patches/dim-unfocused/README.md @@ -8,7 +8,8 @@ There are also two functions that can be bound to a `Key` or `Button`, 2. `toggledimmingclient`: Which toggles dimming for the focused window, as if the client had `neverdim` applied to it. This overwrites an applied `Rule`. ### Download -- [2024-09-18](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/dim-unfocused/dim-unfocused.patch) +- [2026-03-10](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/dim-unfocused/dim-unfocused.patch) +- [2024-09-18](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/dim-unfocused/dim-unfocused-20240918.patch) - [2024-09-03](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/dim-unfocused/dim-unfocused-20240903.patch) - [2024-07-14](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/dim-unfocused/dim-unfocused-20240714.patch) - [2024-05-16](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/dim-unfocused/dim-unfocused-20240516.patch) diff --git a/patches/dim-unfocused/dim-unfocused-20240918.patch b/patches/dim-unfocused/dim-unfocused-20240918.patch new file mode 100644 index 0000000..f73d886 --- /dev/null +++ b/patches/dim-unfocused/dim-unfocused-20240918.patch @@ -0,0 +1,216 @@ +diff --git a/client.h b/client.h +index dabea35..3a31c25 100644 +--- a/client.h ++++ b/client.h +@@ -319,6 +319,12 @@ client_set_border_color(Client *c, const float color[static 4]) + wlr_scene_rect_set_color(c->border[i], color); + } + ++static inline void ++client_set_dimmer_state(Client *c, const int dim) ++{ ++ wlr_scene_node_set_enabled(&c->dimmer->node, DIMOPT && !c->neverdim && dim); ++} ++ + static inline void + client_set_fullscreen(Client *c, int fullscreen) + { +diff --git a/config.def.h b/config.def.h +index 22d2171..4ca21c9 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -10,6 +10,7 @@ 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 unfocuseddim[] = COLOR(0x00000088); + 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 */ +@@ -22,10 +23,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 */ +- /* 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 neverdim monitor */ ++ /* examples: ++ { "Gimp_example", NULL, 0, 1, 0, -1 }, ++ */ ++ { "firefox_example", NULL, 1 << 8, 0, 1, -1 }, + }; + + /* layout(s) */ +@@ -140,8 +142,9 @@ static const Key keys[] = { + { MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} }, + { MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} }, + { MODKEY, XKB_KEY_space, setlayout, {0} }, ++ { MODKEY, XKB_KEY_apostrophe, toggledimming, {0} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} }, +- { MODKEY, XKB_KEY_e, togglefullscreen, {0} }, ++ { MODKEY, XKB_KEY_e, togglefullscreen, {0} }, + { MODKEY, XKB_KEY_0, view, {.ui = ~0} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} }, + { MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} }, +@@ -172,5 +175,6 @@ static const Key keys[] = { + static const Button buttons[] = { + { MODKEY, BTN_LEFT, moveresize, {.ui = CurMove} }, + { MODKEY, BTN_MIDDLE, togglefloating, {0} }, ++ { MODKEY|ShiftMask, BTN_MIDDLE, toggledimmingclient, {0} }, + { MODKEY, BTN_RIGHT, moveresize, {.ui = CurResize} }, + }; +diff --git a/dwl.c b/dwl.c +index dc0c861..dcc3ece 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -112,6 +112,7 @@ typedef struct { + Monitor *mon; + struct wlr_scene_tree *scene; + struct wlr_scene_rect *border[4]; /* top, bottom, left, right */ ++ struct wlr_scene_rect *dimmer; + struct wlr_scene_tree *scene_surface; + struct wl_list link; + struct wl_list flink; +@@ -141,7 +142,7 @@ typedef struct { + #endif + unsigned int bw; + uint32_t tags; +- int isfloating, isurgent, isfullscreen; ++ int isfloating, isurgent, isfullscreen, neverdim; + uint32_t resize; /* configure serial of a pending resize */ + } Client; + +@@ -231,6 +232,7 @@ typedef struct { + const char *title; + uint32_t tags; + int isfloating; ++ int neverdim; + int monitor; + } Rule; + +@@ -338,6 +340,8 @@ 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 toggledimming(const Arg *arg); ++static void toggledimmingclient(const Arg *arg); + static void togglefloating(const Arg *arg); + static void togglefullscreen(const Arg *arg); + static void toggletag(const Arg *arg); +@@ -410,6 +414,7 @@ static struct wlr_output_layout *output_layout; + static struct wlr_box sgeom; + static struct wl_list mons; + static Monitor *selmon; ++static int DIMOPT = 1; + + #ifdef XWAYLAND + static void activatex11(struct wl_listener *listener, void *data); +@@ -466,6 +471,7 @@ applyrules(Client *c) + if ((!r->title || strstr(title, r->title)) + && (!r->id || strstr(appid, r->id))) { + c->isfloating = r->isfloating; ++ c->neverdim = r-> neverdim; + newtags |= r->tags; + i = 0; + wl_list_for_each(m, &mons, link) { +@@ -1365,8 +1371,10 @@ 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) ++ if (!exclusive_focus && !seat->drag) { + client_set_border_color(c, focuscolor); ++ client_set_dimmer_state(c, 0); ++ } + } + + /* Deactivate old client if focus is changing */ +@@ -1384,7 +1392,7 @@ focusclient(Client *c, int lift) + * 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_dimmer_state(old_c, 1); + client_activate_surface(old, 0); + } + } +@@ -1681,8 +1689,7 @@ void + mapnotify(struct wl_listener *listener, void *data) + { + /* Called when the surface is mapped, or ready to display on-screen. */ +- Client *p = NULL; +- Client *w, *c = wl_container_of(listener, c, map); ++ Client *p, *w, *d, *c = wl_container_of(listener, c, map); + Monitor *m; + int i; + +@@ -1716,6 +1723,10 @@ mapnotify(struct wl_listener *listener, void *data) + c->border[i]->node.data = c; + } + ++ c->dimmer = wlr_scene_rect_create(c->scene, 0, 0, unfocuseddim); ++ c->dimmer->node.data = c; ++ client_set_dimmer_state(c, 1); ++ + /* Initialize client geometry with room for border */ + client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT); + c->geom.width += 2 * c->bw; +@@ -1734,6 +1745,10 @@ mapnotify(struct wl_listener *listener, void *data) + setmon(c, p->mon, p->tags); + } else { + applyrules(c); ++ d = focustop(selmon); ++ if (d) { ++ client_set_dimmer_state(d, 0); ++ } + } + printstatus(); + +@@ -2160,7 +2175,7 @@ resize(Client *c, struct wlr_box geo, int interact) + c->geom = geo; + applybounds(c, bbox); + +- /* Update scene-graph, including borders */ ++ /* Update scene-graph, including borders and dimmer*/ + wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y); + wlr_scene_node_set_position(&c->scene_surface->node, c->bw, c->bw); + wlr_scene_rect_set_size(c->border[0], c->geom.width, c->bw); +@@ -2170,6 +2185,8 @@ resize(Client *c, struct wlr_box geo, int interact) + wlr_scene_node_set_position(&c->border[1]->node, 0, c->geom.height - c->bw); + wlr_scene_node_set_position(&c->border[2]->node, 0, c->bw); + wlr_scene_node_set_position(&c->border[3]->node, c->geom.width - c->bw, c->bw); ++ wlr_scene_rect_set_size(c->dimmer, c->geom.width - 2*c->bw, c-> geom.height - 2*c->bw); ++ wlr_scene_node_set_position(&c->dimmer->node, c->bw, c->bw); + + /* this is a no-op if size hasn't changed */ + c->resize = client_set_size(c, c->geom.width - 2 * c->bw, +@@ -2681,6 +2698,27 @@ tile(Monitor *m) + } + } + ++void toggledimming(const Arg *arg) ++{ ++ Client *c; ++ DIMOPT ^= 1; ++ wl_list_for_each(c, &clients, link) ++ { ++ client_set_dimmer_state(c, 1); ++ } ++ c = focustop(selmon); ++ if (c) ++ client_set_dimmer_state(c, 0); ++} ++ ++void ++toggledimmingclient(const Arg *arg) ++{ ++ Client *sel = focustop(selmon); ++ if (sel) ++ sel -> neverdim ^= 1; ++} ++ + void + togglefloating(const Arg *arg) + { diff --git a/patches/dim-unfocused/dim-unfocused.patch b/patches/dim-unfocused/dim-unfocused.patch index f73d886..aa2f0b2 100644 --- a/patches/dim-unfocused/dim-unfocused.patch +++ b/patches/dim-unfocused/dim-unfocused.patch @@ -1,216 +1 @@ -diff --git a/client.h b/client.h -index dabea35..3a31c25 100644 ---- a/client.h -+++ b/client.h -@@ -319,6 +319,12 @@ client_set_border_color(Client *c, const float color[static 4]) - wlr_scene_rect_set_color(c->border[i], color); - } - -+static inline void -+client_set_dimmer_state(Client *c, const int dim) -+{ -+ wlr_scene_node_set_enabled(&c->dimmer->node, DIMOPT && !c->neverdim && dim); -+} -+ - static inline void - client_set_fullscreen(Client *c, int fullscreen) - { -diff --git a/config.def.h b/config.def.h -index 22d2171..4ca21c9 100644 ---- a/config.def.h -+++ b/config.def.h -@@ -10,6 +10,7 @@ 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 unfocuseddim[] = COLOR(0x00000088); - 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 */ -@@ -22,10 +23,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 */ -- /* 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 neverdim monitor */ -+ /* examples: -+ { "Gimp_example", NULL, 0, 1, 0, -1 }, -+ */ -+ { "firefox_example", NULL, 1 << 8, 0, 1, -1 }, - }; - - /* layout(s) */ -@@ -140,8 +142,9 @@ static const Key keys[] = { - { MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} }, - { MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} }, - { MODKEY, XKB_KEY_space, setlayout, {0} }, -+ { MODKEY, XKB_KEY_apostrophe, toggledimming, {0} }, - { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} }, -- { MODKEY, XKB_KEY_e, togglefullscreen, {0} }, -+ { MODKEY, XKB_KEY_e, togglefullscreen, {0} }, - { MODKEY, XKB_KEY_0, view, {.ui = ~0} }, - { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} }, - { MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} }, -@@ -172,5 +175,6 @@ static const Key keys[] = { - static const Button buttons[] = { - { MODKEY, BTN_LEFT, moveresize, {.ui = CurMove} }, - { MODKEY, BTN_MIDDLE, togglefloating, {0} }, -+ { MODKEY|ShiftMask, BTN_MIDDLE, toggledimmingclient, {0} }, - { MODKEY, BTN_RIGHT, moveresize, {.ui = CurResize} }, - }; -diff --git a/dwl.c b/dwl.c -index dc0c861..dcc3ece 100644 ---- a/dwl.c -+++ b/dwl.c -@@ -112,6 +112,7 @@ typedef struct { - Monitor *mon; - struct wlr_scene_tree *scene; - struct wlr_scene_rect *border[4]; /* top, bottom, left, right */ -+ struct wlr_scene_rect *dimmer; - struct wlr_scene_tree *scene_surface; - struct wl_list link; - struct wl_list flink; -@@ -141,7 +142,7 @@ typedef struct { - #endif - unsigned int bw; - uint32_t tags; -- int isfloating, isurgent, isfullscreen; -+ int isfloating, isurgent, isfullscreen, neverdim; - uint32_t resize; /* configure serial of a pending resize */ - } Client; - -@@ -231,6 +232,7 @@ typedef struct { - const char *title; - uint32_t tags; - int isfloating; -+ int neverdim; - int monitor; - } Rule; - -@@ -338,6 +340,8 @@ 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 toggledimming(const Arg *arg); -+static void toggledimmingclient(const Arg *arg); - static void togglefloating(const Arg *arg); - static void togglefullscreen(const Arg *arg); - static void toggletag(const Arg *arg); -@@ -410,6 +414,7 @@ static struct wlr_output_layout *output_layout; - static struct wlr_box sgeom; - static struct wl_list mons; - static Monitor *selmon; -+static int DIMOPT = 1; - - #ifdef XWAYLAND - static void activatex11(struct wl_listener *listener, void *data); -@@ -466,6 +471,7 @@ applyrules(Client *c) - if ((!r->title || strstr(title, r->title)) - && (!r->id || strstr(appid, r->id))) { - c->isfloating = r->isfloating; -+ c->neverdim = r-> neverdim; - newtags |= r->tags; - i = 0; - wl_list_for_each(m, &mons, link) { -@@ -1365,8 +1371,10 @@ 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) -+ if (!exclusive_focus && !seat->drag) { - client_set_border_color(c, focuscolor); -+ client_set_dimmer_state(c, 0); -+ } - } - - /* Deactivate old client if focus is changing */ -@@ -1384,7 +1392,7 @@ focusclient(Client *c, int lift) - * 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_dimmer_state(old_c, 1); - client_activate_surface(old, 0); - } - } -@@ -1681,8 +1689,7 @@ void - mapnotify(struct wl_listener *listener, void *data) - { - /* Called when the surface is mapped, or ready to display on-screen. */ -- Client *p = NULL; -- Client *w, *c = wl_container_of(listener, c, map); -+ Client *p, *w, *d, *c = wl_container_of(listener, c, map); - Monitor *m; - int i; - -@@ -1716,6 +1723,10 @@ mapnotify(struct wl_listener *listener, void *data) - c->border[i]->node.data = c; - } - -+ c->dimmer = wlr_scene_rect_create(c->scene, 0, 0, unfocuseddim); -+ c->dimmer->node.data = c; -+ client_set_dimmer_state(c, 1); -+ - /* Initialize client geometry with room for border */ - client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT); - c->geom.width += 2 * c->bw; -@@ -1734,6 +1745,10 @@ mapnotify(struct wl_listener *listener, void *data) - setmon(c, p->mon, p->tags); - } else { - applyrules(c); -+ d = focustop(selmon); -+ if (d) { -+ client_set_dimmer_state(d, 0); -+ } - } - printstatus(); - -@@ -2160,7 +2175,7 @@ resize(Client *c, struct wlr_box geo, int interact) - c->geom = geo; - applybounds(c, bbox); - -- /* Update scene-graph, including borders */ -+ /* Update scene-graph, including borders and dimmer*/ - wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y); - wlr_scene_node_set_position(&c->scene_surface->node, c->bw, c->bw); - wlr_scene_rect_set_size(c->border[0], c->geom.width, c->bw); -@@ -2170,6 +2185,8 @@ resize(Client *c, struct wlr_box geo, int interact) - wlr_scene_node_set_position(&c->border[1]->node, 0, c->geom.height - c->bw); - wlr_scene_node_set_position(&c->border[2]->node, 0, c->bw); - wlr_scene_node_set_position(&c->border[3]->node, c->geom.width - c->bw, c->bw); -+ wlr_scene_rect_set_size(c->dimmer, c->geom.width - 2*c->bw, c-> geom.height - 2*c->bw); -+ wlr_scene_node_set_position(&c->dimmer->node, c->bw, c->bw); - - /* this is a no-op if size hasn't changed */ - c->resize = client_set_size(c, c->geom.width - 2 * c->bw, -@@ -2681,6 +2698,27 @@ tile(Monitor *m) - } - } - -+void toggledimming(const Arg *arg) -+{ -+ Client *c; -+ DIMOPT ^= 1; -+ wl_list_for_each(c, &clients, link) -+ { -+ client_set_dimmer_state(c, 1); -+ } -+ c = focustop(selmon); -+ if (c) -+ client_set_dimmer_state(c, 0); -+} -+ -+void -+toggledimmingclient(const Arg *arg) -+{ -+ Client *sel = focustop(selmon); -+ if (sel) -+ sel -> neverdim ^= 1; -+} -+ - void - togglefloating(const Arg *arg) - { +09 \ No newline at end of file From e1ad73d51caae1dda97a75489f3e4af3f0773721 Mon Sep 17 00:00:00 2001 From: Diego Viola Date: Tue, 10 Mar 2026 01:38:53 -0300 Subject: [PATCH 08/36] treewide: fix typos --- README.md | 4 ++-- patches/bar-modes/README.md | 2 +- patches/bar-systray/bar-systray-0.7.patch | 2 +- patches/borders/borders.patch | 2 +- patches/btrtile/README.md | 2 +- patches/btrtile/btrtile-v0.7-gaps.patch | 2 +- patches/btrtile/btrtile-v0.7.patch | 2 +- patches/coredump/README.md | 2 +- patches/hot-reload/README.md | 10 +++++----- patches/hot-reload/hot-reload-0.7.patch | 2 +- patches/hot-reload/hot-reload.patch | 2 +- patches/meson/README.md | 2 +- patches/restore-monitor/restore-monitor.patch | 2 +- patches/riverctl/README.md | 2 +- patches/riverctl/riverctl.patch | 6 +++--- patches/singletagset-sticky/README.md | 2 +- patches/spawninfo/screenshotwin | 2 +- patches/unclutter/unclutter.patch | 2 +- patches/virtual-pointer/README.md | 4 ++-- patches/winview/README.md | 2 +- stale-patches/headless/README.md | 4 ++-- stale-patches/ipc/ipc.patch | 20 +++++++++---------- 22 files changed, 40 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index ec42576..6d4e688 100644 --- a/README.md +++ b/README.md @@ -47,9 +47,9 @@ If you target the unstable `main` branch, specify that in the `Download` link on You may choose to include screenshots (hosted in your patch's subdirectory) in your `README.md`. The process is described [here](https://docs.codeberg.org/markdown/using-images/). 8. Use the Codeberg web interface to send a pull request to [dwl-patches] (NOT to [dwl]) -9. WHEN YOUR PULL REQUEST IS APPROVED, your Codeberg account will also be granted commit access to [dwl-patches]. Once you have write access, you can make direct modifications/upates to your patches and you are free to create new patches rather than creating pull requests. +9. WHEN YOUR PULL REQUEST IS APPROVED, your Codeberg account will also be granted commit access to [dwl-patches]. Once you have write access, you can make direct modifications/updates to your patches and you are free to create new patches rather than creating pull requests. -Individuals who have made known that they no longer intend to maintain their patches will have commit access to the [dwl-pathces] repository removed. +Individuals who have made known that they no longer intend to maintain their patches will have commit access to the [dwl-patches] repository removed. A returning user who formerly had commit access is welcome to open an issue on [dwl-patches] requesting commit access be reinstated. When doing so, please link to the original issue opened that granted commit access. diff --git a/patches/bar-modes/README.md b/patches/bar-modes/README.md index e7dc12d..8de981e 100644 --- a/patches/bar-modes/README.md +++ b/patches/bar-modes/README.md @@ -14,7 +14,7 @@ Another usage is to serve as a hint for each modes keybindings: enum { BROWSER, }; -const char *modes_lablels[] = { +const char *modes_labels[] = { "[f]irefox [b]rave [c]hromium [q]utebrowser", }; ``` diff --git a/patches/bar-systray/bar-systray-0.7.patch b/patches/bar-systray/bar-systray-0.7.patch index 1dda4c6..4cf63e3 100644 --- a/patches/bar-systray/bar-systray-0.7.patch +++ b/patches/bar-systray/bar-systray-0.7.patch @@ -301,7 +301,7 @@ index 0000000..125312c + pipefd = ecalloc(2, sizeof(int)); + + /* -+ * Libdbus forbids calling dbus_connection_dipatch from the ++ * Libdbus forbids calling dbus_connection_dispatch from the + * DBusDispatchStatusFunction directly. Notify the event loop of + * updates via a self-pipe. + */ diff --git a/patches/borders/borders.patch b/patches/borders/borders.patch index c8cc847..65b99c5 100644 --- a/patches/borders/borders.patch +++ b/patches/borders/borders.patch @@ -3,7 +3,7 @@ From: wochap 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 +this patch adds 2 extra borders relative to the client, they don't change the size of the client --- client.h | 16 +++++++++++++--- diff --git a/patches/btrtile/README.md b/patches/btrtile/README.md index 4766aa7..d187f5f 100644 --- a/patches/btrtile/README.md +++ b/patches/btrtile/README.md @@ -61,7 +61,7 @@ When a new client appears: btrtile adds couple variables to config.h to fine tune the mouse resizing of tiled clients. 1. **resize_factor** - - A multiplier to transfer pointer movement to client weight ratio. Depends heavily on mouse sensivity. + - A multiplier to transfer pointer movement to client weight ratio. Depends heavily on mouse sensitivity. Defaults to 0.0002f. 2. **resize_interval_ms** diff --git a/patches/btrtile/btrtile-v0.7-gaps.patch b/patches/btrtile/btrtile-v0.7-gaps.patch index 90354d1..dc4e9cf 100644 --- a/patches/btrtile/btrtile-v0.7-gaps.patch +++ b/patches/btrtile/btrtile-v0.7-gaps.patch @@ -606,7 +606,7 @@ index 22d2171..92f3ad6 100644 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 float resize_factor = 0.0002f; /* Resize multiplier for mouse resizing, depends on mouse sensivity. */ ++static const float resize_factor = 0.0002f; /* Resize multiplier for mouse resizing, depends on mouse sensitivity. */ +static const uint32_t resize_interval_ms = 16; /* Resize interval depends on framerate and screen refresh rate. */ +enum Direction { DIR_LEFT, DIR_RIGHT, DIR_UP, DIR_DOWN }; diff --git a/patches/btrtile/btrtile-v0.7.patch b/patches/btrtile/btrtile-v0.7.patch index b85473e..0396500 100644 --- a/patches/btrtile/btrtile-v0.7.patch +++ b/patches/btrtile/btrtile-v0.7.patch @@ -587,7 +587,7 @@ index 22d2171..92f3ad6 100644 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 float resize_factor = 0.0002f; /* Resize multiplier for mouse resizing, depends on mouse sensivity. */ ++static const float resize_factor = 0.0002f; /* Resize multiplier for mouse resizing, depends on mouse sensitivity. */ +static const uint32_t resize_interval_ms = 16; /* Resize interval depends on framerate and screen refresh rate. */ +enum Direction { DIR_LEFT, DIR_RIGHT, DIR_UP, DIR_DOWN }; diff --git a/patches/coredump/README.md b/patches/coredump/README.md index 5300890..f005ecb 100644 --- a/patches/coredump/README.md +++ b/patches/coredump/README.md @@ -1,5 +1,5 @@ ### Description -Generate a coredump if dwl exited abnormally (to be more usefull you need to +Generate a coredump if dwl exited abnormally (to be more useful you need to compile dwl and wlroots with debug symbols) ### Download diff --git a/patches/hot-reload/README.md b/patches/hot-reload/README.md index e387ca4..73f2d40 100644 --- a/patches/hot-reload/README.md +++ b/patches/hot-reload/README.md @@ -15,7 +15,7 @@ So in short: #### Limitations Reloading the compositor will replace all functionality except for `main`, `setup`, `run` and the reload logic. -Note that you're responsible yourself for reloading ressources like fonts, which may only get acquired once. +Note that you're responsible yourself for reloading resources like fonts, which may only get acquired once. A lot of components of dwl will also only get run on a trigger (the tiling for example). So not every change will be immediate. Furthermore, any patch adding more global state to dwl cannot currently be reloaded properly since @@ -47,13 +47,13 @@ From the cold part there are some newly available macros: * `CSYM(T, v)` dynamically accesses the value of the symbol `v` of type `T` from the shared object. Use this to query values from config.h for example. * `LISTEN_GLOBAL(E, L)` is similar to the `LISTEN` macro. `E` is an event and `L` the name of a global listener. Current implementation is a bit messy and I may fix it if someone bothers me about it. -* `UNLISTEN(L)` takes a listener and unregisteres it. This is important for reloading. +* `UNLISTEN(L)` takes a listener and unregisters it. This is important for reloading. When adding new code there are some considerations to be made. Since dwl decorates all symbols with `static` by default, we cannot access them as-is. C's macro system is a bit too powerful though and we use this to our advantage. We will repeatedly define and undefine a macro called `static` in order to replace the `static` keyword inside some sections. -This allows us to do less refactoring and preserve a lot of the original patch compatability since we're only -strategically adding lines. We're tring to be as minimally invasive as we can. +This allows us to do less refactoring and preserve a lot of the original patch compatibility since we're only +strategically adding lines. We're trying to be as minimally invasive as we can. As a general guide: * global state should be global for the cold part and `extern` in the cold part meaning it should be inside a block like this: ```C @@ -87,7 +87,7 @@ Thus, we enclose them the same way we do functions: #ifdef HOT ... // function definitions here #endif -* enfore use of the `LISTEN_GLOBAL` and `UNLISTEN` macros (I know this sucks but what can I do, I need to get +* enforce use of the `LISTEN_GLOBAL` and `UNLISTEN` macros (I know this sucks but what can I do, I need to get access to the callbacks somehow). So you want * `wl_list_remove(listener.link)` to become `UNLISTEN(listener)` and * `wl_signal_add(event, global_listener)` to become `LISTEN_GLOBAL(event, global_listener)`. diff --git a/patches/hot-reload/hot-reload-0.7.patch b/patches/hot-reload/hot-reload-0.7.patch index 1611684..1dad241 100644 --- a/patches/hot-reload/hot-reload-0.7.patch +++ b/patches/hot-reload/hot-reload-0.7.patch @@ -509,7 +509,7 @@ index def2562..1c9ab67 100644 + wlr_log_init(CSYM(enum wlr_log_importance, log_level), NULL); /* The Wayland display is managed by libwayland. It handles accepting - * clients from the Unix socket, manging Wayland globals, and so on. */ + * clients from the Unix socket, managing Wayland globals, and so on. */ @@ -2454,7 +2576,7 @@ setup(void) /* Initialize the scene graph used to lay out windows */ diff --git a/patches/hot-reload/hot-reload.patch b/patches/hot-reload/hot-reload.patch index 627fa88..a9580d1 100644 --- a/patches/hot-reload/hot-reload.patch +++ b/patches/hot-reload/hot-reload.patch @@ -637,7 +637,7 @@ index 4816159..70e99be 100644 + wlr_log_init(CSYM(enum wlr_log_importance, log_level), NULL); /* The Wayland display is managed by libwayland. It handles accepting - * clients from the Unix socket, manging Wayland globals, and so on. */ + * clients from the Unix socket, managing Wayland globals, and so on. */ @@ -2463,7 +2602,7 @@ setup(void) /* Initialize the scene graph used to lay out windows */ diff --git a/patches/meson/README.md b/patches/meson/README.md index 4641bbf..6a3fa9d 100644 --- a/patches/meson/README.md +++ b/patches/meson/README.md @@ -7,7 +7,7 @@ To enable Xwayland support, you will need to enable it in the wlroots subproject ```sh meson setup -Dwlroots:xwayland=enabled build ``` -It is also reccomended to see the wlroots meson project configuration logs for any +It is also recommended to see the wlroots meson project configuration logs for any unusual checks, such as requiring `hwdata` for the DRM backend. ### Download diff --git a/patches/restore-monitor/restore-monitor.patch b/patches/restore-monitor/restore-monitor.patch index 0d6da98..2a818c6 100644 --- a/patches/restore-monitor/restore-monitor.patch +++ b/patches/restore-monitor/restore-monitor.patch @@ -1,7 +1,7 @@ From e42ca1c539437d3098d80983cfe2ad6f938d7a08 Mon Sep 17 00:00:00 2001 From: Eldar Yusupov Date: Sun, 17 Mar 2024 19:12:29 +0300 -Subject: [PATCH] Restore correct montior for client when it is reattached +Subject: [PATCH] Restore correct monitor for client when it is reattached --- dwl.c | 24 ++++++++++++++++++++++-- diff --git a/patches/riverctl/README.md b/patches/riverctl/README.md index ac4bc6b..78e1b2f 100644 --- a/patches/riverctl/README.md +++ b/patches/riverctl/README.md @@ -7,7 +7,7 @@ Most of this patch is stored in river-control.h, It contains a list of functions This patch's main intended use case is to have a startup script that calls dwlctl a bunch to add all the binds/rules you want, without the need of restarting dwl if you make any changes to the list of binds/rules. -This patch also adds keybind modes which allow switching between a diffrent sets of keybinds on the fly. +This patch also adds keybind modes which allow switching between a different sets of keybinds on the fly. Also you can set a keybind mode as oneshot (meaning as soon as a keybind is activated the mode is switched) by using... `dwlctl oneshot-mode _layout_you_want_to_make_oneshot_here_ _layout_you_want_to_switch_to_after_keybind_pressed_` Just make sure to set a mode as oneshot after creating a bind under it otherwise it won't work. diff --git a/patches/riverctl/riverctl.patch b/patches/riverctl/riverctl.patch index f35a55e..14c9335 100644 --- a/patches/riverctl/riverctl.patch +++ b/patches/riverctl/riverctl.patch @@ -120,7 +120,7 @@ index 95c2afa..ccc3edb 100644 -static const Key keys[] = { +/* note keys gets cleared with riverctl clear-binds but the keys_always are excluded from being cleared + * this is to have a list of fallback keybinds if your riverctl script fails -+ * if you won't like to have keys[] declared commented out the KEYS_USED macro bellow to disable the functionality*/ ++ * if you won't like to have keys[] declared commented out the KEYS_USED macro below to disable the functionality*/ +#define KEYS_USED +static Key keys[] = { /* Note that Shift changes certain key codes: c -> C, 2 -> at, etc. */ @@ -1267,7 +1267,7 @@ index 0000000..599cffe + .add_argument = zriver_control_add_argument, + .destroy = zriver_control_destroy, +}; -+static void zriver_control_handle_destory(struct wl_resource *resource) { ++static void zriver_control_handle_destroy(struct wl_resource *resource) { + struct zriver_arg_list_resource *zriver_arg_list_resource = wl_resource_get_user_data(resource); + free(zriver_arg_list_resource); + printf("handle destroy\n"); @@ -1281,7 +1281,7 @@ index 0000000..599cffe + + + wl_resource_set_implementation(resource, &zriver_control_interface, -+ zriver_arg_list_resource, zriver_control_handle_destory); ++ zriver_arg_list_resource, zriver_control_handle_destroy); +} -- 2.51.0 diff --git a/patches/singletagset-sticky/README.md b/patches/singletagset-sticky/README.md index f1f9fd3..cba3e82 100644 --- a/patches/singletagset-sticky/README.md +++ b/patches/singletagset-sticky/README.md @@ -1,6 +1,6 @@ ### Description Makes sticky work as expected with singletagset. The sticky window will -stay on original output until you explicitely put it to a different monitor. +stay on original output until you explicitly put it to a different monitor. This patch expects both [singletagset](https://codeberg.org/dwl/dwl-patches/src/branch/main/patches/singletagset) and [sticky](https://codeberg.org/dwl/dwl-patches/src/branch/main/patches/sticky) patches to be already in your tree committed. It applies onto them. diff --git a/patches/spawninfo/screenshotwin b/patches/spawninfo/screenshotwin index c8a3ebf..8b64a05 100755 --- a/patches/spawninfo/screenshotwin +++ b/patches/spawninfo/screenshotwin @@ -13,7 +13,7 @@ tempdir="/tmp/screenshots" mkdir -p "$tempdir" file="$(mktemp -p "$tempdir" "XXXXXX.png")" -# Grab the screenshot! Very conviniently, GEOMETRY format matches the one +# Grab the screenshot! Very conveniently, GEOMETRY format matches the one # expected by grim grim -g "$GEOMETRY" "$file" || exit diff --git a/patches/unclutter/unclutter.patch b/patches/unclutter/unclutter.patch index 4c9aede..dd5a298 100644 --- a/patches/unclutter/unclutter.patch +++ b/patches/unclutter/unclutter.patch @@ -60,7 +60,7 @@ index 775dadf..621779e 100644 - /* TODO: allow usage of scroll wheel for mousebindings, it can be implemented - * by checking the event's orientation and the delta of the event */ + handlecursoractivity(); -+ /* TODO: allow usage of scroll whell for mousebindings, it can be implemented ++ /* TODO: allow usage of scroll wheel for mousebindings, it can be implemented + * checking the event's orientation and the delta of the event */ /* Notify the client with pointer focus of the axis event. */ wlr_seat_pointer_notify_axis(seat, diff --git a/patches/virtual-pointer/README.md b/patches/virtual-pointer/README.md index 451d26e..0558395 100644 --- a/patches/virtual-pointer/README.md +++ b/patches/virtual-pointer/README.md @@ -1,7 +1,7 @@ ### Description implement wlr_virtual_pointer_v1 for things like wayvnc server to work -**NOTE:** no longer neccessary if you are using a DWL version after https://codeberg.org/dwl/dwl/commit/ac6074f4fdb8cc263c877f08e16a5805d3bb22d2 +**NOTE:** no longer necessary if you are using a DWL version after https://codeberg.org/dwl/dwl/commit/ac6074f4fdb8cc263c877f08e16a5805d3bb22d2 ### Download - [git branch](https://codeberg.org/wochap/dwl/src/v0.5/virtual-pointer) @@ -9,4 +9,4 @@ implement wlr_virtual_pointer_v1 for things like wayvnc server to work ### Authors - [wochap](https://codeberg.org/wochap) -- [youbitchoc](https://github.com/youbitchoc) \ No newline at end of file +- [youbitchoc](https://github.com/youbitchoc) diff --git a/patches/winview/README.md b/patches/winview/README.md index 5b3bb07..1ab4387 100644 --- a/patches/winview/README.md +++ b/patches/winview/README.md @@ -3,7 +3,7 @@ Implements the function `winview` which switches the visible tags to the tags on This patch is inspired from . Citing the description of the dwm patch: -> Dwm tags are a powerfull feature that allows organizing windows in workspaces. Sometime it can be difficult to remember the tag to activate to unhide a window. With the winview patch the window to unhide can be selected from the all-window view. The user switches to the all-window view (Mod1-0), selects the window (Mod1-j/k or using the mouse) and press Mod1-o. The key Mod1-o switches the view to the selected window tag. +> Dwm tags are a powerful feature that allows organizing windows in workspaces. Sometime it can be difficult to remember the tag to activate to unhide a window. With the winview patch the window to unhide can be selected from the all-window view. The user switches to the all-window view (Mod1-0), selects the window (Mod1-j/k or using the mouse) and press Mod1-o. The key Mod1-o switches the view to the selected window tag. > > #### Recommend patches > diff --git a/stale-patches/headless/README.md b/stale-patches/headless/README.md index 990a5d9..cf95778 100644 --- a/stale-patches/headless/README.md +++ b/stale-patches/headless/README.md @@ -1,5 +1,5 @@ ### Description -Implements `swaymsg create_output` command, it allows you to create virtual/headless outputs. But in combination with a VNC server (for example wayvnc), this allows you to essentially have additional monitors, by connecting to the VNC server with an appropiate client (for example on an tablet or laptop). +Implements `swaymsg create_output` command, it allows you to create virtual/headless outputs. But in combination with a VNC server (for example wayvnc), this allows you to essentially have additional monitors, by connecting to the VNC server with an appropriate client (for example on an tablet or laptop). If you plan to use wayvnc, you'll need [virtual-pointer](https://codeberg.org/dwl/dwl-patches/wiki/virtual-pointer.-) patch as well @@ -8,4 +8,4 @@ If you plan to use wayvnc, you'll need [virtual-pointer](https://codeberg.org/dw - [v0.5](https://codeberg.org/dwl/dwl-patches/raw/commit/0096e49402bc59b4050e12cdb9befb79d0011006/headless/headless.patch) ### Authors -- [wochap](https://codeberg.org/wochap) \ No newline at end of file +- [wochap](https://codeberg.org/wochap) diff --git a/stale-patches/ipc/ipc.patch b/stale-patches/ipc/ipc.patch index 9f989e7..8268bd3 100644 --- a/stale-patches/ipc/ipc.patch +++ b/stale-patches/ipc/ipc.patch @@ -417,7 +417,7 @@ index 0000000..0a6e7e5 +I would probably just submit raphi's patchset but I don't think that would be polite. +--> + -+ ++ + This protocol allows clients to update and get updates from dwl. + + Warning! The protocol described in this file is experimental and @@ -437,7 +437,7 @@ index 0000000..0a6e7e5 + 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. ++ After binding the client will receive 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. + + @@ -449,8 +449,8 @@ index 0000000..0a6e7e5 + + + -+ -+ Get a dwl_ipc_outout for the specified wl_output. ++ ++ Get a dwl_ipc_output for the specified wl_output. + + + @@ -459,7 +459,7 @@ index 0000000..0a6e7e5 + + + This event is sent after binding. -+ A roundtrip after binding guarantees the client recieved all tags. ++ A roundtrip after binding guarantees the client received all tags. + + + @@ -467,7 +467,7 @@ index 0000000..0a6e7e5 + + + This event is sent after binding. -+ A roundtrip after binding guarantees the client recieved all layouts. ++ A roundtrip after binding guarantees the client received all layouts. + + + @@ -491,13 +491,13 @@ index 0000000..0a6e7e5 + + + -+ ++ + Indicates to that the client no longer needs this dwl_ipc_output. + + + + -+ ++ + Indicates the client should hide or show themselves. + If the client is visible then hide, if hidden then show. + @@ -544,7 +544,7 @@ index 0000000..0a6e7e5 + + + 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. ++ As opposed to the zdwl_ipc_manager.layout event, this should take precedence when displaying. + You can ignore the zdwl_ipc_output.layout event. + + @@ -573,7 +573,7 @@ index 0000000..0a6e7e5 + + + -+ ++ + + + From a20a1032420925efd65d98dba1f4c2d4b4626df2 Mon Sep 17 00:00:00 2001 From: nate zhou Date: Sun, 8 Mar 2026 11:40:18 +0800 Subject: [PATCH 09/36] decklayout: update for 0.8 --- patches/decklayout/README.md | 3 ++- patches/decklayout/decklayout.patch | 34 ++++++++++++++--------------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/patches/decklayout/README.md b/patches/decklayout/README.md index dc9e52d..9cba91e 100644 --- a/patches/decklayout/README.md +++ b/patches/decklayout/README.md @@ -2,8 +2,9 @@ Deck is a dwl-layout which is inspired by the dwm Deck layout (which is inspired by TTWM window manager). It applies the monocle-layout to the clients in the stack. The master-client is still visible. The stacked clients are like a deck of cards, hence the name. ### Download +- [v0.8](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/decklayout/decklayout.patch) - [git branch](https://codeberg.org/Kana/dwl/src/branch/decklayout) -- [main 2025-10-08](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/decklayout/decklayout.patch) +- [main 2025-10-08](https://codeberg.org/dwl/dwl-patches/raw/commit/f8d1cfad116c19c01593f7436468ec0cb7a3297b/patches/decklayout/decklayout.patch) ### Authors - [André Desgualdo Pereira](https://codeberg.org/Kana) diff --git a/patches/decklayout/decklayout.patch b/patches/decklayout/decklayout.patch index bfd4540..423acdb 100644 --- a/patches/decklayout/decklayout.patch +++ b/patches/decklayout/decklayout.patch @@ -1,7 +1,7 @@ -From 095439425e64f2567f141d5d941178b148ef0d3a Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Andr=C3=A9=20Desgualdo=20Pereira?= -Date: Sun, 12 Oct 2025 11:44:26 -0300 -Subject: [PATCH] decklayout +From c488515313e20f51ab961691002f9d483682ab16 Mon Sep 17 00:00:00 2001 +From: nate zhou +Date: Sat, 28 Feb 2026 21:32:46 +0800 +Subject: [PATCH] Patch: decklayout-0.8.patch --- config.def.h | 2 ++ @@ -9,10 +9,10 @@ Subject: [PATCH] decklayout 2 files changed, 63 insertions(+) diff --git a/config.def.h b/config.def.h -index 95c2afa..cc846eb 100644 +index 8a6eda0..3ec9ceb 100644 --- a/config.def.h +++ b/config.def.h -@@ -34,6 +34,7 @@ static const Layout layouts[] = { +@@ -33,6 +33,7 @@ static const Layout layouts[] = { { "[]=", tile }, { "><>", NULL }, /* no layout function means floating behavior */ { "[M]", monocle }, @@ -20,16 +20,16 @@ index 95c2afa..cc846eb 100644 }; /* monitors */ -@@ -139,6 +140,7 @@ static const Key keys[] = { - { 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_a, setlayout, {.v = &layouts[3]} }, - { MODKEY, XKB_KEY_space, setlayout, {0} }, - { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} }, - { MODKEY, XKB_KEY_e, togglefullscreen, {0} }, +@@ -135,6 +136,7 @@ static const Key keys[] = { + { 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_a, setlayout, {.v = &layouts[3]} }, + { MODKEY, XKB_KEY_space, setlayout, {0} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} }, + { MODKEY, XKB_KEY_e, togglefullscreen, {0} }, diff --git a/dwl.c b/dwl.c -index 12f441e..227004f 100644 +index 44f3ad9..f13f48b 100644 --- a/dwl.c +++ b/dwl.c @@ -278,6 +278,7 @@ static void destroylayersurfacenotify(struct wl_listener *listener, void *data); @@ -40,7 +40,7 @@ index 12f441e..227004f 100644 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); -@@ -1837,6 +1838,66 @@ monocle(Monitor *m) +@@ -1838,6 +1839,66 @@ monocle(Monitor *m) wlr_scene_node_raise_to_top(&c->scene->node); } @@ -108,5 +108,5 @@ index 12f441e..227004f 100644 motionabsolute(struct wl_listener *listener, void *data) { -- -2.51.0 +2.53.0 From 341d6d1fd379d325fcedc9ce5fa376a8511f1a89 Mon Sep 17 00:00:00 2001 From: nate zhou Date: Sun, 8 Mar 2026 11:42:29 +0800 Subject: [PATCH 10/36] gaplessgrid: update for 0.8 --- patches/gaplessgrid/README.md | 3 +- .../gaplessgrid/gaplessgrid-20251031.patch | 102 ++++++++++++++++++ patches/gaplessgrid/gaplessgrid.patch | 32 +++--- 3 files changed, 120 insertions(+), 17 deletions(-) create mode 100644 patches/gaplessgrid/gaplessgrid-20251031.patch diff --git a/patches/gaplessgrid/README.md b/patches/gaplessgrid/README.md index b0354de..b22ead8 100644 --- a/patches/gaplessgrid/README.md +++ b/patches/gaplessgrid/README.md @@ -4,7 +4,8 @@ Arranges windows in a grid. Except it adjusts the number of windows in the first On widescreens (w > 2*h), it splits to three columns before splitting rows. ### Download -- [2025-10-31](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/gaplessgrid/gaplessgrid.patch) +- [v0.8](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/gaplessgrid/gaplessgrid.patch) +- [2025-10-31](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/gaplessgrid/gaplessgrid-20251031.patch) - [2024-09-18](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/gaplessgrid/gaplessgrid-20240918.patch) - [2024-07-14](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/gaplessgrid/gaplessgrid-20240714.patch) - [2023-08-01](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/gaplessgrid/gaplessgrid-20230801.patch) diff --git a/patches/gaplessgrid/gaplessgrid-20251031.patch b/patches/gaplessgrid/gaplessgrid-20251031.patch new file mode 100644 index 0000000..f54e1d1 --- /dev/null +++ b/patches/gaplessgrid/gaplessgrid-20251031.patch @@ -0,0 +1,102 @@ +From 38427b81367e8b4d2aad708a1d463bc793aac65e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Andr=C3=A9=20Desgualdo=20Pereira?= +Date: Fri, 31 Oct 2025 15:46:48 -0300 +Subject: [PATCH] take on gaplessgrid and fix minor indentation and typos + +--- + config.def.h | 2 ++ + dwl.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 53 insertions(+) + +diff --git a/config.def.h b/config.def.h +index 95c2afa..2054107 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -34,6 +34,7 @@ static const Layout layouts[] = { + { "[]=", tile }, + { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, ++ { "###", gaplessgrid }, + }; + + /* monitors */ +@@ -139,6 +140,7 @@ static const Key keys[] = { + { 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_g, setlayout, {.v = &layouts[3]} }, + { MODKEY, XKB_KEY_space, setlayout, {0} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} }, + { MODKEY, XKB_KEY_e, togglefullscreen, {0} }, +diff --git a/dwl.c b/dwl.c +index 12f441e..7ab5323 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -288,6 +288,7 @@ static void focusstack(const Arg *arg); + static Client *focustop(Monitor *m); + static void fullscreennotify(struct wl_listener *listener, void *data); + static void gpureset(struct wl_listener *listener, void *data); ++static void gaplessgrid(Monitor *m); + static void handlesig(int signo); + static void incnmaster(const Arg *arg); + static void inputdevice(struct wl_listener *listener, void *data); +@@ -1566,6 +1567,56 @@ handlesig(int signo) + quit(NULL); + } + ++void ++gaplessgrid(Monitor *m) ++{ ++ int n = 0, i = 0, ch, cw, cn, rn, rows, cols; ++ Client *c; ++ ++ wl_list_for_each(c, &clients, link) ++ if (VISIBLEON(c, m) && !c->isfloating) ++ n++; ++ if (n == 0) ++ return; ++ ++ /* grid dimensions */ ++ for (cols = 0; cols <= (n / 2); cols++) ++ if ((cols * cols) >= n) ++ break; ++ ++ if (n == 5) /* set layout against the general calculation: not 1:2:2, but 2:3 */ ++ cols = 2; ++ ++ /* widescreen is better if 3 columns */ ++ if (n >= 3 && n <= 6 && (m->w.width / m->w.height) > 1) ++ cols = 3; ++ ++ rows = n / cols; ++ ++ /* window geometries */ ++ cw = cols ? m->w.width / cols : m->w.width; ++ cn = 0; /* current column number */ ++ rn = 0; /* current row number */ ++ wl_list_for_each(c, &clients, link) { ++ unsigned int cx, cy; ++ if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) ++ continue; ++ ++ if ((i / rows + 1) > (cols - n % cols)) ++ rows = n / cols + 1; ++ ch = rows ? m->w.height / rows : m->w.height; ++ cx = m->w.x + cn * cw; ++ cy = m->w.y + rn * ch; ++ resize(c, (struct wlr_box) { cx, cy, cw, ch}, 0); ++ rn++; ++ if (rn >= rows) { ++ rn = 0; ++ cn++; ++ } ++ i++; ++ } ++} ++ + void + incnmaster(const Arg *arg) + { +-- +2.51.0 + diff --git a/patches/gaplessgrid/gaplessgrid.patch b/patches/gaplessgrid/gaplessgrid.patch index f54e1d1..d869928 100644 --- a/patches/gaplessgrid/gaplessgrid.patch +++ b/patches/gaplessgrid/gaplessgrid.patch @@ -1,7 +1,7 @@ -From 38427b81367e8b4d2aad708a1d463bc793aac65e Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Andr=C3=A9=20Desgualdo=20Pereira?= -Date: Fri, 31 Oct 2025 15:46:48 -0300 -Subject: [PATCH] take on gaplessgrid and fix minor indentation and typos +From 91d34b1c664fe273a82eb7ff01e92b828feab5a9 Mon Sep 17 00:00:00 2001 +From: nate zhou +Date: Sat, 28 Feb 2026 21:42:04 +0800 +Subject: [PATCH] Patch: gaplessgrid.patch --- config.def.h | 2 ++ @@ -9,10 +9,10 @@ Subject: [PATCH] take on gaplessgrid and fix minor indentation and typos 2 files changed, 53 insertions(+) diff --git a/config.def.h b/config.def.h -index 95c2afa..2054107 100644 +index 8a6eda0..8cb1782 100644 --- a/config.def.h +++ b/config.def.h -@@ -34,6 +34,7 @@ static const Layout layouts[] = { +@@ -33,6 +33,7 @@ static const Layout layouts[] = { { "[]=", tile }, { "><>", NULL }, /* no layout function means floating behavior */ { "[M]", monocle }, @@ -20,16 +20,16 @@ index 95c2afa..2054107 100644 }; /* monitors */ -@@ -139,6 +140,7 @@ static const Key keys[] = { - { 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_g, setlayout, {.v = &layouts[3]} }, - { MODKEY, XKB_KEY_space, setlayout, {0} }, - { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} }, - { MODKEY, XKB_KEY_e, togglefullscreen, {0} }, +@@ -135,6 +136,7 @@ static const Key keys[] = { + { 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_g, setlayout, {.v = &layouts[3]} }, + { MODKEY, XKB_KEY_space, setlayout, {0} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} }, + { MODKEY, XKB_KEY_e, togglefullscreen, {0} }, diff --git a/dwl.c b/dwl.c -index 12f441e..7ab5323 100644 +index 44f3ad9..83c053f 100644 --- a/dwl.c +++ b/dwl.c @@ -288,6 +288,7 @@ static void focusstack(const Arg *arg); @@ -98,5 +98,5 @@ index 12f441e..7ab5323 100644 incnmaster(const Arg *arg) { -- -2.51.0 +2.53.0 From f46616216f0558edb87d0301f992b118c6cb189c Mon Sep 17 00:00:00 2001 From: Dhruva Sambrani <44899822+DhruvaSambrani@users.noreply.github.com> Date: Wed, 11 Mar 2026 19:11:52 +0100 Subject: [PATCH 11/36] add extrabar --- patches/extrabar/README.md | 17 +++ patches/extrabar/extrabar.patch | 204 ++++++++++++++++++++++++++++++++ 2 files changed, 221 insertions(+) create mode 100644 patches/extrabar/README.md create mode 100644 patches/extrabar/extrabar.patch diff --git a/patches/extrabar/README.md b/patches/extrabar/README.md new file mode 100644 index 0000000..57267c4 --- /dev/null +++ b/patches/extrabar/README.md @@ -0,0 +1,17 @@ +### extrabar + +Add an extra bar on the other side of the primary bar, shows additional status sections. +_Requires_ `bar` patch to be applied first. Heavily inspired by `dwm`'s extrabar patch. + +### Status + +The status text on the extra bar is set using the stdin as usual, with the text being split by `;` + +For example, if `status1;status2;status3` was set, +then `status1` is set as the status of the primary bar, + `status2` is set as the left status of extrabar, + `status3` is set as the right status of extrabar. + +### Author + +[dhruva_sambrani](/dhruva_sambrani) diff --git a/patches/extrabar/extrabar.patch b/patches/extrabar/extrabar.patch new file mode 100644 index 0000000..1f4b452 --- /dev/null +++ b/patches/extrabar/extrabar.patch @@ -0,0 +1,204 @@ +diff --git a/dwl.c b/dwl.c +index 7fe9468..b0db7e5 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -205,6 +205,7 @@ struct Monitor { + struct wlr_output *wlr_output; + struct wlr_scene_output *scene_output; + struct wlr_scene_buffer *scene_buffer; /* bar buffer */ ++ struct wlr_scene_buffer *extra_scene_buffer; /* bar buffer */ + struct wlr_scene_rect *fullscreen_bg; /* See createmon() for info */ + struct wl_listener frame; + struct wl_listener destroy; +@@ -230,6 +231,7 @@ struct Monitor { + int asleep; + Drwl *drw; + Buffer *pool[2]; ++ Buffer *extra_pool[2]; + int lrpad; + }; + +@@ -278,7 +280,7 @@ static void bufdestroy(struct wlr_buffer *buffer); + static bool bufdatabegin(struct wlr_buffer *buffer, uint32_t flags, + void **data, uint32_t *format, size_t *stride); + static void bufdataend(struct wlr_buffer *buffer); +-static Buffer *bufmon(Monitor *m); ++static Buffer *bufmon(Monitor *m, Buffer **pool); + static void bufrelease(struct wl_listener *listener, void *data); + static void buttonpress(struct wl_listener *listener, void *data); + static void chvt(const Arg *arg); +@@ -444,6 +446,8 @@ static struct wl_list mons; + static Monitor *selmon; + + static char stext[256]; ++static char stext2[256]; ++static char stext3[256]; + static struct wl_event_source *status_event_source; + + static const struct wlr_buffer_impl buffer_impl = { +@@ -627,6 +631,11 @@ arrangelayers(Monitor *m) + usable_area.y += topbar ? m->b.real_height : 0; + } + ++ if (m->extra_scene_buffer->node.enabled) { ++ usable_area.height -= m->b.real_height; ++ usable_area.y += topbar ? 0 : m->b.real_height; ++ } ++ + /* Arrange exclusive surfaces from top->bottom */ + for (i = 3; i >= 0; i--) + arrangelayer(m, &m->layers[i], &usable_area, 1); +@@ -706,23 +715,23 @@ bufdataend(struct wlr_buffer *wlr_buffer) + } + + Buffer * +-bufmon(Monitor *m) ++bufmon(Monitor *m, Buffer **pool) + { + size_t i; + Buffer *buf = NULL; + + for (i = 0; i < LENGTH(m->pool); i++) { +- if (m->pool[i]) { +- if (m->pool[i]->busy) ++ if (pool[i]) { ++ if (pool[i]->busy) + continue; +- buf = m->pool[i]; ++ buf = pool[i]; + break; + } + + buf = ecalloc(1, sizeof(Buffer) + (m->b.width * 4 * m->b.height)); + buf->image = drwl_image_create(NULL, m->b.width, m->b.height, buf->data); + wlr_buffer_init(&buf->base, &buffer_impl, m->b.width, m->b.height); +- m->pool[i] = buf; ++ pool[i] = buf; + break; + } + if (!buf) +@@ -1255,6 +1264,8 @@ createmon(struct wl_listener *listener, void *data) + + m->scene_buffer = wlr_scene_buffer_create(layers[LyrBottom], NULL); + m->scene_buffer->point_accepts_input = baracceptsinput; ++ m->extra_scene_buffer = wlr_scene_buffer_create(layers[LyrBottom], NULL); ++ m->extra_scene_buffer->point_accepts_input = baracceptsinput; + updatebar(m); + + wl_list_insert(&mons, &m->link); +@@ -1633,13 +1644,72 @@ drawbar(Monitor *m) + wlr_buffer_unlock(&buf->base); + } + ++void ++extrabar(Monitor *m) ++{ ++ int tw = 0; ++ Buffer *buf = NULL; ++ ++ /* Skip if the extra bar scene buffer is not initialized/enabled */ ++ if (!m->extra_scene_buffer || !m->extra_scene_buffer->node.enabled) ++ return; ++ ++ /* Inline bufmon logic for the extra pool to keep changes contained */ ++ if (!(buf = bufmon(m, m->extra_pool))) ++ return; ++ ++ buf->busy = true; ++ LISTEN(&buf->base.events.release, &buf->release, bufrelease); ++ wlr_buffer_lock(&buf->base); ++ drwl_setimage(m->drw, buf->image); ++ ++ drwl_setscheme(m->drw, colors[SchemeNorm]); ++ drwl_rect(m->drw, 0, 0, m->b.width, m->b.height, 1, 1); ++ ++ drwl_setscheme(m->drw, colors[SchemeNorm]); ++ tw = TEXTW(m, stext2) - m->lrpad + 2; /* 2px right padding */ ++ drwl_text(m->drw, 2, 0, tw, m->b.height, 0, stext2, 0); ++ ++ tw = TEXTW(m, stext3) - m->lrpad + 2; /* 2px right padding */ ++ drwl_text(m->drw, m->b.width - tw - 4, 0, tw, m->b.height, 0, stext3, 0); ++ ++ wlr_scene_buffer_set_dest_size(m->extra_scene_buffer, m->b.real_width, m->b.real_height); ++ wlr_scene_node_set_position(&m->extra_scene_buffer->node, m->m.x, ++ m->m.y + (topbar ? m->m.height - m->b.real_height : 0)); ++ ++ wlr_scene_buffer_set_buffer(m->extra_scene_buffer, &buf->base); ++ wlr_buffer_unlock(&buf->base); ++} ++ ++void ++traynotify(void *data) ++{ ++ Monitor *m = data; ++ ++ drawbar(m); ++} ++ ++void ++trayactivate(const Arg *arg) ++{ ++ tray_leftclicked(selmon->tray, arg->ui); ++} ++ ++void ++traymenu(const Arg *arg) ++{ ++ tray_rightclicked(selmon->tray, arg->ui, dmenucmd); ++} ++ + void + drawbars(void) + { + Monitor *m = NULL; + +- wl_list_for_each(m, &mons, link) ++ wl_list_for_each(m, &mons, link) { + drawbar(m); ++ extrabar(m); ++ } + } + + void +@@ -2896,7 +2966,7 @@ startdrag(struct wl_listener *listener, void *data) + int + statusin(int fd, unsigned int mask, void *data) + { +- char status[256]; ++ char status[3*256]; + ssize_t n; + + if (mask & WL_EVENT_ERROR) +@@ -2911,7 +2981,18 @@ statusin(int fd, unsigned int mask, void *data) + status[n] = '\0'; + status[strcspn(status, "\n")] = '\0'; + +- strncpy(stext, status, sizeof(stext)); ++ char *l1 = strchr(status, ';'); ++ if (l1) { ++ *l1 = '\0'; ++ char *l2 = strchr(++l1, ';'); ++ if (l2) { ++ *l2 = '\0'; ++ strncpy(stext3, ++l2, sizeof(stext3)); ++ } ++ strncpy(stext2, l1, sizeof(stext2)); ++ } ++ strncpy(stext, status, sizeof(stext)); ++ + drawbars(); + + return 0; +@@ -3206,6 +3287,12 @@ updatebar(Monitor *m) + m->pool[i] = NULL; + } + ++ for (i = 0; i < LENGTH(m->extra_pool); i++) ++ if (m->extra_pool[i]) { ++ wlr_buffer_drop(&m->extra_pool[i]->base); ++ m->extra_pool[i] = NULL; ++ } ++ + if (m->b.scale == m->wlr_output->scale && m->drw) + return; + From 585f636ea12227b35ddba5e2ab28543676636a69 Mon Sep 17 00:00:00 2001 From: Dhruva Sambrani <44899822+DhruvaSambrani@users.noreply.github.com> Date: Wed, 11 Mar 2026 19:15:26 +0100 Subject: [PATCH 12/36] fix the extrabar patch --- patches/extrabar/extrabar.patch | 30 +++++------------------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/patches/extrabar/extrabar.patch b/patches/extrabar/extrabar.patch index 1f4b452..8adff0a 100644 --- a/patches/extrabar/extrabar.patch +++ b/patches/extrabar/extrabar.patch @@ -1,5 +1,5 @@ diff --git a/dwl.c b/dwl.c -index 7fe9468..b0db7e5 100644 +index 7fe9468..9fb2fbd 100644 --- a/dwl.c +++ b/dwl.c @@ -205,6 +205,7 @@ struct Monitor { @@ -86,7 +86,7 @@ index 7fe9468..b0db7e5 100644 updatebar(m); wl_list_insert(&mons, &m->link); -@@ -1633,13 +1644,72 @@ drawbar(Monitor *m) +@@ -1633,13 +1644,52 @@ drawbar(Monitor *m) wlr_buffer_unlock(&buf->base); } @@ -126,26 +126,6 @@ index 7fe9468..b0db7e5 100644 + wlr_scene_buffer_set_buffer(m->extra_scene_buffer, &buf->base); + wlr_buffer_unlock(&buf->base); +} -+ -+void -+traynotify(void *data) -+{ -+ Monitor *m = data; -+ -+ drawbar(m); -+} -+ -+void -+trayactivate(const Arg *arg) -+{ -+ tray_leftclicked(selmon->tray, arg->ui); -+} -+ -+void -+traymenu(const Arg *arg) -+{ -+ tray_rightclicked(selmon->tray, arg->ui, dmenucmd); -+} + void drawbars(void) @@ -160,7 +140,7 @@ index 7fe9468..b0db7e5 100644 } void -@@ -2896,7 +2966,7 @@ startdrag(struct wl_listener *listener, void *data) +@@ -2896,7 +2946,7 @@ startdrag(struct wl_listener *listener, void *data) int statusin(int fd, unsigned int mask, void *data) { @@ -169,7 +149,7 @@ index 7fe9468..b0db7e5 100644 ssize_t n; if (mask & WL_EVENT_ERROR) -@@ -2911,7 +2981,18 @@ statusin(int fd, unsigned int mask, void *data) +@@ -2911,7 +2961,18 @@ statusin(int fd, unsigned int mask, void *data) status[n] = '\0'; status[strcspn(status, "\n")] = '\0'; @@ -189,7 +169,7 @@ index 7fe9468..b0db7e5 100644 drawbars(); return 0; -@@ -3206,6 +3287,12 @@ updatebar(Monitor *m) +@@ -3206,6 +3267,12 @@ updatebar(Monitor *m) m->pool[i] = NULL; } From 6882f753c53b417a35656d8b629aa7abb6f2c57b Mon Sep 17 00:00:00 2001 From: Dhruva Sambrani <44899822+DhruvaSambrani@users.noreply.github.com> Date: Wed, 11 Mar 2026 19:17:12 +0100 Subject: [PATCH 13/36] fix extrabar again --- patches/extrabar/extrabar.patch | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/patches/extrabar/extrabar.patch b/patches/extrabar/extrabar.patch index 8adff0a..3bdd448 100644 --- a/patches/extrabar/extrabar.patch +++ b/patches/extrabar/extrabar.patch @@ -1,5 +1,5 @@ diff --git a/dwl.c b/dwl.c -index 7fe9468..9fb2fbd 100644 +index 7fe9468..4e3272e 100644 --- a/dwl.c +++ b/dwl.c @@ -205,6 +205,7 @@ struct Monitor { @@ -86,6 +86,15 @@ index 7fe9468..9fb2fbd 100644 updatebar(m); wl_list_insert(&mons, &m->link); +@@ -1580,7 +1591,7 @@ drawbar(Monitor *m) + + if (!m->scene_buffer->node.enabled) + return; +- if (!(buf = bufmon(m))) ++ if (!(buf = bufmon(m, m->pool))) + return; + + /* draw status first so it can be overdrawn by tags later */ @@ -1633,13 +1644,52 @@ drawbar(Monitor *m) wlr_buffer_unlock(&buf->base); } From 16ae2a3a875ab5e5f807c05d4dfce35b494837d2 Mon Sep 17 00:00:00 2001 From: Dhruva Sambrani <44899822+DhruvaSambrani@users.noreply.github.com> Date: Fri, 13 Mar 2026 12:45:31 +0100 Subject: [PATCH 14/36] fix extrabar not updating status --- patches/extrabar/extrabar.patch | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/patches/extrabar/extrabar.patch b/patches/extrabar/extrabar.patch index 3bdd448..e4a0aae 100644 --- a/patches/extrabar/extrabar.patch +++ b/patches/extrabar/extrabar.patch @@ -1,5 +1,5 @@ diff --git a/dwl.c b/dwl.c -index 7fe9468..4e3272e 100644 +index 7fe9468..2614a25 100644 --- a/dwl.c +++ b/dwl.c @@ -205,6 +205,7 @@ struct Monitor { @@ -95,7 +95,7 @@ index 7fe9468..4e3272e 100644 return; /* draw status first so it can be overdrawn by tags later */ -@@ -1633,13 +1644,52 @@ drawbar(Monitor *m) +@@ -1633,13 +1644,47 @@ drawbar(Monitor *m) wlr_buffer_unlock(&buf->base); } @@ -113,11 +113,6 @@ index 7fe9468..4e3272e 100644 + if (!(buf = bufmon(m, m->extra_pool))) + return; + -+ buf->busy = true; -+ LISTEN(&buf->base.events.release, &buf->release, bufrelease); -+ wlr_buffer_lock(&buf->base); -+ drwl_setimage(m->drw, buf->image); -+ + drwl_setscheme(m->drw, colors[SchemeNorm]); + drwl_rect(m->drw, 0, 0, m->b.width, m->b.height, 1, 1); + @@ -149,7 +144,7 @@ index 7fe9468..4e3272e 100644 } void -@@ -2896,7 +2946,7 @@ startdrag(struct wl_listener *listener, void *data) +@@ -2896,7 +2941,7 @@ startdrag(struct wl_listener *listener, void *data) int statusin(int fd, unsigned int mask, void *data) { @@ -158,7 +153,7 @@ index 7fe9468..4e3272e 100644 ssize_t n; if (mask & WL_EVENT_ERROR) -@@ -2911,7 +2961,18 @@ statusin(int fd, unsigned int mask, void *data) +@@ -2911,7 +2956,18 @@ statusin(int fd, unsigned int mask, void *data) status[n] = '\0'; status[strcspn(status, "\n")] = '\0'; @@ -178,7 +173,7 @@ index 7fe9468..4e3272e 100644 drawbars(); return 0; -@@ -3206,6 +3267,12 @@ updatebar(Monitor *m) +@@ -3206,6 +3262,12 @@ updatebar(Monitor *m) m->pool[i] = NULL; } From 47746314360c82048a791bafd9b0e4afaf2578a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Desgualdo=20Pereira?= Date: Sat, 14 Mar 2026 17:44:04 -0300 Subject: [PATCH 15/36] update controlled_fullscreen to dwl 0.8 --- patches/controlled_fullscreen/README.md | 5 +- .../controlled_fullscreen.patch | 30 ++-- .../controlled_fullscreen_2025_11_13.patch | 132 ++++++++++++++++++ 3 files changed, 150 insertions(+), 17 deletions(-) create mode 100644 patches/controlled_fullscreen/controlled_fullscreen_2025_11_13.patch diff --git a/patches/controlled_fullscreen/README.md b/patches/controlled_fullscreen/README.md index e708a00..bb712ea 100644 --- a/patches/controlled_fullscreen/README.md +++ b/patches/controlled_fullscreen/README.md @@ -2,8 +2,9 @@ This patch allows a window to adjust its layout as if it was fullscreen, but it won't change its size and position, and it will stays under the control of dwl. For example a video on a browser can occupy the whole space reserved to the window, but we can still resize it and move it and see the status bar. ### Download -- [git branch](https://codeberg.org/Kana/dwl/src/branch/controlled_fullscreen) -- [main 2025-11-13](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/controlled_fullscreen/controlled_fullscreen.patch) +- [dwl 0.8](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/controlled_fullscreen/controlled_fullscreen.patch) +- [2025-11-13](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/controlled_fullscreen/controlled_fullscreen_2025_11_13.patch) + - [2025-10-08](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/controlled_fullscreen/controlled_fullscreen_2025_10_08.patch) ### Authors diff --git a/patches/controlled_fullscreen/controlled_fullscreen.patch b/patches/controlled_fullscreen/controlled_fullscreen.patch index b3f8325..321f7ec 100644 --- a/patches/controlled_fullscreen/controlled_fullscreen.patch +++ b/patches/controlled_fullscreen/controlled_fullscreen.patch @@ -1,17 +1,17 @@ -From e0cecc228d436425c0d921a1eec5e0370d24613d Mon Sep 17 00:00:00 2001 +From 234dc2774b23d21c647c6967b1dd38e61165fabf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Desgualdo=20Pereira?= -Date: Thu, 13 Nov 2025 09:09:22 -0300 -Subject: [PATCH] controlled fullscreen fix +Date: Sat, 14 Mar 2026 17:20:21 -0300 +Subject: [PATCH] update to dwl 0.8 --- dwl.c | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/dwl.c b/dwl.c -index 12f441e..4f124eb 100644 +index 101a45f..0ec8db6 100644 --- a/dwl.c +++ b/dwl.c -@@ -518,9 +518,6 @@ arrange(Monitor *m) +@@ -519,9 +519,6 @@ arrange(Monitor *m) } } @@ -21,7 +21,7 @@ index 12f441e..4f124eb 100644 strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, LENGTH(m->ltsymbol)); /* We move all clients (except fullscreen and unmanaged) to LyrTile while -@@ -878,7 +875,7 @@ commitnotify(struct wl_listener *listener, void *data) +@@ -879,7 +876,7 @@ commitnotify(struct wl_listener *listener, void *data) return; } @@ -30,7 +30,7 @@ index 12f441e..4f124eb 100644 /* mark a pending resize as completed */ if (c->resize && c->resize <= c->surface.xdg->current.configure_serial) -@@ -1490,7 +1487,7 @@ focusstack(const Arg *arg) +@@ -1491,7 +1488,7 @@ focusstack(const Arg *arg) { /* Focus the next or previous client (in tiling order) on selmon */ Client *c, *sel = focustop(selmon); @@ -39,7 +39,7 @@ index 12f441e..4f124eb 100644 return; if (arg->i > 0) { wl_list_for_each(c, &sel->link, link) { -@@ -1826,7 +1823,7 @@ monocle(Monitor *m) +@@ -1828,7 +1825,7 @@ monocle(Monitor *m) int n = 0; wl_list_for_each(c, &clients, link) { @@ -48,7 +48,7 @@ index 12f441e..4f124eb 100644 continue; resize(c, m->w, 0); n++; -@@ -1957,7 +1954,7 @@ moveresize(const Arg *arg) +@@ -1959,7 +1956,7 @@ moveresize(const Arg *arg) if (cursor_mode != CurNormal && cursor_mode != CurPressed) return; xytonode(cursor->x, cursor->y, NULL, &grabc, NULL, NULL, NULL); @@ -57,7 +57,7 @@ index 12f441e..4f124eb 100644 return; /* Float the window and tell motionnotify to grab it */ -@@ -2332,14 +2329,12 @@ setcursorshape(struct wl_listener *listener, void *data) +@@ -2334,14 +2331,12 @@ setcursorshape(struct wl_listener *listener, void *data) void setfloating(Client *c, int floating) { @@ -74,7 +74,7 @@ index 12f441e..4f124eb 100644 arrange(c->mon); printstatus(); } -@@ -2352,12 +2347,12 @@ setfullscreen(Client *c, int fullscreen) +@@ -2354,12 +2349,12 @@ setfullscreen(Client *c, int fullscreen) return; c->bw = fullscreen ? 0 : borderpx; client_set_fullscreen(c, fullscreen); @@ -90,7 +90,7 @@ index 12f441e..4f124eb 100644 } else { /* restore previous size instead of arrange for floating windows since * client positions are set by the user and cannot be recalculated */ -@@ -2716,7 +2711,7 @@ tile(Monitor *m) +@@ -2719,7 +2714,7 @@ tile(Monitor *m) Client *c; wl_list_for_each(c, &clients, link) @@ -99,7 +99,7 @@ index 12f441e..4f124eb 100644 n++; if (n == 0) return; -@@ -2727,7 +2722,7 @@ tile(Monitor *m) +@@ -2730,7 +2725,7 @@ tile(Monitor *m) mw = m->w.width; i = my = ty = 0; wl_list_for_each(c, &clients, link) { @@ -108,7 +108,7 @@ index 12f441e..4f124eb 100644 continue; if (i < m->nmaster) { resize(c, (struct wlr_box){.x = m->w.x, .y = m->w.y + my, .width = mw, -@@ -2747,7 +2742,7 @@ togglefloating(const Arg *arg) +@@ -2750,7 +2745,7 @@ togglefloating(const Arg *arg) { Client *sel = focustop(selmon); /* return if fullscreen */ @@ -117,7 +117,7 @@ index 12f441e..4f124eb 100644 setfloating(sel, !sel->isfloating); } -@@ -2902,9 +2897,6 @@ updatemons(struct wl_listener *listener, void *data) +@@ -2905,9 +2900,6 @@ updatemons(struct wl_listener *listener, void *data) arrangelayers(m); /* Don't move clients to the left output when plugging monitors */ arrange(m); diff --git a/patches/controlled_fullscreen/controlled_fullscreen_2025_11_13.patch b/patches/controlled_fullscreen/controlled_fullscreen_2025_11_13.patch new file mode 100644 index 0000000..b3f8325 --- /dev/null +++ b/patches/controlled_fullscreen/controlled_fullscreen_2025_11_13.patch @@ -0,0 +1,132 @@ +From e0cecc228d436425c0d921a1eec5e0370d24613d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Andr=C3=A9=20Desgualdo=20Pereira?= +Date: Thu, 13 Nov 2025 09:09:22 -0300 +Subject: [PATCH] controlled fullscreen fix + +--- + dwl.c | 32 ++++++++++++-------------------- + 1 file changed, 12 insertions(+), 20 deletions(-) + +diff --git a/dwl.c b/dwl.c +index 12f441e..4f124eb 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -518,9 +518,6 @@ arrange(Monitor *m) + } + } + +- wlr_scene_node_set_enabled(&m->fullscreen_bg->node, +- (c = focustop(m)) && c->isfullscreen); +- + strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, LENGTH(m->ltsymbol)); + + /* We move all clients (except fullscreen and unmanaged) to LyrTile while +@@ -878,7 +875,7 @@ commitnotify(struct wl_listener *listener, void *data) + return; + } + +- resize(c, c->geom, (c->isfloating && !c->isfullscreen)); ++ resize(c, c->geom, (c->isfloating)); + + /* mark a pending resize as completed */ + if (c->resize && c->resize <= c->surface.xdg->current.configure_serial) +@@ -1490,7 +1487,7 @@ focusstack(const Arg *arg) + { + /* Focus the next or previous client (in tiling order) on selmon */ + Client *c, *sel = focustop(selmon); +- if (!sel || (sel->isfullscreen && !client_has_children(sel))) ++ if (!sel) + return; + if (arg->i > 0) { + wl_list_for_each(c, &sel->link, link) { +@@ -1826,7 +1823,7 @@ monocle(Monitor *m) + int n = 0; + + wl_list_for_each(c, &clients, link) { +- if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) ++ if (!VISIBLEON(c, m) || c->isfloating) + continue; + resize(c, m->w, 0); + n++; +@@ -1957,7 +1954,7 @@ moveresize(const Arg *arg) + if (cursor_mode != CurNormal && cursor_mode != CurPressed) + return; + xytonode(cursor->x, cursor->y, NULL, &grabc, NULL, NULL, NULL); +- if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen) ++ if (!grabc || client_is_unmanaged(grabc)) + return; + + /* Float the window and tell motionnotify to grab it */ +@@ -2332,14 +2329,12 @@ setcursorshape(struct wl_listener *listener, void *data) + void + setfloating(Client *c, int floating) + { +- Client *p = client_get_parent(c); + c->isfloating = floating; + /* If in floating layout do not change the client's layer */ + if (!c->mon || !client_surface(c)->mapped || !c->mon->lt[c->mon->sellt]->arrange) + return; +- wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen || +- (p && p->isfullscreen) ? LyrFS +- : c->isfloating ? LyrFloat : LyrTile]); ++ wlr_scene_node_reparent(&c->scene->node, ++ layers[c->isfloating ? LyrFloat : LyrTile]); + arrange(c->mon); + printstatus(); + } +@@ -2352,12 +2347,12 @@ setfullscreen(Client *c, int fullscreen) + return; + c->bw = fullscreen ? 0 : borderpx; + client_set_fullscreen(c, fullscreen); +- wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen +- ? LyrFS : c->isfloating ? LyrFloat : LyrTile]); + ++ wlr_scene_node_reparent(&c->scene->node, ++ layers[c->isfloating ? LyrFloat : LyrTile]); + if (fullscreen) { + c->prev = c->geom; +- resize(c, c->mon->m, 0); ++ resize(c, c->mon->w, 0); + } else { + /* restore previous size instead of arrange for floating windows since + * client positions are set by the user and cannot be recalculated */ +@@ -2716,7 +2711,7 @@ tile(Monitor *m) + Client *c; + + wl_list_for_each(c, &clients, link) +- if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen) ++ if (VISIBLEON(c, m) && !c->isfloating) + n++; + if (n == 0) + return; +@@ -2727,7 +2722,7 @@ tile(Monitor *m) + mw = m->w.width; + i = my = ty = 0; + wl_list_for_each(c, &clients, link) { +- if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) ++ if (!VISIBLEON(c, m) || c->isfloating) + continue; + if (i < m->nmaster) { + resize(c, (struct wlr_box){.x = m->w.x, .y = m->w.y + my, .width = mw, +@@ -2747,7 +2742,7 @@ togglefloating(const Arg *arg) + { + Client *sel = focustop(selmon); + /* return if fullscreen */ +- if (sel && !sel->isfullscreen) ++ if (sel) + setfloating(sel, !sel->isfloating); + } + +@@ -2902,9 +2897,6 @@ updatemons(struct wl_listener *listener, void *data) + arrangelayers(m); + /* Don't move clients to the left output when plugging monitors */ + arrange(m); +- /* make sure fullscreen clients have the right size */ +- if ((c = focustop(m)) && c->isfullscreen) +- resize(c, m->m, 0); + + /* Try to re-set the gamma LUT when updating monitors, + * it's only really needed when enabling a disabled output, but meh. */ +-- +2.51.0 + From 1b490e51a9874199d936d09bd9246a576305cf44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Desgualdo=20Pereira?= Date: Sat, 14 Mar 2026 17:56:12 -0300 Subject: [PATCH 16/36] update focusonurgent patch to dwl 0.8 --- patches/focusonurgent/README.md | 3 +-- patches/focusonurgent/focusonurgent.patch | 10 +++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/patches/focusonurgent/README.md b/patches/focusonurgent/README.md index 2bef3e4..82927fb 100644 --- a/patches/focusonurgent/README.md +++ b/patches/focusonurgent/README.md @@ -7,8 +7,7 @@ This is the approximately the equivalent of the focusonactive patch of dwm. If you want a more controlled behavior, for example setting which clients can focus, check [activation-rule patch](https://codeberg.org/sevz/dwl-patches/src/branch/activation-rules). ### Download -- [git branch](https://codeberg.org/Kana/dwl/src/branch/focusonurgent) -- [main 2025-10-08](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/focusonurgent/focusonurgent.patch) +- [main dwl 0.8](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/focusonurgent/focusonurgent.patch) ### Authors - [André Desgualdo Pereira](https://codeberg.org/Kana) diff --git a/patches/focusonurgent/focusonurgent.patch b/patches/focusonurgent/focusonurgent.patch index 988090c..c28b652 100644 --- a/patches/focusonurgent/focusonurgent.patch +++ b/patches/focusonurgent/focusonurgent.patch @@ -1,17 +1,17 @@ -From 3613d9a6342fc85279a79ba203f25ff39fc0d8e4 Mon Sep 17 00:00:00 2001 +From 7cdba8df596d59a3ad2883521a65962dcd4fe644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Desgualdo=20Pereira?= -Date: Wed, 29 Oct 2025 09:06:00 -0300 -Subject: [PATCH] add focusonurgent patch +Date: Sat, 14 Mar 2026 17:52:25 -0300 +Subject: [PATCH] update --- dwl.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c -index 12f441e..5620f66 100644 +index 101a45f..6cd2f82 100644 --- a/dwl.c +++ b/dwl.c -@@ -2097,8 +2097,10 @@ printstatus(void) +@@ -2099,8 +2099,10 @@ printstatus(void) if (c->mon != m) continue; occ |= c->tags; From 3ade6519ac69ddb104cb2c926af0f964ab44bbce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Desgualdo=20Pereira?= Date: Sat, 14 Mar 2026 18:00:23 -0300 Subject: [PATCH 17/36] moved gridall to stale --- {patches => stale-patches}/gridall/README.md | 0 {patches => stale-patches}/gridall/gridall.diff | 0 .../gridall/winview+gaplessgrid+gridall.diff | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename {patches => stale-patches}/gridall/README.md (100%) rename {patches => stale-patches}/gridall/gridall.diff (100%) rename {patches => stale-patches}/gridall/winview+gaplessgrid+gridall.diff (100%) diff --git a/patches/gridall/README.md b/stale-patches/gridall/README.md similarity index 100% rename from patches/gridall/README.md rename to stale-patches/gridall/README.md diff --git a/patches/gridall/gridall.diff b/stale-patches/gridall/gridall.diff similarity index 100% rename from patches/gridall/gridall.diff rename to stale-patches/gridall/gridall.diff diff --git a/patches/gridall/winview+gaplessgrid+gridall.diff b/stale-patches/gridall/winview+gaplessgrid+gridall.diff similarity index 100% rename from patches/gridall/winview+gaplessgrid+gridall.diff rename to stale-patches/gridall/winview+gaplessgrid+gridall.diff From 722f0573eb4186dd936ae09f74c2ca1fcc0480f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Desgualdo=20Pereira?= Date: Sat, 14 Mar 2026 18:38:42 -0300 Subject: [PATCH 18/36] update winview to dwl 0.8 --- patches/winview/README.md | 3 +- .../{winview.patch => winview_dwl_07.patch} | 0 patches/winview/winview_dwl_08.patch | 55 +++++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) rename patches/winview/{winview.patch => winview_dwl_07.patch} (100%) create mode 100644 patches/winview/winview_dwl_08.patch diff --git a/patches/winview/README.md b/patches/winview/README.md index 1ab4387..0a3471c 100644 --- a/patches/winview/README.md +++ b/patches/winview/README.md @@ -26,7 +26,8 @@ This patch is inspired from . Citing > An example of how to insert this line can be found in the default config file template, config.def.h. ### Download -- [2024-09-18](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/winview/winview.patch) +- [2026-03-14 dwl 0.8](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/winview/winview_dwl_08.patch) +- [2024-09-18](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/winview/winview_dwl_07.patch) - [2024-06-06](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/winview/winview-20240606.patch) - [git branch](https://codeberg.org/dhruva_sambrani/dwl/src/branch/winview) diff --git a/patches/winview/winview.patch b/patches/winview/winview_dwl_07.patch similarity index 100% rename from patches/winview/winview.patch rename to patches/winview/winview_dwl_07.patch diff --git a/patches/winview/winview_dwl_08.patch b/patches/winview/winview_dwl_08.patch new file mode 100644 index 0000000..a2b77e1 --- /dev/null +++ b/patches/winview/winview_dwl_08.patch @@ -0,0 +1,55 @@ +From d53f64b3baee1e14c59b9b6cead024b91e185bd7 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Andr=C3=A9=20Desgualdo=20Pereira?= +Date: Sat, 14 Mar 2026 18:34:36 -0300 +Subject: [PATCH] update to dwl 0.8 + +--- + config.def.h | 1 + + dwl.c | 12 ++++++++++++ + 2 files changed, 13 insertions(+) + +diff --git a/config.def.h b/config.def.h +index 8a6eda0..3c1f16b 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -139,6 +139,7 @@ static const Key keys[] = { + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} }, + { MODKEY, XKB_KEY_e, togglefullscreen, {0} }, + { MODKEY, XKB_KEY_0, view, {.ui = ~0} }, ++ { MODKEY, XKB_KEY_o, winview, {0}}, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} }, + { MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} }, + { MODKEY, XKB_KEY_period, focusmon, {.i = WLR_DIRECTION_RIGHT} }, +diff --git a/dwl.c b/dwl.c +index 101a45f..f3b75e0 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -348,6 +348,7 @@ static void urgent(struct wl_listener *listener, void *data); + static void view(const Arg *arg); + static void virtualkeyboard(struct wl_listener *listener, void *data); + static void virtualpointer(struct wl_listener *listener, void *data); ++static void winview(const Arg *a); + 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); +@@ -3006,6 +3007,17 @@ virtualpointer(struct wl_listener *listener, void *data) + wlr_cursor_map_input_to_output(cursor, device, event->suggested_output); + } + ++void ++winview(const Arg *a) { ++ Arg b = {0}; ++ Client *sel = focustop(selmon); ++ if(!sel) ++ return; ++ b.ui = sel -> tags; ++ view(&b); ++ return; ++} ++ + Monitor * + xytomon(double x, double y) + { +-- +2.51.0 + From 000aa7d15c9dbb34a251c767d24c847963b729e4 Mon Sep 17 00:00:00 2001 From: nate zhou Date: Sun, 8 Mar 2026 11:31:44 +0800 Subject: [PATCH 19/36] genericgaps: update for 0.8 --- patches/genericgaps/README.md | 5 +- patches/genericgaps/genericgaps.patch | 292 ++++++++++++++------------ 2 files changed, 161 insertions(+), 136 deletions(-) diff --git a/patches/genericgaps/README.md b/patches/genericgaps/README.md index f27897d..c1d0ee3 100644 --- a/patches/genericgaps/README.md +++ b/patches/genericgaps/README.md @@ -15,10 +15,11 @@ gaps to be as big or bigger than "inner" gaps anyway. ### Download -- [0.7 2025-02-11](/dwl/dwl-patches/raw/branch/main/patches/genericgaps/genericgaps-0.7.patch): +- [0.8](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/genericgaps/genericgaps.patch) +- [0.7 2025-02-11](https://codeberg.org/dwl/dwl-patches/raw/commit/58e371fcb31bde1e55f99498a77afd9b567a30c8/patches/genericgaps/genericgaps-0.7.patch): added support for `smartgaps` and `monoclegaps` settings and removed the suck from `arrange()` function -- [0.7](/dwl/dwl-patches/raw/branch/main/patches/genericgaps/genericgaps.patch) +- [0.7](https://codeberg.org/dwl/dwl-patches/raw/commit/f8d1cfad116c19c01593f7436468ec0cb7a3297b/patches/genericgaps/genericgaps.patch) ### Authors diff --git a/patches/genericgaps/genericgaps.patch b/patches/genericgaps/genericgaps.patch index aa62b92..5990890 100644 --- a/patches/genericgaps/genericgaps.patch +++ b/patches/genericgaps/genericgaps.patch @@ -1,23 +1,25 @@ -From f2a1b84369266d252fbea57c6f1eb64253617452 Mon Sep 17 00:00:00 2001 -From: Nikita Ivanov -Date: Thu, 12 Dec 2024 22:44:53 +0100 -Subject: [PATCH] Add genericgaps +From ac0b983fef16eb904b77d6b5e98bd61f5cf719f5 Mon Sep 17 00:00:00 2001 +From: nate zhou +Date: Sat, 28 Feb 2026 23:28:08 +0800 +Subject: [PATCH] Patch: genericgaps-0.8.patch --- - config.def.h | 22 +++++++ - dwl.c | 171 ++++++++++++++++++++++++++++++++++++++++++++++++++- - 2 files changed, 190 insertions(+), 3 deletions(-) + config.def.h | 23 ++++++ + dwl.c | 198 ++++++++++++++++++++++++++++++++++++++++++++++++++- + 2 files changed, 220 insertions(+), 1 deletion(-) diff --git a/config.def.h b/config.def.h -index 22d2171..930af12 100644 +index 8a6eda0..4adc866 100644 --- a/config.def.h +++ b/config.def.h -@@ -7,6 +7,12 @@ +@@ -6,7 +6,14 @@ + /* appearance */ static const int sloppyfocus = 1; /* focus follows mouse */ static const int bypass_surface_visibility = 0; /* 1 means idle inhibitors will disable idle tracking even if it's surface isn't visible */ - static const unsigned int borderpx = 1; /* border pixel of windows */ ++static const int enablegaps = 1; /* 1 means gaps are enabled */ +static const int smartgaps = 0; /* 1 means no outer gap when there is only one window */ +static const int monoclegaps = 0; /* 1 means outer gaps in monocle layout */ + static const unsigned int borderpx = 1; /* border pixel of windows */ +static const unsigned int gappih = 10; /* horiz inner gap between windows */ +static const unsigned int gappiv = 10; /* vert inner gap between windows */ +static const unsigned int gappoh = 10; /* horiz outer gap between windows and screen edge */ @@ -25,45 +27,38 @@ index 22d2171..930af12 100644 static const float rootcolor[] = COLOR(0x222222ff); static const float bordercolor[] = COLOR(0x444444ff); static const float focuscolor[] = COLOR(0x005577ff); -@@ -133,6 +139,22 @@ static const Key keys[] = { - { MODKEY, XKB_KEY_d, incnmaster, {.i = -1} }, - { MODKEY, XKB_KEY_h, setmfact, {.f = -0.05f} }, - { MODKEY, XKB_KEY_l, setmfact, {.f = +0.05f} }, -+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_h, incgaps, {.i = +1 } }, -+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_l, incgaps, {.i = -1 } }, -+ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_H, incogaps, {.i = +1 } }, -+ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_L, incogaps, {.i = -1 } }, -+ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_CTRL, XKB_KEY_h, incigaps, {.i = +1 } }, -+ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_CTRL, XKB_KEY_l, incigaps, {.i = -1 } }, -+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_0, togglegaps, {0} }, -+ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_parenright,defaultgaps, {0} }, -+ { MODKEY, XKB_KEY_y, incihgaps, {.i = +1 } }, -+ { MODKEY, XKB_KEY_o, incihgaps, {.i = -1 } }, -+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_y, incivgaps, {.i = +1 } }, -+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_o, incivgaps, {.i = -1 } }, -+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_y, incohgaps, {.i = +1 } }, -+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_o, incohgaps, {.i = -1 } }, -+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Y, incovgaps, {.i = +1 } }, -+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_O, incovgaps, {.i = -1 } }, - { MODKEY, XKB_KEY_Return, zoom, {0} }, - { MODKEY, XKB_KEY_Tab, view, {0} }, - { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_C, killclient, {0} }, +@@ -129,6 +136,22 @@ static const Key keys[] = { + { MODKEY, XKB_KEY_d, incnmaster, {.i = -1} }, + { MODKEY, XKB_KEY_h, setmfact, {.f = -0.05f} }, + { MODKEY, XKB_KEY_l, setmfact, {.f = +0.05f} }, ++ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_h, incgaps, {.i = +1 } }, ++ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_l, incgaps, {.i = -1 } }, ++ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_H, incogaps, {.i = +1 } }, ++ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_L, incogaps, {.i = -1 } }, ++ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_CTRL, XKB_KEY_h, incigaps, {.i = +1 } }, ++ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_CTRL, XKB_KEY_l, incigaps, {.i = -1 } }, ++ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_0, togglegaps, {0} }, ++ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_parenright,defaultgaps, {0} }, ++ { MODKEY, XKB_KEY_y, incihgaps, {.i = +1 } }, ++ { MODKEY, XKB_KEY_o, incihgaps, {.i = -1 } }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_y, incivgaps, {.i = +1 } }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_o, incivgaps, {.i = -1 } }, ++ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_y, incohgaps, {.i = +1 } }, ++ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_o, incohgaps, {.i = -1 } }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Y, incovgaps, {.i = +1 } }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_O, incovgaps, {.i = -1 } }, + { MODKEY, XKB_KEY_Return, zoom, {0} }, + { MODKEY, XKB_KEY_Tab, view, {0} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_c, killclient, {0} }, diff --git a/dwl.c b/dwl.c -index def2562..c14ae43 100644 +index 44f3ad9..a3164cb 100644 --- a/dwl.c +++ b/dwl.c -@@ -107,6 +107,7 @@ typedef struct Monitor Monitor; - typedef struct { - /* Must keep these three elements in this order */ - unsigned int type; /* XDGShell or X11* */ -+ int interact; - struct wlr_box geom; /* layout-relative, includes border */ - Monitor *mon; - struct wlr_scene_tree *scene; -@@ -200,6 +201,10 @@ struct Monitor { +@@ -197,6 +197,11 @@ struct Monitor { struct wlr_box w; /* window area, layout-relative */ struct wl_list layers[4]; /* LayerSurface.link */ const Layout *lt[2]; ++ int enablegaps; /* enable gaps, used by togglegaps */ + int gappih; /* horizontal gap between windows */ + int gappiv; /* vertical gap between windows */ + int gappoh; /* horizontal outer gaps */ @@ -71,7 +66,15 @@ index def2562..c14ae43 100644 unsigned int seltags; unsigned int sellt; uint32_t tagset[2]; -@@ -273,6 +278,7 @@ static void createpopup(struct wl_listener *listener, void *data); +@@ -243,6 +248,7 @@ typedef struct { + static void applybounds(Client *c, struct wlr_box *bbox); + static void applyrules(Client *c); + static void arrange(Monitor *m); ++void arrangegaps(Monitor *m); + static void arrangelayer(Monitor *m, struct wl_list *list, + struct wlr_box *usable_area, int exclusive); + static void arrangelayers(Monitor *m); +@@ -271,6 +277,7 @@ static void createpopup(struct wl_listener *listener, void *data); static void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint); static void cursorframe(struct wl_listener *listener, void *data); static void cursorwarptohint(void); @@ -79,7 +82,7 @@ index def2562..c14ae43 100644 static void destroydecoration(struct wl_listener *listener, void *data); static void destroydragicon(struct wl_listener *listener, void *data); static void destroyidleinhibitor(struct wl_listener *listener, void *data); -@@ -293,6 +299,13 @@ static void fullscreennotify(struct wl_listener *listener, void *data); +@@ -290,6 +297,13 @@ static void fullscreennotify(struct wl_listener *listener, void *data); static void gpureset(struct wl_listener *listener, void *data); static void handlesig(int signo); static void incnmaster(const Arg *arg); @@ -93,24 +96,23 @@ index def2562..c14ae43 100644 static void inputdevice(struct wl_listener *listener, void *data); static int keybinding(uint32_t mods, xkb_keysym_t sym); static void keypress(struct wl_listener *listener, void *data); -@@ -320,13 +333,15 @@ static void rendermon(struct wl_listener *listener, void *data); - static void requestdecorationmode(struct wl_listener *listener, void *data); - static void requeststartdrag(struct wl_listener *listener, void *data); - static void requestmonstate(struct wl_listener *listener, void *data); --static void resize(Client *c, struct wlr_box geo, int interact); -+static void resizeapply(Client *c, struct wlr_box geo, int interact); -+static void resizenoapply(Client *c, struct wlr_box geo, int interact); - static void run(char *startup_cmd); - static void setcursor(struct wl_listener *listener, void *data); +@@ -310,6 +324,7 @@ static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int + static void outputmgrtest(struct wl_listener *listener, void *data); + static void pointerfocus(Client *c, struct wlr_surface *surface, + double sx, double sy, uint32_t time); ++static void preparegaps(Monitor *m); + static void printstatus(void); + static void powermgrsetmode(struct wl_listener *listener, void *data); + static void quit(const Arg *arg); +@@ -323,6 +338,7 @@ static void setcursor(struct wl_listener *listener, void *data); static void setcursorshape(struct wl_listener *listener, void *data); static void setfloating(Client *c, int floating); static void setfullscreen(Client *c, int fullscreen); - static void setgamma(struct wl_listener *listener, void *data); +static void setgaps(int oh, int ov, int ih, int iv); static void setlayout(const Arg *arg); static void setmfact(const Arg *arg); static void setmon(Client *c, Monitor *m, uint32_t newtags); -@@ -340,6 +355,7 @@ static void tagmon(const Arg *arg); +@@ -336,6 +352,7 @@ static void tagmon(const Arg *arg); static void tile(Monitor *m); static void togglefloating(const Arg *arg); static void togglefullscreen(const Arg *arg); @@ -118,83 +120,82 @@ index def2562..c14ae43 100644 static void toggletag(const Arg *arg); static void toggleview(const Arg *arg); static void unlocksession(struct wl_listener *listener, void *data); -@@ -413,6 +429,9 @@ static struct wlr_box sgeom; - static struct wl_list mons; - static Monitor *selmon; +@@ -435,6 +452,7 @@ 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 int resizelock = 0; /* do not actually resize during arrange */ -+static int enablegaps = 1; /* enables gaps, used by togglegaps */ -+static void (*resize)(Client *c, struct wlr_box geo, int interact) = resizeapply; -+ #ifdef XWAYLAND static void activatex11(struct wl_listener *listener, void *data); - static void associatex11(struct wl_listener *listener, void *data); -@@ -481,9 +500,25 @@ applyrules(Client *c) - setmon(c, mon, newtags); - } - -+void -+applygaps(Client *c) -+{ -+ struct wlr_box geom = c->geom; -+ -+ if (!c->mon) -+ return; -+ -+ geom.x += c->mon->gappih + c->mon->gappoh; -+ geom.y += c->mon->gappiv + c->mon->gappov; -+ geom.width -= c->mon->gappih; -+ geom.height -= c->mon->gappiv; -+ resize(c, geom, 0); -+} -+ - void - arrange(Monitor *m) - { -+ int save_width, save_height; - Client *c; - - if (!m->wlr_output->enabled) -@@ -515,8 +550,26 @@ arrange(Monitor *m) +@@ -537,12 +555,52 @@ arrange(Monitor *m) : c->scene->node.parent); } - if (m->lt[m->sellt]->arrange) + if (m->lt[m->sellt]->arrange) { -+ save_width = m->w.width; -+ save_height = m->w.height; -+ if (enablegaps) { -+ m->w.width -= m->gappih + 2 * m->gappoh; -+ m->w.height -= m->gappiv + 2 * m->gappov; -+ } -+ resize = resizenoapply; ++ preparegaps(m); m->lt[m->sellt]->arrange(m); -+ wl_list_for_each(c, &clients, link) { -+ if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) -+ continue; -+ if (enablegaps) -+ applygaps(c); -+ resizeapply(c, c->geom, c->interact); -+ } -+ m->w.width = save_width; -+ m->w.height = save_height; -+ resize = resizeapply; ++ arrangegaps(m); + } motionnotify(0, NULL, 0, 0, 0, 0); checkidleinhibitor(NULL); } -@@ -993,6 +1046,11 @@ createmon(struct wl_listener *listener, void *data) - wlr_output_state_init(&state); - /* Initialize monitor state using configured rules */ ++void ++arrangegaps(Monitor *m) ++{ ++ Client *c; ++ int n, gaps; + ++ if (!m->enablegaps) ++ return; ++ ++ resizelock = 0; ++ ++ n = 0; ++ wl_list_for_each(c, &clients, link) { ++ if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) ++ continue; ++ n++; ++ } ++ ++ gaps = !(smartgaps && n == 1) && ++ (monoclegaps || m->lt[m->sellt]->arrange != monocle); ++ if (gaps) { ++ m->w.width += m->gappih + 2 * m->gappoh; ++ m->w.height += m->gappiv + 2 * m->gappov; ++ } ++ wl_list_for_each(c, &clients, link) { ++ if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) ++ continue; ++ if (gaps) { ++ c->geom.x += c->mon->gappih + c->mon->gappoh; ++ c->geom.y += c->mon->gappiv + c->mon->gappov; ++ c->geom.width -= c->mon->gappih; ++ c->geom.height -= c->mon->gappiv; ++ } ++ resize(c, c->geom, 0); ++ } ++} ++ + void + arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, int exclusive) + { +@@ -1055,6 +1113,12 @@ createmon(struct wl_listener *listener, void *data) + for (i = 0; i < LENGTH(m->layers); i++) + wl_list_init(&m->layers[i]); + ++ m->enablegaps = enablegaps; + m->gappih = gappih; + m->gappiv = gappiv; + m->gappoh = gappoh; + m->gappov = gappov; ++ + wlr_output_state_init(&state); + /* Initialize monitor state using configured rules */ m->tagset[0] = m->tagset[1] = 1; - for (r = monrules; r < END(monrules); r++) { - if (!r->name || strstr(wlr_output->name, r->name)) { -@@ -1173,6 +1231,12 @@ cursorwarptohint(void) +@@ -1237,6 +1301,12 @@ cursorwarptohint(void) } } @@ -207,7 +208,7 @@ index def2562..c14ae43 100644 void destroydecoration(struct wl_listener *listener, void *data) { -@@ -1526,6 +1590,83 @@ incnmaster(const Arg *arg) +@@ -1575,6 +1645,83 @@ incnmaster(const Arg *arg) arrange(selmon); } @@ -291,31 +292,52 @@ index def2562..c14ae43 100644 void inputdevice(struct wl_listener *listener, void *data) { -@@ -2180,7 +2321,7 @@ requestmonstate(struct wl_listener *listener, void *data) - } - - void --resize(Client *c, struct wlr_box geo, int interact) -+resizeapply(Client *c, struct wlr_box geo, int interact) - { - struct wlr_box *bbox; - struct wlr_box clip; -@@ -2212,6 +2353,13 @@ resize(Client *c, struct wlr_box geo, int interact) - wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip); +@@ -2085,6 +2232,31 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, + wlr_seat_pointer_notify_motion(seat, time, sx, sy); } +void -+resizenoapply(Client *c, struct wlr_box geo, int interact) ++preparegaps(Monitor *m) +{ -+ c->geom = geo; -+ c->interact = interact; ++ Client *c; ++ int n; ++ ++ if (!m->enablegaps) ++ return; ++ ++ n = 0; ++ wl_list_for_each(c, &clients, link) { ++ if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) ++ continue; ++ n++; ++ } ++ ++ resizelock = 1; ++ ++ if ((smartgaps && n == 1) || (!monoclegaps && m->lt[m->sellt]->arrange == monocle)) ++ return; ++ ++ m->w.width -= m->gappih + 2 * m->gappoh; ++ m->w.height -= m->gappiv + 2 * m->gappov; +} + void - run(char *startup_cmd) + printstatus(void) { -@@ -2354,6 +2502,16 @@ setgamma(struct wl_listener *listener, void *data) - wlr_output_schedule_frame(m->wlr_output); +@@ -2208,6 +2380,11 @@ resize(Client *c, struct wlr_box geo, int interact) + struct wlr_box *bbox; + struct wlr_box clip; + ++ if (resizelock) { ++ c->geom = geo; ++ return; ++ } ++ + if (!c->mon || !client_surface(c)->mapped) + return; + +@@ -2368,6 +2545,16 @@ setfullscreen(Client *c, int fullscreen) + printstatus(); } +void @@ -331,14 +353,16 @@ index def2562..c14ae43 100644 void setlayout(const Arg *arg) { -@@ -2741,6 +2899,13 @@ togglefullscreen(const Arg *arg) +@@ -2760,6 +2947,15 @@ togglefullscreen(const Arg *arg) setfullscreen(sel, !sel->isfullscreen); } +void +togglegaps(const Arg *arg) +{ -+ enablegaps = !enablegaps; ++ if (!selmon) ++ return; ++ selmon->enablegaps = !selmon->enablegaps; + arrange(selmon); +} + @@ -346,5 +370,5 @@ index def2562..c14ae43 100644 toggletag(const Arg *arg) { -- -2.47.1 +2.53.0 From ee54a3413cacd705e044fdabb65beef20c370419 Mon Sep 17 00:00:00 2001 From: nate zhou Date: Sun, 15 Mar 2026 14:30:44 +0800 Subject: [PATCH 20/36] bar-modes: show status text when space permits Try to truncate title before hiding status, with longer modes labels. --- patches/bar-modes/bar-modes.patch | 69 ++++++++++++++++++------------- 1 file changed, 41 insertions(+), 28 deletions(-) diff --git a/patches/bar-modes/bar-modes.patch b/patches/bar-modes/bar-modes.patch index 8e239a2..dcab50f 100644 --- a/patches/bar-modes/bar-modes.patch +++ b/patches/bar-modes/bar-modes.patch @@ -1,4 +1,4 @@ -From 4f2c8a99720d90a551bf38f2c8d25ad239346eef Mon Sep 17 00:00:00 2001 +From 04b37902e0098fb69250fd25b5afbc610a284529 Mon Sep 17 00:00:00 2001 From: nate zhou Date: Mon, 2 Mar 2026 21:54:03 +0800 Subject: [PATCH] Patch: bar-modes for 0.8 @@ -6,19 +6,22 @@ Subject: [PATCH] Patch: bar-modes for 0.8 Add modes_labels indicator to bar, which behaves like river-classic's dam bar. This patch has to be applied after the bar and modes patch. --- - dwl.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++---------- - 1 file changed, 59 insertions(+), 11 deletions(-) + dwl.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++----------- + 1 file changed, 72 insertions(+), 15 deletions(-) diff --git a/dwl.c b/dwl.c -index ae290ad..df525d3 100644 +index ae290ad..7229034 100644 --- a/dwl.c +++ b/dwl.c -@@ -1585,19 +1585,26 @@ drawbar(Monitor *m) +@@ -1585,19 +1585,29 @@ drawbar(Monitor *m) uint32_t i, occ = 0, urg = 0; Client *c; Buffer *buf; + char mode_text[256] = ""; + int mode_width = 0; ++ int title_width; ++ int remaining; ++ int status_shown = 0; if (!m->scene_buffer->node.enabled) return; @@ -45,7 +48,7 @@ index ae290ad..df525d3 100644 wl_list_for_each(c, &clients, link) { if (c->mon != m) continue; -@@ -1607,6 +1614,8 @@ drawbar(Monitor *m) +@@ -1607,6 +1617,8 @@ drawbar(Monitor *m) } x = 0; c = focustop(m); @@ -54,7 +57,7 @@ index ae290ad..df525d3 100644 for (i = 0; i < LENGTH(tags); i++) { w = TEXTW(m, tags[i]); drwl_setscheme(m->drw, colors[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); -@@ -1617,19 +1626,57 @@ drawbar(Monitor *m) +@@ -1617,19 +1629,63 @@ drawbar(Monitor *m) urg & 1 << i); x += w; } @@ -70,30 +73,40 @@ index ae290ad..df525d3 100644 w = TEXTW(m, m->ltsymbol); drwl_setscheme(m->drw, colors[SchemeNorm]); - x = drwl_text(m->drw, x, 0, w, m->b.height, m->lrpad / 2, m->ltsymbol, 0); +- +- if ((w = m->b.width - tw - x) > m->b.height) { +- if (c) { +- drwl_setscheme(m->drw, colors[m == selmon ? SchemeSel : SchemeNorm]); +- drwl_text(m->drw, x, 0, w, m->b.height, m->lrpad / 2, client_get_title(c), 0); +- if (c && c->isfloating) +- drwl_rect(m->drw, x + boxs, boxs, boxw, boxw, 0, 0); +- } else { + drwl_text(m->drw, x, 0, w, m->b.height, m->lrpad / 2, m->ltsymbol, 0); + x += w; + -+ int remaining = m->b.width - x; - -- if ((w = m->b.width - tw - x) > m->b.height) { -- if (c) { -+ if (mode_text[0]) { -+ int title_width = remaining; ++ remaining = m->b.width - x; + -+ if (title_width > m->b.height && c) { - drwl_setscheme(m->drw, colors[m == selmon ? SchemeSel : SchemeNorm]); -- drwl_text(m->drw, x, 0, w, m->b.height, m->lrpad / 2, client_get_title(c), 0); -+ drwl_text(m->drw, x, 0, title_width, m->b.height, m->lrpad / 2, -+ client_get_title(c), 0); - if (c && c->isfloating) - drwl_rect(m->drw, x + boxs, boxs, boxw, boxw, 0, 0); -- } else { -+ } else if (title_width > 0) { -+ drwl_setscheme(m->drw, colors[SchemeNorm]); -+ drwl_rect(m->drw, x, 0, title_width, m->b.height, 1, 1); -+ } -+ /* no status text when in a mode - completely omitted */ -+ } else { ++ if (mode_text[0] && remaining >= tw) { ++ drwl_setscheme(m->drw, colors[SchemeNorm]); ++ drwl_text(m->drw, m->b.width - tw, 0, tw, m->b.height, 0, stext, 0); ++ remaining -= tw; ++ status_shown = 1; ++ } ++ ++ title_width = remaining; ++ ++ if (c && title_width > m->b.height) { ++ drwl_setscheme(m->drw, colors[m == selmon ? SchemeSel : SchemeNorm]); ++ drwl_text(m->drw, x, 0, title_width, m->b.height, m->lrpad / 2, ++ client_get_title(c), 0); ++ if (c && c->isfloating) ++ drwl_rect(m->drw, x + boxs, boxs, boxw, boxw, 0, 0); ++ } else if (title_width > 0) { ++ drwl_setscheme(m->drw, colors[SchemeNorm]); ++ drwl_rect(m->drw, x, 0, title_width, m->b.height, 1, 1); ++ } ++ ++ if (!mode_text[0]) { + /* not in a mode - normal behavior with status */ + if (remaining >= tw) { + drwl_setscheme(m->drw, colors[SchemeNorm]); @@ -118,7 +131,7 @@ index ae290ad..df525d3 100644 } } -@@ -3437,6 +3484,7 @@ entermode(const Arg *arg) +@@ -3437,6 +3493,7 @@ entermode(const Arg *arg) { active_mode_index = arg->i; printstatus(); From abba4e9ad02c536ee6679a6309bc1ded6d25923a Mon Sep 17 00:00:00 2001 From: A Frederick Christensen Date: Thu, 26 Feb 2026 17:22:54 -0600 Subject: [PATCH 21/36] simple_scratchpad: Update for 0.8 and for wlroots-next-f4249db --- patches/simple_scratchpad/README.md | 3 +- ...v0.7.patch => simple_scratchpad-0.8.patch} | 66 +++--- ...mple_scratchpad-wlroots-next-f4249db.patch | 219 ++++++++++++++++++ 3 files changed, 254 insertions(+), 34 deletions(-) rename patches/simple_scratchpad/{simple_scratchpad-v0.7.patch => simple_scratchpad-0.8.patch} (75%) create mode 100644 patches/simple_scratchpad/simple_scratchpad-wlroots-next-f4249db.patch diff --git a/patches/simple_scratchpad/README.md b/patches/simple_scratchpad/README.md index 8076d96..d7918bb 100644 --- a/patches/simple_scratchpad/README.md +++ b/patches/simple_scratchpad/README.md @@ -19,7 +19,8 @@ Adds functions to add, toggle and remove clients to/from scratchpad client list. ### Download - [git branch](https://codeberg.org/julmajustus/dwl/src/branch/simple_scratchpad) -- [0.7](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/simple_scratchpad/simple_scratchpad-v0.7.patch) +- [scratchpad-wlroots-next-f4249db.patch](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/simple_scratchpad/simple_scratchpad-f4249db.patch) +- [scratchpad-0.8.patch](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/simple_scratchpad/simple_scratchpad-0.8.patch) ### Authors - [julmajustus](https://codeberg.org/julmajustus) diff --git a/patches/simple_scratchpad/simple_scratchpad-v0.7.patch b/patches/simple_scratchpad/simple_scratchpad-0.8.patch similarity index 75% rename from patches/simple_scratchpad/simple_scratchpad-v0.7.patch rename to patches/simple_scratchpad/simple_scratchpad-0.8.patch index a273559..0d8af99 100644 --- a/patches/simple_scratchpad/simple_scratchpad-v0.7.patch +++ b/patches/simple_scratchpad/simple_scratchpad-0.8.patch @@ -1,34 +1,34 @@ -From a8d29e03f565b54a68c6c2cb2da103366c627825 Mon Sep 17 00:00:00 2001 -From: julmajustus -Date: Sat, 4 Jan 2025 13:22:12 +0200 -Subject: [PATCH] add simple_scratchpad +From 0d603b179ccccbb4270fd202a14a2db18d0a5413 Mon Sep 17 00:00:00 2001 +From: A Frederick Christensen +Date: Thu, 26 Feb 2026 17:09:28 -0600 +Subject: [PATCH] Apply simple_scratchpad patch --- config.def.h | 3 ++ dwl.c | 34 +++++++++++++++++++++-- - simple_scratchpad.c | 67 +++++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 102 insertions(+), 2 deletions(-) + simple_scratchpad.c | 68 +++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 simple_scratchpad.c diff --git a/config.def.h b/config.def.h -index 22d2171..83f19b3 100644 +index 8a6eda0..6254fc9 100644 --- a/config.def.h +++ b/config.def.h -@@ -136,6 +136,9 @@ static const Key keys[] = { - { MODKEY, XKB_KEY_Return, zoom, {0} }, - { MODKEY, XKB_KEY_Tab, view, {0} }, - { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_C, killclient, {0} }, -+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Z, addscratchpad, {0} }, -+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_z, togglescratchpad, {0} }, -+ { MODKEY, XKB_KEY_z, removescratchpad, {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]} }, +@@ -132,6 +132,9 @@ static const Key keys[] = { + { MODKEY, XKB_KEY_Return, zoom, {0} }, + { MODKEY, XKB_KEY_Tab, view, {0} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_c, killclient, {0} }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_z, addscratchpad, {0} }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_z, togglescratchpad, {0} }, ++ { MODKEY, XKB_KEY_z, removescratchpad, {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]} }, diff --git a/dwl.c b/dwl.c -index a2711f6..85f4a51 100644 +index 44f3ad9..215f630 100644 --- a/dwl.c +++ b/dwl.c -@@ -141,6 +141,7 @@ typedef struct { +@@ -139,6 +139,7 @@ typedef struct { uint32_t tags; int isfloating, isurgent, isfullscreen; uint32_t resize; /* configure serial of a pending resize */ @@ -36,7 +36,7 @@ index a2711f6..85f4a51 100644 } Client; typedef struct { -@@ -243,6 +244,7 @@ typedef struct { +@@ -240,6 +241,7 @@ typedef struct { } SessionLock; /* function declarations */ @@ -44,7 +44,7 @@ index a2711f6..85f4a51 100644 static void applybounds(Client *c, struct wlr_box *bbox); static void applyrules(Client *c); static void arrange(Monitor *m); -@@ -317,6 +319,7 @@ static void printstatus(void); +@@ -314,6 +316,7 @@ static void printstatus(void); static void powermgrsetmode(struct wl_listener *listener, void *data); static void quit(const Arg *arg); static void rendermon(struct wl_listener *listener, void *data); @@ -52,7 +52,7 @@ index a2711f6..85f4a51 100644 static void requestdecorationmode(struct wl_listener *listener, void *data); static void requeststartdrag(struct wl_listener *listener, void *data); static void requestmonstate(struct wl_listener *listener, void *data); -@@ -340,6 +343,7 @@ static void tagmon(const Arg *arg); +@@ -336,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); @@ -60,17 +60,17 @@ index a2711f6..85f4a51 100644 static void toggletag(const Arg *arg); static void toggleview(const Arg *arg); static void unlocksession(struct wl_listener *listener, void *data); -@@ -413,6 +417,9 @@ static struct wlr_box sgeom; +@@ -406,6 +410,9 @@ static struct wlr_box sgeom; static struct wl_list mons; static Monitor *selmon; +static struct wl_list scratchpad_clients; +static int scratchpad_visible = 1; + - #ifdef XWAYLAND - static void activatex11(struct wl_listener *listener, void *data); - static void associatex11(struct wl_listener *listener, void *data); -@@ -432,6 +439,8 @@ static xcb_atom_t netatom[NetLast]; + /* global event handlers */ + static struct wl_listener cursor_axis = {.notify = axisnotify}; + static struct wl_listener cursor_button = {.notify = buttonpress}; +@@ -455,6 +462,8 @@ static struct wlr_xwayland *xwayland; /* attempt to encapsulate suck into one file */ #include "client.h" @@ -79,7 +79,7 @@ index a2711f6..85f4a51 100644 /* function implementations */ void applybounds(Client *c, struct wlr_box *bbox) -@@ -1259,10 +1268,20 @@ void +@@ -1328,10 +1337,20 @@ void destroynotify(struct wl_listener *listener, void *data) { /* Called when the xdg_toplevel is destroyed. */ @@ -101,7 +101,7 @@ index a2711f6..85f4a51 100644 #ifdef XWAYLAND if (c->type != XDGShell) { wl_list_remove(&c->activate.link); -@@ -2306,11 +2325,21 @@ setcursorshape(struct wl_listener *listener, void *data) +@@ -2333,11 +2352,21 @@ setcursorshape(struct wl_listener *listener, void *data) void setfloating(Client *c, int floating) { @@ -124,17 +124,17 @@ index a2711f6..85f4a51 100644 wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen || (p && p->isfullscreen) ? LyrFS : c->isfloating ? LyrFloat : LyrTile]); -@@ -2534,6 +2563,7 @@ setup(void) +@@ -2554,6 +2583,7 @@ setup(void) */ wl_list_init(&clients); wl_list_init(&fstack); + wl_list_init(&scratchpad_clients); xdg_shell = wlr_xdg_shell_create(dpy, 6); - LISTEN_STATIC(&xdg_shell->events.new_toplevel, createnotify); + wl_signal_add(&xdg_shell->events.new_toplevel, &new_xdg_toplevel); diff --git a/simple_scratchpad.c b/simple_scratchpad.c new file mode 100644 -index 0000000..381f4b5 +index 0000000..26d6b66 --- /dev/null +++ b/simple_scratchpad.c @@ -0,0 +1,68 @@ @@ -207,5 +207,5 @@ index 0000000..381f4b5 + } +} -- -2.45.2 +2.52.0 diff --git a/patches/simple_scratchpad/simple_scratchpad-wlroots-next-f4249db.patch b/patches/simple_scratchpad/simple_scratchpad-wlroots-next-f4249db.patch new file mode 100644 index 0000000..690fa99 --- /dev/null +++ b/patches/simple_scratchpad/simple_scratchpad-wlroots-next-f4249db.patch @@ -0,0 +1,219 @@ +From 94e649317328c7af091516fe3441ea80a814d1e4 Mon Sep 17 00:00:00 2001 +From: A Frederick Christensen +Date: Thu, 26 Feb 2026 17:18:17 -0600 +Subject: [PATCH] Apply simple_scratchpad patch + +--- + config.def.h | 4 ++- + dwl.c | 34 +++++++++++++++++++++-- + simple_scratchpad.c | 68 +++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 103 insertions(+), 3 deletions(-) + create mode 100644 simple_scratchpad.c + +diff --git a/config.def.h b/config.def.h +index 8a6eda0..bd6cb40 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -132,6 +132,9 @@ static const Key keys[] = { + { MODKEY, XKB_KEY_Return, zoom, {0} }, + { MODKEY, XKB_KEY_Tab, view, {0} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_c, killclient, {0} }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_z, addscratchpad, {0} }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_z, togglescratchpad, {0} }, ++ { MODKEY, XKB_KEY_z, removescratchpad, {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]} }, +@@ -154,7 +157,6 @@ static const Key keys[] = { + TAGKEYS( XKB_KEY_8, XKB_KEY_asterisk, 7), + TAGKEYS( XKB_KEY_9, XKB_KEY_parenleft, 8), + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_q, quit, {0} }, +- + /* Ctrl-Alt-Backspace and Ctrl-Alt-Fx used to be handled by X server */ + { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_Terminate_Server, quit, {0} }, + /* Ctrl-Alt-Fx is used to switch to another VT, if you don't know what a VT is +diff --git a/dwl.c b/dwl.c +index 8a9715d..6ae7707 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -141,6 +141,7 @@ typedef struct { + uint32_t tags; + int isfloating, isurgent, isfullscreen; + uint32_t resize; /* configure serial of a pending resize */ ++ struct wl_list link_temp; + } Client; + + typedef struct { +@@ -242,6 +243,7 @@ typedef struct { + } SessionLock; + + /* function declarations */ ++static void addscratchpad(const Arg *arg); + static void applybounds(Client *c, struct wlr_box *bbox); + static void applyrules(Client *c); + static void arrange(Monitor *m); +@@ -316,6 +318,7 @@ static void printstatus(void); + static void powermgrsetmode(struct wl_listener *listener, void *data); + static void quit(const Arg *arg); + static void rendermon(struct wl_listener *listener, void *data); ++static void removescratchpad(const Arg *arg); + static void requestdecorationmode(struct wl_listener *listener, void *data); + static void requeststartdrag(struct wl_listener *listener, void *data); + static void requestmonstate(struct wl_listener *listener, void *data); +@@ -338,6 +341,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 togglescratchpad(const Arg *arg); + static void toggletag(const Arg *arg); + static void toggleview(const Arg *arg); + static void unlocksession(struct wl_listener *listener, void *data); +@@ -408,6 +412,9 @@ static struct wlr_box sgeom; + static struct wl_list mons; + static Monitor *selmon; + ++static struct wl_list scratchpad_clients; ++static int scratchpad_visible = 1; ++ + /* global event handlers */ + static struct wl_listener cursor_axis = {.notify = axisnotify}; + static struct wl_listener cursor_button = {.notify = buttonpress}; +@@ -457,6 +464,8 @@ static struct wlr_xwayland *xwayland; + /* attempt to encapsulate suck into one file */ + #include "client.h" + ++#include "simple_scratchpad.c" ++ + /* function implementations */ + void + applybounds(Client *c, struct wlr_box *bbox) +@@ -1330,10 +1339,20 @@ void + destroynotify(struct wl_listener *listener, void *data) + { + /* Called when the xdg_toplevel is destroyed. */ +- Client *c = wl_container_of(listener, c, destroy); ++ Client *sc, *c = wl_container_of(listener, c, destroy); + wl_list_remove(&c->destroy.link); + wl_list_remove(&c->set_title.link); + wl_list_remove(&c->fullscreen.link); ++ /* Check if destroyed client was part of scratchpad_clients ++ * and clean it from the list if so. */ ++ if (c && wl_list_length(&scratchpad_clients) > 0) { ++ wl_list_for_each(sc, &scratchpad_clients, link_temp) { ++ if (sc == c) { ++ wl_list_remove(&c->link_temp); ++ break; ++ } ++ } ++ } + #ifdef XWAYLAND + if (c->type != XDGShell) { + wl_list_remove(&c->activate.link); +@@ -2335,11 +2354,21 @@ setcursorshape(struct wl_listener *listener, void *data) + void + setfloating(Client *c, int floating) + { +- Client *p = client_get_parent(c); ++ Client *sc, *p = client_get_parent(c); + c->isfloating = floating; + /* If in floating layout do not change the client's layer */ + if (!c->mon || !client_surface(c)->mapped || !c->mon->lt[c->mon->sellt]->arrange) + return; ++ /* Check if unfloated client was part of scratchpad_clients ++ * and remove it from scratchpad_clients list if so */ ++ if (!floating && wl_list_length(&scratchpad_clients) > 0) { ++ wl_list_for_each(sc, &scratchpad_clients, link_temp) { ++ if (sc == c) { ++ wl_list_remove(&c->link_temp); ++ break; ++ } ++ } ++ } + wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen || + (p && p->isfullscreen) ? LyrFS + : c->isfloating ? LyrFloat : LyrTile]); +@@ -2557,6 +2586,7 @@ setup(void) + */ + wl_list_init(&clients); + wl_list_init(&fstack); ++ wl_list_init(&scratchpad_clients); + + xdg_shell = wlr_xdg_shell_create(dpy, 6); + wl_signal_add(&xdg_shell->events.new_toplevel, &new_xdg_toplevel); +diff --git a/simple_scratchpad.c b/simple_scratchpad.c +new file mode 100644 +index 0000000..26d6b66 +--- /dev/null ++++ b/simple_scratchpad.c +@@ -0,0 +1,68 @@ ++/* ************************************************************************** */ ++/* */ ++/* ::: :::::::: */ ++/* simple_scratchpad.c :+: :+: :+: */ ++/* +:+ +:+ +:+ */ ++/* By: jmakkone +#+ +:+ +#+ */ ++/* +#+#+#+#+#+ +#+ */ ++/* Created: 2024/12/19 19:35:02 by jmakkone #+# #+# */ ++/* Updated: 2025/01/04 13:35:50 by jmakkone ### ########.fr */ ++/* */ ++/* ************************************************************************** */ ++ ++void ++addscratchpad(const Arg *arg) ++{ ++ Client *cc, *c = focustop(selmon); ++ ++ if (!c) ++ return; ++ /* Check if the added client is already a scratchpad client */ ++ wl_list_for_each(cc, &scratchpad_clients, link_temp) { ++ if (cc == c) ++ return; ++ } ++ if (!c->isfloating) { ++ setfloating(c, 1); ++ } ++ wl_list_insert(&scratchpad_clients, &c->link_temp); ++} ++ ++void ++togglescratchpad(const Arg *arg) ++{ ++ Client *c; ++ Monitor *m = selmon; ++ ++ scratchpad_visible = !scratchpad_visible; ++ if (scratchpad_visible) { ++ wl_list_for_each(c, &scratchpad_clients, link_temp) { ++ c->mon = m; ++ c->tags = m->tagset[m->seltags]; ++ arrange(m); ++ focusclient(c, 1); ++ } ++ } else { ++ wl_list_for_each(c, &scratchpad_clients, link_temp) { ++ c->tags = 0; ++ focusclient(focustop(m), 1); ++ arrange(m); ++ } ++ } ++} ++ ++void ++removescratchpad(const Arg *arg) ++{ ++ Client *sc, *c = focustop(selmon); ++ ++ if (c && wl_list_length(&scratchpad_clients) > 0) { ++ /* Check if c is in scratchpad_clients */ ++ wl_list_for_each(sc, &scratchpad_clients, link_temp) { ++ if (sc == c) { ++ wl_list_remove(&c->link_temp); ++ break; ++ } ++ } ++ } ++} +-- +2.52.0 + From ca061466553ad820cc578e9a1b7b3f1fdf16c8bf Mon Sep 17 00:00:00 2001 From: Nikita Ivanov Date: Mon, 16 Mar 2026 20:27:22 +0100 Subject: [PATCH 22/36] Update snail patch to 0.8 --- patches/snail/README.md | 91 +++++++++- .../snail/{snail.patch => snail-0.7.patch} | 0 patches/snail/snail-0.8.patch | 162 ++++++++++++++++++ 3 files changed, 250 insertions(+), 3 deletions(-) rename patches/snail/{snail.patch => snail-0.7.patch} (100%) create mode 100644 patches/snail/snail-0.8.patch diff --git a/patches/snail/README.md b/patches/snail/README.md index 0c94b79..a33073a 100644 --- a/patches/snail/README.md +++ b/patches/snail/README.md @@ -1,9 +1,94 @@ ### Description -Adds a spiral-inspired layout for wide screens. +This layout is a scalable alternative to the "tile" and "spiral" layouts, optimized for wide monitors. Both the master area and the stack are "spirals", but windows in the master area are split horizontally as long as the master area has enough horizontal space, and the first window in the stack is split vertically unless the stack is wide. + +With one window in the master area and mfact = 0.5, it behaves like the spiral layout: + +┌───────────────┬────────────────┐ +│ │ │ +│ │ │ +│ │ │ +│ │ │ +│ ├───┬───┬────────┤ +│ │ │ │ │ +│ ├───┴───┤ │ +│ │ │ │ +│ │ │ │ +└───────────────┴───────┴────────┘ + +With 2 windows in the master area and 2 in the stack: + +┌───────────────┬────────────────┐ +│ │ │ +│ │ │ +│ │ │ +│ │ │ +├───────────────┼────────────────┤ +│ │ │ +│ │ │ +│ │ │ +│ │ │ +└───────────────┴────────────────┘ + +With 3 windows in the master area and 2 in the stack: + +┌───────────────┬────────────────┐ +│ │ │ +│ │ │ +│ │ │ +│ │ │ +├───────┬───────┼────────────────┤ +│ │ │ │ +│ │ │ │ +│ │ │ │ +│ │ │ │ +└───────┴───────┴────────────────┘ + +With many windows in both areas: + +┌───────────────┬────────────────┐ +│ │ │ +│ │ │ +│ │ │ +│ │ │ +├───┬───┬───────┼───┬───┬────────┤ +│ │ │ │ │ │ │ +├───┴───┤ ├───┴───┤ │ +│ │ │ │ │ +│ │ │ │ │ +└───────┴───────┴───────┴────────┘ + +With 2 windows in the master area, many windows in the stack and high mfact: + +┌──────────┬──────────┬──────────┐ +│ │ │ │ +│ │ │ │ +│ │ │ │ +│ │ │ │ +│ master ├──┬stack──┤ +│ │ │ │ │ │ +│ │ ├──┴──┤ │ +│ │ │ │ │ +│ │ │ │ │ +└──────────┴──────────┴─────┴────┘ + +With 2 windows in the master area, many windows in the stack and low mfact: + +┌──────────┬──────────┬──────────┐ +│ │ │ │ +│ │ │ │ +│ │ │ │ +│ │ │ │ +├──master──┤ stack┬──┬────┤ +│ │ │ │ │ │ +│ │ ├──┴──┤ │ +│ │ │ │ │ +│ │ │ │ │ +└──────────┴──────────┴─────┴────┘ ### Download -- [2024-02-11](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/snail/snail.patch) +- [0.8](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/snail/snail-0.8.patch) +- [0.7](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/snail/snail-0.7.patch) ### Authors - [Dima Krasner](https://codeberg.org/dimkr) () -- [Nikita Ivanov](https://github.com/NikitaIvanovV) (fix for flickering) +- [Nikita Ivanov](https://github.com/NikitaIvanovV) diff --git a/patches/snail/snail.patch b/patches/snail/snail-0.7.patch similarity index 100% rename from patches/snail/snail.patch rename to patches/snail/snail-0.7.patch diff --git a/patches/snail/snail-0.8.patch b/patches/snail/snail-0.8.patch new file mode 100644 index 0000000..44de138 --- /dev/null +++ b/patches/snail/snail-0.8.patch @@ -0,0 +1,162 @@ +From 9c97743480e901029fce2a8ccdde4f8b6a34040a Mon Sep 17 00:00:00 2001 +From: Nikita Ivanov +Date: Tue, 25 Mar 2025 02:49:43 +0100 +Subject: [PATCH] Add snail layout + +--- + config.def.h | 4 +- + dwl.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 105 insertions(+), 1 deletion(-) + +diff --git a/config.def.h b/config.def.h +index 8a6eda0..5080a2c 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -33,6 +33,7 @@ static const Layout layouts[] = { + { "[]=", tile }, + { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, ++ { "@|@", snail }, + }; + + /* monitors */ +@@ -43,7 +44,7 @@ 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 }, */ +- { NULL, 0.55f, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 }, ++ { NULL, 0.64f, 1, 1, &layouts[3], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 }, + /* default monitor rule: can be changed but cannot be eliminated; at least one monitor rule must exist */ + }; + +@@ -135,6 +136,7 @@ static const Key keys[] = { + { 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_s, setlayout, {.v = &layouts[3]} }, + { MODKEY, XKB_KEY_space, setlayout, {0} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} }, + { MODKEY, XKB_KEY_e, togglefullscreen, {0} }, +diff --git a/dwl.c b/dwl.c +index 44f3ad9..1250fb7 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -329,6 +329,7 @@ static void setmon(Client *c, Monitor *m, uint32_t newtags); + static void setpsel(struct wl_listener *listener, void *data); + static void setsel(struct wl_listener *listener, void *data); + static void setup(void); ++static void snail(Monitor *m); + static void spawn(const Arg *arg); + static void startdrag(struct wl_listener *listener, void *data); + static void tag(const Arg *arg); +@@ -2666,6 +2667,107 @@ setup(void) + #endif + } + ++void ++snail(Monitor *m) ++{ ++ int i = 0, n = 0; ++ unsigned int mw = m->w.width; ++ Client *c, *prev; ++ enum wlr_direction dir = WLR_DIRECTION_RIGHT; ++ ++ wl_list_for_each(c, &clients, link) ++ if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen) ++ n++; ++ if (n == 0) ++ return; ++ ++ if (n > m->nmaster) ++ mw = m->nmaster ? (int)round(m->w.width * m->mfact) : 0; ++ ++ wl_list_for_each(c, &clients, link) { ++ if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) ++ continue; ++ ++ /* ++ * If the master area exists and this is the first window, fill the ++ * master area with this window ++ */ ++ if (mw > 0 && i == 0) { ++ c->geom = (struct wlr_box){.x = m->w.x, .y = m->w.y, ++ .width = mw, .height = m->w.height}; ++ /* ++ * If the first window in the master area is wide, split it ++ * horizontally and put next one on its right; otherwise, split it ++ * vertically and put the next one below it ++ */ ++ dir = c->geom.width > m->w.height ? WLR_DIRECTION_RIGHT : WLR_DIRECTION_DOWN; ++ /* ++ * If the master area is full or doesn't exist, fill the stack with the ++ * m->nmaster-th window ++ */ ++ } else if (i == m->nmaster) { ++ c->geom = (struct wlr_box){.x = m->w.x + mw, .y = m->w.y, ++ .width = m->w.width - mw, .height = m->w.height}; ++ /* ++ * If the first window in the stack is wide, split it horizontally ++ * and put next one on its right; otherwise, split it vertically and ++ * put the next one below it ++ */ ++ dir = c->geom.width > m->w.height ? WLR_DIRECTION_RIGHT : WLR_DIRECTION_DOWN; ++ /* ++ * Split the previous horizontally and put the current window on the right ++ */ ++ } else if (dir == WLR_DIRECTION_RIGHT) { ++ c->geom = (struct wlr_box){.x = prev->geom.x + prev->geom.width / 2, .y = prev->geom.y, ++ .width = prev->geom.width / 2, .height = prev->geom.height}; ++ prev->geom = (struct wlr_box){.x = prev->geom.x, .y = prev->geom.y, ++ .width = prev->geom.width / 2, .height = prev->geom.height}; ++ /* ++ * If it's a stack window or the first narrow window in the master ++ * area, put the next one below it ++ */ ++ if (i >= m->nmaster || c->geom.width < m->w.height) ++ dir = WLR_DIRECTION_DOWN; ++ /* ++ * Split the previous vertically and put the current window below it ++ */ ++ } else if (dir == WLR_DIRECTION_DOWN) { ++ c->geom = (struct wlr_box){.x = prev->geom.x, .y = prev->geom.y + prev->geom.height / 2, ++ .width = prev->geom.width, .height = prev->geom.height / 2}; ++ prev->geom = (struct wlr_box){.x = prev->geom.x, .y = prev->geom.y, ++ .width = prev->geom.width, .height = prev->geom.height / 2}; ++ dir = WLR_DIRECTION_LEFT; ++ /* ++ * Split the previous horizontally and put the current window on the left ++ */ ++ } else if (dir == WLR_DIRECTION_LEFT) { ++ c->geom = (struct wlr_box){.x = prev->geom.x, .y = prev->geom.y, ++ .width = prev->geom.width / 2, .height = prev->geom.height}; ++ prev->geom = (struct wlr_box){.x = prev->geom.x + prev->geom.width / 2, .y = prev->geom.y, ++ .width = prev->geom.width / 2, .height = prev->geom.height}; ++ dir = WLR_DIRECTION_UP; ++ /* ++ * Split the previous vertically and put the current window above it ++ */ ++ } else { ++ c->geom = (struct wlr_box){.x = prev->geom.x, .y = prev->geom.y, ++ .width = prev->geom.width, .height = prev->geom.height / 2}; ++ prev->geom = (struct wlr_box){.x = prev->geom.x, .y = prev->geom.y + prev->geom.height / 2, ++ .width = prev->geom.width, .height = prev->geom.height / 2}; ++ dir = WLR_DIRECTION_RIGHT; ++ } ++ i++; ++ prev = c; ++ } ++ ++ wl_list_for_each(c, &clients, link) { ++ if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) ++ continue; ++ ++ resize(c, c->geom, 0); ++ } ++} ++ + void + spawn(const Arg *arg) + { +-- +2.53.0 + From 52a7559a870fb532ed5f03ee5665488a7703311c Mon Sep 17 00:00:00 2001 From: Nikita Ivanov Date: Mon, 16 Mar 2026 20:38:06 +0100 Subject: [PATCH 23/36] snail: fix formatting --- patches/snail/README.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/patches/snail/README.md b/patches/snail/README.md index a33073a..9a4646e 100644 --- a/patches/snail/README.md +++ b/patches/snail/README.md @@ -1,8 +1,9 @@ ### Description This layout is a scalable alternative to the "tile" and "spiral" layouts, optimized for wide monitors. Both the master area and the stack are "spirals", but windows in the master area are split horizontally as long as the master area has enough horizontal space, and the first window in the stack is split vertically unless the stack is wide. -With one window in the master area and mfact = 0.5, it behaves like the spiral layout: +With one window in the master area and `mfact = 0.5`, it behaves like the spiral layout: +``` ┌───────────────┬────────────────┐ │ │ │ │ │ │ @@ -14,9 +15,11 @@ With one window in the master area and mfact = 0.5, it behaves like the spiral l │ │ │ │ │ │ │ │ └───────────────┴───────┴────────┘ +``` With 2 windows in the master area and 2 in the stack: +``` ┌───────────────┬────────────────┐ │ │ │ │ │ │ @@ -28,9 +31,11 @@ With 2 windows in the master area and 2 in the stack: │ │ │ │ │ │ └───────────────┴────────────────┘ +``` With 3 windows in the master area and 2 in the stack: +``` ┌───────────────┬────────────────┐ │ │ │ │ │ │ @@ -42,9 +47,11 @@ With 3 windows in the master area and 2 in the stack: │ │ │ │ │ │ │ │ └───────┴───────┴────────────────┘ +``` With many windows in both areas: +``` ┌───────────────┬────────────────┐ │ │ │ │ │ │ @@ -56,9 +63,11 @@ With many windows in both areas: │ │ │ │ │ │ │ │ │ │ └───────┴───────┴───────┴────────┘ +``` With 2 windows in the master area, many windows in the stack and high mfact: +``` ┌──────────┬──────────┬──────────┐ │ │ │ │ │ │ │ │ @@ -70,9 +79,11 @@ With 2 windows in the master area, many windows in the stack and high mfact: │ │ │ │ │ │ │ │ │ │ └──────────┴──────────┴─────┴────┘ +``` With 2 windows in the master area, many windows in the stack and low mfact: +``` ┌──────────┬──────────┬──────────┐ │ │ │ │ │ │ │ │ @@ -84,6 +95,7 @@ With 2 windows in the master area, many windows in the stack and low mfact: │ │ │ │ │ │ │ │ │ │ └──────────┴──────────┴─────┴────┘ +``` ### Download - [0.8](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/snail/snail-0.8.patch) From 5ccc71e0b58674a2a72913d10e6c3990bf4a75db Mon Sep 17 00:00:00 2001 From: Nikita Ivanov Date: Mon, 16 Mar 2026 20:42:50 +0100 Subject: [PATCH 24/36] centeredmaster: update to 0.8 --- patches/centeredmaster/README.md | 3 +- ...dmaster.patch => centeredmaster-0.7.patch} | 0 .../centeredmaster/centeredmaster-0.8.patch | 114 ++++++++++++++++++ 3 files changed, 116 insertions(+), 1 deletion(-) rename patches/centeredmaster/{centeredmaster.patch => centeredmaster-0.7.patch} (100%) create mode 100644 patches/centeredmaster/centeredmaster-0.8.patch diff --git a/patches/centeredmaster/README.md b/patches/centeredmaster/README.md index 2ba6dd9..10bfc1f 100644 --- a/patches/centeredmaster/README.md +++ b/patches/centeredmaster/README.md @@ -24,7 +24,8 @@ With one and two clients in master respectively this results in: ### Download -- [0.7](/dwl/dwl-patches/raw/branch/main/patches/centeredmaster/centeredmaster.patch) +- [0.8](/dwl/dwl-patches/raw/branch/main/patches/centeredmaster/centeredmaster-0.8.patch) +- [0.7](/dwl/dwl-patches/raw/branch/main/patches/centeredmaster/centeredmaster-0.7.patch) - [2024-04-11](https://codeberg.org/dwl/dwl-patches/raw/commit/b104a580a80ebaf9f7e8917fe574e3e97ddd019a/centeredmaster/centeredmaster.patch) - [0.5](https://codeberg.org/dwl/dwl-patches/raw/commit/0f4e40fee49d1b8b430778e241b29496ae3b3b70/centeredmaster/centeredmaster.patch) diff --git a/patches/centeredmaster/centeredmaster.patch b/patches/centeredmaster/centeredmaster-0.7.patch similarity index 100% rename from patches/centeredmaster/centeredmaster.patch rename to patches/centeredmaster/centeredmaster-0.7.patch diff --git a/patches/centeredmaster/centeredmaster-0.8.patch b/patches/centeredmaster/centeredmaster-0.8.patch new file mode 100644 index 0000000..fd61aa5 --- /dev/null +++ b/patches/centeredmaster/centeredmaster-0.8.patch @@ -0,0 +1,114 @@ +From 1fb8c15bb02c50279a5ccbd410dc1293abb747f3 Mon Sep 17 00:00:00 2001 +From: Nikita Ivanov +Date: Sat, 8 Feb 2025 16:10:25 +0100 +Subject: [PATCH] Add centeredmaster layout + +--- + config.def.h | 2 ++ + dwl.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 65 insertions(+) + +diff --git a/config.def.h b/config.def.h +index 8a6eda0..70954cc 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -33,6 +33,7 @@ static const Layout layouts[] = { + { "[]=", tile }, + { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, ++ { "|M|", centeredmaster }, + }; + + /* monitors */ +@@ -135,6 +136,7 @@ static const Key keys[] = { + { 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_c, setlayout, {.v = &layouts[3]} }, + { MODKEY, XKB_KEY_space, setlayout, {0} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} }, + { MODKEY, XKB_KEY_e, togglefullscreen, {0} }, +diff --git a/dwl.c b/dwl.c +index 44f3ad9..bf859b0 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -248,6 +248,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 void centeredmaster(Monitor *m); + static void chvt(const Arg *arg); + static void checkidleinhibitor(struct wlr_surface *exclude); + static void cleanup(void); +@@ -672,6 +673,68 @@ buttonpress(struct wl_listener *listener, void *data) + event->time_msec, event->button, event->state); + } + ++void ++centeredmaster(Monitor *m) ++{ ++ int i, n, h, mw, mx, my, oty, ety, tw; ++ Client *c; ++ ++ n = 0; ++ wl_list_for_each(c, &clients, link) ++ if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen) ++ n++; ++ if (n == 0) ++ return; ++ ++ /* initialize areas */ ++ mw = m->w.width; ++ mx = 0; ++ my = 0; ++ tw = mw; ++ ++ if (n > m->nmaster) { ++ /* go mfact box in the center if more than nmaster clients */ ++ mw = m->nmaster ? (int)roundf(m->w.width * m->mfact) : 0; ++ tw = m->w.width - mw; ++ ++ if (n - m->nmaster > 1) { ++ /* only one client */ ++ mx = (m->w.width - mw) / 2; ++ tw = (m->w.width - mw) / 2; ++ } ++ } ++ ++ i = 0; ++ oty = 0; ++ ety = 0; ++ wl_list_for_each(c, &clients, link) { ++ if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) ++ continue; ++ if (i < m->nmaster) { ++ /* nmaster clients are stacked vertically, in the center ++ * of the screen */ ++ h = (m->w.height - my) / (MIN(n, m->nmaster) - i); ++ resize(c, (struct wlr_box){.x = m->w.x + mx, .y = m->w.y + my, .width = mw, ++ .height = h}, 0); ++ my += c->geom.height; ++ } else { ++ /* stack clients are stacked vertically */ ++ if ((i - m->nmaster) % 2) { ++ h = (m->w.height - ety) / ( (1 + n - i) / 2); ++ resize(c, (struct wlr_box){.x = m->w.x, .y = m->w.y + ety, .width = tw, ++ .height = h}, 0); ++ ety += c->geom.height; ++ } else { ++ h = (m->w.height - oty) / ((1 + n - i) / 2); ++ resize(c, (struct wlr_box){.x = m->w.x + mx + mw, .y = m->w.y + oty, .width = tw, ++ .height = h}, 0); ++ oty += c->geom.height; ++ } ++ } ++ i++; ++ } ++} ++ + void + chvt(const Arg *arg) + { +-- +2.53.0 + From 870a32283199f6cc731eee2eb194b1cd70ed6fa3 Mon Sep 17 00:00:00 2001 From: Nikita Ivanov Date: Mon, 16 Mar 2026 20:52:50 +0100 Subject: [PATCH 25/36] movestack: update to 0.8 --- patches/movestack/README.md | 4 +- .../{movestack.patch => movestack-0.7.patch} | 0 patches/movestack/movestack-0.8.patch | 87 +++++++++++++++++++ 3 files changed, 89 insertions(+), 2 deletions(-) rename patches/movestack/{movestack.patch => movestack-0.7.patch} (100%) create mode 100644 patches/movestack/movestack-0.8.patch diff --git a/patches/movestack/README.md b/patches/movestack/README.md index f50bf68..8695374 100644 --- a/patches/movestack/README.md +++ b/patches/movestack/README.md @@ -2,8 +2,8 @@ Allows you to move a window up and down the stack. ### Download -- [git branch](https://codeberg.org/nikitaivanov/dwl/src/branch/movestack) -- [v0.7](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/movestack/movestack.patch) +- [0.8](/dwl/dwl-patches/raw/branch/main/patches/movestack/movestack-0.8.patch) +- [0.7](/dwl/dwl-patches/raw/branch/main/patches/movestack/movestack-0.7.patch) ### Authors - [wochap](https://codeberg.org/wochap) diff --git a/patches/movestack/movestack.patch b/patches/movestack/movestack-0.7.patch similarity index 100% rename from patches/movestack/movestack.patch rename to patches/movestack/movestack-0.7.patch diff --git a/patches/movestack/movestack-0.8.patch b/patches/movestack/movestack-0.8.patch new file mode 100644 index 0000000..cf0c933 --- /dev/null +++ b/patches/movestack/movestack-0.8.patch @@ -0,0 +1,87 @@ +From 7e8de6e79d24114272b22b55c906d7b55c36173a Mon Sep 17 00:00:00 2001 +From: Nikita Ivanov +Date: Mon, 16 Mar 2026 20:50:44 +0100 +Subject: [PATCH] Add movestack + +--- + config.def.h | 2 ++ + dwl.c | 43 +++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 45 insertions(+) + +diff --git a/config.def.h b/config.def.h +index 8a6eda0..f490e24 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -125,6 +125,8 @@ static const Key keys[] = { + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, spawn, {.v = termcmd} }, + { MODKEY, XKB_KEY_j, focusstack, {.i = +1} }, + { MODKEY, XKB_KEY_k, focusstack, {.i = -1} }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_j, movestack, {.i = +1} }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_k, movestack, {.i = -1} }, + { MODKEY, XKB_KEY_i, incnmaster, {.i = +1} }, + { MODKEY, XKB_KEY_d, incnmaster, {.i = -1} }, + { MODKEY, XKB_KEY_h, setmfact, {.f = -0.05f} }, +diff --git a/dwl.c b/dwl.c +index 44f3ad9..726fbdd 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -300,6 +300,7 @@ static void locksession(struct wl_listener *listener, void *data); + static void mapnotify(struct wl_listener *listener, void *data); + static void maximizenotify(struct wl_listener *listener, void *data); + static void monocle(Monitor *m); ++static void movestack(const Arg *arg); + static void motionabsolute(struct wl_listener *listener, void *data); + static void motionnotify(uint32_t time, struct wlr_input_device *device, double sx, + double sy, double sx_unaccel, double sy_unaccel); +@@ -1838,6 +1839,48 @@ monocle(Monitor *m) + wlr_scene_node_raise_to_top(&c->scene->node); + } + ++void ++movestack(const Arg *arg) ++{ ++ Client *c, *sel = focustop(selmon); ++ ++ if (!sel) { ++ return; ++ } ++ ++ if (wl_list_length(&clients) <= 1) { ++ return; ++ } ++ ++ if (arg->i > 0) { ++ wl_list_for_each(c, &sel->link, link) { ++ if (&c->link == &clients) { ++ c = wl_container_of(&clients, c, link); ++ break; /* wrap past the sentinel node */ ++ } ++ if (VISIBLEON(c, selmon) || &c->link == &clients) { ++ break; /* found it */ ++ } ++ } ++ } else { ++ wl_list_for_each_reverse(c, &sel->link, link) { ++ if (&c->link == &clients) { ++ c = wl_container_of(&clients, c, link); ++ break; /* wrap past the sentinel node */ ++ } ++ if (VISIBLEON(c, selmon) || &c->link == &clients) { ++ break; /* found it */ ++ } ++ } ++ /* backup one client */ ++ c = wl_container_of(c->link.prev, c, link); ++ } ++ ++ wl_list_remove(&sel->link); ++ wl_list_insert(&c->link, &sel->link); ++ arrange(selmon); ++} ++ + void + motionabsolute(struct wl_listener *listener, void *data) + { +-- +2.53.0 + From 36439e54ba54f95a165440fd8c4b64fd22884b90 Mon Sep 17 00:00:00 2001 From: Nikita Ivanov Date: Mon, 16 Mar 2026 20:58:53 +0100 Subject: [PATCH 26/36] kblayout: update to 0.8 --- patches/kblayout/README.md | 4 +- .../{kblayout.patch => kblayout-0.7.patch} | 0 patches/kblayout/kblayout-0.8.patch | 188 ++++++++++++++++++ 3 files changed, 190 insertions(+), 2 deletions(-) rename patches/kblayout/{kblayout.patch => kblayout-0.7.patch} (100%) create mode 100644 patches/kblayout/kblayout-0.8.patch diff --git a/patches/kblayout/README.md b/patches/kblayout/README.md index 259694c..4f2280d 100644 --- a/patches/kblayout/README.md +++ b/patches/kblayout/README.md @@ -23,8 +23,8 @@ any of these features, just disable it in `config.h`. ### Download -- [git branch](https://codeberg.org/nikitaivanov/dwl/src/branch/kblayout) -- [0.7](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/kblayout/kblayout.patch) +- [0.8](/dwl/dwl-patches/raw/branch/main/patches/kblayout/kblayout-0.8.patch) +- [0.7](/dwl/dwl-patches/raw/branch/main/patches/kblayout/kblayout-0.7.patch) ### Authors diff --git a/patches/kblayout/kblayout.patch b/patches/kblayout/kblayout-0.7.patch similarity index 100% rename from patches/kblayout/kblayout.patch rename to patches/kblayout/kblayout-0.7.patch diff --git a/patches/kblayout/kblayout-0.8.patch b/patches/kblayout/kblayout-0.8.patch new file mode 100644 index 0000000..81f7058 --- /dev/null +++ b/patches/kblayout/kblayout-0.8.patch @@ -0,0 +1,188 @@ +From c80ab5d3dacc5cf22382f88287eeba5d298be483 Mon Sep 17 00:00:00 2001 +From: Nikita Ivanov +Date: Tue, 4 Feb 2025 23:42:10 +0100 +Subject: [PATCH] Add per client keyboard layout and status bar info + +--- + config.def.h | 3 +++ + dwl.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++- + 2 files changed, 73 insertions(+), 1 deletion(-) + +diff --git a/config.def.h b/config.def.h +index 8a6eda0..09b6e57 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -13,6 +13,9 @@ static const float focuscolor[] = COLOR(0x005577ff); + static const float urgentcolor[] = COLOR(0xff0000ff); + /* This conforms to the xdg-protocol. Set the alpha to zero to restore the old behavior */ + static const float fullscreen_bg[] = {0.0f, 0.0f, 0.0f, 1.0f}; /* You can also use glsl colors */ ++/* keyboard layout change notification for status bar */ ++static const char kblayout_file[] = "/tmp/dwl-keymap"; ++static const char *kblayout_cmd[] = {"pkill", "-RTMIN+3", "someblocks", NULL}; + + /* tagging - TAGCOUNT must be no greater than 31 */ + #define TAGCOUNT (9) +diff --git a/dwl.c b/dwl.c +index 44f3ad9..279571b 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -139,6 +140,7 @@ typedef struct { + uint32_t tags; + int isfloating, isurgent, isfullscreen; + uint32_t resize; /* configure serial of a pending resize */ ++ unsigned int kblayout_idx; + } Client; + + typedef struct { +@@ -291,6 +293,7 @@ static void gpureset(struct wl_listener *listener, void *data); + static void handlesig(int signo); + static void incnmaster(const Arg *arg); + static void inputdevice(struct wl_listener *listener, void *data); ++static void kblayout(KeyboardGroup *kb); + static int keybinding(uint32_t mods, xkb_keysym_t sym); + static void keypress(struct wl_listener *listener, void *data); + static void keypressmod(struct wl_listener *listener, void *data); +@@ -436,6 +439,8 @@ 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 unsigned int kblayout_idx = -1; ++ + #ifdef XWAYLAND + static void activatex11(struct wl_listener *listener, void *data); + static void associatex11(struct wl_listener *listener, void *data); +@@ -945,6 +950,8 @@ createkeyboard(struct wlr_keyboard *keyboard) + + /* Add the new keyboard to the group */ + wlr_keyboard_group_add_keyboard(kb_group->wlr_group, keyboard); ++ ++ kblayout(kb_group); + } + + KeyboardGroup * +@@ -1122,11 +1129,13 @@ createnotify(struct wl_listener *listener, void *data) + /* This event is raised when a client creates a new toplevel (application window). */ + struct wlr_xdg_toplevel *toplevel = data; + Client *c = NULL; ++ struct wlr_keyboard *kb = wlr_seat_get_keyboard(seat); + + /* Allocate a Client for this surface */ + c = toplevel->base->data = ecalloc(1, sizeof(*c)); + c->surface.xdg = toplevel->base; + c->bw = borderpx; ++ c->kblayout_idx = kb ? kb->modifiers.group : 0; + + LISTEN(&toplevel->base->surface->events.commit, &c->commit, commitnotify); + LISTEN(&toplevel->base->surface->events.map, &c->map, mapnotify); +@@ -1402,10 +1411,24 @@ dirtomon(enum wlr_direction dir) + void + focusclient(Client *c, int lift) + { ++ /* Copied from wlroots/types/wlr_keyboard_group.c */ ++ struct keyboard_group_device { ++ struct wlr_keyboard *keyboard; ++ struct wl_listener key; ++ struct wl_listener modifiers; ++ struct wl_listener keymap; ++ struct wl_listener repeat_info; ++ struct wl_listener destroy; ++ struct wl_list link; // wlr_keyboard_group.devices ++ }; ++ + struct wlr_surface *old = seat->keyboard_state.focused_surface; + int unused_lx, unused_ly, old_client_type; + Client *old_c = NULL; + LayerSurface *old_l = NULL; ++ struct keyboard_group_device *device; ++ struct wlr_keyboard *kb = wlr_seat_get_keyboard(seat); ++ struct wlr_keyboard_group *group = kb ? wlr_keyboard_group_from_wlr_keyboard(kb) : NULL; + + if (locked) + return; +@@ -1457,6 +1480,20 @@ focusclient(Client *c, int lift) + } + printstatus(); + ++ /* Update keyboard layout */ ++ if (group) { ++ // Update the first real device, because kb or group->kb is not a real ++ // keyboard and its effective layout gets overwritten ++ device = wl_container_of(group->devices.next, device, link); ++ if (device->keyboard) ++ wlr_keyboard_notify_modifiers(device->keyboard, ++ device->keyboard->modifiers.depressed, ++ device->keyboard->modifiers.latched, ++ device->keyboard->modifiers.locked, ++ c ? c->kblayout_idx : 0 ++ ); ++ } ++ + if (!c) { + /* With no client, all we have left is to clear focus */ + wlr_seat_keyboard_notify_clear_focus(seat); +@@ -1467,7 +1504,7 @@ focusclient(Client *c, int lift) + motionnotify(0, NULL, 0, 0, 0, 0); + + /* Have a client, so focus its top-level wlr_surface */ +- client_notify_enter(client_surface(c), wlr_seat_get_keyboard(seat)); ++ client_notify_enter(client_surface(c), kb); + + /* Activate the new client */ + client_activate_surface(client_surface(c), 1); +@@ -1605,6 +1642,36 @@ inputdevice(struct wl_listener *listener, void *data) + wlr_seat_set_capabilities(seat, caps); + } + ++void ++kblayout(KeyboardGroup *kb) ++{ ++ FILE *f; ++ Client *c; ++ unsigned int idx = kb->wlr_group->keyboard.modifiers.group; ++ ++ // If layout did not change, do nothing ++ if (kblayout_idx == idx) ++ return; ++ kblayout_idx = idx; ++ ++ // Update client layout ++ if ((c = focustop(selmon))) ++ c->kblayout_idx = kblayout_idx; ++ ++ // Save current layout to kblayout_file ++ if (*kblayout_file && (f = fopen(kblayout_file, "w"))) { ++ fputs(xkb_keymap_layout_get_name(kb->wlr_group->keyboard.keymap, ++ idx), f); ++ fclose(f); ++ } ++ ++ // Run kblayout_cmd ++ if (kblayout_cmd[0] && fork() == 0) { ++ execvp(kblayout_cmd[0], (char *const *)kblayout_cmd); ++ die("dwl: execvp %s failed:", kblayout_cmd[0]); ++ } ++} ++ + int + keybinding(uint32_t mods, xkb_keysym_t sym) + { +@@ -1683,6 +1750,8 @@ keypressmod(struct wl_listener *listener, void *data) + /* Send modifiers to the client. */ + wlr_seat_keyboard_notify_modifiers(seat, + &group->wlr_group->keyboard.modifiers); ++ ++ kblayout(group); + } + + int +-- +2.53.0 + From 05895bbe1b71ab117cbf2b3667502e4404e6f10a Mon Sep 17 00:00:00 2001 From: Nikita Ivanov Date: Mon, 16 Mar 2026 21:06:48 +0100 Subject: [PATCH 27/36] en-keycodes: update to 0.8 --- patches/en-keycodes/README.md | 4 +- ...n-keycodes.patch => en-keycodes-0.7.patch} | 0 patches/en-keycodes/en-keycodes-0.8.patch | 75 +++++++++++++++++++ 3 files changed, 77 insertions(+), 2 deletions(-) rename patches/en-keycodes/{en-keycodes.patch => en-keycodes-0.7.patch} (100%) create mode 100644 patches/en-keycodes/en-keycodes-0.8.patch diff --git a/patches/en-keycodes/README.md b/patches/en-keycodes/README.md index c089dc2..ac7f564 100644 --- a/patches/en-keycodes/README.md +++ b/patches/en-keycodes/README.md @@ -2,8 +2,8 @@ Always use the English keymap to get keycodes, so key bindings work even when using a non-English keyboard layout. ### Download -- [git branch](https://codeberg.org/ForzCross/dwl/src/branch/en-keycodes.patch) -- [v0.7](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/en-keycodes/en-keycodes.patch) +- [0.8](/dwl/dwl-patches/raw/branch/main/patches/en-keycodes/en-keycodes-0.8.patch) +- [0.7](/dwl/dwl-patches/raw/branch/main/patches/en-keycodes/en-keycodes-0.7.patch) ### Authors - [Nikita Ivanov](https://codeberg.org/nikitaivanov) ([GitHub](https://github.com/NikitaIvanovV)) diff --git a/patches/en-keycodes/en-keycodes.patch b/patches/en-keycodes/en-keycodes-0.7.patch similarity index 100% rename from patches/en-keycodes/en-keycodes.patch rename to patches/en-keycodes/en-keycodes-0.7.patch diff --git a/patches/en-keycodes/en-keycodes-0.8.patch b/patches/en-keycodes/en-keycodes-0.8.patch new file mode 100644 index 0000000..ec080e2 --- /dev/null +++ b/patches/en-keycodes/en-keycodes-0.8.patch @@ -0,0 +1,75 @@ +From cde0921304c11c2e1341539a5ada4f7b9715b178 Mon Sep 17 00:00:00 2001 +From: Nikita Ivanov +Date: Sun, 30 Nov 2025 23:22:42 +0100 +Subject: [PATCH] Always use the English keymap to get keycodes + +--- + dwl.c | 25 +++++++++++++++++++++---- + 1 file changed, 21 insertions(+), 4 deletions(-) + +diff --git a/dwl.c b/dwl.c +index 44f3ad9..97ba897 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -436,6 +436,11 @@ 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 xkb_rule_names en_rules; ++static struct xkb_context *en_context; ++static struct xkb_keymap *en_keymap; ++static struct xkb_state *en_state; ++ + #ifdef XWAYLAND + static void activatex11(struct wl_listener *listener, void *data); + static void associatex11(struct wl_listener *listener, void *data); +@@ -718,6 +723,9 @@ cleanup(void) + wlr_backend_destroy(backend); + + wl_display_destroy(dpy); ++ xkb_state_unref(en_state); ++ xkb_keymap_unref(en_keymap); ++ xkb_context_unref(en_context); + /* Destroy after the wayland display (when the monitors are already destroyed) + to avoid destroying them with an invalid scene output. */ + wlr_scene_node_destroy(&scene->tree.node); +@@ -1632,16 +1640,19 @@ keypress(struct wl_listener *listener, void *data) + /* This event is raised when a key is pressed or released. */ + KeyboardGroup *group = wl_container_of(listener, group, key); + struct wlr_keyboard_key_event *event = data; ++ int nsyms, handled; + + /* Translate libinput keycode -> xkbcommon */ + uint32_t keycode = event->keycode + 8; + /* Get a list of keysyms based on the keymap for this keyboard */ + const xkb_keysym_t *syms; +- int nsyms = xkb_state_key_get_syms( +- group->wlr_group->keyboard.xkb_state, keycode, &syms); +- +- int handled = 0; + uint32_t mods = wlr_keyboard_get_modifiers(&group->wlr_group->keyboard); ++ xkb_state_update_key(en_state, keycode, ++ (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) ++ ? XKB_KEY_DOWN : XKB_KEY_UP); ++ nsyms = xkb_state_key_get_syms(en_state, keycode, &syms); ++ ++ handled = 0; + + wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); + +@@ -2624,6 +2635,12 @@ setup(void) + * pointer, touch, and drawing tablet device. We also rig up a listener to + * let us know when new input devices are available on the backend. + */ ++ en_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); ++ en_rules = xkb_rules; ++ en_rules.layout = "us"; ++ en_keymap = xkb_keymap_new_from_names(en_context, &en_rules, ++ XKB_KEYMAP_COMPILE_NO_FLAGS); ++ en_state = xkb_state_new(en_keymap); + wl_signal_add(&backend->events.new_input, &new_input_device); + virtual_keyboard_mgr = wlr_virtual_keyboard_manager_v1_create(dpy); + wl_signal_add(&virtual_keyboard_mgr->events.new_virtual_keyboard, +-- +2.53.0 + From 6abdb9f50c80bc6db091c97a851422990f1a97c8 Mon Sep 17 00:00:00 2001 From: Nikita Ivanov Date: Mon, 16 Mar 2026 21:17:03 +0100 Subject: [PATCH 28/36] setrule: update to 0.8 --- patches/setrule/README.md | 3 +- .../{setrule.patch => setrule-0.7.patch} | 0 patches/setrule/setrule-0.8.patch | 153 ++++++++++++++++++ 3 files changed, 155 insertions(+), 1 deletion(-) rename patches/setrule/{setrule.patch => setrule-0.7.patch} (100%) create mode 100644 patches/setrule/setrule-0.8.patch diff --git a/patches/setrule/README.md b/patches/setrule/README.md index bdb7408..fda71f3 100644 --- a/patches/setrule/README.md +++ b/patches/setrule/README.md @@ -38,7 +38,8 @@ with dmenu. ### Download -- [v0.7](/dwl/dwl-patches/raw/branch/main/patches/setrule/setrule.patch) +- [0.8](/dwl/dwl-patches/raw/branch/main/patches/setrule/setrule-0.8.patch) +- [0.7](/dwl/dwl-patches/raw/branch/main/patches/setrule/setrule-0.7.patch) - [2025-02-14 v0.7](https://codeberg.org/dwl/dwl-patches/raw/commit/268bee3cee239e5bd52cceed88a52bfc21143cc3/patches/setrule/setrule.patch) ### Authors diff --git a/patches/setrule/setrule.patch b/patches/setrule/setrule-0.7.patch similarity index 100% rename from patches/setrule/setrule.patch rename to patches/setrule/setrule-0.7.patch diff --git a/patches/setrule/setrule-0.8.patch b/patches/setrule/setrule-0.8.patch new file mode 100644 index 0000000..d0a9308 --- /dev/null +++ b/patches/setrule/setrule-0.8.patch @@ -0,0 +1,153 @@ +From b46440c982a5149570bb58369f84ef5082279123 Mon Sep 17 00:00:00 2001 +From: Nikita Ivanov +Date: Sun, 9 Feb 2025 23:12:09 +0100 +Subject: [PATCH] setrule: add/change rules at runtime + +--- + config.def.h | 4 ++++ + dwl.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++- + 2 files changed, 68 insertions(+), 1 deletion(-) + +diff --git a/config.def.h b/config.def.h +index 8a6eda0..48acf2d 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -20,6 +20,9 @@ static const float fullscreen_bg[] = {0.0f, 0.0f, 0.0f, 1.0f}; /* You ca + /* logging */ + static int log_level = WLR_ERROR; + ++/* Max amount of dynamically added rules */ ++#define RULES_MAX 100 ++ + static const Rule rules[] = { + /* app_id title tags mask isfloating monitor */ + { "Gimp_EXAMPLE", NULL, 0, 1, -1 }, /* Start on currently visible tags floating, not tiled */ +@@ -138,6 +141,7 @@ static const Key keys[] = { + { MODKEY, XKB_KEY_space, setlayout, {0} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} }, + { MODKEY, XKB_KEY_e, togglefullscreen, {0} }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_R, setruleisfloating,{0} }, + { MODKEY, XKB_KEY_0, view, {.ui = ~0} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} }, + { MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} }, +diff --git a/dwl.c b/dwl.c +index 44f3ad9..3c765d1 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -287,6 +287,7 @@ static void focusmon(const Arg *arg); + static void focusstack(const Arg *arg); + static Client *focustop(Monitor *m); + static void fullscreennotify(struct wl_listener *listener, void *data); ++static Rule *getrule(Client *c); + static void gpureset(struct wl_listener *listener, void *data); + static void handlesig(int signo); + static void incnmaster(const Arg *arg); +@@ -327,6 +328,7 @@ static void setlayout(const Arg *arg); + static void setmfact(const Arg *arg); + static void setmon(Client *c, Monitor *m, uint32_t newtags); + static void setpsel(struct wl_listener *listener, void *data); ++static void setruleisfloating(const Arg *arg); + static void setsel(struct wl_listener *listener, void *data); + static void setup(void); + static void spawn(const Arg *arg); +@@ -436,6 +438,9 @@ 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 Rule *drules; ++static size_t druleslen; ++ + #ifdef XWAYLAND + static void activatex11(struct wl_listener *listener, void *data); + static void associatex11(struct wl_listener *listener, void *data); +@@ -486,7 +491,7 @@ applyrules(Client *c) + appid = client_get_appid(c); + title = client_get_title(c); + +- for (r = rules; r < END(rules); r++) { ++ for (r = drules; r < drules + druleslen; r++) { + if ((!r->title || strstr(title, r->title)) + && (!r->id || strstr(appid, r->id))) { + c->isfloating = r->isfloating; +@@ -1532,6 +1537,51 @@ fullscreennotify(struct wl_listener *listener, void *data) + setfullscreen(c, client_wants_fullscreen(c)); + } + ++Rule * ++getrule(Client *c) ++{ ++ Rule *r; ++ const Rule *e; ++ const char *appid, *title; ++ ++ if (!c) ++ return NULL; ++ ++ appid = client_get_appid(c); ++ title = client_get_title(c); ++ ++ for (r = drules + druleslen - 1; r >= drules; r--) ++ if ((!r->title || strstr(title, r->title)) ++ && (!r->id || strstr(appid, r->id))) ++ goto found; ++ ++ if (druleslen >= LENGTH(rules) + RULES_MAX) ++ return NULL; /* No free slots left */ ++ ++ r = drules + druleslen++; ++ ++ /* Use [NULL,NULL] as the default rule if exists */ ++ for (e = rules; e < END(rules); e++) ++ if (!e->title && !e->id) { ++ *r = *e; ++ break; ++ } ++ ++ /* No default rule found, set reasoble defaults */ ++ if (e >= END(rules)) { ++ r->monitor = -1; ++ } ++ ++ /* Only set title if appid is unset */ ++ if (strcmp(appid, "broken") == 0) ++ r->title = strdup(title); ++ else ++ r->id = strdup(appid); ++ ++found: ++ return r; ++} ++ + void + gpureset(struct wl_listener *listener, void *data) + { +@@ -2431,6 +2481,15 @@ setpsel(struct wl_listener *listener, void *data) + wlr_seat_set_primary_selection(seat, event->source, event->serial); + } + ++void ++setruleisfloating(const Arg *arg) ++{ ++ Rule *r = getrule(focustop(selmon)); ++ if (!r) ++ return; ++ r->isfloating = !r->isfloating; ++} ++ + void + setsel(struct wl_listener *listener, void *data) + { +@@ -2664,6 +2723,10 @@ setup(void) + fprintf(stderr, "failed to setup XWayland X server, continuing without it\n"); + } + #endif ++ ++ drules = ecalloc(LENGTH(rules) + RULES_MAX, sizeof(Rule)); ++ memcpy(drules, rules, sizeof(rules)); ++ druleslen = LENGTH(rules); + } + + void +-- +2.53.0 + From 79de70e29ef57b62983a026d1602b0c073829f39 Mon Sep 17 00:00:00 2001 From: Nikita Ivanov Date: Mon, 16 Mar 2026 21:25:48 +0100 Subject: [PATCH 29/36] swallow: update to 0.8 --- patches/swallow/README.md | 3 +- .../{swallow.patch => swallow-0.7.patch} | 0 patches/swallow/swallow-0.8.patch | 350 ++++++++++++++++++ 3 files changed, 352 insertions(+), 1 deletion(-) rename patches/swallow/{swallow.patch => swallow-0.7.patch} (100%) create mode 100644 patches/swallow/swallow-0.8.patch diff --git a/patches/swallow/README.md b/patches/swallow/README.md index 6c16162..f03fa79 100644 --- a/patches/swallow/README.md +++ b/patches/swallow/README.md @@ -22,7 +22,8 @@ scratch to make it more robust and add a few more features: #### swallow.patch -- [v0.7](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/swallow/swallow.patch) +- [v0.8](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/swallow/swallow-0.8.patch) +- [v0.7](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/swallow/swallow-0.7.patch) - [2025-03-03 v0.7](https://codeberg.org/dwl/dwl-patches/raw/commit/2e5748edfe1129f95c7bb1bf9dd590a897f55f57/patches/swallow/swallow.patch) (added "dynamic swallowing" support) - [2024-07-13](https://codeberg.org/dwl/dwl-patches/raw/commit/f1ed83eaeba46108f4ee8164094cb431d64a3e68/patches/swallow/swallow.patch) - [2024-07-13](https://codeberg.org/dwl/dwl-patches/raw/commit/f64d701bab2f9f52d3637edd091684f920407d87/patches/swallow/swallow.patch) diff --git a/patches/swallow/swallow.patch b/patches/swallow/swallow-0.7.patch similarity index 100% rename from patches/swallow/swallow.patch rename to patches/swallow/swallow-0.7.patch diff --git a/patches/swallow/swallow-0.8.patch b/patches/swallow/swallow-0.8.patch new file mode 100644 index 0000000..5204467 --- /dev/null +++ b/patches/swallow/swallow-0.8.patch @@ -0,0 +1,350 @@ +From 66efeab90dad4b6702a17b771cb08aeef159506a Mon Sep 17 00:00:00 2001 +From: Nikita Ivanov +Date: Wed, 5 Feb 2025 02:34:39 +0100 +Subject: [PATCH] swallow: hide the terminal when it spawns a client + +--- + client.h | 12 ++++ + config.def.h | 11 +++- + dwl.c | 152 +++++++++++++++++++++++++++++++++++++++++++++++++-- + 3 files changed, 168 insertions(+), 7 deletions(-) + +diff --git a/client.h b/client.h +index d9f90bb..83e1c57 100644 +--- a/client.h ++++ b/client.h +@@ -131,6 +131,18 @@ client_get_appid(Client *c) + return c->surface.xdg->toplevel->app_id ? c->surface.xdg->toplevel->app_id : "broken"; + } + ++static inline int ++client_get_pid(Client *c) ++{ ++ pid_t pid; ++#ifdef XWAYLAND ++ if (client_is_x11(c)) ++ return c->surface.xwayland->pid; ++#endif ++ wl_client_get_credentials(c->surface.xdg->client->client, &pid, NULL, NULL); ++ return pid; ++} ++ + static inline void + client_get_clip(Client *c, struct wlr_box *clip) + { +diff --git a/config.def.h b/config.def.h +index 8a6eda0..1a82020 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -13,6 +13,8 @@ static const float focuscolor[] = COLOR(0x005577ff); + static const float urgentcolor[] = COLOR(0xff0000ff); + /* This conforms to the xdg-protocol. Set the alpha to zero to restore the old behavior */ + static const float fullscreen_bg[] = {0.0f, 0.0f, 0.0f, 1.0f}; /* You can also use glsl colors */ ++static int enableautoswallow = 1; /* enables autoswallowing newly spawned clients */ ++static float swallowborder = 1.0f; /* add this multiplied by borderpx to border when a client is swallowed */ + + /* tagging - TAGCOUNT must be no greater than 31 */ + #define TAGCOUNT (9) +@@ -21,9 +23,10 @@ static const float fullscreen_bg[] = {0.0f, 0.0f, 0.0f, 1.0f}; /* You ca + static int log_level = WLR_ERROR; + + static const Rule rules[] = { +- /* app_id title tags mask isfloating monitor */ +- { "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, 1, -1 }, ++ { "Gimp_EXAMPLE", NULL, 0, 1, 0, 0, -1 }, /* Start on currently visible tags floating, not tiled */ ++ { "firefox_EXAMPLE", NULL, 1 << 8, 0, 0, 0, -1 }, /* Start on ONLY tag "9" */ + /* default/example rule: can be changed but cannot be eliminated; at least one rule must exist */ + }; + +@@ -138,6 +141,8 @@ static const Key keys[] = { + { MODKEY, XKB_KEY_space, setlayout, {0} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} }, + { MODKEY, XKB_KEY_e, togglefullscreen, {0} }, ++ { MODKEY, XKB_KEY_a, toggleswallow, {0} }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_A, toggleautoswallow,{0} }, + { MODKEY, XKB_KEY_0, view, {.ui = ~0} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} }, + { MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} }, +diff --git a/dwl.c b/dwl.c +index 44f3ad9..066b705 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -74,12 +74,13 @@ + #define MAX(A, B) ((A) > (B) ? (A) : (B)) + #define MIN(A, B) ((A) < (B) ? (A) : (B)) + #define CLEANMASK(mask) (mask & ~WLR_MODIFIER_CAPS) +-#define VISIBLEON(C, M) ((M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags])) ++#define VISIBLEON(C, M) ((M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags]) && !(C)->swallowedby) + #define LENGTH(X) (sizeof X / sizeof X[0]) + #define END(A) ((A) + LENGTH(A)) + #define TAGMASK ((1u << TAGCOUNT) - 1) + #define LISTEN(E, L, H) wl_signal_add((E), ((L)->notify = (H), (L))) + #define LISTEN_STATIC(E, H) do { struct wl_listener *_l = ecalloc(1, sizeof(*_l)); _l->notify = (H); wl_signal_add((E), _l); } while (0) ++#define BORDERPX(C) (borderpx + ((C)->swallowing ? (int)ceilf(swallowborder * (C)->swallowing->bw) : 0)) + + /* enums */ + enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */ +@@ -101,7 +102,8 @@ typedef struct { + } Button; + + typedef struct Monitor Monitor; +-typedef struct { ++typedef struct Client Client; ++struct Client { + /* Must keep this field first */ + unsigned int type; /* XDGShell or X11* */ + +@@ -138,8 +140,12 @@ typedef struct { + unsigned int bw; + uint32_t tags; + int isfloating, isurgent, isfullscreen; ++ int isterm, noswallow; + uint32_t resize; /* configure serial of a pending resize */ +-} Client; ++ pid_t pid; ++ Client *swallowing; /* client being hidden */ ++ Client *swallowedby; ++}; + + typedef struct { + uint32_t mod; +@@ -227,6 +233,8 @@ typedef struct { + const char *title; + uint32_t tags; + int isfloating; ++ int isterm; ++ int noswallow; + int monitor; + } Rule; + +@@ -308,6 +316,7 @@ static void moveresize(const Arg *arg); + static void outputmgrapply(struct wl_listener *listener, void *data); + static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test); + static void outputmgrtest(struct wl_listener *listener, void *data); ++static pid_t parentpid(pid_t pid); + static void pointerfocus(Client *c, struct wlr_surface *surface, + double sx, double sy, uint32_t time); + static void printstatus(void); +@@ -331,11 +340,15 @@ static void setsel(struct wl_listener *listener, void *data); + static void setup(void); + static void spawn(const Arg *arg); + static void startdrag(struct wl_listener *listener, void *data); ++static void swallow(Client *c, Client *toswallow); + static void tag(const Arg *arg); + static void tagmon(const Arg *arg); ++static Client *termforwin(Client *c); + static void tile(Monitor *m); + static void togglefloating(const Arg *arg); + static void togglefullscreen(const Arg *arg); ++static void toggleswallow(const Arg *arg); ++static void toggleautoswallow(const Arg *arg); + static void toggletag(const Arg *arg); + static void toggleview(const Arg *arg); + static void unlocksession(struct wl_listener *listener, void *data); +@@ -486,11 +499,15 @@ applyrules(Client *c) + appid = client_get_appid(c); + title = client_get_title(c); + ++ c->pid = client_get_pid(c); ++ + for (r = rules; r < END(rules); r++) { + if ((!r->title || strstr(title, r->title)) + && (!r->id || strstr(appid, r->id))) { + c->isfloating = r->isfloating; + newtags |= r->tags; ++ c->isterm = r->isterm; ++ c->noswallow = r->noswallow; + i = 0; + wl_list_for_each(m, &mons, link) { + if (r->monitor == i++) +@@ -500,6 +517,12 @@ applyrules(Client *c) + } + + c->isfloating |= client_is_float_type(c); ++ if (enableautoswallow && !c->noswallow && !c->isfloating && ++ !c->surface.xdg->initial_commit) { ++ Client *p = termforwin(c); ++ if (p) ++ swallow(c, p); ++ } + setmon(c, mon, newtags); + } + +@@ -2057,6 +2080,20 @@ outputmgrtest(struct wl_listener *listener, void *data) + outputmgrapplyortest(config, 1); + } + ++pid_t ++parentpid(pid_t pid) ++{ ++ unsigned int v = 0; ++ FILE *f; ++ char buf[256]; ++ snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)pid); ++ if (!(f = fopen(buf, "r"))) ++ return 0; ++ fscanf(f, "%*u %*s %*c %u", &v); ++ fclose(f); ++ return (pid_t)v; ++} ++ + void + pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, + uint32_t time) +@@ -2351,7 +2388,7 @@ setfullscreen(Client *c, int fullscreen) + c->isfullscreen = fullscreen; + if (!c->mon || !client_surface(c)->mapped) + return; +- c->bw = fullscreen ? 0 : borderpx; ++ c->bw = fullscreen ? 0 : BORDERPX(c); + client_set_fullscreen(c, fullscreen); + wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen + ? LyrFS : c->isfloating ? LyrFloat : LyrTile]); +@@ -2418,6 +2455,9 @@ setmon(Client *c, Monitor *m, uint32_t newtags) + setfloating(c, c->isfloating); + } + focusclient(focustop(selmon), 1); ++ ++ if (c->swallowing) ++ setmon(c->swallowing, m, newtags); + } + + void +@@ -2688,6 +2728,44 @@ startdrag(struct wl_listener *listener, void *data) + LISTEN_STATIC(&drag->icon->events.destroy, destroydragicon); + } + ++void ++swallow(Client *c, Client *toswallow) ++{ ++ /* Do not allow a client to swallow itself */ ++ if (c == toswallow) ++ return; ++ ++ /* Swallow */ ++ if (toswallow && !c->swallowing) { ++ c->swallowing = toswallow; ++ toswallow->swallowedby = c; ++ toswallow->mon = c->mon; ++ toswallow->mon = c->mon; ++ wl_list_remove(&c->link); ++ wl_list_insert(&c->swallowing->link, &c->link); ++ wl_list_remove(&c->flink); ++ wl_list_insert(&c->swallowing->flink, &c->flink); ++ c->bw = BORDERPX(c); ++ c->tags = toswallow->tags; ++ c->isfloating = toswallow->isfloating; ++ c->geom = toswallow->geom; ++ setfullscreen(toswallow, 0); ++ } ++ ++ /* Unswallow */ ++ else if (c->swallowing) { ++ wl_list_remove(&c->swallowing->link); ++ wl_list_insert(&c->link, &c->swallowing->link); ++ wl_list_remove(&c->swallowing->flink); ++ wl_list_insert(&c->flink, &c->swallowing->flink); ++ c->swallowing->tags = c->tags; ++ c->swallowing->swallowedby = NULL; ++ c->swallowing = NULL; ++ c->bw = BORDERPX(c); ++ setfullscreen(c, 0); ++ } ++} ++ + void + tag(const Arg *arg) + { +@@ -2709,6 +2787,40 @@ tagmon(const Arg *arg) + setmon(sel, dirtomon(arg->i), 0); + } + ++Client * ++termforwin(Client *c) ++{ ++ Client *p; ++ pid_t pid; ++ pid_t pids[32]; ++ size_t i, pids_len; ++ ++ if (!c->pid || c->isterm) ++ return NULL; ++ ++ /* Get all parent pids */ ++ pids_len = 0; ++ pid = c->pid; ++ while (pids_len < LENGTH(pids)) { ++ pid = parentpid(pid); ++ if (!pid) ++ break; ++ pids[pids_len++] = pid; ++ } ++ ++ /* Find closest parent */ ++ for (i = 0; i < pids_len; i++) { ++ wl_list_for_each(p, &clients, link) { ++ if (!p->pid || !p->isterm || p->swallowedby) ++ continue; ++ if (pids[i] == p->pid) ++ return p; ++ } ++ } ++ ++ return NULL; ++} ++ + void + tile(Monitor *m) + { +@@ -2760,6 +2872,32 @@ togglefullscreen(const Arg *arg) + setfullscreen(sel, !sel->isfullscreen); + } + ++void ++toggleswallow(const Arg *arg) ++{ ++ Client *c, *sel = focustop(selmon); ++ if (!sel) ++ return; ++ ++ if (sel->swallowing) { ++ swallow(sel, NULL); ++ } else { ++ wl_list_for_each(c, &sel->flink, flink) { ++ if (&c->flink == &fstack) ++ continue; /* wrap past the sentinel node */ ++ if (VISIBLEON(c, selmon)) ++ break; /* found it */ ++ } ++ swallow(sel, c); ++ } ++} ++ ++void ++toggleautoswallow(const Arg *arg) ++{ ++ enableautoswallow = !enableautoswallow; ++} ++ + void + toggletag(const Arg *arg) + { +@@ -2820,6 +2958,12 @@ unmapnotify(struct wl_listener *listener, void *data) + grabc = NULL; + } + ++ if (c->swallowing) { ++ swallow(c, NULL); ++ } else if (c->swallowedby) { ++ swallow(c->swallowedby, NULL); ++ } ++ + if (client_is_unmanaged(c)) { + if (c == exclusive_focus) { + exclusive_focus = NULL; +-- +2.53.0 + From 50651a80d22b92625e95129cdbfa0faa82c8ad62 Mon Sep 17 00:00:00 2001 From: Nikita Ivanov Date: Mon, 16 Mar 2026 21:44:32 +0100 Subject: [PATCH 30/36] menu: update to 0.8 --- patches/menu/README.md | 5 +- patches/menu/{menu.patch => menu-0.7.patch} | 0 patches/menu/menu-0.8.patch | 227 ++++++++++++++++++++ 3 files changed, 230 insertions(+), 2 deletions(-) rename patches/menu/{menu.patch => menu-0.7.patch} (100%) create mode 100644 patches/menu/menu-0.8.patch diff --git a/patches/menu/README.md b/patches/menu/README.md index ad37016..9942fe6 100644 --- a/patches/menu/README.md +++ b/patches/menu/README.md @@ -11,8 +11,9 @@ Edit `menus` array in `config.h` to add/change menus and use a different dmenu program (`wmenu` is the default). ### Download -- [2025-03-21 v0.7](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/menu/menu.patch) -- [2024-07-13 v0.7](https://codeberg.org/dwl/dwl-patches/raw/commit/65ea99519bbf7a52f48932aea7385f81f8b30867/patches/menu/menu.patch) +- [v0.8](/dwl-patches/raw/branch/main/patches/menu/menu-0.8.patch) +- [2025-03-21 v0.7](/dwl/dwl-patches/raw/branch/main/patches/menu/menu-0.7.patch) +- [2024-07-13 v0.7](/dwl/dwl-patches/raw/commit/65ea99519bbf7a52f48932aea7385f81f8b30867/patches/menu/menu.patch) ### Authors - [Nikita Ivanov](https://codeberg.org/nikitaivanov) ([GitHub](https://github.com/NikitaIvanovV)) diff --git a/patches/menu/menu.patch b/patches/menu/menu-0.7.patch similarity index 100% rename from patches/menu/menu.patch rename to patches/menu/menu-0.7.patch diff --git a/patches/menu/menu-0.8.patch b/patches/menu/menu-0.8.patch new file mode 100644 index 0000000..ede822c --- /dev/null +++ b/patches/menu/menu-0.8.patch @@ -0,0 +1,227 @@ +From 4347c2fd720166c526384827ef4f586a84d22040 Mon Sep 17 00:00:00 2001 +From: Nikita Ivanov +Date: Fri, 21 Mar 2025 21:48:42 +0100 +Subject: [PATCH] Add menu command + +--- + config.def.h | 8 +++ + dwl.c | 156 +++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 164 insertions(+) + +diff --git a/config.def.h b/config.def.h +index 8a6eda0..1de52fa 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -20,6 +20,12 @@ static const float fullscreen_bg[] = {0.0f, 0.0f, 0.0f, 1.0f}; /* You ca + /* logging */ + static int log_level = WLR_ERROR; + ++static const Menu menus[] = { ++ /* command feed function action function */ ++ { "wmenu -i -l 10 -p Windows", menuwinfeed, menuwinaction }, ++ { "wmenu -i -p Layouts", menulayoutfeed, menulayoutaction }, ++}; ++ + static const Rule rules[] = { + /* app_id title tags mask isfloating monitor */ + { "Gimp_EXAMPLE", NULL, 0, 1, -1 }, /* Start on currently visible tags floating, not tiled */ +@@ -136,6 +142,8 @@ static const Key keys[] = { + { MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} }, + { MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} }, + { MODKEY, XKB_KEY_space, setlayout, {0} }, ++ { MODKEY, XKB_KEY_o, menu, {.v = &menus[0]} }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_O, menu, {.v = &menus[1]} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} }, + { MODKEY, XKB_KEY_e, togglefullscreen, {0} }, + { MODKEY, XKB_KEY_0, view, {.ui = ~0} }, +diff --git a/dwl.c b/dwl.c +index 44f3ad9..7e0e7b6 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -239,6 +239,12 @@ typedef struct { + struct wl_listener destroy; + } SessionLock; + ++typedef struct { ++ const char *cmd; /* command to run a menu */ ++ void (*feed)(FILE *f); /* feed input to menu */ ++ void (*action)(char *line); /* do action based on menu output */ ++} Menu; ++ + /* function declarations */ + static void applybounds(Client *c, struct wlr_box *bbox); + static void applyrules(Client *c); +@@ -299,6 +305,12 @@ static void killclient(const Arg *arg); + static void locksession(struct wl_listener *listener, void *data); + static void mapnotify(struct wl_listener *listener, void *data); + static void maximizenotify(struct wl_listener *listener, void *data); ++static void menu(const Arg *arg); ++static int menuread(int fd, uint32_t mask, void *data); ++static void menuwinfeed(FILE *f); ++static void menuwinaction(char *line); ++static void menulayoutfeed(FILE *f); ++static void menulayoutaction(char *line); + static void monocle(Monitor *m); + static void motionabsolute(struct wl_listener *listener, void *data); + static void motionnotify(uint32_t time, struct wlr_input_device *device, double sx, +@@ -436,6 +448,11 @@ 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 const Menu *menu_current; ++static int menu_fd; ++static pid_t menu_pid; ++static struct wl_event_source *menu_source; ++ + #ifdef XWAYLAND + static void activatex11(struct wl_listener *listener, void *data); + static void associatex11(struct wl_listener *listener, void *data); +@@ -1820,6 +1837,145 @@ maximizenotify(struct wl_listener *listener, void *data) + wlr_xdg_surface_schedule_configure(c->surface.xdg); + } + ++void ++menu(const Arg *arg) ++{ ++ FILE *f; ++ int fd_right[2], fd_left[2]; ++ ++ if (menu_current != NULL) { ++ wl_event_source_remove(menu_source); ++ close(menu_fd); ++ kill(menu_pid, SIGTERM); ++ menu_current = NULL; ++ if (!arg->v) ++ return; ++ } ++ ++ if (pipe(fd_right) == -1 || pipe(fd_left) == -1) ++ return; ++ if ((menu_pid = fork()) == -1) ++ return; ++ if (menu_pid == 0) { ++ close(fd_right[1]); ++ close(fd_left[0]); ++ dup2(fd_right[0], STDIN_FILENO); ++ close(fd_right[0]); ++ dup2(fd_left[1], STDOUT_FILENO); ++ close(fd_left[1]); ++ execl("/bin/sh", "/bin/sh", "-c", ((Menu *)(arg->v))->cmd, NULL); ++ die("dwl: execl %s failed:", "/bin/sh"); ++ } ++ ++ close(fd_right[0]); ++ close(fd_left[1]); ++ menu_fd = fd_left[0]; ++ if (fd_set_nonblock(menu_fd) == -1) ++ return; ++ if (!(f = fdopen(fd_right[1], "w"))) ++ return; ++ menu_current = arg->v; ++ menu_current->feed(f); ++ fclose(f); ++ menu_source = wl_event_loop_add_fd(event_loop, ++ menu_fd, WL_EVENT_READABLE, menuread, NULL); ++} ++ ++int ++menuread(int fd, uint32_t mask, void *data) ++{ ++ char *s; ++ int n; ++ static char line[512]; ++ static int i = 0; ++ ++ if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) { ++ i = 0; ++ menu(&(const Arg){ .v = NULL }); ++ return 0; ++ } ++ if ((n = read(menu_fd, line + i, LENGTH(line) - 1 - i)) == -1) { ++ if (errno != EAGAIN) { ++ i = 0; ++ menu(&(const Arg){ .v = NULL }); ++ } ++ return 0; ++ } ++ line[i + n] = '\0'; ++ if (!(s = strchr(line + i, '\n'))) { ++ i += n; ++ return 0; ++ } ++ i = 0; ++ *s = '\0'; ++ menu_current->action(line); ++ return 0; ++} ++ ++void ++menuwinfeed(FILE *f) ++{ ++ Client *c; ++ const char *title, *appid; ++ ++ wl_list_for_each(c, &fstack, flink) { ++ if (!(title = client_get_title(c))) ++ continue; ++ fprintf(f, "%s", title); ++ if ((appid = client_get_appid(c))) ++ fprintf(f, " | %s", appid); ++ fputc('\n', f); ++ } ++} ++ ++void ++menuwinaction(char *line) ++{ ++ Client *c; ++ const char *appid, *title; ++ static char buf[512]; ++ ++ wl_list_for_each(c, &fstack, flink) { ++ if (!(title = client_get_title(c))) ++ continue; ++ appid = client_get_appid(c); ++ snprintf(buf, LENGTH(buf) - 1, "%s%s%s", ++ title, appid ? " | " : "", appid ? appid : ""); ++ if (strcmp(line, buf) == 0) ++ goto found; ++ } ++ return; ++ ++found: ++ if (!c->mon) ++ return; ++ wl_list_remove(&c->flink); ++ wl_list_insert(&fstack, &c->flink); ++ selmon = c->mon; ++ view(&(const Arg){ .ui = c->tags }); ++} ++ ++void ++menulayoutfeed(FILE *f) ++{ ++ const Layout *l; ++ for (l = layouts; l < END(layouts); l++) ++ fprintf(f, "%s\n", l->symbol); ++} ++ ++void ++menulayoutaction(char *line) ++{ ++ const Layout *l; ++ for (l = layouts; l < END(layouts); l++) ++ if (strcmp(line, l->symbol) == 0) ++ goto found; ++ return; ++ ++found: ++ setlayout(&(const Arg){ .v = l }); ++} ++ + void + monocle(Monitor *m) + { +-- +2.53.0 + From 8ac41e9d73436aa806437898629166e280a37b10 Mon Sep 17 00:00:00 2001 From: Nikita Ivanov Date: Mon, 16 Mar 2026 21:49:06 +0100 Subject: [PATCH 31/36] menurule: update to 0.8 --- patches/menurule/README.md | 3 +- .../{menurule.patch => menurule-0.7.patch} | 0 patches/menurule/menurule-0.8.patch | 169 ++++++++++++++++++ 3 files changed, 171 insertions(+), 1 deletion(-) rename patches/menurule/{menurule.patch => menurule-0.7.patch} (100%) create mode 100644 patches/menurule/menurule-0.8.patch diff --git a/patches/menurule/README.md b/patches/menurule/README.md index 8d8b757..64f682c 100644 --- a/patches/menurule/README.md +++ b/patches/menurule/README.md @@ -63,7 +63,8 @@ index 34397fc..f1b31ea 100644 ### Download -- [v0.7](/dwl/dwl-patches/raw/branch/main/patches/menurule/menurule.patch) +- [0.8](/dwl/dwl-patches/raw/branch/main/patches/menurule/menurule-0.8.patch) +- [0.7](/dwl/dwl-patches/raw/branch/main/patches/menurule/menurule-0.7.patch) ### Authors diff --git a/patches/menurule/menurule.patch b/patches/menurule/menurule-0.7.patch similarity index 100% rename from patches/menurule/menurule.patch rename to patches/menurule/menurule-0.7.patch diff --git a/patches/menurule/menurule-0.8.patch b/patches/menurule/menurule-0.8.patch new file mode 100644 index 0000000..eb90158 --- /dev/null +++ b/patches/menurule/menurule-0.8.patch @@ -0,0 +1,169 @@ +From ffbc9d95316ec42e3f8e08816bfcf7b91a6801b6 Mon Sep 17 00:00:00 2001 +From: Nikita Ivanov +Date: Wed, 19 Mar 2025 02:28:46 +0100 +Subject: [PATCH] Add menurule to tweak rules at runtime + +--- + config.def.h | 2 + + dwl.c | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 120 insertions(+) + +diff --git a/config.def.h b/config.def.h +index 77a72a5..e7601a4 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -24,6 +24,7 @@ static const Menu menus[] = { + /* command feed function action function */ + { "wmenu -i -l 10 -p Windows", menuwinfeed, menuwinaction }, + { "wmenu -i -p Layouts", menulayoutfeed, menulayoutaction }, ++ { "wmenu -i -l 10 -p Rules", menurulefeed, menuruleaction }, + }; + + /* Max amount of dynamically added rules */ +@@ -147,6 +148,7 @@ static const Key keys[] = { + { MODKEY, XKB_KEY_space, setlayout, {0} }, + { MODKEY, XKB_KEY_o, menu, {.v = &menus[0]} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_O, menu, {.v = &menus[1]} }, ++ { MODKEY, XKB_KEY_r, menu, {.v = &menus[2]} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} }, + { MODKEY, XKB_KEY_e, togglefullscreen, {0} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_R, setruleisfloating,{0} }, +diff --git a/dwl.c b/dwl.c +index 3d65e16..49daddb 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -312,6 +312,8 @@ static void menuwinfeed(FILE *f); + static void menuwinaction(char *line); + static void menulayoutfeed(FILE *f); + static void menulayoutaction(char *line); ++static void menurulefeed(FILE *f); ++static void menuruleaction(char *line); + static void monocle(Monitor *m); + static void motionabsolute(struct wl_listener *listener, void *data); + static void motionnotify(uint32_t time, struct wlr_input_device *device, double sx, +@@ -2026,6 +2028,122 @@ found: + setlayout(&(const Arg){ .v = l }); + } + ++void ++menurulefeed(FILE *f) ++{ ++ Rule t, *p, *r; ++ const char *appid, *title; ++ static char buf[515]; ++ Client *c = focustop(selmon); ++ int n, wid = 0, match; ++ ++ t = (Rule){ 0 }; ++ t.monitor = -1; ++ if (c) { ++ t.id = appid = client_get_appid(c); ++ t.title = title = client_get_title(c); ++ if (strcmp(t.id, "broken") == 0) ++ t.id = NULL; ++ if (strcmp(t.title, "broken") == 0) ++ t.title = NULL; ++ } ++ ++ for (p = drules; p <= drules + druleslen; p++) { ++ r = (p == drules + druleslen) ? &t : p; ++ n = 0; ++ n += strlen(r->id ? r->id : "NULL"); ++ n += strlen(r->title ? r->title : "NULL"); ++ n += 3; ++ wid = MAX(wid, n); ++ } ++ wid = MIN(wid, 40); ++ ++ for (p = drules; p <= drules + druleslen; p++) { ++ match = 0; ++ /* Check if rule applies to the focused client */ ++ if (c && p < drules + druleslen) { ++ match = (!p->title || strstr(title, p->title)) ++ && (!p->id || strstr(appid, p->id)); ++ if (match && p->id) ++ t.id = NULL; ++ if (match && p->title) ++ t.title = NULL; ++ } ++ r = (p == drules + druleslen) ? &t : p; ++ if (r == &t && t.id) ++ t.title = NULL; ++ /* Do not suggest to add a new empty rule */ ++ if (r == &t && !(t.id || t.title)) ++ continue; ++ if (r->id && r->title && ++ strcmp(r->id, "removedrule") == 0 && strcmp(r->title, "removedrule") == 0) ++ continue; ++ snprintf(buf, LENGTH(buf) - 1, "[%s|%s]", ++ r->id ? r->id : "NULL", r->title ? r->title : "NULL"); ++ fprintf(f, "%-*s " ++ " tags:%-4"PRIi32 ++ " isfloating:%-2d" ++ " monitor:%-2d" ++ "%s\n", wid, buf, ++ r->tags, ++ r->isfloating, ++ r->monitor, ++ (r == &t) ? " (NEW)" : match ? " <" : ""); ++ } ++} ++ ++void ++menuruleaction(char *line) ++{ ++ Rule r, *f; ++ static char appid[256], title[256]; ++ int del = 0, end; ++ ++ if (line[0] == '-') { ++ del = 1; ++ line++; ++ } ++ end = 0; ++ sscanf(line, "[%255[^|]|%255[^]]]" ++ " tags:%"SCNu32 ++ " isfloating:%d" ++ " monitor:%d" ++ "%n", appid, title, ++ &r.tags, ++ &r.isfloating, ++ &r.monitor, ++ &end); ++ ++ /* Full line was not parsed, exit */ ++ if (!end) ++ return; ++ ++ r.id = (strcmp(appid, "NULL") != 0) ? appid : NULL; ++ r.title = (strcmp(title, "NULL") != 0) ? title : NULL; ++ ++ /* Find which rule we are trying to edit */ ++ for (f = drules; f < drules + druleslen; f++) ++ if (((!r.title && !f->title) || (r.title && f->title && !strcmp(r.title, f->title))) ++ && (((!r.id && !f->id) || (r.id && f->id && !strcmp(r.id, f->id))))) ++ goto found; ++ ++ if (druleslen >= LENGTH(rules) + RULES_MAX) ++ return; /* No free slots left */ ++ ++ f = drules + druleslen++; ++ f->id = r.id ? strdup(r.id) : NULL; ++ f->title = r.title ? strdup(r.title) : NULL; ++ ++found: ++ if (del) { ++ f->id = f->title = "removedrule"; ++ return; ++ } ++ r.id = f->id; ++ r.title = f->title; ++ *f = r; ++} ++ + void + monocle(Monitor *m) + { +-- +2.53.0 + From 6d4b7d3a57feeb09b603c6e8122695a108742849 Mon Sep 17 00:00:00 2001 From: LaKato Date: Wed, 18 Mar 2026 20:51:39 -0400 Subject: [PATCH 32/36] togglekblayoutandoptions: update for dwl v0.8 No changes, just a rebase to explicitly advertise support. --- patches/togglekblayoutandoptions/README.md | 5 +- ...tch => togglekblayoutandoptions-0.7.patch} | 0 .../togglekblayoutandoptions-0.8.patch | 160 ++++++++++++++++++ 3 files changed, 164 insertions(+), 1 deletion(-) rename patches/togglekblayoutandoptions/{togglekblayoutandoptions.patch => togglekblayoutandoptions-0.7.patch} (100%) create mode 100644 patches/togglekblayoutandoptions/togglekblayoutandoptions-0.8.patch diff --git a/patches/togglekblayoutandoptions/README.md b/patches/togglekblayoutandoptions/README.md index 883dd6d..05bb078 100644 --- a/patches/togglekblayoutandoptions/README.md +++ b/patches/togglekblayoutandoptions/README.md @@ -1,8 +1,11 @@ ### Description Switch between multiple keyboard layouts, variants, and options at runtime. +Supports both assigning a specific keyboard to a shortcut and cycling through +keyboards. ### Download -- [0.7](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/togglekblayoutandoptions/togglekblayoutandoptions.patch) +- [0.8](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/togglekblayoutandoptions/togglekblayoutandoptions-0.8.patch) +- [0.7](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/togglekblayoutandoptions/togglekblayoutandoptions-0.7.patch) ### Authors - [LaKato](https://codeberg.org/LaKato) diff --git a/patches/togglekblayoutandoptions/togglekblayoutandoptions.patch b/patches/togglekblayoutandoptions/togglekblayoutandoptions-0.7.patch similarity index 100% rename from patches/togglekblayoutandoptions/togglekblayoutandoptions.patch rename to patches/togglekblayoutandoptions/togglekblayoutandoptions-0.7.patch diff --git a/patches/togglekblayoutandoptions/togglekblayoutandoptions-0.8.patch b/patches/togglekblayoutandoptions/togglekblayoutandoptions-0.8.patch new file mode 100644 index 0000000..f68f49c --- /dev/null +++ b/patches/togglekblayoutandoptions/togglekblayoutandoptions-0.8.patch @@ -0,0 +1,160 @@ +From 6c028b4cacd21f9e6a195ce7b0e1987868fb3e62 Mon Sep 17 00:00:00 2001 +From: LaKato +Date: Wed, 18 Mar 2026 20:47:30 -0400 +Subject: [PATCH] Rebase togglekblayoutandoptions to v0.8 + +No actual changes, and the previous patch applied just fine if +whitespace was ignored. Just adding this to explicitly advertise +support for v0.8 and reduce noise about patches being applied at an +offset. +--- + config.def.h | 17 +++++++++++------ + dwl.c | 44 +++++++++++++++++++++++++++++++++----------- + 2 files changed, 44 insertions(+), 17 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 8a6eda0..850f322 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -48,12 +48,15 @@ static const MonitorRule monrules[] = { + }; + + /* keyboard */ +-static const struct xkb_rule_names xkb_rules = { +- /* can specify fields: rules, model, layout, variant, options */ +- /* example: +- .options = "ctrl:nocaps", +- */ +- .options = NULL, ++static const struct xkb_rule_names xkb_rules[] = { ++ { ++ .layout = "us", ++ }, ++ /*{ ++ .layout = "us", ++ .variant = "dvp", ++ .options = "compose:102,numpad:shift3,kpdl:semi,keypad:atm,caps:super" ++ }*/ + }; + + static const int repeat_rate = 25; +@@ -144,6 +147,8 @@ static const Key keys[] = { + { 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|WLR_MODIFIER_SHIFT, XKB_KEY_A, incxkbrules, {.i = +1} }, ++ /*{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_E, setxkbrules, {.i = +1} },*/ + TAGKEYS( XKB_KEY_1, XKB_KEY_exclam, 0), + TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1), + TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2), +diff --git a/dwl.c b/dwl.c +index 44f3ad9..a86c692 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -246,6 +246,7 @@ static void arrange(Monitor *m); + static void arrangelayer(Monitor *m, struct wl_list *list, + struct wlr_box *usable_area, int exclusive); + static void arrangelayers(Monitor *m); ++static void assignkeymap(struct wlr_keyboard *keyboard); + static void axisnotify(struct wl_listener *listener, void *data); + static void buttonpress(struct wl_listener *listener, void *data); + static void chvt(const Arg *arg); +@@ -290,6 +291,7 @@ static void fullscreennotify(struct wl_listener *listener, void *data); + static void gpureset(struct wl_listener *listener, void *data); + static void handlesig(int signo); + static void incnmaster(const Arg *arg); ++static void incxkbrules(const Arg *arg); + static void inputdevice(struct wl_listener *listener, void *data); + static int keybinding(uint32_t mods, xkb_keysym_t sym); + static void keypress(struct wl_listener *listener, void *data); +@@ -329,6 +331,7 @@ static void setmon(Client *c, Monitor *m, uint32_t newtags); + static void setpsel(struct wl_listener *listener, void *data); + static void setsel(struct wl_listener *listener, void *data); + static void setup(void); ++static void setxkbrules(const Arg *arg); + static void spawn(const Arg *arg); + static void startdrag(struct wl_listener *listener, void *data); + static void tag(const Arg *arg); +@@ -397,6 +400,7 @@ static struct wlr_session_lock_v1 *cur_lock; + + static struct wlr_seat *seat; + static KeyboardGroup *kb_group; ++static unsigned int kblayout = 0; + static unsigned int cursor_mode; + static Client *grabc; + static int grabcx, grabcy; /* client-relative */ +@@ -603,6 +607,20 @@ arrangelayers(Monitor *m) + } + } + ++void ++assignkeymap(struct wlr_keyboard *keyboard) { ++ struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); ++ struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, &xkb_rules[kblayout], ++ XKB_KEYMAP_COMPILE_NO_FLAGS); ++ ++ if (!keymap) ++ die("failed to compile keymap"); ++ ++ wlr_keyboard_set_keymap(keyboard, keymap); ++ xkb_keymap_unref(keymap); ++ xkb_context_unref(context); ++} ++ + void + axisnotify(struct wl_listener *listener, void *data) + { +@@ -951,21 +969,11 @@ KeyboardGroup * + createkeyboardgroup(void) + { + KeyboardGroup *group = ecalloc(1, sizeof(*group)); +- struct xkb_context *context; +- struct xkb_keymap *keymap; + + group->wlr_group = wlr_keyboard_group_create(); + group->wlr_group->data = group; + +- /* Prepare an XKB keymap and assign it to the keyboard group. */ +- context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); +- if (!(keymap = xkb_keymap_new_from_names(context, &xkb_rules, +- XKB_KEYMAP_COMPILE_NO_FLAGS))) +- die("failed to compile keymap"); +- +- wlr_keyboard_set_keymap(&group->wlr_group->keyboard, keymap); +- xkb_keymap_unref(keymap); +- xkb_context_unref(context); ++ assignkeymap(&group->wlr_group->keyboard); + + wlr_keyboard_set_repeat_info(&group->wlr_group->keyboard, repeat_rate, repeat_delay); + +@@ -1575,6 +1583,13 @@ incnmaster(const Arg *arg) + arrange(selmon); + } + ++void ++incxkbrules(const Arg *arg) ++{ ++ kblayout = (kblayout + arg->i) % LENGTH(xkb_rules); ++ assignkeymap(&kb_group->wlr_group->keyboard); ++} ++ + void + inputdevice(struct wl_listener *listener, void *data) + { +@@ -2666,6 +2681,13 @@ setup(void) + #endif + } + ++void ++setxkbrules(const Arg *arg) ++{ ++ kblayout = arg->i; ++ assignkeymap(&kb_group->wlr_group->keyboard); ++} ++ + void + spawn(const Arg *arg) + { +-- +2.52.0 + From ee75e70f7963344267d92cff31be9ed0c0ea2585 Mon Sep 17 00:00:00 2001 From: julmajustus Date: Thu, 19 Mar 2026 23:20:11 +0200 Subject: [PATCH 33/36] btrtile update for dwl v0.8 and wlroots-next --- patches/btrtile/README.md | 6 +- ...0.7-gaps.patch => btrtile-v0.8-gaps.patch} | 129 ++- patches/btrtile/btrtile-v0.8.patch | 894 +++++++++++++++++ .../btrtile-wlroots-next-d41ecb7-gaps.patch | 913 ++++++++++++++++++ ...tch => btrtile-wlroots-next-d41ecb7.patch} | 121 ++- 5 files changed, 1927 insertions(+), 136 deletions(-) rename patches/btrtile/{btrtile-v0.7-gaps.patch => btrtile-v0.8-gaps.patch} (89%) create mode 100644 patches/btrtile/btrtile-v0.8.patch create mode 100644 patches/btrtile/btrtile-wlroots-next-d41ecb7-gaps.patch rename patches/btrtile/{btrtile-v0.7.patch => btrtile-wlroots-next-d41ecb7.patch} (89%) diff --git a/patches/btrtile/README.md b/patches/btrtile/README.md index d187f5f..7e679c4 100644 --- a/patches/btrtile/README.md +++ b/patches/btrtile/README.md @@ -96,8 +96,10 @@ If mouse resizing feels sluggish, you can try compiling dwl with more aggressive ### Download - [git branch](https://codeberg.org/julmajustus/dwl/src/branch/btrtile-dev) -- [0.7](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/btrtile/btrtile-v0.7.patch) -- [0.7 WITH gaps](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/btrtile/btrtile-v0.7-gaps.patch) +- [0.8](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/btrtile/btrtile-v0.8.patch) +- [0.8 WITH gaps](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/btrtile/btrtile-v0.8-gaps.patch) +- [wlroots-next](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/btrtile/btrtile-wlroots-next-d41ecb7.patch) +- [wlroots-next WITH gaps](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/btrtile/btrtile-wlroots-next-d41ecb7-gaps.patch) ### Authors - [julmajustus](https://codeberg.org/julmajustus) diff --git a/patches/btrtile/btrtile-v0.7-gaps.patch b/patches/btrtile/btrtile-v0.8-gaps.patch similarity index 89% rename from patches/btrtile/btrtile-v0.7-gaps.patch rename to patches/btrtile/btrtile-v0.8-gaps.patch index dc4e9cf..17cc5ca 100644 --- a/patches/btrtile/btrtile-v0.7-gaps.patch +++ b/patches/btrtile/btrtile-v0.8-gaps.patch @@ -1,30 +1,32 @@ -From 858ef20d36c2d5e6a23a69b3b5909a80fab05f97 Mon Sep 17 00:00:00 2001 +From f5d7fe287bd4405879a92d649243d3f646652617 Mon Sep 17 00:00:00 2001 From: julmajustus -Date: Thu, 13 Feb 2025 23:25:20 +0200 -Subject: [PATCH] btrtile-gaps with multi-tag support +Date: Thu, 19 Mar 2026 00:08:10 +0200 +Subject: [PATCH] Refactor btrtile-gaps to v0.8 --- - btrtile.c | 582 +++++++++++++++++++++++++++++++++++++++++++++++++++ + btrtile.c | 584 +++++++++++++++++++++++++++++++++++++++++++++++++++ config.def.h | 12 ++ - dwl.c | 152 +++++++++++--- - 3 files changed, 717 insertions(+), 29 deletions(-) + dwl.c | 148 ++++++++++--- + 3 files changed, 717 insertions(+), 27 deletions(-) create mode 100644 btrtile.c diff --git a/btrtile.c b/btrtile.c new file mode 100644 -index 0000000..650cab5 +index 0000000..afe3c8f --- /dev/null +++ b/btrtile.c -@@ -0,0 +1,582 @@ +@@ -0,0 +1,584 @@ +/* ************************************************************************** */ -+/* */ -+/* ::: :::::::: */ -+/* btrtile.c :+: :+: :+: */ -+/* +:+ +:+ +:+ */ -+/* By: jmakkone +#+ +:+ +#+ */ -+/* +#+#+#+#+#+ +#+ */ -+/* Created: 2024/12/15 00:26:07 by jmakkone #+# #+# */ -+/* Updated: 2025/02/13 23:25:03 by jmakkone ### ########.fr */ ++/* @@@ @@@@@@@@ */ ++/* @@@ @@@@@@@@@@ */ ++/* @@! @@! @@@@ */ ++/* !@! !@! @!@!@ */ ++/* btrtile.c @!! @!@ @! !@! */ ++/* !!! !@!!! !!! */ ++/* By: julmajustus !!: !!:! !!! */ ++/* ::! :!: !:! */ ++/* Created: 2024/12/15 00:26:07 by julmajustus :: ::::::: :: */ ++/* Updated: 2026/03/19 00:07:19 by julmajustus : : : : : : */ +/* */ +/* ************************************************************************** */ + @@ -74,7 +76,7 @@ index 0000000..650cab5 + if (!node) + return; + -+ if (is_root && e) { ++ if (is_root && e) { + area.x += gappx; + area.y += gappx; + area.width -= 2 * gappx; @@ -127,7 +129,7 @@ index 0000000..650cab5 + right_area.width = area.width - mid; + right_area.height = area.height; + -+ if (e) { ++ if (e) { + left_area.width -= gappx / 2; + right_area.x += gappx / 2; + right_area.width -= gappx / 2; @@ -145,7 +147,7 @@ index 0000000..650cab5 + right_area.width = area.width; + right_area.height= area.height - mid; + -+ if (e) { ++ if (e) { + left_area.height -= gappx / 2; + right_area.y += gappx / 2; + right_area.height -= gappx / 2; @@ -599,21 +601,21 @@ index 0000000..650cab5 + return closest; +} diff --git a/config.def.h b/config.def.h -index 22d2171..92f3ad6 100644 +index 8a6eda0..bc04e3f 100644 --- a/config.def.h +++ b/config.def.h @@ -13,7 +13,10 @@ 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 float resize_factor = 0.0002f; /* Resize multiplier for mouse resizing, depends on mouse sensitivity. */ + static const float fullscreen_bg[] = {0.0f, 0.0f, 0.0f, 1.0f}; /* You can also use glsl colors */ ++static const float resize_factor = 0.0002f; /* Resize multiplier for mouse resizing, depends on mouse sensivity. */ +static const uint32_t resize_interval_ms = 16; /* Resize interval depends on framerate and screen refresh rate. */ +enum Direction { DIR_LEFT, DIR_RIGHT, DIR_UP, DIR_DOWN }; /* tagging - TAGCOUNT must be no greater than 31 */ #define TAGCOUNT (9) -@@ -31,6 +34,7 @@ static const Rule rules[] = { +@@ -30,6 +33,7 @@ static const Rule rules[] = { /* layout(s) */ static const Layout layouts[] = { /* symbol arrange function */ @@ -621,23 +623,23 @@ index 22d2171..92f3ad6 100644 { "[]=", tile }, { "><>", NULL }, /* no layout function means floating behavior */ { "[M]", monocle }, -@@ -148,6 +152,14 @@ static const Key keys[] = { - { 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|WLR_MODIFIER_SHIFT, XKB_KEY_Up, swapclients, {.i = DIR_UP} }, -+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Down, swapclients, {.i = DIR_DOWN} }, -+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Right, swapclients, {.i = DIR_RIGHT} }, -+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Left, swapclients, {.i = DIR_LEFT} }, -+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Right, setratio_h, {.f = +0.025f} }, -+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Left, setratio_h, {.f = -0.025f} }, -+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Up, setratio_v, {.f = -0.025f} }, -+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Down, setratio_v, {.f = +0.025f} }, - TAGKEYS( XKB_KEY_1, XKB_KEY_exclam, 0), - TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1), - TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2), +@@ -144,6 +148,14 @@ static const Key keys[] = { + { 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|WLR_MODIFIER_SHIFT, XKB_KEY_Up, swapclients, {.i = DIR_UP} }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Down, swapclients, {.i = DIR_DOWN} }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Right, swapclients, {.i = DIR_RIGHT} }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Left, swapclients, {.i = DIR_LEFT} }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Right, setratio_h, {.f = +0.025f} }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Left, setratio_h, {.f = -0.025f} }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Up, setratio_v, {.f = -0.025f} }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Down, setratio_v, {.f = +0.025f} }, + TAGKEYS( XKB_KEY_1, XKB_KEY_exclam, 0), + TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1), + TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2), diff --git a/dwl.c b/dwl.c -index a2711f6..e49a061 100644 +index 44f3ad9..f74d9a1 100644 --- a/dwl.c +++ b/dwl.c @@ -1,6 +1,7 @@ @@ -648,15 +650,15 @@ index a2711f6..e49a061 100644 #include #include #include -@@ -103,6 +104,7 @@ typedef struct { +@@ -100,6 +101,7 @@ typedef struct { const Arg arg; } Button; +typedef struct LayoutNode LayoutNode; typedef struct Monitor Monitor; typedef struct { - /* Must keep these three elements in this order */ -@@ -139,8 +141,9 @@ typedef struct { + /* Must keep this field first */ +@@ -137,8 +139,9 @@ typedef struct { #endif unsigned int bw; uint32_t tags; @@ -667,7 +669,7 @@ index a2711f6..e49a061 100644 } Client; typedef struct { -@@ -208,6 +211,7 @@ struct Monitor { +@@ -205,6 +208,7 @@ struct Monitor { int nmaster; char ltsymbol[16]; int asleep; @@ -675,7 +677,7 @@ index a2711f6..e49a061 100644 }; typedef struct { -@@ -250,6 +254,7 @@ static void arrangelayer(Monitor *m, struct wl_list *list, +@@ -247,6 +251,7 @@ static void arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, int exclusive); static void arrangelayers(Monitor *m); static void axisnotify(struct wl_listener *listener, void *data); @@ -683,7 +685,7 @@ index a2711f6..e49a061 100644 static void buttonpress(struct wl_listener *listener, void *data); static void chvt(const Arg *arg); static void checkidleinhibitor(struct wlr_surface *exclude); -@@ -333,6 +338,9 @@ static void setmon(Client *c, Monitor *m, uint32_t newtags); +@@ -329,6 +334,9 @@ static void setmon(Client *c, Monitor *m, uint32_t newtags); static void setpsel(struct wl_listener *listener, void *data); static void setsel(struct wl_listener *listener, void *data); static void setup(void); @@ -693,7 +695,7 @@ index a2711f6..e49a061 100644 static void spawn(const Arg *arg); static void startdrag(struct wl_listener *listener, void *data); static void tag(const Arg *arg); -@@ -431,6 +439,7 @@ static xcb_atom_t netatom[NetLast]; +@@ -454,6 +462,7 @@ static struct wlr_xwayland *xwayland; /* attempt to encapsulate suck into one file */ #include "client.h" @@ -701,7 +703,7 @@ index a2711f6..e49a061 100644 /* function implementations */ void -@@ -601,7 +610,7 @@ buttonpress(struct wl_listener *listener, void *data) +@@ -624,7 +633,7 @@ buttonpress(struct wl_listener *listener, void *data) struct wlr_pointer_button_event *event = data; struct wlr_keyboard *keyboard; uint32_t mods; @@ -710,7 +712,7 @@ index a2711f6..e49a061 100644 const Button *b; wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); -@@ -622,7 +631,7 @@ buttonpress(struct wl_listener *listener, void *data) +@@ -645,7 +654,7 @@ buttonpress(struct wl_listener *listener, void *data) mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; for (b = buttons; b < END(buttons); b++) { if (CLEANMASK(mods) == CLEANMASK(b->mod) && @@ -719,9 +721,9 @@ index a2711f6..e49a061 100644 b->func(&b->arg); return; } -@@ -632,15 +641,36 @@ buttonpress(struct wl_listener *listener, void *data) +@@ -655,6 +664,27 @@ buttonpress(struct wl_listener *listener, void *data) /* If you released any buttons, we exit interactive move/resize mode. */ - /* TODO should reset to the pointer focus's current setcursor */ + /* TODO: should reset to the pointer focus's current setcursor */ if (!locked && cursor_mode != CurNormal && cursor_mode != CurPressed) { + c = grabc; + if (c && c->was_tiled && !strcmp(selmon->ltsymbol, "|w|")) { @@ -747,18 +749,7 @@ index a2711f6..e49a061 100644 wlr_cursor_set_xcursor(cursor, cursor_mgr, "default"); cursor_mode = CurNormal; /* Drop the window off on its new monitor */ - selmon = xytomon(cursor->x, cursor->y); - setmon(grabc, selmon, 0); -+ grabc = NULL; - return; -- } else { -- cursor_mode = CurNormal; - } -+ cursor_mode = CurNormal; - break; - } - /* If the event wasn't handled by the compositor, notify the client with -@@ -720,6 +750,7 @@ cleanupmon(struct wl_listener *listener, void *data) +@@ -746,6 +776,7 @@ cleanupmon(struct wl_listener *listener, void *data) wlr_output_layout_remove(output_layout, m->wlr_output); wlr_scene_output_destroy(m->scene_output); @@ -766,7 +757,7 @@ index a2711f6..e49a061 100644 closemon(m); wlr_scene_node_destroy(&m->fullscreen_bg->node); free(m); -@@ -1024,6 +1055,7 @@ createmon(struct wl_listener *listener, void *data) +@@ -1090,6 +1121,7 @@ createmon(struct wl_listener *listener, void *data) wl_list_insert(&mons, &m->link); printstatus(); @@ -774,7 +765,7 @@ index a2711f6..e49a061 100644 /* The xdg-protocol specifies: * -@@ -1263,6 +1295,10 @@ destroynotify(struct wl_listener *listener, void *data) +@@ -1332,6 +1364,10 @@ destroynotify(struct wl_listener *listener, void *data) wl_list_remove(&c->destroy.link); wl_list_remove(&c->set_title.link); wl_list_remove(&c->fullscreen.link); @@ -785,7 +776,7 @@ index a2711f6..e49a061 100644 #ifdef XWAYLAND if (c->type != XDGShell) { wl_list_remove(&c->activate.link); -@@ -1809,7 +1845,8 @@ void +@@ -1862,7 +1898,8 @@ void motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double dy, double dx_unaccel, double dy_unaccel) { @@ -795,7 +786,7 @@ index a2711f6..e49a061 100644 Client *c = NULL, *w = NULL; LayerSurface *l = NULL; struct wlr_surface *surface = NULL; -@@ -1863,18 +1900,56 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d +@@ -1916,18 +1953,56 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d /* Update drag icon's position */ wlr_scene_node_set_position(&drag_icon->node, (int)round(cursor->x), (int)round(cursor->y)); @@ -859,7 +850,7 @@ index a2711f6..e49a061 100644 /* If there's no client surface under the cursor, set the cursor image to a * default. This is what makes the cursor image appear when you move it * off of a client or over its border. */ -@@ -1908,22 +1983,41 @@ moveresize(const Arg *arg) +@@ -1961,22 +2036,41 @@ moveresize(const Arg *arg) if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen) return; @@ -869,7 +860,7 @@ index a2711f6..e49a061 100644 - case CurMove: - grabcx = (int)round(cursor->x) - grabc->geom.x; - grabcy = (int)round(cursor->y) - grabc->geom.y; -- wlr_cursor_set_xcursor(cursor, cursor_mgr, "fleur"); +- wlr_cursor_set_xcursor(cursor, cursor_mgr, "all-scroll"); - break; - case CurResize: - /* Doesn't work for X11 output - the next absolute motion event @@ -918,5 +909,5 @@ index a2711f6..e49a061 100644 } -- -2.45.3 +2.52.0 diff --git a/patches/btrtile/btrtile-v0.8.patch b/patches/btrtile/btrtile-v0.8.patch new file mode 100644 index 0000000..f43312b --- /dev/null +++ b/patches/btrtile/btrtile-v0.8.patch @@ -0,0 +1,894 @@ +From 1b75163aac16942a715c6fc0aaf5d508e778dd6a Mon Sep 17 00:00:00 2001 +From: julmajustus +Date: Wed, 18 Mar 2026 20:23:00 +0200 +Subject: [PATCH] Refactor btrtile for v0.8 + +--- + btrtile.c | 565 +++++++++++++++++++++++++++++++++++++++++++++++++++ + config.def.h | 12 ++ + dwl.c | 148 +++++++++++--- + 3 files changed, 698 insertions(+), 27 deletions(-) + create mode 100644 btrtile.c + +diff --git a/btrtile.c b/btrtile.c +new file mode 100644 +index 0000000..d706c3b +--- /dev/null ++++ b/btrtile.c +@@ -0,0 +1,565 @@ ++/* ************************************************************************** */ ++/* @@@ @@@@@@@@ */ ++/* @@@ @@@@@@@@@@ */ ++/* @@! @@! @@@@ */ ++/* !@! !@! @!@!@ */ ++/* btrtile.c @!! @!@ @! !@! */ ++/* !!! !@!!! !!! */ ++/* By: julmajustus !!: !!:! !!! */ ++/* ::! :!: !:! */ ++/* Created: 2024/12/15 00:26:07 by julmajustus :: ::::::: :: */ ++/* Updated: 2026/03/18 20:26:17 by julmajustus : : : : : : */ ++/* */ ++/* ************************************************************************** */ ++ ++typedef struct LayoutNode { ++ unsigned int is_client_node; ++ unsigned int is_split_vertically; ++ float split_ratio; ++ struct LayoutNode *left; ++ struct LayoutNode *right; ++ struct LayoutNode *split_node; ++ Client *client; ++} LayoutNode; ++ ++static void apply_layout(Monitor *m, LayoutNode *node, ++ struct wlr_box area, unsigned int is_root); ++static void btrtile(Monitor *m); ++static LayoutNode *create_client_node(Client *c); ++static LayoutNode *create_split_node(unsigned int is_split_vertically, ++ LayoutNode *left, LayoutNode *right); ++static void destroy_node(LayoutNode *node); ++static void destroy_tree(Monitor *m); ++static LayoutNode *find_client_node(LayoutNode *node, Client *c); ++static LayoutNode *find_suitable_split(LayoutNode *start, unsigned int need_vert); ++static void init_tree(Monitor *m); ++static void insert_client(Monitor *m, Client *focused_client, Client *new_client); ++static LayoutNode *remove_client_node(LayoutNode *node, Client *c); ++static void remove_client(Monitor *m, Client *c); ++static void setratio_h(const Arg *arg); ++static void setratio_v(const Arg *arg); ++static void swapclients(const Arg *arg); ++static unsigned int visible_count(LayoutNode *node, Monitor *m); ++static Client *xytoclient(double x, double y); ++ ++static int resizing_from_mouse = 0; ++static double resize_last_update_x, resize_last_update_y; ++static uint32_t last_resize_time = 0; ++ ++void ++apply_layout(Monitor *m, LayoutNode *node, ++ struct wlr_box area, unsigned int is_root) ++{ ++ Client *c; ++ float ratio; ++ unsigned int left_count, right_count, mid; ++ struct wlr_box left_area, right_area; ++ ++ if (!node) ++ return; ++ ++ /* If this node is a client node, check if it is visible. */ ++ if (node->is_client_node) { ++ c = node->client; ++ if (!c || !VISIBLEON(c, m) || c->isfloating || c->isfullscreen) ++ return; ++ resize(c, area, 0); ++ c->old_geom = area; ++ return; ++ } ++ ++ /* For a split node, we see how many visible children are on each side: */ ++ left_count = visible_count(node->left, m); ++ right_count = visible_count(node->right, m); ++ ++ if (left_count == 0 && right_count == 0) { ++ return; ++ } else if (left_count > 0 && right_count == 0) { ++ apply_layout(m, node->left, area, 0); ++ return; ++ } else if (left_count == 0 && right_count > 0) { ++ apply_layout(m, node->right, area, 0); ++ return; ++ } ++ ++ /* If we’re here, we have visible clients in both subtrees. */ ++ ratio = node->split_ratio; ++ if (ratio < 0.05f) ++ ratio = 0.05f; ++ if (ratio > 0.95f) ++ ratio = 0.95f; ++ ++ memset(&left_area, 0, sizeof(left_area)); ++ memset(&right_area, 0, sizeof(right_area)); ++ ++ if (node->is_split_vertically) { ++ mid = (unsigned int)(area.width * ratio); ++ left_area.x = area.x; ++ left_area.y = area.y; ++ left_area.width = mid; ++ left_area.height = area.height; ++ ++ right_area.x = area.x + mid; ++ right_area.y = area.y; ++ right_area.width = area.width - mid; ++ right_area.height = area.height; ++ } else { ++ /* horizontal split */ ++ mid = (unsigned int)(area.height * ratio); ++ left_area.x = area.x; ++ left_area.y = area.y; ++ left_area.width = area.width; ++ left_area.height = mid; ++ ++ right_area.x = area.x; ++ right_area.y = area.y + mid; ++ right_area.width = area.width; ++ right_area.height= area.height - mid; ++ } ++ ++ apply_layout(m, node->left, left_area, 0); ++ apply_layout(m, node->right, right_area, 0); ++} ++ ++void ++btrtile(Monitor *m) ++{ ++ Client *c, *focused = NULL; ++ int n = 0; ++ LayoutNode *found; ++ struct wlr_box full_area; ++ ++ if (!m || !m->root) ++ return; ++ ++ /* Remove non tiled clients from tree. */ ++ wl_list_for_each(c, &clients, link) { ++ if (c->mon == m && !c->isfloating && !c->isfullscreen) { ++ } else { ++ remove_client(m, c); ++ } ++ } ++ ++ /* If no client is found under cursor, fallback to focustop(m) */ ++ if (!(focused = xytoclient(cursor->x, cursor->y))) ++ focused = focustop(m); ++ ++ /* Insert visible clients that are not part of the tree. */ ++ wl_list_for_each(c, &clients, link) { ++ if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen && c->mon == m) { ++ found = find_client_node(m->root, c); ++ if (!found) { ++ insert_client(m, focused, c); ++ } ++ n++; ++ } ++ } ++ ++ if (n == 0) ++ return; ++ ++ full_area = m->w; ++ apply_layout(m, m->root, full_area, 1); ++} ++ ++LayoutNode * ++create_client_node(Client *c) ++{ ++ LayoutNode *node = calloc(1, sizeof(LayoutNode)); ++ ++ if (!node) ++ return NULL; ++ node->is_client_node = 1; ++ node->split_ratio = 0.5f; ++ node->client = c; ++ return node; ++} ++ ++LayoutNode * ++create_split_node(unsigned int is_split_vertically, ++ LayoutNode *left, LayoutNode *right) ++{ ++ LayoutNode *node = calloc(1, sizeof(LayoutNode)); ++ ++ if (!node) ++ return NULL; ++ node->is_client_node = 0; ++ node->split_ratio = 0.5f; ++ node->is_split_vertically = is_split_vertically; ++ node->left = left; ++ node->right = right; ++ if (left) ++ left->split_node = node; ++ if (right) ++ right->split_node = node; ++ return node; ++} ++ ++void ++destroy_node(LayoutNode *node) ++{ ++ if (!node) ++ return; ++ if (!node->is_client_node) { ++ destroy_node(node->left); ++ destroy_node(node->right); ++ } ++ free(node); ++} ++ ++void ++destroy_tree(Monitor *m) ++{ ++ if (!m || !m->root) ++ return; ++ destroy_node(m->root); ++ m->root = NULL; ++} ++ ++LayoutNode * ++find_client_node(LayoutNode *node, Client *c) ++{ ++ LayoutNode *res; ++ ++ if (!node || !c) ++ return NULL; ++ if (node->is_client_node) { ++ return (node->client == c) ? node : NULL; ++ } ++ res = find_client_node(node->left, c); ++ return res ? res : find_client_node(node->right, c); ++} ++ ++LayoutNode * ++find_suitable_split(LayoutNode *start_node, unsigned int need_vertical) ++{ ++ LayoutNode *n = start_node; ++ /* if we started from a client node, jump to its parent: */ ++ if (n && n->is_client_node) ++ n = n->split_node; ++ ++ while (n) { ++ if (!n->is_client_node && n->is_split_vertically == need_vertical && ++ visible_count(n->left, selmon) > 0 && visible_count(n->right, selmon) > 0) ++ return n; ++ n = n->split_node; ++ } ++ return NULL; ++} ++ ++void ++init_tree(Monitor *m) ++{ ++ if (!m) ++ return; ++ m->root = calloc(1, sizeof(LayoutNode)); ++ if (!m->root) ++ m->root = NULL; ++} ++ ++void ++insert_client(Monitor *m, Client *focused_client, Client *new_client) ++{ ++ Client *old_client; ++ LayoutNode **root = &m->root, *old_root, ++ *focused_node, *new_client_node, *old_client_node; ++ unsigned int wider, mid_x, mid_y; ++ ++ /* If no root , new client becomes the root. */ ++ if (!*root) { ++ *root = create_client_node(new_client); ++ return; ++ } ++ ++ /* Find the focused_client node, ++ * if not found split the root. */ ++ focused_node = focused_client ? ++ find_client_node(*root, focused_client) : NULL; ++ if (!focused_node) { ++ old_root = *root; ++ new_client_node = create_client_node(new_client); ++ *root = create_split_node(1, old_root, new_client_node); ++ return; ++ } ++ ++ /* Turn focused node from a client node into a split node, ++ * and attach old_client + new_client. */ ++ old_client = focused_node->client; ++ old_client_node = create_client_node(old_client); ++ new_client_node = create_client_node(new_client); ++ ++ /* Decide split direction. */ ++ wider = (focused_client->geom.width >= focused_client->geom.height); ++ focused_node->is_client_node = 0; ++ focused_node->client = NULL; ++ focused_node->is_split_vertically = (wider ? 1 : 0); ++ ++ /* Pick new_client side depending on the cursor position. */ ++ mid_x = focused_client->geom.x + focused_client->geom.width / 2; ++ mid_y = focused_client->geom.y + focused_client->geom.height / 2; ++ ++ if (wider) { ++ /* vertical split => left vs right */ ++ if (cursor->x <= mid_x) { ++ focused_node->left = new_client_node; ++ focused_node->right = old_client_node; ++ } else { ++ focused_node->left = old_client_node; ++ focused_node->right = new_client_node; ++ } ++ } else { ++ /* horizontal split => top vs bottom */ ++ if (cursor->y <= mid_y) { ++ focused_node->left = new_client_node; ++ focused_node->right = old_client_node; ++ } else { ++ focused_node->left = old_client_node; ++ focused_node->right = new_client_node; ++ } ++ } ++ old_client_node->split_node = focused_node; ++ new_client_node->split_node = focused_node; ++ focused_node->split_ratio = 0.5f; ++} ++ ++LayoutNode * ++remove_client_node(LayoutNode *node, Client *c) ++{ ++ LayoutNode *tmp; ++ if (!node) ++ return NULL; ++ if (node->is_client_node) { ++ /* If this client_node is the client we're removing, ++ * return NULL to remove it */ ++ if (node->client == c) { ++ free(node); ++ return NULL; ++ } ++ return node; ++ } ++ ++ node->left = remove_client_node(node->left, c); ++ node->right = remove_client_node(node->right, c); ++ ++ /* If one of the client node is NULL after removal and the other is not, ++ * we "lift" the other client node up to replace this split node. */ ++ if (!node->left && node->right) { ++ tmp = node->right; ++ ++ /* Save pointer to split node */ ++ if (tmp) ++ tmp->split_node = node->split_node; ++ ++ free(node); ++ return tmp; ++ } ++ ++ if (!node->right && node->left) { ++ tmp = node->left; ++ ++ /* Save pointer to split node */ ++ if (tmp) ++ tmp->split_node = node->split_node; ++ ++ free(node); ++ return tmp; ++ } ++ ++ /* If both children exist or both are NULL (empty tree), ++ * return node as is. */ ++ return node; ++} ++ ++void ++remove_client(Monitor *m, Client *c) ++{ ++ if (!m->root || !c) ++ return; ++ m->root = remove_client_node(m->root, c); ++} ++ ++void ++setratio_h(const Arg *arg) ++{ ++ Client *sel = focustop(selmon); ++ LayoutNode *client_node, *split_node; ++ float new_ratio; ++ ++ if (!sel || !selmon || !selmon->lt[selmon->sellt]->arrange) ++ return; ++ ++ client_node = find_client_node(selmon->root, sel); ++ if (!client_node) ++ return; ++ ++ split_node = find_suitable_split(client_node, 1); ++ if (!split_node) ++ return; ++ ++ new_ratio = (arg->f != 0.0f) ? (split_node->split_ratio + arg->f) : 0.5f; ++ if (new_ratio < 0.05f) ++ new_ratio = 0.05f; ++ if (new_ratio > 0.95f) ++ new_ratio = 0.95f; ++ split_node->split_ratio = new_ratio; ++ ++ /* Skip the arrange if done resizing by mouse, ++ * we call arrange from motionotify */ ++ if (!resizing_from_mouse) { ++ arrange(selmon); ++ } ++} ++ ++void ++setratio_v(const Arg *arg) ++{ ++ Client *sel = focustop(selmon); ++ LayoutNode *client_node, *split_node; ++ float new_ratio; ++ ++ if (!sel || !selmon || !selmon->lt[selmon->sellt]->arrange) ++ return; ++ ++ client_node = find_client_node(selmon->root, sel); ++ if (!client_node) ++ return; ++ ++ split_node = find_suitable_split(client_node, 0); ++ if (!split_node) ++ return; ++ ++ new_ratio = (arg->f != 0.0f) ? (split_node->split_ratio + arg->f) : 0.5f; ++ if (new_ratio < 0.05f) ++ new_ratio = 0.05f; ++ if (new_ratio > 0.95f) ++ new_ratio = 0.95f; ++ split_node->split_ratio = new_ratio; ++ ++ /* Skip the arrange if done resizing by mouse, ++ * we call arrange from motionotify */ ++ if (!resizing_from_mouse) { ++ arrange(selmon); ++ } ++} ++ ++void swapclients(const Arg *arg) { ++ Client *c, *tmp, *target = NULL, *sel = focustop(selmon); ++ LayoutNode *sel_node, *target_node; ++ int closest_dist = INT_MAX, dist, sel_center_x, sel_center_y, ++ cand_center_x, cand_center_y; ++ ++ if (!sel || sel->isfullscreen || ++ !selmon->root || !selmon->lt[selmon->sellt]->arrange) ++ return; ++ ++ ++ /* Get the center coordinates of the selected client */ ++ sel_center_x = sel->geom.x + sel->geom.width / 2; ++ sel_center_y = sel->geom.y + sel->geom.height / 2; ++ ++ wl_list_for_each(c, &clients, link) { ++ if (!VISIBLEON(c, selmon) || c->isfloating || c->isfullscreen || c == sel) ++ continue; ++ ++ /* Get the center of candidate client */ ++ cand_center_x = c->geom.x + c->geom.width / 2; ++ cand_center_y = c->geom.y + c->geom.height / 2; ++ ++ /* Check that the candidate lies in the requested direction. */ ++ switch (arg->ui) { ++ case 0: ++ if (cand_center_x >= sel_center_x) ++ continue; ++ break; ++ case 1: ++ if (cand_center_x <= sel_center_x) ++ continue; ++ break; ++ case 2: ++ if (cand_center_y >= sel_center_y) ++ continue; ++ break; ++ case 3: ++ if (cand_center_y <= sel_center_y) ++ continue; ++ break; ++ default: ++ continue; ++ } ++ ++ /* Get distance between the centers */ ++ dist = abs(sel_center_x - cand_center_x) + abs(sel_center_y - cand_center_y); ++ if (dist < closest_dist) { ++ closest_dist = dist; ++ target = c; ++ } ++ } ++ ++ /* If target is found, swap the two clients’ positions in the layout tree */ ++ if (target) { ++ sel_node = find_client_node(selmon->root, sel); ++ target_node = find_client_node(selmon->root, target); ++ if (sel_node && target_node) { ++ tmp = sel_node->client; ++ sel_node->client = target_node->client; ++ target_node->client = tmp; ++ arrange(selmon); ++ } ++ } ++} ++ ++unsigned int ++visible_count(LayoutNode *node, Monitor *m) ++{ ++ Client *c; ++ ++ if (!node) ++ return 0; ++ /* Check if this client is visible. */ ++ if (node->is_client_node) { ++ c = node->client; ++ if (c && VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen) ++ return 1; ++ return 0; ++ } ++ /* Else it’s a split node. */ ++ return visible_count(node->left, m) + visible_count(node->right, m); ++} ++ ++Client * ++xytoclient(double x, double y) { ++ Client *c, *closest = NULL; ++ double dist, mindist = INT_MAX, dx, dy; ++ ++ wl_list_for_each_reverse(c, &clients, link) { ++ if (VISIBLEON(c, selmon) && !c->isfloating && !c->isfullscreen && ++ x >= c->geom.x && x <= (c->geom.x + c->geom.width) && ++ y >= c->geom.y && y <= (c->geom.y + c->geom.height)){ ++ return c; ++ } ++ } ++ ++ /* If no client was found at cursor position fallback to closest. */ ++ wl_list_for_each_reverse(c, &clients, link) { ++ if (VISIBLEON(c, selmon) && !c->isfloating && !c->isfullscreen) { ++ dx = 0, dy = 0; ++ ++ if (x < c->geom.x) ++ dx = c->geom.x - x; ++ else if (x > (c->geom.x + c->geom.width)) ++ dx = x - (c->geom.x + c->geom.width); ++ ++ if (y < c->geom.y) ++ dy = c->geom.y - y; ++ else if (y > (c->geom.y + c->geom.height)) ++ dy = y - (c->geom.y + c->geom.height); ++ ++ dist = sqrt(dx * dx + dy * dy); ++ if (dist < mindist) { ++ mindist = dist; ++ closest = c; ++ } ++ } ++ } ++ return closest; ++} +diff --git a/config.def.h b/config.def.h +index 8a6eda0..bc04e3f 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -13,7 +13,10 @@ static const float focuscolor[] = COLOR(0x005577ff); + static const float urgentcolor[] = COLOR(0xff0000ff); + /* This conforms to the xdg-protocol. Set the alpha to zero to restore the old behavior */ + static const float fullscreen_bg[] = {0.0f, 0.0f, 0.0f, 1.0f}; /* You can also use glsl colors */ ++static const float resize_factor = 0.0002f; /* Resize multiplier for mouse resizing, depends on mouse sensivity. */ ++static const uint32_t resize_interval_ms = 16; /* Resize interval depends on framerate and screen refresh rate. */ + ++enum Direction { DIR_LEFT, DIR_RIGHT, DIR_UP, DIR_DOWN }; + /* tagging - TAGCOUNT must be no greater than 31 */ + #define TAGCOUNT (9) + +@@ -30,6 +33,7 @@ static const Rule rules[] = { + /* layout(s) */ + static const Layout layouts[] = { + /* symbol arrange function */ ++ { "|w|", btrtile }, + { "[]=", tile }, + { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, +@@ -144,6 +148,14 @@ static const Key keys[] = { + { 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|WLR_MODIFIER_SHIFT, XKB_KEY_Up, swapclients, {.i = DIR_UP} }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Down, swapclients, {.i = DIR_DOWN} }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Right, swapclients, {.i = DIR_RIGHT} }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Left, swapclients, {.i = DIR_LEFT} }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Right, setratio_h, {.f = +0.025f} }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Left, setratio_h, {.f = -0.025f} }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Up, setratio_v, {.f = -0.025f} }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Down, setratio_v, {.f = +0.025f} }, + TAGKEYS( XKB_KEY_1, XKB_KEY_exclam, 0), + TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1), + TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2), +diff --git a/dwl.c b/dwl.c +index 44f3ad9..f74d9a1 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -1,6 +1,7 @@ + /* + * See LICENSE file for copyright and license details. + */ ++#include + #include + #include + #include +@@ -100,6 +101,7 @@ typedef struct { + const Arg arg; + } Button; + ++typedef struct LayoutNode LayoutNode; + typedef struct Monitor Monitor; + typedef struct { + /* Must keep this field first */ +@@ -137,8 +139,9 @@ typedef struct { + #endif + unsigned int bw; + uint32_t tags; +- int isfloating, isurgent, isfullscreen; ++ int isfloating, isurgent, isfullscreen, was_tiled; + uint32_t resize; /* configure serial of a pending resize */ ++ struct wlr_box old_geom; + } Client; + + typedef struct { +@@ -205,6 +208,7 @@ struct Monitor { + int nmaster; + char ltsymbol[16]; + int asleep; ++ LayoutNode *root; + }; + + typedef struct { +@@ -247,6 +251,7 @@ static void arrangelayer(Monitor *m, struct wl_list *list, + struct wlr_box *usable_area, int exclusive); + static void arrangelayers(Monitor *m); + static void axisnotify(struct wl_listener *listener, void *data); ++static void btrtile(Monitor *m); + static void buttonpress(struct wl_listener *listener, void *data); + static void chvt(const Arg *arg); + static void checkidleinhibitor(struct wlr_surface *exclude); +@@ -329,6 +334,9 @@ static void setmon(Client *c, Monitor *m, uint32_t newtags); + static void setpsel(struct wl_listener *listener, void *data); + static void setsel(struct wl_listener *listener, void *data); + static void setup(void); ++static void setratio_h(const Arg *arg); ++static void setratio_v(const Arg *arg); ++static void swapclients(const Arg *arg); + static void spawn(const Arg *arg); + static void startdrag(struct wl_listener *listener, void *data); + static void tag(const Arg *arg); +@@ -454,6 +462,7 @@ static struct wlr_xwayland *xwayland; + + /* attempt to encapsulate suck into one file */ + #include "client.h" ++#include "btrtile.c" + + /* function implementations */ + void +@@ -624,7 +633,7 @@ buttonpress(struct wl_listener *listener, void *data) + struct wlr_pointer_button_event *event = data; + struct wlr_keyboard *keyboard; + uint32_t mods; +- Client *c; ++ Client *c, *target = NULL; + const Button *b; + + wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); +@@ -645,7 +654,7 @@ buttonpress(struct wl_listener *listener, void *data) + mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; + for (b = buttons; b < END(buttons); b++) { + if (CLEANMASK(mods) == CLEANMASK(b->mod) && +- event->button == b->button && b->func) { ++ event->button == b->button && b->func) { + b->func(&b->arg); + return; + } +@@ -655,6 +664,27 @@ buttonpress(struct wl_listener *listener, void *data) + /* If you released any buttons, we exit interactive move/resize mode. */ + /* TODO: should reset to the pointer focus's current setcursor */ + if (!locked && cursor_mode != CurNormal && cursor_mode != CurPressed) { ++ c = grabc; ++ if (c && c->was_tiled && !strcmp(selmon->ltsymbol, "|w|")) { ++ if (cursor_mode == CurMove && c->isfloating) { ++ target = xytoclient(cursor->x, cursor->y); ++ ++ if (target && !target->isfloating && !target->isfullscreen) ++ insert_client(selmon, target, c); ++ else ++ selmon->root = create_client_node(c); ++ ++ setfloating(c, 0); ++ arrange(selmon); ++ ++ } else if (cursor_mode == CurResize && !c->isfloating) { ++ resizing_from_mouse = 0; ++ } ++ } else { ++ if (cursor_mode == CurResize && resizing_from_mouse) ++ resizing_from_mouse = 0; ++ } ++ /* Default behaviour */ + wlr_cursor_set_xcursor(cursor, cursor_mgr, "default"); + cursor_mode = CurNormal; + /* Drop the window off on its new monitor */ +@@ -746,6 +776,7 @@ cleanupmon(struct wl_listener *listener, void *data) + wlr_output_layout_remove(output_layout, m->wlr_output); + wlr_scene_output_destroy(m->scene_output); + ++ destroy_tree(m); + closemon(m); + wlr_scene_node_destroy(&m->fullscreen_bg->node); + free(m); +@@ -1090,6 +1121,7 @@ createmon(struct wl_listener *listener, void *data) + + wl_list_insert(&mons, &m->link); + printstatus(); ++ init_tree(m); + + /* The xdg-protocol specifies: + * +@@ -1332,6 +1364,10 @@ destroynotify(struct wl_listener *listener, void *data) + wl_list_remove(&c->destroy.link); + wl_list_remove(&c->set_title.link); + wl_list_remove(&c->fullscreen.link); ++ /* We check if the destroyed client was part of any tiled_list, to catch ++ * client removals even if they would not be currently managed by btrtile */ ++ if (selmon && selmon->root) ++ remove_client(selmon, c); + #ifdef XWAYLAND + if (c->type != XDGShell) { + wl_list_remove(&c->activate.link); +@@ -1862,7 +1898,8 @@ void + motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double dy, + double dx_unaccel, double dy_unaccel) + { +- double sx = 0, sy = 0, sx_confined, sy_confined; ++ int tiled = 0; ++ double sx = 0, sy = 0, sx_confined, sy_confined, dx_total, dy_total; + Client *c = NULL, *w = NULL; + LayerSurface *l = NULL; + struct wlr_surface *surface = NULL; +@@ -1916,18 +1953,56 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d + /* Update drag icon's position */ + wlr_scene_node_set_position(&drag_icon->node, (int)round(cursor->x), (int)round(cursor->y)); + +- /* If we are currently grabbing the mouse, handle and return */ ++ /* Skip if internal call or already resizing */ ++ if (time == 0 && resizing_from_mouse) ++ goto focus; ++ ++ tiled = grabc && !grabc->isfloating && !grabc->isfullscreen; + if (cursor_mode == CurMove) { + /* Move the grabbed client to the new position. */ +- resize(grabc, (struct wlr_box){.x = (int)round(cursor->x) - grabcx, .y = (int)round(cursor->y) - grabcy, +- .width = grabc->geom.width, .height = grabc->geom.height}, 1); +- return; ++ if (grabc && grabc->isfloating) { ++ resize(grabc, (struct wlr_box){ ++ .x = (int)round(cursor->x) - grabcx, ++ .y = (int)round(cursor->y) - grabcy, ++ .width = grabc->geom.width, ++ .height = grabc->geom.height ++ }, 1); ++ return; ++ } + } else if (cursor_mode == CurResize) { +- resize(grabc, (struct wlr_box){.x = grabc->geom.x, .y = grabc->geom.y, +- .width = (int)round(cursor->x) - grabc->geom.x, .height = (int)round(cursor->y) - grabc->geom.y}, 1); +- return; ++ if (tiled && resizing_from_mouse) { ++ dx_total = cursor->x - resize_last_update_x; ++ dy_total = cursor->y - resize_last_update_y; ++ ++ if (time - last_resize_time >= resize_interval_ms) { ++ Arg a = {0}; ++ if (fabs(dx_total) > fabs(dy_total)) { ++ a.f = (float)(dx_total * resize_factor); ++ setratio_h(&a); ++ } else { ++ a.f = (float)(dy_total * resize_factor); ++ setratio_v(&a); ++ } ++ arrange(selmon); ++ ++ last_resize_time = time; ++ resize_last_update_x = cursor->x; ++ resize_last_update_y = cursor->y; ++ } ++ ++ } else if (grabc && grabc->isfloating) { ++ /* Floating resize as original */ ++ resize(grabc, (struct wlr_box){ ++ .x = grabc->geom.x, ++ .y = grabc->geom.y, ++ .width = (int)round(cursor->x) - grabc->geom.x, ++ .height = (int)round(cursor->y) - grabc->geom.y ++ }, 1); ++ return; ++ } + } + ++focus: + /* If there's no client surface under the cursor, set the cursor image to a + * default. This is what makes the cursor image appear when you move it + * off of a client or over its border. */ +@@ -1961,22 +2036,41 @@ moveresize(const Arg *arg) + if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen) + return; + +- /* Float the window and tell motionnotify to grab it */ +- setfloating(grabc, 1); +- switch (cursor_mode = arg->ui) { +- case CurMove: +- grabcx = (int)round(cursor->x) - grabc->geom.x; +- grabcy = (int)round(cursor->y) - grabc->geom.y; +- wlr_cursor_set_xcursor(cursor, cursor_mgr, "all-scroll"); +- break; +- case CurResize: +- /* Doesn't work for X11 output - the next absolute motion event +- * returns the cursor to where it started */ +- wlr_cursor_warp_closest(cursor, NULL, +- grabc->geom.x + grabc->geom.width, +- grabc->geom.y + grabc->geom.height); +- wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize"); +- break; ++ cursor_mode = arg->ui; ++ grabc->was_tiled = (!grabc->isfloating && !grabc->isfullscreen); ++ ++ if (grabc->was_tiled) { ++ switch (cursor_mode) { ++ case CurMove: ++ setfloating(grabc, 1); ++ grabcx = (int)round(cursor->x) - grabc->geom.x; ++ grabcy = (int)round(cursor->y) - grabc->geom.y; ++ wlr_cursor_set_xcursor(cursor, cursor_mgr, "fleur"); ++ break; ++ case CurResize: ++ wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize"); ++ resize_last_update_x = cursor->x; ++ resize_last_update_y = cursor->y; ++ resizing_from_mouse = 1; ++ break; ++ } ++ } else { ++ /* Default floating logic */ ++ /* Float the window and tell motionnotify to grab it */ ++ setfloating(grabc, 1); ++ switch (cursor_mode) { ++ case CurMove: ++ grabcx = (int)round(cursor->x) - grabc->geom.x; ++ grabcy = (int)round(cursor->y) - grabc->geom.y; ++ wlr_cursor_set_xcursor(cursor, cursor_mgr, "fleur"); ++ break; ++ case CurResize: ++ wlr_cursor_warp_closest(cursor, NULL, ++ grabc->geom.x + grabc->geom.width, ++ grabc->geom.y + grabc->geom.height); ++ wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize"); ++ break; ++ } + } + } + +-- +2.52.0 + diff --git a/patches/btrtile/btrtile-wlroots-next-d41ecb7-gaps.patch b/patches/btrtile/btrtile-wlroots-next-d41ecb7-gaps.patch new file mode 100644 index 0000000..3177c56 --- /dev/null +++ b/patches/btrtile/btrtile-wlroots-next-d41ecb7-gaps.patch @@ -0,0 +1,913 @@ +From a9329abe48083a4c5b64eee726ceefdb4bf0bb9f Mon Sep 17 00:00:00 2001 +From: julmajustus +Date: Thu, 19 Mar 2026 22:59:49 +0200 +Subject: [PATCH] Refactor btrtile-gaps to wlroots-next + +--- + btrtile.c | 584 +++++++++++++++++++++++++++++++++++++++++++++++++++ + config.def.h | 12 ++ + dwl.c | 148 ++++++++++--- + 3 files changed, 717 insertions(+), 27 deletions(-) + create mode 100644 btrtile.c + +diff --git a/btrtile.c b/btrtile.c +new file mode 100644 +index 0000000..8703617 +--- /dev/null ++++ b/btrtile.c +@@ -0,0 +1,584 @@ ++/* ************************************************************************** */ ++/* @@@ @@@@@@@@ */ ++/* @@@ @@@@@@@@@@ */ ++/* @@! @@! @@@@ */ ++/* !@! !@! @!@!@ */ ++/* btrtile.c @!! @!@ @! !@! */ ++/* !!! !@!!! !!! */ ++/* By: julmajustus !!: !!:! !!! */ ++/* ::! :!: !:! */ ++/* Created: 2024/12/15 00:26:07 by julmajustus :: ::::::: :: */ ++/* Updated: 2026/03/19 22:59:12 by julmajustus : : : : : : */ ++/* */ ++/* ************************************************************************** */ ++ ++typedef struct LayoutNode { ++ unsigned int is_client_node; ++ unsigned int is_split_vertically; ++ float split_ratio; ++ struct LayoutNode *left; ++ struct LayoutNode *right; ++ struct LayoutNode *split_node; ++ Client *client; ++} LayoutNode; ++ ++static void apply_layout(Monitor *m, LayoutNode *node, ++ struct wlr_box area, unsigned int is_root); ++static void btrtile(Monitor *m); ++static LayoutNode *create_client_node(Client *c); ++static LayoutNode *create_split_node(unsigned int is_split_vertically, ++ LayoutNode *left, LayoutNode *right); ++static void destroy_node(LayoutNode *node); ++static void destroy_tree(Monitor *m); ++static LayoutNode *find_client_node(LayoutNode *node, Client *c); ++static LayoutNode *find_suitable_split(LayoutNode *start, unsigned int need_vert); ++static void init_tree(Monitor *m); ++static void insert_client(Monitor *m, Client *focused_client, Client *new_client); ++static LayoutNode *remove_client_node(LayoutNode *node, Client *c); ++static void remove_client(Monitor *m, Client *c); ++static void setratio_h(const Arg *arg); ++static void setratio_v(const Arg *arg); ++static void swapclients(const Arg *arg); ++static unsigned int visible_count(LayoutNode *node, Monitor *m); ++static Client *xytoclient(double x, double y); ++ ++static int resizing_from_mouse = 0; ++static double resize_last_update_x, resize_last_update_y; ++static uint32_t last_resize_time = 0; ++ ++void ++apply_layout(Monitor *m, LayoutNode *node, ++ struct wlr_box area, unsigned int is_root) ++{ ++ Client *c; ++ float ratio; ++ unsigned int left_count, right_count, mid, e = m->gaps; ++ struct wlr_box left_area, right_area; ++ ++ if (!node) ++ return; ++ ++ if (is_root && e) { ++ area.x += gappx; ++ area.y += gappx; ++ area.width -= 2 * gappx; ++ area.height -= 2 * gappx; ++ } ++ ++ /* If this node is a client node, check if it is visible. */ ++ if (node->is_client_node) { ++ c = node->client; ++ if (!c || !VISIBLEON(c, m) || c->isfloating || c->isfullscreen) ++ return; ++ resize(c, area, 0); ++ c->old_geom = area; ++ return; ++ } ++ ++ /* For a split node, we see how many visible children are on each side: */ ++ left_count = visible_count(node->left, m); ++ right_count = visible_count(node->right, m); ++ ++ if (left_count == 0 && right_count == 0) { ++ return; ++ } else if (left_count > 0 && right_count == 0) { ++ apply_layout(m, node->left, area, 0); ++ return; ++ } else if (left_count == 0 && right_count > 0) { ++ apply_layout(m, node->right, area, 0); ++ return; ++ } ++ ++ /* If we’re here, we have visible clients in both subtrees. */ ++ ratio = node->split_ratio; ++ if (ratio < 0.05f) ++ ratio = 0.05f; ++ if (ratio > 0.95f) ++ ratio = 0.95f; ++ ++ memset(&left_area, 0, sizeof(left_area)); ++ memset(&right_area, 0, sizeof(right_area)); ++ ++ if (node->is_split_vertically) { ++ mid = (unsigned int)(area.width * ratio); ++ left_area.x = area.x; ++ left_area.y = area.y; ++ left_area.width = mid; ++ left_area.height = area.height; ++ ++ right_area.x = area.x + mid; ++ right_area.y = area.y; ++ right_area.width = area.width - mid; ++ right_area.height = area.height; ++ ++ if (e) { ++ left_area.width -= gappx / 2; ++ right_area.x += gappx / 2; ++ right_area.width -= gappx / 2; ++ } ++ } else { ++ /* horizontal split */ ++ mid = (unsigned int)(area.height * ratio); ++ left_area.x = area.x; ++ left_area.y = area.y; ++ left_area.width = area.width; ++ left_area.height = mid; ++ ++ right_area.x = area.x; ++ right_area.y = area.y + mid; ++ right_area.width = area.width; ++ right_area.height= area.height - mid; ++ ++ if (e) { ++ left_area.height -= gappx / 2; ++ right_area.y += gappx / 2; ++ right_area.height -= gappx / 2; ++ } ++ } ++ ++ apply_layout(m, node->left, left_area, 0); ++ apply_layout(m, node->right, right_area, 0); ++} ++ ++void ++btrtile(Monitor *m) ++{ ++ Client *c, *focused = NULL; ++ int n = 0; ++ LayoutNode *found; ++ struct wlr_box full_area; ++ ++ if (!m || !m->root) ++ return; ++ ++ /* Remove non tiled clients from tree. */ ++ wl_list_for_each(c, &clients, link) { ++ if (c->mon == m && !c->isfloating && !c->isfullscreen) { ++ } else { ++ remove_client(m, c); ++ } ++ } ++ ++ /* If no client is found under cursor, fallback to focustop(m) */ ++ if (!(focused = xytoclient(cursor->x, cursor->y))) ++ focused = focustop(m); ++ ++ /* Insert visible clients that are not part of the tree. */ ++ wl_list_for_each(c, &clients, link) { ++ if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen && c->mon == m) { ++ found = find_client_node(m->root, c); ++ if (!found) { ++ insert_client(m, focused, c); ++ } ++ n++; ++ } ++ } ++ ++ if (n == 0) ++ return; ++ ++ full_area = m->w; ++ apply_layout(m, m->root, full_area, 1); ++} ++ ++LayoutNode * ++create_client_node(Client *c) ++{ ++ LayoutNode *node = calloc(1, sizeof(LayoutNode)); ++ ++ if (!node) ++ return NULL; ++ node->is_client_node = 1; ++ node->split_ratio = 0.5f; ++ node->client = c; ++ return node; ++} ++ ++LayoutNode * ++create_split_node(unsigned int is_split_vertically, ++ LayoutNode *left, LayoutNode *right) ++{ ++ LayoutNode *node = calloc(1, sizeof(LayoutNode)); ++ ++ if (!node) ++ return NULL; ++ node->is_client_node = 0; ++ node->split_ratio = 0.5f; ++ node->is_split_vertically = is_split_vertically; ++ node->left = left; ++ node->right = right; ++ if (left) ++ left->split_node = node; ++ if (right) ++ right->split_node = node; ++ return node; ++} ++ ++void ++destroy_node(LayoutNode *node) ++{ ++ if (!node) ++ return; ++ if (!node->is_client_node) { ++ destroy_node(node->left); ++ destroy_node(node->right); ++ } ++ free(node); ++} ++ ++void ++destroy_tree(Monitor *m) ++{ ++ if (!m || !m->root) ++ return; ++ destroy_node(m->root); ++ m->root = NULL; ++} ++ ++LayoutNode * ++find_client_node(LayoutNode *node, Client *c) ++{ ++ LayoutNode *res; ++ ++ if (!node || !c) ++ return NULL; ++ if (node->is_client_node) { ++ return (node->client == c) ? node : NULL; ++ } ++ res = find_client_node(node->left, c); ++ return res ? res : find_client_node(node->right, c); ++} ++ ++LayoutNode * ++find_suitable_split(LayoutNode *start_node, unsigned int need_vertical) ++{ ++ LayoutNode *n = start_node; ++ /* if we started from a client node, jump to its parent: */ ++ if (n && n->is_client_node) ++ n = n->split_node; ++ ++ while (n) { ++ if (!n->is_client_node && n->is_split_vertically == need_vertical && ++ visible_count(n->left, selmon) > 0 && visible_count(n->right, selmon) > 0) ++ return n; ++ n = n->split_node; ++ } ++ return NULL; ++} ++ ++void ++init_tree(Monitor *m) ++{ ++ if (!m) ++ return; ++ m->root = calloc(1, sizeof(LayoutNode)); ++ if (!m->root) ++ m->root = NULL; ++} ++ ++void ++insert_client(Monitor *m, Client *focused_client, Client *new_client) ++{ ++ Client *old_client; ++ LayoutNode **root = &m->root, *old_root, ++ *focused_node, *new_client_node, *old_client_node; ++ unsigned int wider, mid_x, mid_y; ++ ++ /* If no root , new client becomes the root. */ ++ if (!*root) { ++ *root = create_client_node(new_client); ++ return; ++ } ++ ++ /* Find the focused_client node, ++ * if not found split the root. */ ++ focused_node = focused_client ? ++ find_client_node(*root, focused_client) : NULL; ++ if (!focused_node) { ++ old_root = *root; ++ new_client_node = create_client_node(new_client); ++ *root = create_split_node(1, old_root, new_client_node); ++ return; ++ } ++ ++ /* Turn focused node from a client node into a split node, ++ * and attach old_client + new_client. */ ++ old_client = focused_node->client; ++ old_client_node = create_client_node(old_client); ++ new_client_node = create_client_node(new_client); ++ ++ /* Decide split direction. */ ++ wider = (focused_client->geom.width >= focused_client->geom.height); ++ focused_node->is_client_node = 0; ++ focused_node->client = NULL; ++ focused_node->is_split_vertically = (wider ? 1 : 0); ++ ++ /* Pick new_client side depending on the cursor position. */ ++ mid_x = focused_client->geom.x + focused_client->geom.width / 2; ++ mid_y = focused_client->geom.y + focused_client->geom.height / 2; ++ ++ if (wider) { ++ /* vertical split => left vs right */ ++ if (cursor->x <= mid_x) { ++ focused_node->left = new_client_node; ++ focused_node->right = old_client_node; ++ } else { ++ focused_node->left = old_client_node; ++ focused_node->right = new_client_node; ++ } ++ } else { ++ /* horizontal split => top vs bottom */ ++ if (cursor->y <= mid_y) { ++ focused_node->left = new_client_node; ++ focused_node->right = old_client_node; ++ } else { ++ focused_node->left = old_client_node; ++ focused_node->right = new_client_node; ++ } ++ } ++ old_client_node->split_node = focused_node; ++ new_client_node->split_node = focused_node; ++ focused_node->split_ratio = 0.5f; ++} ++ ++LayoutNode * ++remove_client_node(LayoutNode *node, Client *c) ++{ ++ LayoutNode *tmp; ++ if (!node) ++ return NULL; ++ if (node->is_client_node) { ++ /* If this client_node is the client we're removing, ++ * return NULL to remove it */ ++ if (node->client == c) { ++ free(node); ++ return NULL; ++ } ++ return node; ++ } ++ ++ node->left = remove_client_node(node->left, c); ++ node->right = remove_client_node(node->right, c); ++ ++ /* If one of the client node is NULL after removal and the other is not, ++ * we "lift" the other client node up to replace this split node. */ ++ if (!node->left && node->right) { ++ tmp = node->right; ++ ++ /* Save pointer to split node */ ++ if (tmp) ++ tmp->split_node = node->split_node; ++ ++ free(node); ++ return tmp; ++ } ++ ++ if (!node->right && node->left) { ++ tmp = node->left; ++ ++ /* Save pointer to split node */ ++ if (tmp) ++ tmp->split_node = node->split_node; ++ ++ free(node); ++ return tmp; ++ } ++ ++ /* If both children exist or both are NULL (empty tree), ++ * return node as is. */ ++ return node; ++} ++ ++void ++remove_client(Monitor *m, Client *c) ++{ ++ if (!m->root || !c) ++ return; ++ m->root = remove_client_node(m->root, c); ++} ++ ++void ++setratio_h(const Arg *arg) ++{ ++ Client *sel = focustop(selmon); ++ LayoutNode *client_node, *split_node; ++ float new_ratio; ++ ++ if (!sel || !selmon || !selmon->lt[selmon->sellt]->arrange) ++ return; ++ ++ client_node = find_client_node(selmon->root, sel); ++ if (!client_node) ++ return; ++ ++ split_node = find_suitable_split(client_node, 1); ++ if (!split_node) ++ return; ++ ++ new_ratio = (arg->f != 0.0f) ? (split_node->split_ratio + arg->f) : 0.5f; ++ if (new_ratio < 0.05f) ++ new_ratio = 0.05f; ++ if (new_ratio > 0.95f) ++ new_ratio = 0.95f; ++ split_node->split_ratio = new_ratio; ++ ++ /* Skip the arrange if done resizing by mouse, ++ * we call arrange from motionotify */ ++ if (!resizing_from_mouse) { ++ arrange(selmon); ++ } ++} ++ ++void ++setratio_v(const Arg *arg) ++{ ++ Client *sel = focustop(selmon); ++ LayoutNode *client_node, *split_node; ++ float new_ratio; ++ ++ if (!sel || !selmon || !selmon->lt[selmon->sellt]->arrange) ++ return; ++ ++ client_node = find_client_node(selmon->root, sel); ++ if (!client_node) ++ return; ++ ++ split_node = find_suitable_split(client_node, 0); ++ if (!split_node) ++ return; ++ ++ new_ratio = (arg->f != 0.0f) ? (split_node->split_ratio + arg->f) : 0.5f; ++ if (new_ratio < 0.05f) ++ new_ratio = 0.05f; ++ if (new_ratio > 0.95f) ++ new_ratio = 0.95f; ++ split_node->split_ratio = new_ratio; ++ ++ /* Skip the arrange if done resizing by mouse, ++ * we call arrange from motionotify */ ++ if (!resizing_from_mouse) { ++ arrange(selmon); ++ } ++} ++ ++void swapclients(const Arg *arg) { ++ Client *c, *tmp, *target = NULL, *sel = focustop(selmon); ++ LayoutNode *sel_node, *target_node; ++ int closest_dist = INT_MAX, dist, sel_center_x, sel_center_y, ++ cand_center_x, cand_center_y; ++ ++ if (!sel || sel->isfullscreen || ++ !selmon->root || !selmon->lt[selmon->sellt]->arrange) ++ return; ++ ++ ++ /* Get the center coordinates of the selected client */ ++ sel_center_x = sel->geom.x + sel->geom.width / 2; ++ sel_center_y = sel->geom.y + sel->geom.height / 2; ++ ++ wl_list_for_each(c, &clients, link) { ++ if (!VISIBLEON(c, selmon) || c->isfloating || c->isfullscreen || c == sel) ++ continue; ++ ++ /* Get the center of candidate client */ ++ cand_center_x = c->geom.x + c->geom.width / 2; ++ cand_center_y = c->geom.y + c->geom.height / 2; ++ ++ /* Check that the candidate lies in the requested direction. */ ++ switch (arg->ui) { ++ case 0: ++ if (cand_center_x >= sel_center_x) ++ continue; ++ break; ++ case 1: ++ if (cand_center_x <= sel_center_x) ++ continue; ++ break; ++ case 2: ++ if (cand_center_y >= sel_center_y) ++ continue; ++ break; ++ case 3: ++ if (cand_center_y <= sel_center_y) ++ continue; ++ break; ++ default: ++ continue; ++ } ++ ++ /* Get distance between the centers */ ++ dist = abs(sel_center_x - cand_center_x) + abs(sel_center_y - cand_center_y); ++ if (dist < closest_dist) { ++ closest_dist = dist; ++ target = c; ++ } ++ } ++ ++ /* If target is found, swap the two clients’ positions in the layout tree */ ++ if (target) { ++ sel_node = find_client_node(selmon->root, sel); ++ target_node = find_client_node(selmon->root, target); ++ if (sel_node && target_node) { ++ tmp = sel_node->client; ++ sel_node->client = target_node->client; ++ target_node->client = tmp; ++ arrange(selmon); ++ } ++ } ++} ++ ++unsigned int ++visible_count(LayoutNode *node, Monitor *m) ++{ ++ Client *c; ++ ++ if (!node) ++ return 0; ++ /* Check if this client is visible. */ ++ if (node->is_client_node) { ++ c = node->client; ++ if (c && VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen) ++ return 1; ++ return 0; ++ } ++ /* Else it’s a split node. */ ++ return visible_count(node->left, m) + visible_count(node->right, m); ++} ++ ++Client * ++xytoclient(double x, double y) { ++ Client *c, *closest = NULL; ++ double dist, mindist = INT_MAX, dx, dy; ++ ++ wl_list_for_each_reverse(c, &clients, link) { ++ if (VISIBLEON(c, selmon) && !c->isfloating && !c->isfullscreen && ++ x >= c->geom.x && x <= (c->geom.x + c->geom.width) && ++ y >= c->geom.y && y <= (c->geom.y + c->geom.height)){ ++ return c; ++ } ++ } ++ ++ /* If no client was found at cursor position fallback to closest. */ ++ wl_list_for_each_reverse(c, &clients, link) { ++ if (VISIBLEON(c, selmon) && !c->isfloating && !c->isfullscreen) { ++ dx = 0, dy = 0; ++ ++ if (x < c->geom.x) ++ dx = c->geom.x - x; ++ else if (x > (c->geom.x + c->geom.width)) ++ dx = x - (c->geom.x + c->geom.width); ++ ++ if (y < c->geom.y) ++ dy = c->geom.y - y; ++ else if (y > (c->geom.y + c->geom.height)) ++ dy = y - (c->geom.y + c->geom.height); ++ ++ dist = sqrt(dx * dx + dy * dy); ++ if (dist < mindist) { ++ mindist = dist; ++ closest = c; ++ } ++ } ++ } ++ return closest; ++} +diff --git a/config.def.h b/config.def.h +index 8a6eda0..bc04e3f 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -13,7 +13,10 @@ static const float focuscolor[] = COLOR(0x005577ff); + static const float urgentcolor[] = COLOR(0xff0000ff); + /* This conforms to the xdg-protocol. Set the alpha to zero to restore the old behavior */ + static const float fullscreen_bg[] = {0.0f, 0.0f, 0.0f, 1.0f}; /* You can also use glsl colors */ ++static const float resize_factor = 0.0002f; /* Resize multiplier for mouse resizing, depends on mouse sensivity. */ ++static const uint32_t resize_interval_ms = 16; /* Resize interval depends on framerate and screen refresh rate. */ + ++enum Direction { DIR_LEFT, DIR_RIGHT, DIR_UP, DIR_DOWN }; + /* tagging - TAGCOUNT must be no greater than 31 */ + #define TAGCOUNT (9) + +@@ -30,6 +33,7 @@ static const Rule rules[] = { + /* layout(s) */ + static const Layout layouts[] = { + /* symbol arrange function */ ++ { "|w|", btrtile }, + { "[]=", tile }, + { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, +@@ -144,6 +148,14 @@ static const Key keys[] = { + { 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|WLR_MODIFIER_SHIFT, XKB_KEY_Up, swapclients, {.i = DIR_UP} }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Down, swapclients, {.i = DIR_DOWN} }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Right, swapclients, {.i = DIR_RIGHT} }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Left, swapclients, {.i = DIR_LEFT} }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Right, setratio_h, {.f = +0.025f} }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Left, setratio_h, {.f = -0.025f} }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Up, setratio_v, {.f = -0.025f} }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Down, setratio_v, {.f = +0.025f} }, + TAGKEYS( XKB_KEY_1, XKB_KEY_exclam, 0), + TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1), + TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2), +diff --git a/dwl.c b/dwl.c +index 8101ffa..e4e7074 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -1,6 +1,7 @@ + /* + * See LICENSE file for copyright and license details. + */ ++#include + #include + #include + #include +@@ -104,6 +105,7 @@ typedef struct { + const Arg arg; + } Button; + ++typedef struct LayoutNode LayoutNode; + typedef struct Monitor Monitor; + typedef struct { + /* Must keep this field first */ +@@ -141,8 +143,9 @@ typedef struct { + #endif + unsigned int bw; + uint32_t tags; +- int isfloating, isurgent, isfullscreen; ++ int isfloating, isurgent, isfullscreen, was_tiled; + uint32_t resize; /* configure serial of a pending resize */ ++ struct wlr_box old_geom; + } Client; + + typedef struct { +@@ -209,6 +212,7 @@ struct Monitor { + int nmaster; + char ltsymbol[16]; + int asleep; ++ LayoutNode *root; + }; + + typedef struct { +@@ -251,6 +255,7 @@ static void arrangelayer(Monitor *m, struct wl_list *list, + struct wlr_box *usable_area, int exclusive); + static void arrangelayers(Monitor *m); + static void axisnotify(struct wl_listener *listener, void *data); ++static void btrtile(Monitor *m); + static void buttonpress(struct wl_listener *listener, void *data); + static void chvt(const Arg *arg); + static void checkidleinhibitor(struct wlr_surface *exclude); +@@ -333,6 +338,9 @@ static void setmon(Client *c, Monitor *m, uint32_t newtags); + static void setpsel(struct wl_listener *listener, void *data); + static void setsel(struct wl_listener *listener, void *data); + static void setup(void); ++static void setratio_h(const Arg *arg); ++static void setratio_v(const Arg *arg); ++static void swapclients(const Arg *arg); + static void spawn(const Arg *arg); + static void startdrag(struct wl_listener *listener, void *data); + static void tag(const Arg *arg); +@@ -458,6 +466,7 @@ static struct wlr_xwayland *xwayland; + + /* attempt to encapsulate suck into one file */ + #include "client.h" ++#include "btrtile.c" + + /* function implementations */ + void +@@ -628,7 +637,7 @@ buttonpress(struct wl_listener *listener, void *data) + struct wlr_pointer_button_event *event = data; + struct wlr_keyboard *keyboard; + uint32_t mods; +- Client *c; ++ Client *c, *target = NULL; + const Button *b; + + wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); +@@ -649,7 +658,7 @@ buttonpress(struct wl_listener *listener, void *data) + mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; + for (b = buttons; b < END(buttons); b++) { + if (CLEANMASK(mods) == CLEANMASK(b->mod) && +- event->button == b->button && b->func) { ++ event->button == b->button && b->func) { + b->func(&b->arg); + return; + } +@@ -659,6 +668,27 @@ buttonpress(struct wl_listener *listener, void *data) + /* If you released any buttons, we exit interactive move/resize mode. */ + /* TODO: should reset to the pointer focus's current setcursor */ + if (!locked && cursor_mode != CurNormal && cursor_mode != CurPressed) { ++ c = grabc; ++ if (c && c->was_tiled && !strcmp(selmon->ltsymbol, "|w|")) { ++ if (cursor_mode == CurMove && c->isfloating) { ++ target = xytoclient(cursor->x, cursor->y); ++ ++ if (target && !target->isfloating && !target->isfullscreen) ++ insert_client(selmon, target, c); ++ else ++ selmon->root = create_client_node(c); ++ ++ setfloating(c, 0); ++ arrange(selmon); ++ ++ } else if (cursor_mode == CurResize && !c->isfloating) { ++ resizing_from_mouse = 0; ++ } ++ } else { ++ if (cursor_mode == CurResize && resizing_from_mouse) ++ resizing_from_mouse = 0; ++ } ++ /* Default behaviour */ + wlr_cursor_set_xcursor(cursor, cursor_mgr, "default"); + cursor_mode = CurNormal; + /* Drop the window off on its new monitor */ +@@ -750,6 +780,7 @@ cleanupmon(struct wl_listener *listener, void *data) + wlr_output_layout_remove(output_layout, m->wlr_output); + wlr_scene_output_destroy(m->scene_output); + ++ destroy_tree(m); + closemon(m); + wlr_scene_node_destroy(&m->fullscreen_bg->node); + free(m); +@@ -1094,6 +1125,7 @@ createmon(struct wl_listener *listener, void *data) + + wl_list_insert(&mons, &m->link); + printstatus(); ++ init_tree(m); + + /* The xdg-protocol specifies: + * +@@ -1336,6 +1368,10 @@ destroynotify(struct wl_listener *listener, void *data) + wl_list_remove(&c->destroy.link); + wl_list_remove(&c->set_title.link); + wl_list_remove(&c->fullscreen.link); ++ /* We check if the destroyed client was part of any tiled_list, to catch ++ * client removals even if they would not be currently managed by btrtile */ ++ if (selmon && selmon->root) ++ remove_client(selmon, c); + #ifdef XWAYLAND + if (c->type != XDGShell) { + wl_list_remove(&c->activate.link); +@@ -1866,7 +1902,8 @@ void + motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double dy, + double dx_unaccel, double dy_unaccel) + { +- double sx = 0, sy = 0, sx_confined, sy_confined; ++ int tiled = 0; ++ double sx = 0, sy = 0, sx_confined, sy_confined, dx_total, dy_total; + Client *c = NULL, *w = NULL; + LayerSurface *l = NULL; + struct wlr_surface *surface = NULL; +@@ -1920,18 +1957,56 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d + /* Update drag icon's position */ + wlr_scene_node_set_position(&drag_icon->node, (int)round(cursor->x), (int)round(cursor->y)); + +- /* If we are currently grabbing the mouse, handle and return */ ++ /* Skip if internal call or already resizing */ ++ if (time == 0 && resizing_from_mouse) ++ goto focus; ++ ++ tiled = grabc && !grabc->isfloating && !grabc->isfullscreen; + if (cursor_mode == CurMove) { + /* Move the grabbed client to the new position. */ +- resize(grabc, (struct wlr_box){.x = (int)round(cursor->x) - grabcx, .y = (int)round(cursor->y) - grabcy, +- .width = grabc->geom.width, .height = grabc->geom.height}, 1); +- return; ++ if (grabc && grabc->isfloating) { ++ resize(grabc, (struct wlr_box){ ++ .x = (int)round(cursor->x) - grabcx, ++ .y = (int)round(cursor->y) - grabcy, ++ .width = grabc->geom.width, ++ .height = grabc->geom.height ++ }, 1); ++ return; ++ } + } else if (cursor_mode == CurResize) { +- resize(grabc, (struct wlr_box){.x = grabc->geom.x, .y = grabc->geom.y, +- .width = (int)round(cursor->x) - grabc->geom.x, .height = (int)round(cursor->y) - grabc->geom.y}, 1); +- return; ++ if (tiled && resizing_from_mouse) { ++ dx_total = cursor->x - resize_last_update_x; ++ dy_total = cursor->y - resize_last_update_y; ++ ++ if (time - last_resize_time >= resize_interval_ms) { ++ Arg a = {0}; ++ if (fabs(dx_total) > fabs(dy_total)) { ++ a.f = (float)(dx_total * resize_factor); ++ setratio_h(&a); ++ } else { ++ a.f = (float)(dy_total * resize_factor); ++ setratio_v(&a); ++ } ++ arrange(selmon); ++ ++ last_resize_time = time; ++ resize_last_update_x = cursor->x; ++ resize_last_update_y = cursor->y; ++ } ++ ++ } else if (grabc && grabc->isfloating) { ++ /* Floating resize as original */ ++ resize(grabc, (struct wlr_box){ ++ .x = grabc->geom.x, ++ .y = grabc->geom.y, ++ .width = (int)round(cursor->x) - grabc->geom.x, ++ .height = (int)round(cursor->y) - grabc->geom.y ++ }, 1); ++ return; ++ } + } + ++focus: + /* If there's no client surface under the cursor, set the cursor image to a + * default. This is what makes the cursor image appear when you move it + * off of a client or over its border. */ +@@ -1965,22 +2040,41 @@ moveresize(const Arg *arg) + if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen) + return; + +- /* Float the window and tell motionnotify to grab it */ +- setfloating(grabc, 1); +- switch (cursor_mode = arg->ui) { +- case CurMove: +- grabcx = (int)round(cursor->x) - grabc->geom.x; +- grabcy = (int)round(cursor->y) - grabc->geom.y; +- wlr_cursor_set_xcursor(cursor, cursor_mgr, "all-scroll"); +- break; +- case CurResize: +- /* Doesn't work for X11 output - the next absolute motion event +- * returns the cursor to where it started */ +- wlr_cursor_warp_closest(cursor, NULL, +- grabc->geom.x + grabc->geom.width, +- grabc->geom.y + grabc->geom.height); +- wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize"); +- break; ++ cursor_mode = arg->ui; ++ grabc->was_tiled = (!grabc->isfloating && !grabc->isfullscreen); ++ ++ if (grabc->was_tiled) { ++ switch (cursor_mode) { ++ case CurMove: ++ setfloating(grabc, 1); ++ grabcx = (int)round(cursor->x) - grabc->geom.x; ++ grabcy = (int)round(cursor->y) - grabc->geom.y; ++ wlr_cursor_set_xcursor(cursor, cursor_mgr, "fleur"); ++ break; ++ case CurResize: ++ wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize"); ++ resize_last_update_x = cursor->x; ++ resize_last_update_y = cursor->y; ++ resizing_from_mouse = 1; ++ break; ++ } ++ } else { ++ /* Default floating logic */ ++ /* Float the window and tell motionnotify to grab it */ ++ setfloating(grabc, 1); ++ switch (cursor_mode) { ++ case CurMove: ++ grabcx = (int)round(cursor->x) - grabc->geom.x; ++ grabcy = (int)round(cursor->y) - grabc->geom.y; ++ wlr_cursor_set_xcursor(cursor, cursor_mgr, "fleur"); ++ break; ++ case CurResize: ++ wlr_cursor_warp_closest(cursor, NULL, ++ grabc->geom.x + grabc->geom.width, ++ grabc->geom.y + grabc->geom.height); ++ wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize"); ++ break; ++ } + } + } + +-- +2.52.0 + diff --git a/patches/btrtile/btrtile-v0.7.patch b/patches/btrtile/btrtile-wlroots-next-d41ecb7.patch similarity index 89% rename from patches/btrtile/btrtile-v0.7.patch rename to patches/btrtile/btrtile-wlroots-next-d41ecb7.patch index 0396500..87f4e99 100644 --- a/patches/btrtile/btrtile-v0.7.patch +++ b/patches/btrtile/btrtile-wlroots-next-d41ecb7.patch @@ -1,30 +1,32 @@ -From b9789420f166c20579f29ecd171a8956c681848d Mon Sep 17 00:00:00 2001 +From 912701a41b19c013a9adcb176a7b6503aa1cf9c1 Mon Sep 17 00:00:00 2001 From: julmajustus -Date: Thu, 13 Feb 2025 23:23:40 +0200 -Subject: [PATCH] btrtile with multi-tag support +Date: Thu, 19 Mar 2026 22:56:45 +0200 +Subject: [PATCH] Refactor btrtile to wlroots-next --- - btrtile.c | 563 +++++++++++++++++++++++++++++++++++++++++++++++++++ + btrtile.c | 565 +++++++++++++++++++++++++++++++++++++++++++++++++++ config.def.h | 12 ++ - dwl.c | 152 +++++++++++--- - 3 files changed, 698 insertions(+), 29 deletions(-) + dwl.c | 148 +++++++++++--- + 3 files changed, 698 insertions(+), 27 deletions(-) create mode 100644 btrtile.c diff --git a/btrtile.c b/btrtile.c new file mode 100644 -index 0000000..03f4680 +index 0000000..d706c3b --- /dev/null +++ b/btrtile.c -@@ -0,0 +1,563 @@ +@@ -0,0 +1,565 @@ +/* ************************************************************************** */ -+/* */ -+/* ::: :::::::: */ -+/* btrtile.c :+: :+: :+: */ -+/* +:+ +:+ +:+ */ -+/* By: jmakkone +#+ +:+ +#+ */ -+/* +#+#+#+#+#+ +#+ */ -+/* Created: 2024/12/15 00:26:07 by jmakkone #+# #+# */ -+/* Updated: 2025/02/13 23:22:33 by jmakkone ### ########.fr */ ++/* @@@ @@@@@@@@ */ ++/* @@@ @@@@@@@@@@ */ ++/* @@! @@! @@@@ */ ++/* !@! !@! @!@!@ */ ++/* btrtile.c @!! @!@ @! !@! */ ++/* !!! !@!!! !!! */ ++/* By: julmajustus !!: !!:! !!! */ ++/* ::! :!: !:! */ ++/* Created: 2024/12/15 00:26:07 by julmajustus :: ::::::: :: */ ++/* Updated: 2026/03/18 20:26:17 by julmajustus : : : : : : */ +/* */ +/* ************************************************************************** */ + @@ -580,21 +582,21 @@ index 0000000..03f4680 + return closest; +} diff --git a/config.def.h b/config.def.h -index 22d2171..92f3ad6 100644 +index 8a6eda0..bc04e3f 100644 --- a/config.def.h +++ b/config.def.h @@ -13,7 +13,10 @@ 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 float resize_factor = 0.0002f; /* Resize multiplier for mouse resizing, depends on mouse sensitivity. */ + static const float fullscreen_bg[] = {0.0f, 0.0f, 0.0f, 1.0f}; /* You can also use glsl colors */ ++static const float resize_factor = 0.0002f; /* Resize multiplier for mouse resizing, depends on mouse sensivity. */ +static const uint32_t resize_interval_ms = 16; /* Resize interval depends on framerate and screen refresh rate. */ +enum Direction { DIR_LEFT, DIR_RIGHT, DIR_UP, DIR_DOWN }; /* tagging - TAGCOUNT must be no greater than 31 */ #define TAGCOUNT (9) -@@ -31,6 +34,7 @@ static const Rule rules[] = { +@@ -30,6 +33,7 @@ static const Rule rules[] = { /* layout(s) */ static const Layout layouts[] = { /* symbol arrange function */ @@ -602,23 +604,23 @@ index 22d2171..92f3ad6 100644 { "[]=", tile }, { "><>", NULL }, /* no layout function means floating behavior */ { "[M]", monocle }, -@@ -148,6 +152,14 @@ static const Key keys[] = { - { 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|WLR_MODIFIER_SHIFT, XKB_KEY_Up, swapclients, {.i = DIR_UP} }, -+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Down, swapclients, {.i = DIR_DOWN} }, -+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Right, swapclients, {.i = DIR_RIGHT} }, -+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Left, swapclients, {.i = DIR_LEFT} }, -+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Right, setratio_h, {.f = +0.025f} }, -+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Left, setratio_h, {.f = -0.025f} }, -+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Up, setratio_v, {.f = -0.025f} }, -+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Down, setratio_v, {.f = +0.025f} }, - TAGKEYS( XKB_KEY_1, XKB_KEY_exclam, 0), - TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1), - TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2), +@@ -144,6 +148,14 @@ static const Key keys[] = { + { 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|WLR_MODIFIER_SHIFT, XKB_KEY_Up, swapclients, {.i = DIR_UP} }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Down, swapclients, {.i = DIR_DOWN} }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Right, swapclients, {.i = DIR_RIGHT} }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Left, swapclients, {.i = DIR_LEFT} }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Right, setratio_h, {.f = +0.025f} }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Left, setratio_h, {.f = -0.025f} }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Up, setratio_v, {.f = -0.025f} }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Down, setratio_v, {.f = +0.025f} }, + TAGKEYS( XKB_KEY_1, XKB_KEY_exclam, 0), + TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1), + TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2), diff --git a/dwl.c b/dwl.c -index a2711f6..e49a061 100644 +index 8101ffa..e4e7074 100644 --- a/dwl.c +++ b/dwl.c @@ -1,6 +1,7 @@ @@ -629,15 +631,15 @@ index a2711f6..e49a061 100644 #include #include #include -@@ -103,6 +104,7 @@ typedef struct { +@@ -104,6 +105,7 @@ typedef struct { const Arg arg; } Button; +typedef struct LayoutNode LayoutNode; typedef struct Monitor Monitor; typedef struct { - /* Must keep these three elements in this order */ -@@ -139,8 +141,9 @@ typedef struct { + /* Must keep this field first */ +@@ -141,8 +143,9 @@ typedef struct { #endif unsigned int bw; uint32_t tags; @@ -648,7 +650,7 @@ index a2711f6..e49a061 100644 } Client; typedef struct { -@@ -208,6 +211,7 @@ struct Monitor { +@@ -209,6 +212,7 @@ struct Monitor { int nmaster; char ltsymbol[16]; int asleep; @@ -656,7 +658,7 @@ index a2711f6..e49a061 100644 }; typedef struct { -@@ -250,6 +254,7 @@ static void arrangelayer(Monitor *m, struct wl_list *list, +@@ -251,6 +255,7 @@ static void arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, int exclusive); static void arrangelayers(Monitor *m); static void axisnotify(struct wl_listener *listener, void *data); @@ -674,7 +676,7 @@ index a2711f6..e49a061 100644 static void spawn(const Arg *arg); static void startdrag(struct wl_listener *listener, void *data); static void tag(const Arg *arg); -@@ -431,6 +439,7 @@ static xcb_atom_t netatom[NetLast]; +@@ -458,6 +466,7 @@ static struct wlr_xwayland *xwayland; /* attempt to encapsulate suck into one file */ #include "client.h" @@ -682,7 +684,7 @@ index a2711f6..e49a061 100644 /* function implementations */ void -@@ -601,7 +610,7 @@ buttonpress(struct wl_listener *listener, void *data) +@@ -628,7 +637,7 @@ buttonpress(struct wl_listener *listener, void *data) struct wlr_pointer_button_event *event = data; struct wlr_keyboard *keyboard; uint32_t mods; @@ -691,7 +693,7 @@ index a2711f6..e49a061 100644 const Button *b; wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); -@@ -622,7 +631,7 @@ buttonpress(struct wl_listener *listener, void *data) +@@ -649,7 +658,7 @@ buttonpress(struct wl_listener *listener, void *data) mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; for (b = buttons; b < END(buttons); b++) { if (CLEANMASK(mods) == CLEANMASK(b->mod) && @@ -700,9 +702,9 @@ index a2711f6..e49a061 100644 b->func(&b->arg); return; } -@@ -632,15 +641,36 @@ buttonpress(struct wl_listener *listener, void *data) +@@ -659,6 +668,27 @@ buttonpress(struct wl_listener *listener, void *data) /* If you released any buttons, we exit interactive move/resize mode. */ - /* TODO should reset to the pointer focus's current setcursor */ + /* TODO: should reset to the pointer focus's current setcursor */ if (!locked && cursor_mode != CurNormal && cursor_mode != CurPressed) { + c = grabc; + if (c && c->was_tiled && !strcmp(selmon->ltsymbol, "|w|")) { @@ -728,18 +730,7 @@ index a2711f6..e49a061 100644 wlr_cursor_set_xcursor(cursor, cursor_mgr, "default"); cursor_mode = CurNormal; /* Drop the window off on its new monitor */ - selmon = xytomon(cursor->x, cursor->y); - setmon(grabc, selmon, 0); -+ grabc = NULL; - return; -- } else { -- cursor_mode = CurNormal; - } -+ cursor_mode = CurNormal; - break; - } - /* If the event wasn't handled by the compositor, notify the client with -@@ -720,6 +750,7 @@ cleanupmon(struct wl_listener *listener, void *data) +@@ -750,6 +780,7 @@ cleanupmon(struct wl_listener *listener, void *data) wlr_output_layout_remove(output_layout, m->wlr_output); wlr_scene_output_destroy(m->scene_output); @@ -747,7 +738,7 @@ index a2711f6..e49a061 100644 closemon(m); wlr_scene_node_destroy(&m->fullscreen_bg->node); free(m); -@@ -1024,6 +1055,7 @@ createmon(struct wl_listener *listener, void *data) +@@ -1094,6 +1125,7 @@ createmon(struct wl_listener *listener, void *data) wl_list_insert(&mons, &m->link); printstatus(); @@ -755,7 +746,7 @@ index a2711f6..e49a061 100644 /* The xdg-protocol specifies: * -@@ -1263,6 +1295,10 @@ destroynotify(struct wl_listener *listener, void *data) +@@ -1336,6 +1368,10 @@ destroynotify(struct wl_listener *listener, void *data) wl_list_remove(&c->destroy.link); wl_list_remove(&c->set_title.link); wl_list_remove(&c->fullscreen.link); @@ -766,7 +757,7 @@ index a2711f6..e49a061 100644 #ifdef XWAYLAND if (c->type != XDGShell) { wl_list_remove(&c->activate.link); -@@ -1809,7 +1845,8 @@ void +@@ -1866,7 +1902,8 @@ void motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double dy, double dx_unaccel, double dy_unaccel) { @@ -776,7 +767,7 @@ index a2711f6..e49a061 100644 Client *c = NULL, *w = NULL; LayerSurface *l = NULL; struct wlr_surface *surface = NULL; -@@ -1863,18 +1900,56 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d +@@ -1920,18 +1957,56 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d /* Update drag icon's position */ wlr_scene_node_set_position(&drag_icon->node, (int)round(cursor->x), (int)round(cursor->y)); @@ -840,7 +831,7 @@ index a2711f6..e49a061 100644 /* If there's no client surface under the cursor, set the cursor image to a * default. This is what makes the cursor image appear when you move it * off of a client or over its border. */ -@@ -1908,22 +1983,41 @@ moveresize(const Arg *arg) +@@ -1965,22 +2040,41 @@ moveresize(const Arg *arg) if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen) return; @@ -850,7 +841,7 @@ index a2711f6..e49a061 100644 - case CurMove: - grabcx = (int)round(cursor->x) - grabc->geom.x; - grabcy = (int)round(cursor->y) - grabc->geom.y; -- wlr_cursor_set_xcursor(cursor, cursor_mgr, "fleur"); +- wlr_cursor_set_xcursor(cursor, cursor_mgr, "all-scroll"); - break; - case CurResize: - /* Doesn't work for X11 output - the next absolute motion event @@ -899,5 +890,5 @@ index a2711f6..e49a061 100644 } -- -2.45.3 +2.52.0 From 039be1a6cf0c17832f2413f2eea856d28485163b Mon Sep 17 00:00:00 2001 From: nate zhou Date: Mon, 23 Mar 2026 01:34:20 +0800 Subject: [PATCH 34/36] Add: shifttag Shift to next/previous tag, with skipping occupied/unoccupied variants. This patch is an alternative to `shiftview` patch with more controls: - `shifttag`: shift to next/previous tags - `shifttag_occupied`: skipping unoccupied tags - `shifttag_unoccupied`: skipping occupied tags `shifttag-bar` is a variant to be applied on top of the bar patch. --- patches/shifttag/README.md | 22 +++++ patches/shifttag/shifttag-bar.patch | 146 ++++++++++++++++++++++++++++ patches/shifttag/shifttag.patch | 145 +++++++++++++++++++++++++++ 3 files changed, 313 insertions(+) create mode 100644 patches/shifttag/README.md create mode 100644 patches/shifttag/shifttag-bar.patch create mode 100644 patches/shifttag/shifttag.patch diff --git a/patches/shifttag/README.md b/patches/shifttag/README.md new file mode 100644 index 0000000..ed7e1f9 --- /dev/null +++ b/patches/shifttag/README.md @@ -0,0 +1,22 @@ +### Description + +Shift to next/previous tag, with skipping occupied/unoccupied variants. + +This patch is an alternative to +[shiftview](https://codeberg.org/dwl/dwl-patches/src/branch/main/patches/shiftview) with more controls: + +| function | behavior | +| :--- | :---| +| shifttag | shift to next/previous tags | +| shifttag_occupied | skipping unoccupied tags | +| shifttag_unoccupied | skipping occupied tags | + +`shifttag-bar` is a variant to be applied on top of the +[bar](https://codeberg.org/dwl/dwl-patches/src/branch/main/patches/bar/) patch. + +### Download +- [shifttag-v0.8](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/shifttag/shifttag.patch) +- [shifttag-bar-v0.8](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/shifttag/shifttag-bar.patch) + +### Authors +- [unixchad](https://codeberg.org/unixchad/) diff --git a/patches/shifttag/shifttag-bar.patch b/patches/shifttag/shifttag-bar.patch new file mode 100644 index 0000000..863b8e6 --- /dev/null +++ b/patches/shifttag/shifttag-bar.patch @@ -0,0 +1,146 @@ +From 6d3a0506f45243cf11f0f7ba06048f453576567c Mon Sep 17 00:00:00 2001 +From: nate zhou +Date: Mon, 23 Mar 2026 01:16:06 +0800 +Subject: [PATCH] shifttag with filtering occupied/unoccupied tags support for + bar patch + +--- + config.def.h | 8 +++++ + shifttag.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 107 insertions(+) + create mode 100644 shifttag.c + +diff --git a/config.def.h b/config.def.h +index 7da50d2..9fbcf2a 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -124,6 +124,8 @@ static const enum libinput_config_tap_button_map button_map = LIBINPUT_CONFIG_TA + static const char *termcmd[] = { "foot", NULL }; + static const char *menucmd[] = { "wmenu-run", NULL }; + ++#include "shifttag.c" ++ + static const Key keys[] = { + /* Note that Shift changes certain key codes: 2 -> at, etc. */ + /* modifier key function argument */ +@@ -138,6 +140,12 @@ static const Key keys[] = { + { MODKEY, XKB_KEY_l, setmfact, {.f = +0.05f} }, + { MODKEY, XKB_KEY_Return, zoom, {0} }, + { MODKEY, XKB_KEY_Tab, view, {0} }, ++ { MODKEY, XKB_KEY_apostrophe, shifttag_occupied, { .i = 1 } }, ++ { MODKEY, XKB_KEY_semicolon, shifttag_occupied, { .i = -1 } }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_quotedbl, shifttag, { .i = 1 } }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_colon, shifttag, { .i = -1 } }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_apostrophe, shifttag_unoccupied, { .i = 1 } }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_semicolon, shifttag_unoccupied, { .i = -1 } }, + { 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/shifttag.c b/shifttag.c +new file mode 100644 +index 0000000..2615f4c +--- /dev/null ++++ b/shifttag.c +@@ -0,0 +1,99 @@ ++// "arg->i" stores the number of tags to shift right (positive value) ++// or left (negative value) ++ ++static uint32_t ++get_occupied_tags(Monitor *m) ++{ ++ uint32_t occupied = 0; ++ Client *c; ++ wl_list_for_each(c, &clients, link) { ++ if (c->mon == m) ++ occupied |= c->tags; ++ } ++ return occupied & TAGMASK; ++} ++ ++static uint32_t ++find_next_tag(uint32_t current_tag, int direction, bool skip_unoccupied, bool skip_occupied) ++{ ++ uint32_t occupied = get_occupied_tags(selmon); ++ uint32_t start = current_tag; ++ uint32_t test_tag = current_tag; ++ uint32_t new_tag = current_tag; ++ int count = 0; ++ ++ if (!skip_unoccupied && !skip_occupied) { ++ if (direction > 0) { ++ new_tag = (current_tag << 1) | (current_tag >> (LENGTH(tags) - 1)); ++ } else { ++ new_tag = (current_tag >> 1) | (current_tag << (LENGTH(tags) - 1)); ++ } ++ return new_tag & TAGMASK; ++ } ++ ++ do { ++ if (direction > 0) { ++ if (test_tag << 1 && (test_tag << 1) <= TAGMASK) ++ test_tag = test_tag << 1; ++ else ++ test_tag = 1; ++ } else { ++ if (test_tag >> 1) ++ test_tag = test_tag >> 1; ++ else ++ test_tag = 1 << (LENGTH(tags) - 1); ++ } ++ ++ int is_occupied = (occupied & test_tag) != 0; ++ int should_select = (skip_unoccupied && is_occupied) || ++ (skip_occupied && !is_occupied); ++ ++ if (should_select) { ++ new_tag = test_tag; ++ break; ++ } ++ count++; ++ } while (test_tag != start && count < LENGTH(tags)); ++ ++ return new_tag; ++} ++ ++static void ++shifttag_with_filter(const Arg *arg, bool skip_unoccupied, bool skip_occupied) ++{ ++ Arg a; ++ if (!selmon) ++ return; ++ ++ uint32_t occupied = get_occupied_tags(selmon); ++ if (skip_unoccupied && !skip_occupied && occupied == 0) ++ return; ++ if (skip_occupied && !skip_unoccupied && (occupied == TAGMASK)) ++ return; ++ ++ uint32_t curseltags = selmon->tagset[selmon->seltags]; ++ uint32_t nextseltags = find_next_tag(curseltags, arg->i, skip_unoccupied, skip_occupied); ++ ++ if (nextseltags != curseltags) { ++ a.i = nextseltags; ++ view(&a); ++ } ++} ++ ++void ++shifttag(const Arg *arg) ++{ ++ shifttag_with_filter(arg, false, false); ++} ++ ++void ++shifttag_occupied(const Arg *arg) ++{ ++ shifttag_with_filter(arg, true, false); ++} ++ ++void ++shifttag_unoccupied(const Arg *arg) ++{ ++ shifttag_with_filter(arg, false, true); ++} +-- +2.53.0 + diff --git a/patches/shifttag/shifttag.patch b/patches/shifttag/shifttag.patch new file mode 100644 index 0000000..82ccf15 --- /dev/null +++ b/patches/shifttag/shifttag.patch @@ -0,0 +1,145 @@ +From 49b0da41f1f28a60cf216f5bffecc0ce1ea1ec9c Mon Sep 17 00:00:00 2001 +From: nate zhou +Date: Mon, 23 Mar 2026 00:50:54 +0800 +Subject: [PATCH] shifttag with filtering occupied/unoccupied tags support + +--- + config.def.h | 8 +++++ + shifttag.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 107 insertions(+) + create mode 100644 shifttag.c + +diff --git a/config.def.h b/config.def.h +index 8a6eda0..2377fa5 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -118,6 +118,8 @@ static const enum libinput_config_tap_button_map button_map = LIBINPUT_CONFIG_TA + static const char *termcmd[] = { "foot", NULL }; + static const char *menucmd[] = { "wmenu-run", NULL }; + ++#include "shifttag.c" ++ + static const Key keys[] = { + /* Note that Shift changes certain key codes: 2 -> at, etc. */ + /* modifier key function argument */ +@@ -131,6 +133,12 @@ static const Key keys[] = { + { MODKEY, XKB_KEY_l, setmfact, {.f = +0.05f} }, + { MODKEY, XKB_KEY_Return, zoom, {0} }, + { MODKEY, XKB_KEY_Tab, view, {0} }, ++ { MODKEY, XKB_KEY_apostrophe, shifttag_occupied, { .i = 1 } }, ++ { MODKEY, XKB_KEY_semicolon, shifttag_occupied, { .i = -1 } }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_quotedbl, shifttag, { .i = 1 } }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_colon, shifttag, { .i = -1 } }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_apostrophe, shifttag_unoccupied, { .i = 1 } }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_semicolon, shifttag_unoccupied, { .i = -1 } }, + { 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/shifttag.c b/shifttag.c +new file mode 100644 +index 0000000..5aeee00 +--- /dev/null ++++ b/shifttag.c +@@ -0,0 +1,99 @@ ++// "arg->i" stores the number of tags to shift right (positive value) ++// or left (negative value) ++ ++static uint32_t ++get_occupied_tags(Monitor *m) ++{ ++ uint32_t occupied = 0; ++ Client *c; ++ wl_list_for_each(c, &clients, link) { ++ if (c->mon == m) ++ occupied |= c->tags; ++ } ++ return occupied & TAGMASK; ++} ++ ++static uint32_t ++find_next_tag(uint32_t current_tag, int direction, bool skip_unoccupied, bool skip_occupied) ++{ ++ uint32_t occupied = get_occupied_tags(selmon); ++ uint32_t start = current_tag; ++ uint32_t test_tag = current_tag; ++ uint32_t new_tag = current_tag; ++ int count = 0; ++ ++ if (!skip_unoccupied && !skip_occupied) { ++ if (direction > 0) { ++ new_tag = (current_tag << 1) | (current_tag >> (TAGCOUNT - 1)); ++ } else { ++ new_tag = (current_tag >> 1) | (current_tag << (TAGCOUNT - 1)); ++ } ++ return new_tag & TAGMASK; ++ } ++ ++ do { ++ if (direction > 0) { ++ if (test_tag << 1 && (test_tag << 1) <= TAGMASK) ++ test_tag = test_tag << 1; ++ else ++ test_tag = 1; ++ } else { ++ if (test_tag >> 1) ++ test_tag = test_tag >> 1; ++ else ++ test_tag = 1 << (TAGCOUNT - 1); ++ } ++ ++ int is_occupied = (occupied & test_tag) != 0; ++ int should_select = (skip_unoccupied && is_occupied) || ++ (skip_occupied && !is_occupied); ++ ++ if (should_select) { ++ new_tag = test_tag; ++ break; ++ } ++ count++; ++ } while (test_tag != start && count < TAGCOUNT); ++ ++ return new_tag; ++} ++ ++static void ++shifttag_with_filter(const Arg *arg, bool skip_unoccupied, bool skip_occupied) ++{ ++ Arg a; ++ if (!selmon) ++ return; ++ ++ uint32_t occupied = get_occupied_tags(selmon); ++ if (skip_unoccupied && !skip_occupied && occupied == 0) ++ return; ++ if (skip_occupied && !skip_unoccupied && (occupied == TAGMASK)) ++ return; ++ ++ uint32_t curseltags = selmon->tagset[selmon->seltags]; ++ uint32_t nextseltags = find_next_tag(curseltags, arg->i, skip_unoccupied, skip_occupied); ++ ++ if (nextseltags != curseltags) { ++ a.i = nextseltags; ++ view(&a); ++ } ++} ++ ++void ++shifttag(const Arg *arg) ++{ ++ shifttag_with_filter(arg, false, false); ++} ++ ++void ++shifttag_occupied(const Arg *arg) ++{ ++ shifttag_with_filter(arg, true, false); ++} ++ ++void ++shifttag_unoccupied(const Arg *arg) ++{ ++ shifttag_with_filter(arg, false, true); ++} +-- +2.53.0 + From 24fa6e04cecfde11fa62cf9b9f5853160c5e863b Mon Sep 17 00:00:00 2001 From: nate zhou Date: Mon, 23 Mar 2026 20:24:42 +0800 Subject: [PATCH 35/36] shifttag: fix shifting to unoccupied with multiple tags --- patches/shifttag/shifttag-bar.patch | 98 +++++++++++++++++------------ patches/shifttag/shifttag.patch | 98 +++++++++++++++++------------ 2 files changed, 114 insertions(+), 82 deletions(-) diff --git a/patches/shifttag/shifttag-bar.patch b/patches/shifttag/shifttag-bar.patch index 863b8e6..df781b5 100644 --- a/patches/shifttag/shifttag-bar.patch +++ b/patches/shifttag/shifttag-bar.patch @@ -1,13 +1,13 @@ -From 6d3a0506f45243cf11f0f7ba06048f453576567c Mon Sep 17 00:00:00 2001 +From 8dff34be15664705654e5e4d41f990f8eba8b79d Mon Sep 17 00:00:00 2001 From: nate zhou Date: Mon, 23 Mar 2026 01:16:06 +0800 Subject: [PATCH] shifttag with filtering occupied/unoccupied tags support for bar patch --- - config.def.h | 8 +++++ - shifttag.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 107 insertions(+) + config.def.h | 8 ++++ + shifttag.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 123 insertions(+) create mode 100644 shifttag.c diff --git a/config.def.h b/config.def.h @@ -38,10 +38,10 @@ index 7da50d2..9fbcf2a 100644 { MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} }, diff --git a/shifttag.c b/shifttag.c new file mode 100644 -index 0000000..2615f4c +index 0000000..5f1d5c2 --- /dev/null +++ b/shifttag.c -@@ -0,0 +1,99 @@ +@@ -0,0 +1,115 @@ +// "arg->i" stores the number of tags to shift right (positive value) +// or left (negative value) + @@ -61,45 +61,61 @@ index 0000000..2615f4c +find_next_tag(uint32_t current_tag, int direction, bool skip_unoccupied, bool skip_occupied) +{ + uint32_t occupied = get_occupied_tags(selmon); -+ uint32_t start = current_tag; -+ uint32_t test_tag = current_tag; -+ uint32_t new_tag = current_tag; -+ int count = 0; ++ uint32_t result = 0; + -+ if (!skip_unoccupied && !skip_occupied) { -+ if (direction > 0) { -+ new_tag = (current_tag << 1) | (current_tag >> (LENGTH(tags) - 1)); -+ } else { -+ new_tag = (current_tag >> 1) | (current_tag << (LENGTH(tags) - 1)); ++ for (int i = 0; i < LENGTH(tags); i++) { ++ uint32_t bit = 1 << i; ++ ++ if (current_tag & bit) { ++ uint32_t new_bit = bit; ++ ++ if (!skip_unoccupied && !skip_occupied) { ++ if (direction > 0) { ++ if (bit << 1 && (bit << 1) <= TAGMASK) ++ new_bit = bit << 1; ++ else ++ new_bit = 1; ++ } else { ++ if (bit >> 1) ++ new_bit = bit >> 1; ++ else ++ new_bit = 1 << (LENGTH(tags) - 1); ++ } ++ } else { ++ uint32_t test_bit = bit; ++ uint32_t start_bit = bit; ++ int count = 0; ++ ++ do { ++ if (direction > 0) { ++ if (test_bit << 1 && (test_bit << 1) <= TAGMASK) ++ test_bit = test_bit << 1; ++ else ++ test_bit = 1; ++ } else { ++ if (test_bit >> 1) ++ test_bit = test_bit >> 1; ++ else ++ test_bit = 1 << (LENGTH(tags) - 1); ++ } ++ ++ int is_occupied = (occupied & test_bit) != 0; ++ int should_select = (skip_unoccupied && is_occupied) || ++ (skip_occupied && !is_occupied); ++ ++ if (should_select) { ++ new_bit = test_bit; ++ break; ++ } ++ count++; ++ } while (test_bit != start_bit && count < LENGTH(tags)); ++ } ++ ++ result |= new_bit; + } -+ return new_tag & TAGMASK; + } + -+ do { -+ if (direction > 0) { -+ if (test_tag << 1 && (test_tag << 1) <= TAGMASK) -+ test_tag = test_tag << 1; -+ else -+ test_tag = 1; -+ } else { -+ if (test_tag >> 1) -+ test_tag = test_tag >> 1; -+ else -+ test_tag = 1 << (LENGTH(tags) - 1); -+ } -+ -+ int is_occupied = (occupied & test_tag) != 0; -+ int should_select = (skip_unoccupied && is_occupied) || -+ (skip_occupied && !is_occupied); -+ -+ if (should_select) { -+ new_tag = test_tag; -+ break; -+ } -+ count++; -+ } while (test_tag != start && count < LENGTH(tags)); -+ -+ return new_tag; ++ return result; +} + +static void diff --git a/patches/shifttag/shifttag.patch b/patches/shifttag/shifttag.patch index 82ccf15..1220a8f 100644 --- a/patches/shifttag/shifttag.patch +++ b/patches/shifttag/shifttag.patch @@ -1,12 +1,12 @@ -From 49b0da41f1f28a60cf216f5bffecc0ce1ea1ec9c Mon Sep 17 00:00:00 2001 +From 2a4417a6344c9d4b83989d63df17d9cdb0f81aea Mon Sep 17 00:00:00 2001 From: nate zhou Date: Mon, 23 Mar 2026 00:50:54 +0800 Subject: [PATCH] shifttag with filtering occupied/unoccupied tags support --- - config.def.h | 8 +++++ - shifttag.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 107 insertions(+) + config.def.h | 8 ++++ + shifttag.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 123 insertions(+) create mode 100644 shifttag.c diff --git a/config.def.h b/config.def.h @@ -37,10 +37,10 @@ index 8a6eda0..2377fa5 100644 { MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} }, diff --git a/shifttag.c b/shifttag.c new file mode 100644 -index 0000000..5aeee00 +index 0000000..971216b --- /dev/null +++ b/shifttag.c -@@ -0,0 +1,99 @@ +@@ -0,0 +1,115 @@ +// "arg->i" stores the number of tags to shift right (positive value) +// or left (negative value) + @@ -60,45 +60,61 @@ index 0000000..5aeee00 +find_next_tag(uint32_t current_tag, int direction, bool skip_unoccupied, bool skip_occupied) +{ + uint32_t occupied = get_occupied_tags(selmon); -+ uint32_t start = current_tag; -+ uint32_t test_tag = current_tag; -+ uint32_t new_tag = current_tag; -+ int count = 0; ++ uint32_t result = 0; + -+ if (!skip_unoccupied && !skip_occupied) { -+ if (direction > 0) { -+ new_tag = (current_tag << 1) | (current_tag >> (TAGCOUNT - 1)); -+ } else { -+ new_tag = (current_tag >> 1) | (current_tag << (TAGCOUNT - 1)); ++ for (int i = 0; i < TAGCOUNT; i++) { ++ uint32_t bit = 1 << i; ++ ++ if (current_tag & bit) { ++ uint32_t new_bit = bit; ++ ++ if (!skip_unoccupied && !skip_occupied) { ++ if (direction > 0) { ++ if (bit << 1 && (bit << 1) <= TAGMASK) ++ new_bit = bit << 1; ++ else ++ new_bit = 1; ++ } else { ++ if (bit >> 1) ++ new_bit = bit >> 1; ++ else ++ new_bit = 1 << (TAGCOUNT - 1); ++ } ++ } else { ++ uint32_t test_bit = bit; ++ uint32_t start_bit = bit; ++ int count = 0; ++ ++ do { ++ if (direction > 0) { ++ if (test_bit << 1 && (test_bit << 1) <= TAGMASK) ++ test_bit = test_bit << 1; ++ else ++ test_bit = 1; ++ } else { ++ if (test_bit >> 1) ++ test_bit = test_bit >> 1; ++ else ++ test_bit = 1 << (TAGCOUNT - 1); ++ } ++ ++ int is_occupied = (occupied & test_bit) != 0; ++ int should_select = (skip_unoccupied && is_occupied) || ++ (skip_occupied && !is_occupied); ++ ++ if (should_select) { ++ new_bit = test_bit; ++ break; ++ } ++ count++; ++ } while (test_bit != start_bit && count < TAGCOUNT); ++ } ++ ++ result |= new_bit; + } -+ return new_tag & TAGMASK; + } + -+ do { -+ if (direction > 0) { -+ if (test_tag << 1 && (test_tag << 1) <= TAGMASK) -+ test_tag = test_tag << 1; -+ else -+ test_tag = 1; -+ } else { -+ if (test_tag >> 1) -+ test_tag = test_tag >> 1; -+ else -+ test_tag = 1 << (TAGCOUNT - 1); -+ } -+ -+ int is_occupied = (occupied & test_tag) != 0; -+ int should_select = (skip_unoccupied && is_occupied) || -+ (skip_occupied && !is_occupied); -+ -+ if (should_select) { -+ new_tag = test_tag; -+ break; -+ } -+ count++; -+ } while (test_tag != start && count < TAGCOUNT); -+ -+ return new_tag; ++ return result; +} + +static void From 998808b30367ddf630f44c5e838463f13406b28f Mon Sep 17 00:00:00 2001 From: nate zhou Date: Sun, 8 Mar 2026 10:58:28 +0800 Subject: [PATCH 36/36] sticky: update for 0.8 --- patches/sticky/README.md | 3 ++- patches/sticky/sticky.patch | 28 ++++++++++++++-------------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/patches/sticky/README.md b/patches/sticky/README.md index af0e81f..4f1b36d 100644 --- a/patches/sticky/README.md +++ b/patches/sticky/README.md @@ -4,7 +4,8 @@ Adds a toggleable function that makes a sticky client that is visible on all tag Originally based on [dwm sticky patch](https://dwm.suckless.org/patches/sticky). ### Download -- [2024-07-26](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/sticky/sticky.patch) +- [v0.8](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/sticky/sticky.patch) +- [v0.7](https://codeberg.org/dwl/dwl-patches/raw/commit/898bc7a946c7be14034a665bd7a7b042632465ea/patches/sticky/sticky.patch) - [v0.4](https://github.com/djpohly/dwl/compare/main...dm1tz:04-sticky.patch) - [git branch](https://codeberg.org/Rutherther/dwl/src/branch/v0.7/sticky) diff --git a/patches/sticky/sticky.patch b/patches/sticky/sticky.patch index ece8308..f0cd55d 100644 --- a/patches/sticky/sticky.patch +++ b/patches/sticky/sticky.patch @@ -1,17 +1,17 @@ -From f113cdc0b4cecceaaf28679489852ae61a1aa3f5 Mon Sep 17 00:00:00 2001 -From: Rutherther -Date: Fri, 19 Jul 2024 16:29:43 +0200 -Subject: [PATCH] sticky +From d36eee35fdc4b0e666fb04a580200e8c5d2b1f9e Mon Sep 17 00:00:00 2001 +From: nate zhou +Date: Sat, 28 Feb 2026 22:55:33 +0800 +Subject: [PATCH] Update sticky patch to v0.8 --- dwl.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c -index 5bf995e..820f4af 100644 +index 44f3ad9..05070ef 100644 --- a/dwl.c +++ b/dwl.c -@@ -73,7 +73,7 @@ +@@ -74,7 +74,7 @@ #define MAX(A, B) ((A) > (B) ? (A) : (B)) #define MIN(A, B) ((A) < (B) ? (A) : (B)) #define CLEANMASK(mask) (mask & ~WLR_MODIFIER_CAPS) @@ -20,7 +20,7 @@ index 5bf995e..820f4af 100644 #define LENGTH(X) (sizeof X / sizeof X[0]) #define END(A) ((A) + LENGTH(A)) #define TAGMASK ((1u << TAGCOUNT) - 1) -@@ -139,7 +139,7 @@ typedef struct { +@@ -137,7 +137,7 @@ typedef struct { #endif unsigned int bw; uint32_t tags; @@ -29,15 +29,15 @@ index 5bf995e..820f4af 100644 uint32_t resize; /* configure serial of a pending resize */ } Client; -@@ -326,6 +326,7 @@ static void setcursor(struct wl_listener *listener, void *data); +@@ -323,6 +323,7 @@ static void setcursor(struct wl_listener *listener, void *data); static void setcursorshape(struct wl_listener *listener, void *data); static void setfloating(Client *c, int floating); static void setfullscreen(Client *c, int fullscreen); +static void setsticky(Client *c, int sticky); - static void setgamma(struct wl_listener *listener, void *data); static void setlayout(const Arg *arg); static void setmfact(const Arg *arg); -@@ -339,6 +340,7 @@ static void tag(const Arg *arg); + static void setmon(Client *c, Monitor *m, uint32_t newtags); +@@ -335,6 +336,7 @@ static void tag(const Arg *arg); static void tagmon(const Arg *arg); static void tile(Monitor *m); static void togglefloating(const Arg *arg); @@ -45,8 +45,8 @@ index 5bf995e..820f4af 100644 static void togglefullscreen(const Arg *arg); static void toggletag(const Arg *arg); static void toggleview(const Arg *arg); -@@ -2351,6 +2353,17 @@ setgamma(struct wl_listener *listener, void *data) - wlr_output_schedule_frame(m->wlr_output); +@@ -2368,6 +2370,17 @@ setfullscreen(Client *c, int fullscreen) + printstatus(); } +void @@ -63,7 +63,7 @@ index 5bf995e..820f4af 100644 void setlayout(const Arg *arg) { -@@ -2738,6 +2751,16 @@ togglefullscreen(const Arg *arg) +@@ -2760,6 +2773,16 @@ togglefullscreen(const Arg *arg) setfullscreen(sel, !sel->isfullscreen); } @@ -81,5 +81,5 @@ index 5bf995e..820f4af 100644 toggletag(const Arg *arg) { -- -2.45.2 +2.53.0