From 83ae7d4816a49f46063ab16ffecd32b2e7e61784 Mon Sep 17 00:00:00 2001
From: choc <notchoc@proton.me>
Date: Fri, 15 Sep 2023 11:45:16 +0800
Subject: [PATCH] dragresize: implement rio-like window resizing

select window to resize then drag out an area for it to occupy
---
 dwl.c | 132 +++++++++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 98 insertions(+), 34 deletions(-)

diff --git a/dwl.c b/dwl.c
index dc0437e..843aa7a 100644
--- a/dwl.c
+++ b/dwl.c
@@ -80,7 +80,7 @@
 #define LISTEN_STATIC(E, H)     do { static struct wl_listener _l = {.notify = (H)}; wl_signal_add((E), &_l); } while (0)
 
 /* enums */
-enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */
+enum { CurNormal, CurPressed, CurSelect, CurMove, CurResize, CurBind }; /* cursor */
 enum { XDGShell, LayerShell, X11 }; /* client types */
 enum { LyrBg, LyrBottom, LyrTile, LyrFloat, LyrTop, LyrFS, LyrOverlay, LyrBlock, NUM_LAYERS }; /* scene layers */
 #ifdef XWAYLAND
@@ -603,15 +603,21 @@ buttonpress(struct wl_listener *listener, void *data)
 
 	switch (event->state) {
 	case WLR_BUTTON_PRESSED:
-		cursor_mode = CurPressed;
 		selmon = xytomon(cursor->x, cursor->y);
-		if (locked)
+		if (locked) {
+			cursor_mode = CurPressed;
 			break;
+		}
 
-		/* Change focus if the button was _pressed_ over a client */
-		xytonode(cursor->x, cursor->y, NULL, &c, NULL, NULL, NULL);
-		if (c && (!client_is_unmanaged(c) || client_wants_focus(c)))
-			focusclient(c, 1);
+		if (cursor_mode == CurNormal)
+			cursor_mode = CurPressed;
+
+		if (cursor_mode != CurResize) {
+			/* Change focus if the button was _pressed_ over a client */
+			xytonode(cursor->x, cursor->y, NULL, &c, NULL, NULL, NULL);
+			if (c && (!client_is_unmanaged(c) || client_wants_focus(c)))
+				focusclient(c, 1);
+		}
 
 		keyboard = wlr_seat_get_keyboard(seat);
 		mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
@@ -624,17 +630,42 @@ buttonpress(struct wl_listener *listener, void *data)
 		}
 		break;
 	case WLR_BUTTON_RELEASED:
+		if (locked) {
+			cursor_mode = CurNormal;
+			break;
+		}
 		/* If you released any buttons, we exit interactive move/resize mode. */
-		/* TODO should reset to the pointer focus's current setcursor */
-		if (!locked && cursor_mode != CurNormal && cursor_mode != CurPressed) {
-			wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
+		switch (cursor_mode) {
+		case CurPressed:
 			cursor_mode = CurNormal;
-			/* Drop the window off on its new monitor */
-			selmon = xytomon(cursor->x, cursor->y);
-			setmon(grabc, selmon, 0);
+		case CurNormal:
+			break;
+		case CurSelect:
 			return;
-		} else {
+		case CurResize:
+			/* a label can only be part of a statement - Wpedantic */
+			{
+				int nw = abs((int) cursor->x - grabcx);
+				int nh = abs((int) cursor->y - grabcy);
+				if (nw > 1 && nh > 1) {
+					setfloating(grabc, 1);
+					resize(grabc, (struct wlr_box){.x = MIN(ROUND(cursor->x), grabcx),
+							.y = MIN(ROUND(cursor->y), grabcy),
+							.width = nw, .height = nh}, 1);
+				}
+			}
+			/* fallthrough */
+		default:
+			/* TODO should reset to the pointer focus's current setcursor */
+			wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
+			/* Drop the window off on its new monitor */
+			if (grabc && cursor_mode != CurBind) {
+				selmon = xytomon(cursor->x, cursor->y);
+				setmon(grabc, selmon, 0);
+				grabc = NULL;
+			}
 			cursor_mode = CurNormal;
+			return;
 		}
 		break;
 	}
@@ -1815,15 +1846,33 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d
 	wlr_scene_node_set_position(&drag_icon->node, (int)round(cursor->x), (int)round(cursor->y));
 
 	/* If we are currently grabbing the mouse, handle and return */
-	if (cursor_mode == CurMove) {
+	switch (cursor_mode) {
+	case CurSelect:
+		return;
+	case CurMove:
 		/* Move the grabbed client to the new position. */
 		resize(grabc, (struct wlr_box){.x = (int)round(cursor->x) - grabcx, .y = (int)round(cursor->y) - grabcy,
 			.width = grabc->geom.width, .height = grabc->geom.height}, 1);
 		return;
-	} else if (cursor_mode == CurResize) {
-		resize(grabc, (struct wlr_box){.x = grabc->geom.x, .y = grabc->geom.y,
-			.width = (int)round(cursor->x) - grabc->geom.x, .height = (int)round(cursor->y) - grabc->geom.y}, 1);
-		return;
+	case CurResize:
+		{
+			int w, h, x, y;
+			if (!grabc)
+				return;
+			w = abs(grabcx - (int)round(cursor->x));
+			h = abs(grabcy - (int)round(cursor->y));
+			x = MIN(grabcx, (int)round(cursor->x)) - grabc->geom.x;
+			y = MIN(grabcy, (int)round(cursor->y)) - grabc->geom.y;
+			wlr_scene_rect_set_size(grabc->border[0], w, grabc->bw);
+			wlr_scene_rect_set_size(grabc->border[1], w, grabc->bw);
+			wlr_scene_rect_set_size(grabc->border[2], grabc->bw, h - 2 * grabc->bw);
+			wlr_scene_rect_set_size(grabc->border[3], grabc->bw, h - 2 * grabc->bw);
+			wlr_scene_node_set_position(&grabc->border[0]->node, x, y);
+			wlr_scene_node_set_position(&grabc->border[1]->node, x, y + h - grabc->bw);
+			wlr_scene_node_set_position(&grabc->border[2]->node, x, y + grabc->bw);
+			wlr_scene_node_set_position(&grabc->border[3]->node, x + w - grabc->bw, y + grabc->bw);
+			return;
+		}
 	}
 
 	/* If there's no client surface under the cursor, set the cursor image to a
@@ -1853,29 +1902,43 @@ motionrelative(struct wl_listener *listener, void *data)
 void
 moveresize(const Arg *arg)
 {
-	if (cursor_mode != CurNormal && cursor_mode != CurPressed)
-		return;
-	xytonode(cursor->x, cursor->y, NULL, &grabc, NULL, NULL, NULL);
-	if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen)
+	/* Consider global select bool instead of this + CurSelect enum */
+	bool selected = (cursor_mode == CurSelect);
+	if (!selected) {
+		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) {
+		grabc = NULL;
+		cursor_mode = CurNormal;
 		return;
+	}
 
-	/* Float the window and tell motionnotify to grab it */
-	setfloating(grabc, 1);
+	/* TODO: factor out selected bool */
 	switch (cursor_mode = arg->ui) {
+	case CurResize:
+		if (!selected) break;
+		grabcx = ROUND(cursor->x);
+		grabcy = ROUND(cursor->y);
+		wlr_cursor_set_xcursor(cursor, cursor_mgr, "tcross");
+		return;
 	case CurMove:
 		grabcx = (int)round(cursor->x) - grabc->geom.x;
 		grabcy = (int)round(cursor->y) - grabc->geom.y;
+		setfloating(grabc, 1);
 		wlr_cursor_set_xcursor(cursor, cursor_mgr, "fleur");
-		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;
+		return;
+	default:
+		grabc = NULL;
+		cursor_mode = CurNormal;
+		wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
+		return;
 	}
+
+	cursor_mode = CurSelect;
+	wlr_cursor_set_xcursor(cursor, cursor_mgr, "crosshair");
 }
 
 void
@@ -2149,6 +2212,7 @@ resize(Client *c, struct wlr_box geo, int interact)
 	wlr_scene_rect_set_size(c->border[1], c->geom.width, c->bw);
 	wlr_scene_rect_set_size(c->border[2], c->bw, c->geom.height - 2 * c->bw);
 	wlr_scene_rect_set_size(c->border[3], c->bw, c->geom.height - 2 * c->bw);
+	wlr_scene_node_set_position(&c->border[0]->node, 0, 0);
 	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);
-- 
2.43.0

