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/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/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);