From d224594e8f077ac49a1369670f6ff6a5d27ec75a Mon Sep 17 00:00:00 2001 From: wochap Date: Tue, 5 Mar 2024 23:50:01 -0500 Subject: [PATCH] implement swap and focus direction functions you should expect the same behavior as in river wm, swap only works on tiling windows focus works on tiling and floating windows --- Makefile | 2 +- config.def.h | 8 +++ dwl.c | 163 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 172 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6cde460..22f4372 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ DWLDEVCFLAGS = -g -pedantic -Wall -Wextra -Wdeclaration-after-statement -Wno-unu # CFLAGS / LDFLAGS PKGS = wlroots wayland-server xkbcommon libinput $(XLIBS) DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(DWLCPPFLAGS) $(DWLDEVCFLAGS) $(CFLAGS) -LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` $(LIBS) +LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` -lm $(LIBS) all: dwl dwl: dwl.o util.o diff --git a/config.def.h b/config.def.h index db0babc..b584f3e 100644 --- a/config.def.h +++ b/config.def.h @@ -122,6 +122,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.05} }, diff --git a/dwl.c b/dwl.c index ef27a1d..8d3540e 100644 --- a/dwl.c +++ b/dwl.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -226,6 +227,11 @@ typedef struct { struct wl_listener destroy; } SessionLock; +typedef struct { + int x; + int y; +} Vector; + /* function declarations */ static void applybounds(Client *c, struct wlr_box *bbox); static void applyrules(Client *c); @@ -264,6 +270,8 @@ static Monitor *dirtomon(enum wlr_direction dir); static void focusclient(Client *c, int lift); static void focusmon(const Arg *arg); static void focusstack(const Arg *arg); +static void focusdir(const Arg *arg); +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); @@ -1282,6 +1290,161 @@ focusstack(const Arg *arg) focusclient(c, 1); } +Vector +position_of_box(const struct wlr_box *box) +{ + return (Vector){ + .x = box->x + box->width / 2, + .y = box->y + box->height / 2, + }; +} + +Vector +diff_of_vectors(Vector *a, Vector *b) +{ + return (Vector){ + .x = b->x - a->x, + .y = b->y - a->y, + }; +} + +const char * +direction_of_vector(Vector *vector) +{ + // A zero length vector has no direction + if (vector->x == 0 && vector->y == 0) return ""; + + if (abs(vector->y) > abs(vector->x)) { + // Careful: We are operating in a Y-inverted coordinate system. + return (vector->y > 0) ? "bottom" : "top"; + } else { + return (vector->x > 0) ? "right" : "left"; + } +} + +uint32_t +vector_length(Vector *vector) +{ + // Euclidean distance formula + return (uint32_t)sqrt(vector->x * vector->x + vector->y * vector->y); +} + +// Spatial direction, based on focused client position. +Client * +client_in_direction(const char *direction, const int *skipfloat) +{ + Client *cfocused = focustop(selmon); + Vector cfocusedposition = position_of_box(&cfocused->geom); + Client *ctarget = NULL; + double targetdistance = INFINITY; + Client *c; + + if (!cfocused || cfocused->isfullscreen || (skipfloat && cfocused->isfloating)) + return NULL; + + wl_list_for_each(c, &clients, link) { + Vector cposition; + Vector positiondiff; + uint32_t distance; + + if (c == cfocused) + continue; + + if (skipfloat && c->isfloating) + continue; + + if (!VISIBLEON(c, selmon)) + continue; + + cposition = position_of_box(&c->geom); + positiondiff = diff_of_vectors(&cfocusedposition, &cposition); + + if (strcmp(direction, direction_of_vector(&positiondiff)) != 0) + continue; + + distance = vector_length(&positiondiff); + + if (distance < targetdistance) { + ctarget = c; + targetdistance = distance; + } + } + + return ctarget; +} + +void +focusdir(const Arg *arg) +{ + Client *c = NULL; + + if (arg->ui == 0) + c = client_in_direction("left", (int *)0); + if (arg->ui == 1) + c = client_in_direction("right", (int *)0); + if (arg->ui == 2) + c = client_in_direction("top", (int *)0); + if (arg->ui == 3) + c = client_in_direction("bottom", (int *)0); + + if (c != NULL) + focusclient(c, 1); +} + +void +wl_list_swap(struct wl_list *list1, struct wl_list *list2) +{ + struct wl_list *prev1, *next1, *prev2, *next2; + struct wl_list temp; + + if (list1 == list2) { + // No need to swap the same list + return; + } + + // Get the lists before and after list1 + prev1 = list1->prev; + next1 = list1->next; + + // Get the lists before and after list2 + prev2 = list2->prev; + next2 = list2->next; + + // Update the next and previous pointers of adjacent lists + prev1->next = list2; + next1->prev = list2; + prev2->next = list1; + next2->prev = list1; + + // Swap the next and previous pointers of the lists to actually swap them + temp = *list1; + *list1 = *list2; + *list2 = temp; +} + +void +swapdir(const Arg *arg) +{ + Client *c = NULL; + Client *cfocused; + + if (arg->ui == 0) + c = client_in_direction("left", (int *)1); + if (arg->ui == 1) + c = client_in_direction("right", (int *)1); + if (arg->ui == 2) + c = client_in_direction("top", (int *)1); + if (arg->ui == 3) + c = client_in_direction("bottom", (int *)1); + + if (c == NULL) + return; + + cfocused = focustop(selmon); + wl_list_swap(&cfocused->link, &c->link); + arrange(selmon); +} + /* We probably should change the name of this, it sounds like * will focus the topmost client of this mon, when actually will * only return that client */ -- 2.42.0