sites

public wiki contents of suckless.org
git clone git://git.suckless.org/sites
Log | Files | Refs

commit 1b881c96fc17f257b235684704470bee442cc559
parent 85878e73f4d115bc4b223f1912d6408f5df8df93
Author: Lucy <tkf.x1os@gmail.com>
Date:   Mon, 23 Mar 2026 17:29:26 +1300

Added a new "canvas" patch for dwm.

Diffstat:
Adwm.suckless.org/patches/canvas/dwm-canvas-6.2.diff | 472+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adwm.suckless.org/patches/canvas/index.md | 19+++++++++++++++++++
2 files changed, 491 insertions(+), 0 deletions(-)

diff --git a/dwm.suckless.org/patches/canvas/dwm-canvas-6.2.diff b/dwm.suckless.org/patches/canvas/dwm-canvas-6.2.diff @@ -0,0 +1,472 @@ +diff --git a/config.def.h b/config.def.h +index 81c3fc0..9154d29 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -18,6 +18,9 @@ static const char *colors[][3] = { + [SchemeSel] = { col_gray4, col_cyan, col_cyan }, + }; + ++#define MOVE_CANVAS_STEP 120 ++#define COORDINATES_DIVISOR 10 ++ + /* tagging */ + static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; + +@@ -86,6 +89,12 @@ static const Key keys[] = { + { MODKEY, XK_period, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, + { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, ++ { MODKEY, XK_r, homecanvas, {0} }, // Return to x:0, y:0 position ++ { MODKEY|ShiftMask, XK_Left, movecanvas, {.i = 0} }, // Move your position to left ++ { MODKEY|ShiftMask, XK_Right, movecanvas, {.i = 1} }, // Move your position to right ++ { MODKEY|ShiftMask, XK_Up, movecanvas, {.i = 2} }, // Move your position up ++ { MODKEY|ShiftMask, XK_Down, movecanvas, {.i = 3} }, // Move your position down ++ { MODKEY|ShiftMask, XK_d, centerwindow, {0} }, + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) +@@ -113,5 +122,7 @@ static const Button buttons[] = { + { ClkTagBar, 0, Button3, toggleview, {0} }, + { ClkTagBar, MODKEY, Button1, tag, {0} }, + { ClkTagBar, MODKEY, Button3, toggletag, {0} }, ++ { ClkRootWin, MODKEY|ShiftMask, Button1, manuallymovecanvas, {0} }, ++ { ClkClientWin, MODKEY|ShiftMask, Button1, manuallymovecanvas, {0} } + }; + +diff --git a/drw.o b/drw.o +new file mode 100644 +index 0000000..522c426 +Binary files /dev/null and b/drw.o differ +diff --git a/dwm.c b/dwm.c +index ab3a84c..c70ff19 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -56,6 +56,11 @@ + #define TAGMASK ((1 << LENGTH(tags)) - 1) + #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) + ++#if !WINDOWMAP ++ #undef WINDOWMAP ++ #define WINDOWMAP 1 ++#endif ++ + /* enums */ + enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ + enum { SchemeNorm, SchemeSel }; /* color schemes */ +@@ -96,6 +101,10 @@ struct Client { + Client *snext; + Monitor *mon; + Window win; ++ int saved_cx, saved_cy; ++ int saved_cw, saved_ch; ++ int was_on_canvas; ++ int ismapped; + }; + + typedef struct { +@@ -110,6 +119,11 @@ typedef struct { + void (*arrange)(Monitor *); + } Layout; + ++typedef struct { ++ int cx, cy; ++ int saved_cx, saved_cy; ++} CanvasOffset; ++ + struct Monitor { + char ltsymbol[16]; + float mfact; +@@ -129,6 +143,7 @@ struct Monitor { + Monitor *next; + Window barwin; + const Layout *lt[2]; ++ CanvasOffset *canvas; + }; + + typedef struct { +@@ -267,8 +282,11 @@ static Drw *drw; + static Monitor *mons, *selmon; + static Window root, wmcheckwin; + ++#include "infinitetags.h" ++ + /* configuration, allows nested code to access above variables */ + #include "config.h" ++#include "infinitetags.c" + + /* compile-time check if all tags fit into an unsigned int bit array. */ + struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; +@@ -643,6 +661,12 @@ createmon(void) + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); ++ m->canvas = ecalloc(LENGTH(tags), sizeof(CanvasOffset)); ++ unsigned int i; ++ for (i = 0; i < LENGTH(tags); i++){ ++ m->canvas[i].cx = 0; ++ m->canvas[i].cy = 0; ++ } + return m; + } + +@@ -719,6 +743,7 @@ drawbar(Monitor *m) + urg |= c->tags; + } + x = 0; ++ + for (i = 0; i < LENGTH(tags); i++) { + w = TEXTW(tags[i]); + drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); +@@ -732,6 +757,20 @@ drawbar(Monitor *m) + w = TEXTW(m->ltsymbol); + drw_setscheme(drw, scheme[SchemeNorm]); + x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); ++ ++ /* Draw the coordinates in canvas mode */ ++ if (selmon->lt[selmon->sellt]->arrange == NULL){ ++ int tagidx = getcurrenttag(m); ++ char coords[64]; ++ snprintf(coords, sizeof(coords), "[x%d y%d]", ++ m->canvas[tagidx].cx / COORDINATES_DIVISOR, ++ m->canvas[tagidx].cy / COORDINATES_DIVISOR); ++ w = TEXTW(coords); ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, coords, 0); ++ x += w; ++ } ++ + + if ((w = m->ww - tw - x) > bh) { + if (m->sel) { +@@ -744,6 +783,7 @@ drawbar(Monitor *m) + drw_rect(drw, x, 0, w, bh, 1, 1); + } + } ++ + drw_map(drw, m->barwin, 0, 0, m->ww, bh); + } + +@@ -856,7 +896,7 @@ focusstack(const Arg *arg) + } + if (c) { + focus(c); +- restack(selmon); ++ centerwindow(NULL); + } + } + +@@ -1510,10 +1550,32 @@ setfullscreen(Client *c, int fullscreen) + void + setlayout(const Arg *arg) + { ++ const Layout *temp_new_layout = (arg && arg->v) ? (Layout *)arg->v : selmon->lt[selmon->sellt ^ 1]; ++ if (temp_new_layout == selmon->lt[selmon->sellt]) return; ++ ++ const Layout *old_layout = selmon->lt[selmon->sellt]; ++ + if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) + selmon->sellt ^= 1; + if (arg && arg->v) + selmon->lt[selmon->sellt] = (Layout *)arg->v; ++ ++ const Layout *new_layout = selmon->lt[selmon->sellt]; ++ if (old_layout->arrange == NULL && new_layout->arrange != NULL) { ++ save_canvas_positions(selmon); ++ homecanvas(NULL); ++ Client *c; ++ for (c = selmon->clients; c; c = c->next) ++ if (!c->isfixed) c->isfloating = 0; ++ } ++ ++ if (new_layout->arrange == NULL) { ++ restore_canvas_positions(selmon); ++ Client *c; ++ for (c = selmon->clients; c; c = c->next) ++ c->isfloating = 1; ++ } ++ + strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); + if (selmon->sel) + arrange(selmon); +@@ -1670,6 +1732,21 @@ void + tag(const Arg *arg) + { + if (selmon->sel && arg->ui & TAGMASK) { ++ Client *c = selmon->sel; ++ unsigned int target_tag_mask = arg->ui & TAGMASK; ++ int i; ++ ++ for (i = 0; i < LENGTH(tags); i++) { ++ if (target_tag_mask & (1 << i)) { ++ c->saved_cx = selmon->canvas[i].cx + (selmon->ww - WIDTH(c)) / 2; ++ c->saved_cy = selmon->canvas[i].cy + (selmon->wh - HEIGHT(c)) / 2; ++ c->saved_cw = c->w; ++ c->saved_ch = c->h; ++ c->was_on_canvas = 1; ++ break; ++ } ++ } ++ + selmon->sel->tags = arg->ui & TAGMASK; + focus(NULL); + arrange(selmon); +@@ -2053,11 +2130,26 @@ updatewmhints(Client *c) + void + view(const Arg *arg) + { ++ if (selmon->lt[selmon->sellt]->arrange == NULL) ++ save_canvas_positions(selmon); ++ + if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) + return; + selmon->seltags ^= 1; /* toggle sel tagset */ + if (arg->ui & TAGMASK) + selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; ++ ++ int newtag = getcurrenttag(selmon); ++ if (selmon->lt[selmon->sellt]->arrange != NULL){ ++ selmon->canvas[newtag].cx = 0; ++ selmon->canvas[newtag].cy = 0; ++ } else { ++ restore_canvas_positions(selmon); ++ Client *c; ++ for (c = selmon->clients;c; c = c->next) ++ if (ISVISIBLE(c)) ++ c->isfloating = 1; ++ } + focus(NULL); + arrange(selmon); + } +diff --git a/dwm.o b/dwm.o +new file mode 100644 +index 0000000..65173d6 +Binary files /dev/null and b/dwm.o differ +diff --git a/infinitetags.c b/infinitetags.c +new file mode 100644 +index 0000000..46267ee +--- /dev/null ++++ b/infinitetags.c +@@ -0,0 +1,206 @@ ++int ++getcurrenttag(Monitor *m) { ++ unsigned int i; ++ for (i = 0; i < LENGTH(tags) && !(m->tagset[m->seltags] & (1 << i)); i++); ++ return i < LENGTH(tags) ? i : 0; ++} ++ ++void ++homecanvas(const Arg *arg) { ++ Client *c; ++ int tagidx = getcurrenttag(selmon); ++ int cx = selmon->canvas[tagidx].cx; ++ int cy = selmon->canvas[tagidx].cy; ++ ++ for (c = selmon->clients; c; c = c->next) { ++ if (c->tags & (1 << tagidx)) { ++ c->x -= cx; ++ c->y -= cy; ++ XMoveWindow(dpy, c->win, c->x, c->y); ++ } ++ } ++ ++ selmon->canvas[tagidx].cx = 0; ++ selmon->canvas[tagidx].cy = 0; ++ drawbar(selmon); ++ XFlush(dpy); ++} ++ ++void ++movecanvas(const Arg *arg) ++{ ++ if (selmon->lt[selmon->sellt]->arrange != NULL) ++ return; ++ if (selmon->sel && selmon->sel->isfullscreen) ++ return; ++ ++ int tagidx = getcurrenttag(selmon); ++ int dx = 0, dy = 0; ++ ++#ifndef MOVE_CANVAS_STEP ++#define MOVE_CANVAS_STEP 120 ++#endif ++ ++ switch(arg->i) { ++ case 0: dx = -MOVE_CANVAS_STEP; break; ++ case 1: dx = MOVE_CANVAS_STEP; break; ++ case 2: dy = -MOVE_CANVAS_STEP; break; ++ case 3: dy = MOVE_CANVAS_STEP; break; ++ } ++ ++ selmon->canvas[tagidx].cx -= dx; ++ selmon->canvas[tagidx].cy -= dy; ++ ++ Client *c; ++ for (c = selmon->clients; c; c = c->next) { ++ if (ISVISIBLE(c)) { ++ c->x -= dx; ++ c->y -= dy; ++ XMoveWindow(dpy, c->win, c->x, c->y); ++ } ++ } ++ ++ drawbar(selmon); ++} ++ ++void ++manuallymovecanvas(const Arg *arg) { ++ if (selmon->lt[selmon->sellt]->arrange != NULL) ++ return; ++ if (selmon->sel && selmon->sel->isfullscreen) ++ return; ++ int start_x, start_y; ++ Window dummy; ++ int di; ++ unsigned int dui; ++ int tagidx = getcurrenttag(selmon); ++#if LOCK_MOVE_RESIZE_REFRESH_RATE ++ Time lasttime = 0; ++#endif ++ ++ if (!XQueryPointer(dpy, root, &dummy, &dummy, &start_x, &start_y, &di, &di, &dui)) ++ return; ++ ++ if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, ++ None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) ++ return; ++ ++ XEvent ev; ++ do { ++ XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); ++ ++ switch (ev.type) { ++ case MotionNotify: ++ { ++#if LOCK_MOVE_RESIZE_REFRESH_RATE ++ if ((ev.xmotion.time - lasttime) <= (1000 / refreshrate)) ++ continue; ++ lasttime = ev.xmotion.time; ++#endif ++ int nx = ev.xmotion.x - start_x; ++ int ny = ev.xmotion.y - start_y; ++ ++ for (Client *c = selmon->clients; c; c = c->next) { ++ if (c->tags & (1 << tagidx)) { ++ c->x += nx; ++ c->y += ny; ++ XMoveWindow(dpy, c->win, c->x, c->y); ++ } ++ } ++ ++ selmon->canvas[tagidx].cx += nx; ++ selmon->canvas[tagidx].cy += ny; ++ drawbar(selmon); ++ start_x = ev.xmotion.x; ++ start_y = ev.xmotion.y; ++ } break; ++ } ++ } while (ev.type != ButtonRelease); ++ ++ XUngrabPointer(dpy, CurrentTime); ++} ++ ++void ++save_canvas_positions(Monitor *m) { ++ Client *c; ++ int tagidx = getcurrenttag(m); ++ ++ m->canvas[tagidx].saved_cx = m->canvas[tagidx].cx; ++ m->canvas[tagidx].saved_cy = m->canvas[tagidx].cy; ++ ++ for (c = m->clients; c; c = c->next) { ++ if (ISVISIBLE(c)) { ++ c->saved_cx = c->x + m->canvas[tagidx].cx; ++ c->saved_cy = c->y + m->canvas[tagidx].cy; ++ c->saved_cw = c->w; ++ c->saved_ch = c->h; ++ c->was_on_canvas = 1; ++ } ++ } ++} ++ ++void ++restore_canvas_positions(Monitor *m) { ++ Client *c; ++ int tagidx = getcurrenttag(m); ++ ++ m->canvas[tagidx].cx = m->canvas[tagidx].saved_cx; ++ m->canvas[tagidx].cy = m->canvas[tagidx].saved_cy; ++ ++ for (c = m->clients; c; c = c->next) { ++ if (ISVISIBLE(c) && c->was_on_canvas) { ++ c->isfloating = 1; ++ ++ int target_x = c->saved_cx - m->canvas[tagidx].cx; ++ int target_y = c->saved_cy - m->canvas[tagidx].cy; ++ ++ c->x = target_x; ++ c->y = target_y; ++ c->w = c->saved_cw; ++ c->h = c->saved_ch; ++ ++ XMoveResizeWindow(dpy, c->win, target_x, target_y, c->w, c->h); ++ ++ configure(c); ++ } ++ } ++ XSync(dpy, False); ++} ++ ++void ++centerwindow(const Arg *arg) ++{ ++ Client *c = (arg && arg->v) ? (Client *)arg->v : selmon->sel; ++ ++ if (!c || !c->mon || c->mon->lt[c->mon->sellt]->arrange != NULL) ++ return; ++ ++ Monitor *m = c->mon; ++ int tagidx = getcurrenttag(m); ++ ++ int screen_center_x = m->wx + (m->ww / 2); ++ int screen_center_y = m->wy + (m->wh / 2); ++ ++ int win_center_x = c->x + (c->w + 2 * c->bw) / 2; ++ int win_center_y = c->y + (c->h + 2 * c->bw) / 2; ++ ++ int dx = screen_center_x - win_center_x; ++ int dy = screen_center_y - win_center_y; ++ ++ if (dx == 0 && dy == 0) ++ return; ++ ++ Client *tmp; ++ for (tmp = m->clients; tmp; tmp = tmp->next) { ++ if (ISVISIBLE(tmp)) { ++ tmp->x += dx; ++ tmp->y += dy; ++ XMoveWindow(dpy, tmp->win, tmp->x, tmp->y); ++ } ++ } ++ ++ m->canvas[tagidx].cx += dx; ++ m->canvas[tagidx].cy += dy; ++ ++ drawbar(m); ++} +diff --git a/infinitetags.h b/infinitetags.h +new file mode 100644 +index 0000000..adcb69e +--- /dev/null ++++ b/infinitetags.h +@@ -0,0 +1,7 @@ ++static void movecanvas(const Arg *arg); ++static void manuallymovecanvas(const Arg *arg); ++static void homecanvas(const Arg *arg); ++static int getcurrenttag(Monitor *m); ++static void save_canvas_positions(Monitor *m); ++static void restore_canvas_positions(Monitor *m); ++static void centerwindow(const Arg *arg); +diff --git a/util.o b/util.o +new file mode 100644 +index 0000000..34da63d +Binary files /dev/null and b/util.o differ diff --git a/dwm.suckless.org/patches/canvas/index.md b/dwm.suckless.org/patches/canvas/index.md @@ -0,0 +1,19 @@ +canvas +=== + +Description +----------- +Canvas is a patch that implements the "canvas" mode +from [vxwm](https://codeberg.org/wh1tepearl/vxwm). + +See [here](https://github.com/x3hy/canvas) for more +information. + +Download +-------- +* [dwm-canvas-6.2.diff](dwm-canvas-6.2.diff) + +Authors +------- +* Lucy Adams - <x3hy@protonmail.com> +* "wh1tepearl" - <https://codeberg.org/wh1tepearl/vxwm> (vxwm creator)