diff options
| author | Bear <bear@bengtsson.win> | 2021-12-27 09:29:58 +0000 |
|---|---|---|
| committer | Bear <bear@bengtsson.win> | 2021-12-27 09:29:58 +0000 |
| commit | 69262b01ced79c2d776fab9b889926d1816a1e7a (patch) | |
| tree | f304cd6fa8734e83a7772d07dc9b484781565155 /patch | |
Added DWM
Diffstat (limited to 'patch')
246 files changed, 13799 insertions, 0 deletions
diff --git a/patch/1627554433214.webm b/patch/1627554433214.webm Binary files differnew file mode 100644 index 0000000..8c4a62d --- /dev/null +++ b/patch/1627554433214.webm diff --git a/patch/aspectresize.c b/patch/aspectresize.c new file mode 100644 index 0000000..cdd5afd --- /dev/null +++ b/patch/aspectresize.c @@ -0,0 +1,25 @@ +void +aspectresize(const Arg *arg) +{ + /* only floating windows can be moved */ + Client *c; + c = selmon->sel; + float ratio; + int w, h,nw, nh; + + if (!c || !arg) + return; + if (selmon->lt[selmon->sellt]->arrange && !c->isfloating) + return; + + ratio = (float)c->w / (float)c->h; + h = arg->i; + w = (int)(ratio * h); + + nw = c->w + w; + nh = c->h + h; + + XRaiseWindow(dpy, c->win); + resize(c, c->x, c->y, nw, nh, True); +} + diff --git a/patch/aspectresize.h b/patch/aspectresize.h new file mode 100644 index 0000000..4ac7cbb --- /dev/null +++ b/patch/aspectresize.h @@ -0,0 +1,2 @@ +static void aspectresize(const Arg *arg); + diff --git a/patch/attachx.c b/patch/attachx.c new file mode 100644 index 0000000..9e06b94 --- /dev/null +++ b/patch/attachx.c @@ -0,0 +1,43 @@ +void +attachx(Client *c) +{ + #if ATTACHABOVE_PATCH + Client *at; + if (!(c->mon->sel == NULL || c->mon->sel == c->mon->clients || c->mon->sel->isfloating)) { + for (at = c->mon->clients; at->next != c->mon->sel; at = at->next); + c->next = at->next; + at->next = c; + return; + } + #elif ATTACHASIDE_PATCH + Client *at; + unsigned int n; + + for (at = c->mon->clients, n = 0; at; at = at->next) + if (!at->isfloating && ISVISIBLEONTAG(at, c->tags)) + if (++n >= c->mon->nmaster) + break; + + if (at && c->mon->nmaster) { + c->next = at->next; + at->next = c; + return; + } + #elif ATTACHBELOW_PATCH + if (!(c->mon->sel == NULL || c->mon->sel == c || c->mon->sel->isfloating)) { + c->next = c->mon->sel->next; + c->mon->sel->next = c; + return; + } + #elif ATTACHBOTTOM_PATCH + Client *at; + for (at = c->mon->clients; at && at->next; at = at->next); + if (at) { + at->next = c; + c->next = NULL; + return; + } + #endif + attach(c); // master (default) +} + diff --git a/patch/attachx.h b/patch/attachx.h new file mode 100644 index 0000000..e522d27 --- /dev/null +++ b/patch/attachx.h @@ -0,0 +1,2 @@ +static void attachx(Client *c); + diff --git a/patch/autostart.c b/patch/autostart.c new file mode 100644 index 0000000..03477bb --- /dev/null +++ b/patch/autostart.c @@ -0,0 +1,84 @@ +void +runautostart(void) +{ + char *pathpfx; + char *path; + char *xdgdatahome; + char *home; + + if ((home = getenv("HOME")) == NULL) + /* this is almost impossible */ + return; + + /* if $XDG_DATA_HOME is defined, use $XDG_DATA_HOME/dwm, + * otherwise use ~/.local/share/dwm as autostart script directory + */ + if ((xdgdatahome = getenv("XDG_DATA_HOME")) != NULL) { + /* space for path segments, separators and nul */ + if ((pathpfx = malloc(strlen(xdgdatahome) + strlen(dwmdir) + 2)) == NULL) + return; + + if (sprintf(pathpfx, "%s/%s", xdgdatahome, dwmdir) <= 0) { + free(pathpfx); + return; + } + } else { + /* space for path segments, separators and nul */ + if ((pathpfx = malloc(strlen(home) + strlen(localshare) + strlen(dwmdir) + 3)) == NULL) + return; + + if (sprintf(pathpfx, "%s/%s/%s", home, localshare, dwmdir) < 0) { + free(pathpfx); + return; + } + } + + /* check if the autostart script directory exists */ + struct stat sb; + + if (! (stat(pathpfx, &sb) == 0 && S_ISDIR(sb.st_mode))) { + /* the XDG conformant path does not exist or are not directories + * so we try ~/.dwm instead + */ + if (realloc(pathpfx, strlen(home) + strlen(dwmdir) + 3) == NULL) { + free(pathpfx); + return; + } + + if (sprintf(pathpfx, "%s/.%s", home, dwmdir) <= 0) { + free(pathpfx); + return; + } + } + + /* try the blocking script first */ + if ((path = malloc(strlen(pathpfx) + strlen(autostartblocksh) + 2)) == NULL) { + free(pathpfx); + return; + } else + if (sprintf(path, "%s/%s", pathpfx, autostartblocksh) <= 0) { + free(path); + free(pathpfx); + } + + if (access(path, X_OK) == 0) + system(path); + + /* now the non-blocking script */ + if ((path = realloc(path, strlen(pathpfx) + strlen(autostartsh) + 4)) == NULL) { + free(pathpfx); + free(path); + return; + } else + if (sprintf(path, "%s/%s", pathpfx, autostartsh) <= 0) { + free(path); + free(pathpfx); + } + + if (access(path, X_OK) == 0) { + system(strcat(path, " &")); + free(pathpfx); + free(path); + } +} + diff --git a/patch/autostart.h b/patch/autostart.h new file mode 100644 index 0000000..db29377 --- /dev/null +++ b/patch/autostart.h @@ -0,0 +1,2 @@ +static void runautostart(void); + diff --git a/patch/bar_alpha.c b/patch/bar_alpha.c new file mode 100644 index 0000000..465f6f2 --- /dev/null +++ b/patch/bar_alpha.c @@ -0,0 +1,43 @@ + +static int useargb = 0; +static Visual *visual; +static int depth; +static Colormap cmap; + +void +xinitvisual() +{ + XVisualInfo *infos; + XRenderPictFormat *fmt; + int nitems; + int i; + + XVisualInfo tpl = { + .screen = screen, + .depth = 32, + .class = TrueColor + }; + long masks = VisualScreenMask | VisualDepthMask | VisualClassMask; + + infos = XGetVisualInfo(dpy, masks, &tpl, &nitems); + visual = NULL; + for (i = 0; i < nitems; i ++) { + fmt = XRenderFindVisualFormat(dpy, infos[i].visual); + if (fmt->type == PictTypeDirect && fmt->direct.alphaMask) { + visual = infos[i].visual; + depth = infos[i].depth; + cmap = XCreateColormap(dpy, root, visual, AllocNone); + useargb = 1; + break; + } + } + + XFree(infos); + + if (!visual) { + visual = DefaultVisual(dpy, screen); + depth = DefaultDepth(dpy, screen); + cmap = DefaultColormap(dpy, screen); + } +} + diff --git a/patch/bar_alpha.h b/patch/bar_alpha.h new file mode 100644 index 0000000..1c2a012 --- /dev/null +++ b/patch/bar_alpha.h @@ -0,0 +1,4 @@ +#define OPAQUE 0xffU + +static void xinitvisual(); + diff --git a/patch/bar_alternativetags.c b/patch/bar_alternativetags.c new file mode 100644 index 0000000..bc800f2 --- /dev/null +++ b/patch/bar_alternativetags.c @@ -0,0 +1,7 @@ +void +togglealttag() +{ + selmon->alttag = !selmon->alttag; + drawbar(selmon); +} + diff --git a/patch/bar_alternativetags.h b/patch/bar_alternativetags.h new file mode 100644 index 0000000..0f7a25a --- /dev/null +++ b/patch/bar_alternativetags.h @@ -0,0 +1,2 @@ +static void togglealttag(); + diff --git a/patch/bar_anybar.c b/patch/bar_anybar.c new file mode 100644 index 0000000..d9bdb27 --- /dev/null +++ b/patch/bar_anybar.c @@ -0,0 +1,94 @@ +void +managealtbar(Window win, XWindowAttributes *wa) +{ + Monitor *m; + Bar *bar; + int i = 0; + if (!(m = recttomon(wa->x, wa->y, wa->width, wa->height))) + return; + for (bar = m->bar; bar && bar->win && bar->next; bar = bar->next); // find last bar + if (!bar) { + bar = m->bar = ecalloc(1, sizeof(Bar)); + bar->topbar = topbar; + } else if (bar && bar->win) { + i = bar->idx + 1; + bar->next = ecalloc(1, sizeof(Bar)); + #if BAR_ANYBAR_TOP_AND_BOTTOM_BARS_PATCH + bar->next->topbar = !bar->topbar; + #else + bar->next->topbar = topbar; + #endif // BAR_ANYBAR_TOP_AND_BOTTOM_BARS_PATCH + bar = bar->next; + } + bar->external = 1; + bar->showbar = 1; + bar->mon = m; + bar->idx = i; + bar->borderpx = 0; + bar->win = win; + bar->bw = wa->width; + bar->bh = wa->height; + updatebarpos(m); + arrange(m); + XSelectInput(dpy, win, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); + XMapWindow(dpy, win); + XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + arrange(selmon); + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &win, 1); +} + +void +spawnbar() +{ + if (*altbarcmd) + system(altbarcmd); +} + +void +unmanagealtbar(Window w) +{ + Monitor *m = wintomon(w); + Bar *bar, *next, *prev = NULL; + + if (!m) + return; + + for (bar = m->bar; bar && bar->win; bar = next) { + next = bar->next; + if (bar->win == w) { + if (prev) + prev->next = next; + else + m->bar = next; + free(bar); + break; + } + prev = bar; + } + updatebarpos(m); + arrange(m); +} + +int +wmclasscontains(Window win, const char *class, const char *name) +{ + XClassHint ch = { NULL, NULL }; + int res = 1; + + if (XGetClassHint(dpy, win, &ch)) { + if (ch.res_name && strstr(ch.res_name, name) == NULL) + res = 0; + if (ch.res_class && strstr(ch.res_class, class) == NULL) + res = 0; + } else + res = 0; + + if (ch.res_class) + XFree(ch.res_class); + if (ch.res_name) + XFree(ch.res_name); + + return res; +} + diff --git a/patch/bar_anybar.h b/patch/bar_anybar.h new file mode 100644 index 0000000..980f5e2 --- /dev/null +++ b/patch/bar_anybar.h @@ -0,0 +1,5 @@ +static void managealtbar(Window win, XWindowAttributes *wa); +static void spawnbar(); +static void unmanagealtbar(Window w); +static int wmclasscontains(Window win, const char *class, const char *name); + diff --git a/patch/bar_awesomebar.c b/patch/bar_awesomebar.c new file mode 100644 index 0000000..b02c6e7 --- /dev/null +++ b/patch/bar_awesomebar.c @@ -0,0 +1,90 @@ +int +width_awesomebar(Bar *bar, BarArg *a) +{ + return a->w; +} + +int +draw_awesomebar(Bar *bar, BarArg *a) +{ + int n = 0, scm, remainder = 0, tabw, pad; + unsigned int i; + #if BAR_TITLE_LEFT_PAD_PATCH && BAR_TITLE_RIGHT_PAD_PATCH + int x = a->x + lrpad / 2, w = a->w - lrpad; + #elif BAR_TITLE_LEFT_PAD_PATCH + int x = a->x + lrpad / 2, w = a->w - lrpad / 2; + #elif BAR_TITLE_RIGHT_PAD_PATCH + int x = a->x, w = a->w - lrpad / 2; + #else + int x = a->x, w = a->w; + #endif // BAR_TITLE_LEFT_PAD_PATCH | BAR_TITLE_RIGHT_PAD_PATCH + + Client *c; + for (c = bar->mon->clients; c; c = c->next) + if (ISVISIBLE(c)) + n++; + + if (n > 0) { + remainder = w % n; + tabw = w / n; + for (i = 0, c = bar->mon->clients; c; c = c->next, i++) { + if (!ISVISIBLE(c)) + continue; + if (bar->mon->sel == c && HIDDEN(c)) + scm = SchemeHidSel; + else if (HIDDEN(c)) + scm = SchemeHidNorm; + else if (bar->mon->sel == c) + scm = SchemeTitleSel; + else + scm = SchemeTitleNorm; + + pad = lrpad / 2; + #if BAR_CENTEREDWINDOWNAME_PATCH + if (TEXTW(c->name) < tabw) + pad = (tabw - TEXTW(c->name) + lrpad) / 2; + #endif // BAR_CENTEREDWINDOWNAME_PATCH + + drw_setscheme(drw, scheme[scm]); + + #if BAR_WINICON_PATCH + drw_text(drw, x, a->y, tabw + (i < remainder ? 1 : 0), a->h, pad + (c->icon ? c->icon->width + ICONSPACING : 0), c->name, 0, False); + if (c->icon) + drw_img(drw, x + pad, a->y + (a->h - c->icon->height) / 2, c->icon, tmpicon); + #else + drw_text(drw, x, a->y, tabw + (i < remainder ? 1 : 0), a->h, pad, c->name, 0, False); + #endif // BAR_WINICON_PATCH + + drawstateindicator(c->mon, c, 1, x, a->y, tabw + (i < remainder ? 1 : 0), a->h, 0, 0, c->isfixed); + x += tabw + (i < remainder ? 1 : 0); + } + } + return n; +} + +int +click_awesomebar(Bar *bar, Arg *arg, BarArg *a) +{ + int x = 0, n = 0; + Client *c; + + for (c = bar->mon->clients; c; c = c->next) + if (ISVISIBLE(c)) + n++; + + c = bar->mon->clients; + + do { + if (!c || !ISVISIBLE(c)) + continue; + else + x += (1.0 / (double)n) * a->w; + } while (c && a->x > x && (c = c->next)); + + if (c) { + arg->v = c; + return ClkWinTitle; + } + return -1; +} + diff --git a/patch/bar_awesomebar.h b/patch/bar_awesomebar.h new file mode 100644 index 0000000..5ea51f6 --- /dev/null +++ b/patch/bar_awesomebar.h @@ -0,0 +1,4 @@ +static int width_awesomebar(Bar *bar, BarArg *a); +static int draw_awesomebar(Bar *bar, BarArg *a); +static int click_awesomebar(Bar *bar, Arg *arg, BarArg *a); + diff --git a/patch/bar_dwmblocks.c b/patch/bar_dwmblocks.c new file mode 100644 index 0000000..25268c5 --- /dev/null +++ b/patch/bar_dwmblocks.c @@ -0,0 +1,51 @@ +static int statussig; +pid_t statuspid = -1; + +pid_t +getstatusbarpid() +{ + char buf[32], *str = buf, *c; + FILE *fp; + + if (statuspid > 0) { + snprintf(buf, sizeof(buf), "/proc/%u/cmdline", statuspid); + if ((fp = fopen(buf, "r"))) { + fgets(buf, sizeof(buf), fp); + while ((c = strchr(str, '/'))) + str = c + 1; + fclose(fp); + if (!strcmp(str, STATUSBAR)) + return statuspid; + } + } + if (!(fp = popen("pidof -s "STATUSBAR, "r"))) + return -1; + fgets(buf, sizeof(buf), fp); + pclose(fp); + return strtol(buf, NULL, 10); +} + +void +sigstatusbar(const Arg *arg) +{ + union sigval sv; + + if (!statussig) + return; + if ((statuspid = getstatusbarpid()) <= 0) + return; + + #if BAR_DWMBLOCKS_SIGUSR1_PATCH + sv.sival_int = (statussig << 8) | arg->i; + if (sigqueue(statuspid, SIGUSR1, sv) == -1) { + if (errno == ESRCH) { + if (!getstatusbarpid()) + sigqueue(statuspid, SIGUSR1, sv); + } + } + #else + sv.sival_int = arg->i; + sigqueue(statuspid, SIGRTMIN+statussig, sv); + #endif // BAR_DWMBLOCKS_SIGUSR1_PATCH +} + diff --git a/patch/bar_dwmblocks.h b/patch/bar_dwmblocks.h new file mode 100644 index 0000000..4db9467 --- /dev/null +++ b/patch/bar_dwmblocks.h @@ -0,0 +1,3 @@ +static int getstatusbarpid(); +static void sigstatusbar(const Arg *arg); + diff --git a/patch/bar_ewmhtags.c b/patch/bar_ewmhtags.c new file mode 100644 index 0000000..d86fd88 --- /dev/null +++ b/patch/bar_ewmhtags.c @@ -0,0 +1,53 @@ +void +setcurrentdesktop(void) +{ + long data[] = { 0 }; + XChangeProperty(dpy, root, netatom[NetCurrentDesktop], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 1); +} + +void +setdesktopnames(void) +{ + int i; + XTextProperty text; + char *tags[NUMTAGS]; + for (i = 0; i < NUMTAGS; i++) + tags[i] = tagicon(selmon, i); + Xutf8TextListToTextProperty(dpy, tags, NUMTAGS, XUTF8StringStyle, &text); + XSetTextProperty(dpy, root, &text, netatom[NetDesktopNames]); +} + +void +setfloatinghint(Client *c) +{ + Atom target = XInternAtom(dpy, "_IS_FLOATING", 0); + unsigned int floating[1] = {c->isfloating}; + XChangeProperty(dpy, c->win, target, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)floating, 1); +} + +void +setnumdesktops(void) +{ + long data[] = { NUMTAGS }; + XChangeProperty(dpy, root, netatom[NetNumberOfDesktops], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 1); +} + +void +setviewport(void) +{ + long data[] = { 0, 0 }; + XChangeProperty(dpy, root, netatom[NetDesktopViewport], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 2); +} + +void +updatecurrentdesktop(void) +{ + long rawdata[] = { selmon->tagset[selmon->seltags] }; + int i = 0; + while (*rawdata >> (i + 1)) { + i++; + } + long data[] = { i }; + XChangeProperty(dpy, root, netatom[NetCurrentDesktop], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 1); +} + diff --git a/patch/bar_ewmhtags.h b/patch/bar_ewmhtags.h new file mode 100644 index 0000000..4d6a74b --- /dev/null +++ b/patch/bar_ewmhtags.h @@ -0,0 +1,7 @@ +static void setcurrentdesktop(void); +static void setdesktopnames(void); +static void setfloatinghint(Client *c); +static void setnumdesktops(void); +static void setviewport(void); +static void updatecurrentdesktop(void); + diff --git a/patch/bar_fancybar.c b/patch/bar_fancybar.c new file mode 100644 index 0000000..10bac83 --- /dev/null +++ b/patch/bar_fancybar.c @@ -0,0 +1,88 @@ +int +width_fancybar(Bar *bar, BarArg *a) +{ + return a->w; +} + +int +draw_fancybar(Bar *bar, BarArg *a) +{ + int ftw, mw, ew = 0, n = 0; + unsigned int i; + Client *c; + Monitor *m = bar->mon; + + #if BAR_TITLE_LEFT_PAD_PATCH && BAR_TITLE_RIGHT_PAD_PATCH + int x = a->x + lrpad / 2, w = a->w - lrpad; + #elif BAR_TITLE_LEFT_PAD_PATCH + int x = a->x + lrpad / 2, w = a->w - lrpad / 2; + #elif BAR_TITLE_RIGHT_PAD_PATCH + int x = a->x, w = a->w - lrpad / 2; + #else + int x = a->x, w = a->w; + #endif // BAR_TITLE_LEFT_PAD_PATCH | BAR_TITLE_RIGHT_PAD_PATCH + + for (c = m->clients; c; c = c->next) { + if (ISVISIBLE(c)) + n++; + } + + if (n > 0) { + ftw = TEXTW(m->sel->name); + #if BAR_WINICON_PATCH + if (m->sel->icon) + ftw += m->sel->icon->width + ICONSPACING; + #endif // BAR_WINICON_PATCH + mw = (ftw >= w || n == 1) ? 0 : (w - ftw) / (n - 1); + + i = 0; + + for (c = m->clients; c; c = c->next) { + if (!ISVISIBLE(c) || c == m->sel) + continue; + ftw = TEXTW(c->name); + #if BAR_WINICON_PATCH + if (c->icon) + ftw += c->icon->width + ICONSPACING; + #endif // BAR_WINICON_PATCH + if (ftw < mw) + ew += (mw - ftw); + else + i++; + } + + if (i > 0) + mw += ew / i; + + for (c = m->clients; c; c = c->next) { + if (!ISVISIBLE(c)) + continue; + ftw = MIN(m->sel == c ? w : mw, TEXTW(c->name)); + drw_setscheme(drw, scheme[m->sel == c ? SchemeTitleSel : SchemeTitleNorm]); + if (ftw > 0) { /* trap special handling of 0 in drw_text */ + + drw_text(drw, x, a->y, ftw, a->h, lrpad / 2, c->name, 0, False); + + #if BAR_WINICON_PATCH + drw_text(drw, x, a->y, ftw, a->h, lrpad / 2 + (c->icon ? c->icon->width + ICONSPACING : 0), c->name, 0, False); + if (c->icon) + drw_img(drw, x + lrpad / 2, a->y + (a->h - c->icon->height) / 2, c->icon, tmpicon); + #else + drw_text(drw, x, a->y, ftw, a->h, lrpad / 2, c->name, 0, False); + #endif // BAR_WINICON_PATCH + + } + drawstateindicator(c->mon, c, 1, x, a->y, ftw, a->h, 0, 0, c->isfixed); + x += ftw; + w -= ftw; + } + } + return n; +} + +int +click_fancybar(Bar *bar, Arg *arg, BarArg *a) +{ + return ClkWinTitle; +} + diff --git a/patch/bar_fancybar.h b/patch/bar_fancybar.h new file mode 100644 index 0000000..473cca3 --- /dev/null +++ b/patch/bar_fancybar.h @@ -0,0 +1,4 @@ +static int width_fancybar(Bar *bar, BarArg *a); +static int draw_fancybar(Bar *bar, BarArg *a); +static int click_fancybar(Bar *bar, Arg *arg, BarArg *a); + diff --git a/patch/bar_flexwintitle.c b/patch/bar_flexwintitle.c new file mode 100644 index 0000000..2f59240 --- /dev/null +++ b/patch/bar_flexwintitle.c @@ -0,0 +1,446 @@ +/* Flexwintitle properties, you can override these in your config.h if you want. */ +#ifndef FLEXWINTITLE_BORDERS +#define FLEXWINTITLE_BORDERS 1 // 0 = off, 1 = on +#endif +#ifndef FLEXWINTITLE_SHOWFLOATING +#define FLEXWINTITLE_SHOWFLOATING 0 // whether to show titles for floating windows, hidden clients are always shown +#endif +#ifndef FLEXWINTITLE_MASTERWEIGHT +#define FLEXWINTITLE_MASTERWEIGHT 9 // master weight compared to stack, hidden and floating window titles +#endif +#ifndef FLEXWINTITLE_STACKWEIGHT +#define FLEXWINTITLE_STACKWEIGHT 3 // stack weight compared to master, hidden and floating window titles +#endif +#ifndef FLEXWINTITLE_HIDDENWEIGHT +#define FLEXWINTITLE_HIDDENWEIGHT 0 // hidden window title weight +#endif +#ifndef FLEXWINTITLE_FLOATWEIGHT +#define FLEXWINTITLE_FLOATWEIGHT 0 // floating window title weight, set to 0 to not show floating windows +#endif + +#define SCHEMEFOR(c) getschemefor(m, c, groupactive == c) + +enum { GRP_NOSELECTION, GRP_MASTER, GRP_STACK1, GRP_STACK2, GRP_FLOAT, GRP_HIDDEN }; + +int +width_flexwintitle(Bar *bar, BarArg *a) +{ + return a->w; +} + +int +draw_flexwintitle(Bar *bar, BarArg *a) +{ + drw_rect(drw, a->x, a->y, a->w, a->h, 1, 1); + return flextitlecalculate(bar->mon, a->x, a->w, -1, flextitledraw, NULL, a); +} + +int +click_flexwintitle(Bar *bar, Arg *arg, BarArg *a) +{ + flextitlecalculate(bar->mon, 0, a->w, a->x, flextitleclick, arg, a); + return ClkWinTitle; +} + +Client * +flextitledrawarea(Monitor *m, Client *c, int x, int r, int w, int max_clients, int scheme, int draw_tiled, int draw_hidden, int draw_floating, + int passx, void(*tabfn)(Monitor *, Client *, int, int, int, int, Arg *arg, BarArg *barg), Arg *arg, BarArg *barg) +{ + int i; + for (i = 0; c && i < max_clients; c = c->next) { + if ( + ISVISIBLE(c) && + ( + (draw_tiled && !c->isfloating && !HIDDEN(c)) || + (draw_floating && c->isfloating && !HIDDEN(c)) || + (draw_hidden && HIDDEN(c)) + ) + ) { + tabfn(m, c, passx, x, w + (i < r ? 1 : 0), scheme, arg, barg); + x += w + (i < r ? 1 : 0); + i++; + } + } + return c; +} + +int +getschemefor(Monitor *m, int group, int activegroup) +{ + switch (group) { + case GRP_NOSELECTION: + case GRP_MASTER: + case GRP_STACK1: + case GRP_STACK2: + #if BSTACK_LAYOUT + if (m->lt[m->sellt]->arrange == &bstack) + return (activegroup ? SchemeFlexActLTR : SchemeFlexInaLTR); + #endif // BSTACK_LAYOUT + #if BSTACKHORIZ_LAYOUT + if (m->lt[m->sellt]->arrange == &bstackhoriz) { + if (group == GRP_MASTER) + return (activegroup ? SchemeFlexActLTR : SchemeFlexInaLTR); + else + return (activegroup ? SchemeFlexActTTB : SchemeFlexInaTTB); + } + #endif // BSTACKHORIZ_LAYOUT + #if CENTEREDMASTER_LAYOUT + if (m->lt[m->sellt]->arrange == ¢eredmaster) + return (activegroup ? SchemeFlexActTTB : SchemeFlexInaTTB); + #endif // CENTEREDMASTER_LAYOUT + #if CENTEREDFLOATINGMASTER_LAYOUT + if (m->lt[m->sellt]->arrange == ¢eredfloatingmaster) + return (activegroup ? SchemeFlexActLTR : SchemeFlexInaLTR); + #endif // CENTEREDFLOATINGMASTER_LAYOUT + #if COLUMNS_LAYOUT + if (m->lt[m->sellt]->arrange == &col) { + if (group == GRP_MASTER) + return (activegroup ? SchemeFlexActLTR : SchemeFlexInaLTR); + else + return (activegroup ? SchemeFlexActTTB : SchemeFlexInaTTB); + } + #endif // COLUMNS_LAYOUT + #if DECK_LAYOUT + if (m->lt[m->sellt]->arrange == &deck) { + if (group == GRP_MASTER) + return (activegroup ? SchemeFlexActTTB : SchemeFlexInaTTB); + else + return (activegroup ? SchemeFlexActMONO : SchemeFlexInaMONO); + } + #endif // DECK_LAYOUT + #if FIBONACCI_DWINDLE_LAYOUT + if (m->lt[m->sellt]->arrange == &dwindle) + return (activegroup ? SchemeFlexActDWDL : SchemeFlexInaDWDL); + #endif // FIBONACCI_DWINDLE_LAYOUT + #if FIBONACCI_SPIRAL_LAYOUT + if (m->lt[m->sellt]->arrange == &spiral) + return (activegroup ? SchemeFlexActSPRL : SchemeFlexInaSPRL); + #endif // FIBONACCI_SPIRAL_LAYOUT + #if FLEXTILE_DELUXE_LAYOUT + if (m->lt[m->sellt]->arrange == &flextile) + return (activegroup ? SchemeFlexActTTB + m->ltaxis[group] : SchemeFlexInaTTB + m->ltaxis[group]); + #endif // FLEXTILE_DELUXE_LAYOUT + #if GAPPLESSGRID_LAYOUT + if (m->lt[m->sellt]->arrange == &gaplessgrid) + return (activegroup ? SchemeFlexActGRID : SchemeFlexInaGRID); + #endif // GAPPLESSGRID_LAYOUT + #if GRIDMODE_LAYOUT + if (m->lt[m->sellt]->arrange == &grid) + return (activegroup ? SchemeFlexActGRDM : SchemeFlexInaGRDM); + #endif // GRIDMODE_LAYOUT + #if HORIZGRID_LAYOUT + if (m->lt[m->sellt]->arrange == &horizgrid) + return (activegroup ? SchemeFlexActHGRD : SchemeFlexInaHGRD); + #endif // HORIZGRID_LAYOUT + #if NROWGRID_LAYOUT + if (m->lt[m->sellt]->arrange == &nrowgrid) + return (activegroup ? SchemeFlexActGRD1 : SchemeFlexInaGRD1); + #endif // NROWGRID_LAYOUT + #if TILE_LAYOUT + if (m->lt[m->sellt]->arrange == &tile) + return (activegroup ? SchemeFlexActTTB : SchemeFlexInaTTB); + #endif // TILE_LAYOUT + #if MONOCLE_LAYOUT + if (m->lt[m->sellt]->arrange == &monocle) + return (activegroup ? SchemeFlexActMONO : SchemeFlexInaMONO); + #endif // MONOCLE_LAYOUT + return SchemeTitleNorm; + case GRP_HIDDEN: + return SchemeHidNorm; + case GRP_FLOAT: + return (activegroup ? SchemeFlexActFloat : SchemeFlexInaFloat); + } + return SchemeTitleNorm; +} + +int +getselschemefor(int scheme) +{ + if (scheme == SchemeFlexActFloat || scheme == SchemeFlexInaFloat) + return SchemeFlexSelFloat; + if (scheme >= SchemeFlexInaTTB) + return scheme + SchemeFlexInaTTB - SchemeFlexActTTB; + if (scheme >= SchemeFlexActTTB) + return scheme + SchemeFlexSelTTB - SchemeFlexActTTB; + return SchemeTitleSel; +} + +void +flextitledraw(Monitor *m, Client *c, int unused, int x, int w, int tabscheme, Arg *arg, BarArg *barg) +{ + if (!c) + return; + int i, nclienttags = 0, nviewtags = 0, pad = lrpad / 2; + int clientscheme = ( + c == selmon->sel && HIDDEN(c) + ? SchemeHidSel + : HIDDEN(c) + ? SchemeHidNorm + : c == selmon->sel + ? getselschemefor(tabscheme) + : c->isurgent + ? SchemeUrg + : tabscheme + ); + drw_setscheme(drw, scheme[clientscheme]); + XSetWindowBorder(dpy, c->win, scheme[clientscheme][ColBorder].pixel); + if (w <= TEXTW("A") - lrpad + pad) // reduce text padding if wintitle is too small + pad = (w - TEXTW("A") + lrpad < 0 ? 0 : (w - TEXTW("A") + lrpad) / 2); + #if BAR_CENTEREDWINDOWNAME_PATCH + else if (TEXTW(c->name) < w) + pad = (w - TEXTW(c->name) + lrpad) / 2; + #endif // BAR_CENTEREDWINDOWNAME_PATCH + + #if BAR_WINICON_PATCH + drw_text(drw, x, barg->y, w, barg->h, pad + (c->icon ? c->icon->width + ICONSPACING : 0), c->name, 0, False); + if (c->icon) + drw_img(drw, x + pad, barg->y + (barg->h - c->icon->height) / 2, c->icon, tmpicon); + #else + drw_text(drw, x, barg->y, w, barg->h, pad, c->name, 0, False); + #endif // BAR_WINICON_PATCH + + drawstateindicator(m, c, 1, x + 2, barg->y, w, barg->h, 0, 0, 0); + + if (FLEXWINTITLE_BORDERS) { + XSetForeground(drw->dpy, drw->gc, scheme[SchemeSel][ColBorder].pixel); + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, barg->y, 3, barg->h); + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x + w - (x + w >= barg->w ? 1 : 0), barg->y, 3, barg->h); + } + /* Optional tags icons */ + for (i = 0; i < NUMTAGS; i++) { + if ((m->tagset[m->seltags] >> i) & 1) + nviewtags++; + if ((c->tags >> i) & 1) + nclienttags++; + } + + if (TAGSINDICATOR == 2 || nclienttags > 1 || nviewtags > 1) + drawindicator(m, c, 1, x, barg->y, w, barg->h, 0, 0, 0, INDICATOR_RIGHT_TAGS); +} + +#ifndef HIDDEN +#define HIDDEN(C) 0 +#endif + +void +flextitleclick(Monitor *m, Client *c, int passx, int x, int w, int unused, Arg *arg, BarArg *barg) +{ + if (passx >= x && passx <= x + w) + arg->v = c; +} + +int +flextitlecalculate( + Monitor *m, int offx, int tabw, int passx, + void(*tabfn)(Monitor *, Client *, int, int, int, int, Arg *arg, BarArg *barg), + Arg *arg, BarArg *barg +) { + Client *c; + int n, center = 0, mirror = 0, fixed = 0; // layout configuration + int clientsnmaster = 0, clientsnstack = 0, clientsnfloating = 0, clientsnhidden = 0; + int i, w, r, num = 0, den, fulllayout = 0; + int clientsnstack2 = 0; + int groupactive = 0; + int selidx = 0; + int dualstack = 0; + int rw, rr; + + int mas_x = offx, st1_x = offx, st2_x = offx, hid_x = offx, flt_x = offx; + int mas_w, st1_w, st2_w, hid_w; + + for (i = 0, c = m->clients; c; c = c->next) { + if (!ISVISIBLE(c)) + continue; + if (HIDDEN(c)) { + if (FLEXWINTITLE_HIDDENWEIGHT) + clientsnhidden++; + continue; + } + + if (c->isfloating) { + if (FLEXWINTITLE_FLOATWEIGHT) + clientsnfloating++; + continue; + } + + if (m->sel == c) + selidx = i; + + if (i < m->nmaster) + clientsnmaster++; + #if FLEXTILE_DELUXE_LAYOUT + else if (m->nstack) { + if (clientsnstack < m->nstack) + clientsnstack++; + else + clientsnstack2++; + } + #endif // FLEXTILE_DELUXE_LAYOUT + else if ((i - m->nmaster) % 2) + clientsnstack2++; + else + clientsnstack++; + i++; + } + + if (!m->sel) + groupactive = GRP_NOSELECTION; + else if (HIDDEN(m->sel)) + groupactive = GRP_HIDDEN; + else if (m->sel->isfloating) + groupactive = GRP_FLOAT; + else if (selidx < clientsnmaster) + groupactive = GRP_MASTER; + else if (selidx < clientsnmaster + clientsnstack) + groupactive = GRP_STACK1; + else if (selidx < clientsnmaster + clientsnstack + clientsnstack2) + groupactive = GRP_STACK2; + + n = clientsnmaster + clientsnstack + clientsnstack2 + clientsnfloating + clientsnhidden; + if (n == 0) + return 0; + #if FLEXTILE_DELUXE_LAYOUT + else if (m->lt[m->sellt]->arrange == &flextile) { + int layout = m->ltaxis[LAYOUT]; + if (layout < 0) { + mirror = 1; + layout *= -1; + } + if (layout > FLOATING_MASTER) { + layout -= FLOATING_MASTER; + fixed = 1; + } + + if (layout == SPLIT_HORIZONTAL_DUAL_STACK || layout == SPLIT_HORIZONTAL_DUAL_STACK_FIXED) + dualstack = 1; + else if (layout == SPLIT_CENTERED_VERTICAL && (fixed || n - m->nmaster > 1)) + center = 1; + else if (layout == FLOATING_MASTER) + center = 1; + else if (layout == SPLIT_CENTERED_HORIZONTAL) { + if (fixed || n - m->nmaster > 1) + center = 1; + } + } + #endif // FLEXTILE_DELUXE_LAYOUT + #if CENTEREDMASTER_LAYOUT + else if (m->lt[m->sellt]->arrange == ¢eredmaster && (fixed || n - m->nmaster > 1)) + center = 1; + #endif // CENTEREDMASTER_LAYOUT + #if CENTEREDFLOATINGMASTER_LAYOUT + else if (m->lt[m->sellt]->arrange == ¢eredfloatingmaster) + center = 1; + #endif // CENTEREDFLOATINGMASTER_LAYOUT + + /* Certain layouts have no master / stack areas */ + if (!m->lt[m->sellt]->arrange // floating layout + || (!n || (!fixed && m->nmaster && n <= m->nmaster)) // no master + #if MONOCLE_LAYOUT + || m->lt[m->sellt]->arrange == &monocle + #endif // MONOCLE_LAYOUT + #if GRIDMODE_LAYOUT + || m->lt[m->sellt]->arrange == &grid + #endif // GRIDMODE_LAYOUT + #if HORIZGRID_LAYOUT + || m->lt[m->sellt]->arrange == &horizgrid + #endif // HORIZGRID_LAYOUT + #if GAPPLESSGRID_LAYOUT + || m->lt[m->sellt]->arrange == &gaplessgrid + #endif // GAPPLESSGRID_LAYOUT + #if NROWGRID_LAYOUT + || m->lt[m->sellt]->arrange == &nrowgrid + #endif // NROWGRID_LAYOUT + #if FLEXTILE_DELUXE_LAYOUT + || (m->lt[m->sellt]->arrange == &flextile && m->ltaxis[LAYOUT] == NO_SPLIT) + #endif // FLEXTILE_DELUXE_LAYOUT + ) + fulllayout = 1; + + num = tabw; + c = m->clients; + + /* floating mode */ + if ((fulllayout && FLEXWINTITLE_FLOATWEIGHT > 0) || clientsnmaster + clientsnstack == 0 || !m->lt[m->sellt]->arrange) { + den = clientsnmaster + clientsnstack + clientsnstack2 + clientsnfloating + clientsnhidden; + w = num / den; + r = num % den; // rest + c = flextitledrawarea(m, c, mas_x, r, w, den, !m->lt[m->sellt]->arrange ? SchemeFlexActFloat : SCHEMEFOR(GRP_MASTER), 1, FLEXWINTITLE_HIDDENWEIGHT, FLEXWINTITLE_FLOATWEIGHT, passx, tabfn, arg, barg); // floating + /* no master and stack mode, e.g. monocole, grid layouts, fibonacci */ + } else if (fulllayout) { + den = clientsnmaster + clientsnstack + clientsnstack2 + clientsnhidden; + w = num / den; + r = num % den; // rest + c = flextitledrawarea(m, c, mas_x, r, w, den, SCHEMEFOR(GRP_MASTER), 1, FLEXWINTITLE_HIDDENWEIGHT, 0, passx, tabfn, arg, barg); // full + /* tiled mode */ + } else { + den = clientsnmaster * FLEXWINTITLE_MASTERWEIGHT + (clientsnstack + clientsnstack2) * FLEXWINTITLE_STACKWEIGHT + clientsnfloating * FLEXWINTITLE_FLOATWEIGHT + clientsnhidden * FLEXWINTITLE_HIDDENWEIGHT; + w = num / den; // weight width per client + r = num % den; // weight rest width + rw = r / n; // rest incr per client + rr = r % n; // rest rest + #if FLEXTILE_DELUXE_LAYOUT + if ((!center && !dualstack) || (center && n <= m->nmaster + (m->nstack ? m->nstack : 1))) + #else + if ((!center && !dualstack) || (center && n <= m->nmaster + 1)) + #endif // FLEXTILE_DELUXE_LAYOUT + { + clientsnstack += clientsnstack2; + clientsnstack2 = 0; + if (groupactive == GRP_STACK2) + groupactive = GRP_STACK1; + } + + mas_w = clientsnmaster * rw + w * clientsnmaster * FLEXWINTITLE_MASTERWEIGHT + (rr > 0 ? MIN(rr, clientsnmaster) : 0); + rr -= clientsnmaster; + st1_w = clientsnstack * (rw + w * FLEXWINTITLE_STACKWEIGHT) + (rr > 0 ? MIN(rr, clientsnstack) : 0); + rr -= clientsnstack; + st2_w = clientsnstack2 * (rw + w * FLEXWINTITLE_STACKWEIGHT) + (rr > 0 ? MIN(rr, clientsnstack2) : 0); + rr -= clientsnstack2; + hid_w = clientsnhidden * (rw + w * FLEXWINTITLE_HIDDENWEIGHT) + (rr > 0 ? MIN(rr, clientsnhidden) : 0); + rr -= clientsnhidden; + rr = r % n; + + if (mirror) { + if (center && clientsnstack2) { + mas_x = st1_x + st1_w; + st2_x = mas_x + mas_w; + hid_x = st2_x + st2_w; + } else { + if (clientsnstack2) { + st2_x = st1_x + st1_w; + mas_x = st2_x + st2_w; + } else + mas_x = st1_x + st1_w; + hid_x = mas_x + mas_w; + } + } else { + if (center && clientsnstack2) { + mas_x = st2_x + st2_w; + st1_x = mas_x + mas_w; + hid_x = st1_x + st1_w; + } else { + st1_x = mas_x + mas_w; + if (clientsnstack2) { + st2_x = st1_x + st1_w; + hid_x = st2_x + st2_w; + } else + hid_x = st1_x + st1_w; + } + } + + flt_x = hid_x + hid_w; + c = flextitledrawarea(m, c, mas_x, rr, w * FLEXWINTITLE_MASTERWEIGHT + rw, clientsnmaster, SCHEMEFOR(GRP_MASTER), 1, 0, 0, passx, tabfn, arg, barg); // master + rr -= clientsnmaster; + c = flextitledrawarea(m, c, st1_x, rr, w * FLEXWINTITLE_STACKWEIGHT + rw, clientsnstack, SCHEMEFOR(GRP_STACK1), 1, 0, 0, passx, tabfn, arg, barg); // stack1 + rr -= clientsnstack; + if (clientsnstack2) { + c = flextitledrawarea(m, c, st2_x, rr, w * FLEXWINTITLE_STACKWEIGHT + rw, clientsnstack2, SCHEMEFOR(GRP_STACK2), 1, 0, 0, passx, tabfn, arg, barg); // stack2 + rr -= clientsnstack2; + } + c = flextitledrawarea(m, m->clients, hid_x, rr, w * FLEXWINTITLE_HIDDENWEIGHT + rw, clientsnhidden, SCHEMEFOR(GRP_HIDDEN), 0, 1, 0, passx, tabfn, arg, barg); // hidden + rr -= clientsnhidden; + c = flextitledrawarea(m, m->clients, flt_x, rr, w * FLEXWINTITLE_FLOATWEIGHT + rw, clientsnfloating, SCHEMEFOR(GRP_FLOAT), 0, 0, 1, passx, tabfn, arg, barg); // floating + } + return 1; +} + diff --git a/patch/bar_flexwintitle.h b/patch/bar_flexwintitle.h new file mode 100644 index 0000000..f4c6612 --- /dev/null +++ b/patch/bar_flexwintitle.h @@ -0,0 +1,11 @@ +static int width_flexwintitle(Bar *bar, BarArg *a); +static int draw_flexwintitle(Bar *bar, BarArg *a); +static int click_flexwintitle(Bar *bar, Arg *arg, BarArg *a); + +static void flextitledraw(Monitor *m, Client *c, int unused, int x, int w, int groupactive, Arg *arg, BarArg *barg); +static void flextitleclick(Monitor *m, Client *c, int passx, int x, int w, int unused, Arg *arg, BarArg *barg); +static int flextitlecalculate(Monitor *m, int offx, int w, int passx, void(*tabfn)(Monitor *, Client *, int, int, int, int, Arg *arg, BarArg *barg), Arg *arg, BarArg *barg); +static int getschemefor(Monitor *m, int group, int activegroup); +static int getselschemefor(int scheme); +static Client *flextitledrawarea(Monitor *m, Client *c, int x, int r, int w, int max_clients, int tabscheme, int draw_tiled, int draw_hidden, int draw_floating, int passx, void(*tabfn)(Monitor *, Client *, int, int, int, int, Arg *arg, BarArg *barg), Arg *arg, BarArg *barg); + diff --git a/patch/bar_holdbar.c b/patch/bar_holdbar.c new file mode 100644 index 0000000..1e00dc8 --- /dev/null +++ b/patch/bar_holdbar.c @@ -0,0 +1,38 @@ +void +holdbar(const Arg *arg) +{ + if (selmon->showbar) + return; + Bar *bar; + selmon->showbar = 2; + updatebarpos(selmon); + for (bar = selmon->bar; bar; bar = bar->next) + XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); +} + +void +keyrelease(XEvent *e) +{ + Bar *bar; + if (XEventsQueued(dpy, QueuedAfterReading)) { + XEvent ne; + XPeekEvent(dpy, &ne); + + if (ne.type == KeyPress && ne.xkey.time == e->xkey.time && + ne.xkey.keycode == e->xkey.keycode) { + XNextEvent(dpy, &ne); + return; + } + } + if (e->xkey.keycode == XKeysymToKeycode(dpy, HOLDKEY) && selmon->showbar == 2) { + selmon->showbar = 0; + updatebarpos(selmon); + for (bar = selmon->bar; bar; bar = bar->next) + XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + arrange(selmon); + } + #if COMBO_PATCH + combo = 0; + #endif // COMBO_PATCH +} + diff --git a/patch/bar_holdbar.h b/patch/bar_holdbar.h new file mode 100644 index 0000000..bcde089 --- /dev/null +++ b/patch/bar_holdbar.h @@ -0,0 +1,3 @@ +static void keyrelease(XEvent *e); +static void holdbar(const Arg *arg); + diff --git a/patch/bar_indicators.c b/patch/bar_indicators.c new file mode 100644 index 0000000..62326bc --- /dev/null +++ b/patch/bar_indicators.c @@ -0,0 +1,111 @@ +/* Indicator properties, you can override these in your config.h if you want. */ +#ifndef TAGSINDICATOR +#define TAGSINDICATOR 1 // 0 = off, 1 = on if >1 client/view tag, 2 = always on +#endif +#ifndef TAGSPX +#define TAGSPX 5 // # pixels for tag grid boxes +#endif +#ifndef TAGSROWS +#define TAGSROWS 3 // # rows in tag grid (9 tags, e.g. 3x3) +#endif + +void +drawindicator(Monitor *m, Client *c, unsigned int occ, int x, int y, int w, int h, unsigned int tag, int filled, int invert, int type) +{ + int i, boxw, boxs, indn = 0; + if (!(occ & 1 << tag) || type == INDICATOR_NONE) + return; + + boxs = drw->fonts->h / 9; + boxw = drw->fonts->h / 6 + 2; + if (filled == -1) + filled = m == selmon && m->sel && m->sel->tags & 1 << tag; + + switch (type) { + default: + case INDICATOR_TOP_LEFT_SQUARE: + drw_rect(drw, x + boxs, y + boxs, boxw, boxw, filled, invert); + break; + case INDICATOR_TOP_LEFT_LARGER_SQUARE: + drw_rect(drw, x + boxs + 2, y + boxs+1, boxw+1, boxw+1, filled, invert); + break; + case INDICATOR_TOP_BAR: + drw_rect(drw, x + boxw, y, w - ( 2 * boxw + 1), boxw/2, filled, invert); + break; + case INDICATOR_TOP_BAR_SLIM: + drw_rect(drw, x + boxw, y, w - ( 2 * boxw + 1), 1, 0, invert); + break; + case INDICATOR_BOTTOM_BAR: + drw_rect(drw, x + boxw, y + h - boxw/2, w - ( 2 * boxw + 1), boxw/2, filled, invert); + break; + case INDICATOR_BOTTOM_BAR_SLIM: + drw_rect(drw, x + boxw, y + h - 1, w - ( 2 * boxw + 1), 1, 0, invert); + break; + case INDICATOR_BOX: + drw_rect(drw, x + boxw, y, w - 2 * boxw, h, 0, invert); + break; + case INDICATOR_BOX_WIDER: + drw_rect(drw, x + boxw/2, y, w - boxw, h, 0, invert); + break; + case INDICATOR_BOX_FULL: + drw_rect(drw, x, y, w - 2, h, 0, invert); + break; + case INDICATOR_CLIENT_DOTS: + for (c = m->clients; c; c = c->next) { + if (c->tags & (1 << tag)) { + drw_rect(drw, x, 1 + (indn * 2), m->sel == c ? 6 : 1, 1, 1, invert); + indn++; + } + if (h <= 1 + (indn * 2)) { + indn = 0; + x += 2; + } + } + break; + case INDICATOR_RIGHT_TAGS: + if (!c) + break; + for (i = 0; i < NUMTAGS; i++) { + drw_rect(drw, + ( x + w - 2 - ((NUMTAGS / TAGSROWS) * TAGSPX) + - (i % (NUMTAGS/TAGSROWS)) + ((i % (NUMTAGS / TAGSROWS)) * TAGSPX) + ), + ( y + 2 + ((i / (NUMTAGS/TAGSROWS)) * TAGSPX) + - ((i / (NUMTAGS/TAGSROWS))) + ), + TAGSPX, TAGSPX, (c->tags >> i) & 1, 0 + ); + } + break; + case INDICATOR_PLUS_AND_LARGER_SQUARE: + boxs += 2; + boxw += 2; + /* falls through */ + case INDICATOR_PLUS_AND_SQUARE: + drw_rect(drw, x + boxs, y + boxs, boxw % 2 ? boxw : boxw + 1, boxw % 2 ? boxw : boxw + 1, filled, invert); + /* falls through */ + case INDICATOR_PLUS: + if (!(boxw % 2)) + boxw += 1; + drw_rect(drw, x + boxs + boxw / 2, y + boxs, 1, boxw, filled, invert); // | + drw_rect(drw, x + boxs, y + boxs + boxw / 2, boxw + 1, 1, filled, invert); // ‒ + break; + } +} + +void +drawstateindicator(Monitor *m, Client *c, unsigned int occ, int x, int y, int w, int h, unsigned int tag, int filled, int invert) +{ + #if FAKEFULLSCREEN_CLIENT_PATCH && !FAKEFULLSCREEN_PATCH + if (c->fakefullscreen && c->isfloating) + drawindicator(m, c, occ, x, y, w, h, tag, filled, invert, floatfakefsindicatortype); + else if (c->fakefullscreen) + drawindicator(m, c, occ, x, y, w, h, tag, filled, invert, fakefsindicatortype); + else + #endif // FAKEFULLSCREEN_CLIENT_PATCH + if (c->isfloating) + drawindicator(m, c, occ, x, y, w, h, tag, filled, invert, floatindicatortype); + else + drawindicator(m, c, occ, x, y, w, h, tag, filled, invert, tiledindicatortype); +} + diff --git a/patch/bar_indicators.h b/patch/bar_indicators.h new file mode 100644 index 0000000..c66e4f0 --- /dev/null +++ b/patch/bar_indicators.h @@ -0,0 +1,21 @@ +enum { + INDICATOR_NONE, + INDICATOR_TOP_LEFT_SQUARE, + INDICATOR_TOP_LEFT_LARGER_SQUARE, + INDICATOR_TOP_BAR, + INDICATOR_TOP_BAR_SLIM, + INDICATOR_BOTTOM_BAR, + INDICATOR_BOTTOM_BAR_SLIM, + INDICATOR_BOX, + INDICATOR_BOX_WIDER, + INDICATOR_BOX_FULL, + INDICATOR_CLIENT_DOTS, + INDICATOR_RIGHT_TAGS, + INDICATOR_PLUS, + INDICATOR_PLUS_AND_SQUARE, + INDICATOR_PLUS_AND_LARGER_SQUARE, +}; + +static void drawindicator(Monitor *m, Client *c, unsigned int occ, int x, int y, int w, int h, unsigned int tag, int filled, int invert, int type); +static void drawstateindicator(Monitor *m, Client *c, unsigned int occ, int x, int y, int w, int h, unsigned int tag, int filled, int invert); + diff --git a/patch/bar_layoutmenu.c b/patch/bar_layoutmenu.c new file mode 100644 index 0000000..1b95069 --- /dev/null +++ b/patch/bar_layoutmenu.c @@ -0,0 +1,18 @@ +void +layoutmenu(const Arg *arg) { + FILE *p; + char c[3], *s; + int i; + + if (!(p = popen(layoutmenu_cmd, "r"))) + return; + s = fgets(c, sizeof(c), p); + pclose(p); + + if (!s || *s == '\0' || c == '\0') + return; + + i = atoi(c); + setlayout(&((Arg) { .v = &layouts[i] })); +} + diff --git a/patch/bar_layoutmenu.h b/patch/bar_layoutmenu.h new file mode 100644 index 0000000..d05455c --- /dev/null +++ b/patch/bar_layoutmenu.h @@ -0,0 +1,2 @@ +static void layoutmenu(const Arg *arg); + diff --git a/patch/bar_ltsymbol.c b/patch/bar_ltsymbol.c new file mode 100644 index 0000000..1fbd1b8 --- /dev/null +++ b/patch/bar_ltsymbol.c @@ -0,0 +1,18 @@ +int +width_ltsymbol(Bar *bar, BarArg *a) +{ + return TEXTW(bar->mon->ltsymbol); +} + +int +draw_ltsymbol(Bar *bar, BarArg *a) +{ + return drw_text(drw, a->x, a->y, a->w, a->h, lrpad / 2, bar->mon->ltsymbol, 0, False); +} + +int +click_ltsymbol(Bar *bar, Arg *arg, BarArg *a) +{ + return ClkLtSymbol; +} + diff --git a/patch/bar_ltsymbol.h b/patch/bar_ltsymbol.h new file mode 100644 index 0000000..4de5720 --- /dev/null +++ b/patch/bar_ltsymbol.h @@ -0,0 +1,4 @@ +static int width_ltsymbol(Bar *bar, BarArg *a); +static int draw_ltsymbol(Bar *bar, BarArg *a); +static int click_ltsymbol(Bar *bar, Arg *arg, BarArg *a); + diff --git a/patch/bar_powerline_status.c b/patch/bar_powerline_status.c new file mode 100644 index 0000000..a09487f --- /dev/null +++ b/patch/bar_powerline_status.c @@ -0,0 +1,122 @@ +static Clr **statusscheme; + +int +width_pwrl_status(Bar *bar, BarArg *a) +{ + #if BAR_STATUSCMD_PATCH + return widthpowerlinestatus(rawstext); + #else + return widthpowerlinestatus(stext); + #endif // BAR_STATUSCMD_PATCH +} + +#if BAR_EXTRASTATUS_PATCH +int +width_pwrl_status_es(Bar *bar, BarArg *a) +{ + #if BAR_STATUSCMD_PATCH + return widthpowerlinestatus(rawestext); + #else + return widthpowerlinestatus(estext); + #endif // BAR_STATUSCMD_PATCH +} +#endif // BAR_EXTRASTATUS_PATCH + +int +draw_pwrl_status(Bar *bar, BarArg *a) +{ + #if BAR_STATUSCMD_PATCH + return drawpowerlinestatus(a->x + a->w, rawstext, a); + #else + return drawpowerlinestatus(a->x + a->w, stext, a); + #endif // BAR_STATUSCMD_PATCH +} + +#if BAR_EXTRASTATUS_PATCH +int +draw_pwrl_status_es(Bar *bar, BarArg *a) +{ + #if BAR_STATUSCMD_PATCH + return drawpowerlinestatus(a->x + a->w, rawestext, a); + #else + return drawpowerlinestatus(a->x + a->w, estext, a); + #endif // BAR_STATUSCMD_PATCH +} +#endif // BAR_EXTRASTATUS_PATCH + +int +click_pwrl_status(Bar *bar, Arg *arg, BarArg *a) +{ + return ClkStatusText; +} + +int +widthpowerlinestatus(char *stext) +{ + char status[512]; + int w = 0, i, n = strlen(stext); + int plw = drw->fonts->h / 2 + 1; + char *bs, bp = '|'; + strcpy(status, stext); + + for (i = n, bs = &status[n-1]; i >= 0; i--, bs--) { + if (*bs == '<' || *bs == '/' || *bs == '\\' || *bs == '>' || *bs == '|') { /* block start */ + if (bp != '|') + w += plw; + w += TEXTW(bs+2); + bp = *bs; + *bs = 0; + } + } + if (bp != '|') + w += plw * 2; + + return w; +} + +int +drawpowerlinestatus(int xpos, char *stext, BarArg *barg) +{ + char status[512]; + int i, n = strlen(stext), cn = 0; + int x = xpos, w = 0; + int plw = drw->fonts->h / 2 + 1; + char *bs, bp = '|'; + Clr *prevscheme = statusscheme[0], *nxtscheme; + strcpy(status, stext); + + for (i = n, bs = &status[n-1]; i >= 0; i--, bs--) { + if (*bs == '<' || *bs == '/' || *bs == '\\' || *bs == '>' || *bs == '|') { /* block start */ + cn = ((int) *(bs+1)) - 1; + + if (cn < LENGTH(statuscolors)) { + drw_settrans(drw, prevscheme, (nxtscheme = statusscheme[cn])); + } else { + drw_settrans(drw, prevscheme, (nxtscheme = statusscheme[0])); + } + + if (bp != '|') { + drw_arrow(drw, x - plw, barg->y, plw, barg->h, bp == '\\' || bp == '>' ? 1 : 0, bp == '<' ? 0 : 1); + x -= plw; + } + + drw_setscheme(drw, nxtscheme); + w = TEXTW(bs+2); + drw_text(drw, x - w, barg->y, w, barg->h, lrpad / 2, bs+2, 0, False); + x -= w; + + bp = *bs; + *bs = 0; + prevscheme = nxtscheme; + } + } + if (bp != '|') { + drw_settrans(drw, prevscheme, scheme[SchemeNorm]); + drw_arrow(drw, x - plw, barg->y, plw, barg->h, bp == '\\' || bp == '>' ? 1 : 0, bp == '<' ? 0 : 1); + drw_rect(drw, x - 2 * plw, barg->y, plw, barg->h, 1, 1); + x -= plw * 2; + } + + return xpos - x; +} + diff --git a/patch/bar_powerline_status.h b/patch/bar_powerline_status.h new file mode 100644 index 0000000..46b78e0 --- /dev/null +++ b/patch/bar_powerline_status.h @@ -0,0 +1,12 @@ +static int width_pwrl_status(Bar *bar, BarArg *a); +#if BAR_EXTRASTATUS_PATCH +static int width_pwrl_status_es(Bar *bar, BarArg *a); +#endif // BAR_EXTRASTATUS_PATCH +static int draw_pwrl_status(Bar *bar, BarArg *a); +#if BAR_EXTRASTATUS_PATCH +static int draw_pwrl_status_es(Bar *bar, BarArg *a); +#endif // BAR_EXTRASTATUS_PATCH +static int click_pwrl_status(Bar *bar, Arg *arg, BarArg *a); +static int drawpowerlinestatus(int x, char *stext, BarArg *a); +static int widthpowerlinestatus(char *stext); + diff --git a/patch/bar_powerline_tags.c b/patch/bar_powerline_tags.c new file mode 100644 index 0000000..d5ad787 --- /dev/null +++ b/patch/bar_powerline_tags.c @@ -0,0 +1,107 @@ +int +width_pwrl_tags(Bar *bar, BarArg *a) +{ + int w, i; + int plw = drw->fonts->h / 2 + 1; + #if BAR_HIDEVACANTTAGS_PATCH + Client *c; + unsigned int occ = 0; + for (c = bar->mon->clients; c; c = c->next) + occ |= c->tags == 255 ? 0 : c->tags; + #endif // BAR_HIDEVACANTTAGS_PATCH + + for (w = 0, i = 0; i < NUMTAGS; i++) { + #if BAR_HIDEVACANTTAGS_PATCH + if (!(occ & 1 << i || bar->mon->tagset[bar->mon->seltags] & 1 << i)) + continue; + #endif // BAR_HIDEVACANTTAGS_PATCH + w += TEXTW(tagicon(bar->mon, i)) + plw; + } + return w + lrpad; +} + +int +draw_pwrl_tags(Bar *bar, BarArg *a) +{ + int x, w; + int invert; + int plw = drw->fonts->h / 2 + 1; + unsigned int i, occ = 0, urg = 0; + char *icon; + Client *c; + Clr *prevscheme, *nxtscheme; + + for (c = bar->mon->clients; c; c = c->next) { + #if BAR_HIDEVACANTTAGS_PATCH + occ |= c->tags == 255 ? 0 : c->tags; + #else + occ |= c->tags; + #endif // BAR_HIDEVACANTTAGS_PATCH + if (c->isurgent) + urg |= c->tags; + } + x = a->x; + prevscheme = scheme[SchemeNorm]; + for (i = 0; i < NUMTAGS; i++) { + #if BAR_HIDEVACANTTAGS_PATCH + /* do not draw vacant tags */ + if (!(occ & 1 << i || bar->mon->tagset[bar->mon->seltags] & 1 << i)) + continue; + #endif // BAR_HIDEVACANTTAGS_PATCH + + icon = tagicon(bar->mon, i); + invert = 0; + w = TEXTW(icon); + if (urg & 1 << i) { + drw_settrans(drw, prevscheme, (nxtscheme = scheme[bar->mon->tagset[bar->mon->seltags] & 1 << i ? SchemeSel : SchemeUrg])); + } else { + drw_settrans(drw, prevscheme, (nxtscheme = scheme[bar->mon->tagset[bar->mon->seltags] & 1 << i ? SchemeSel : SchemeNorm])); + } + #if BAR_POWERLINE_TAGS_SLASH_PATCH + drw_arrow(drw, x, a->y, plw, a->h, 1, 1); + #else + drw_arrow(drw, x, a->y, plw, a->h, 1, 0); + #endif // BAR_POWERLINE_TAGS_SLASH_PATCH + x += plw; + drw_setscheme(drw, nxtscheme); + drw_text(drw, x, a->y, w, a->h, lrpad / 2, icon, invert, False); + drawindicator(bar->mon, NULL, occ, x, a->y, w, a->h, i, -1, invert, tagindicatortype); + x += w; + prevscheme = nxtscheme; + } + nxtscheme = scheme[SchemeNorm]; + + drw_settrans(drw, prevscheme, nxtscheme); + #if BAR_POWERLINE_TAGS_SLASH_PATCH + drw_arrow(drw, x, a->y, plw, a->h, 1, 1); + #else + drw_arrow(drw, x, a->y, plw, a->h, 1, 0); + #endif // BAR_POWERLINE_TAGS_SLASH_PATCH + return 1; +} + +int +click_pwrl_tags(Bar *bar, Arg *arg, BarArg *a) +{ + int i = 0, x = lrpad / 2; + int plw = drw->fonts->h / 2 + 1; + #if BAR_HIDEVACANTTAGS_PATCH + Client *c; + unsigned int occ = 0; + for (c = bar->mon->clients; c; c = c->next) + occ |= c->tags == 255 ? 0 : c->tags; + #endif // BAR_HIDEVACANTTAGS_PATCH + + do { + #if BAR_HIDEVACANTTAGS_PATCH + if (!(occ & 1 << i || bar->mon->tagset[bar->mon->seltags] & 1 << i)) + continue; + #endif // BAR_HIDEVACANTTAGS_PATCH + x += TEXTW(tagicon(bar->mon, i)) + plw; + } while (a->x >= x && ++i < NUMTAGS); + if (i < NUMTAGS) { + arg->ui = 1 << i; + } + return ClkTagBar; +} + diff --git a/patch/bar_powerline_tags.h b/patch/bar_powerline_tags.h new file mode 100644 index 0000000..a51dcc7 --- /dev/null +++ b/patch/bar_powerline_tags.h @@ -0,0 +1,4 @@ +static int width_pwrl_tags(Bar *bar, BarArg *a); +static int draw_pwrl_tags(Bar *bar, BarArg *a); +static int click_pwrl_tags(Bar *bar, Arg *arg, BarArg *a); + diff --git a/patch/bar_status.c b/patch/bar_status.c new file mode 100644 index 0000000..715b6d2 --- /dev/null +++ b/patch/bar_status.c @@ -0,0 +1,34 @@ +int +width_status(Bar *bar, BarArg *a) +{ + return TEXTWM(stext); +} + +#if BAR_EXTRASTATUS_PATCH +int +width_status_es(Bar *bar, BarArg *a) +{ + return TEXTWM(estext) - lrpad; +} +#endif // BAR_EXTRASTATUS_PATCH + +int +draw_status(Bar *bar, BarArg *a) +{ + return drw_text(drw, a->x, a->y, a->w, a->h, lrpad / 2, stext, 0, True); +} + +#if BAR_EXTRASTATUS_PATCH +int +draw_status_es(Bar *bar, BarArg *a) +{ + return drw_text(drw, a->x, a->y, a->w, a->h, 0, estext, 0, True); +} +#endif // BAR_EXTRASTATUS_PATCH + +int +click_status(Bar *bar, Arg *arg, BarArg *a) +{ + return ClkStatusText; +} + diff --git a/patch/bar_status.h b/patch/bar_status.h new file mode 100644 index 0000000..4bf5e58 --- /dev/null +++ b/patch/bar_status.h @@ -0,0 +1,10 @@ +static int width_status(Bar *bar, BarArg *a); +#if BAR_EXTRASTATUS_PATCH +static int width_status_es(Bar *bar, BarArg *a); +#endif // BAR_EXTRASTATUS_PATCH +static int draw_status(Bar *bar, BarArg *a); +#if BAR_EXTRASTATUS_PATCH +static int draw_status_es(Bar *bar, BarArg *a); +#endif // BAR_EXTRASTATUS_PATCH +static int click_status(Bar *bar, Arg *arg, BarArg *a); + diff --git a/patch/bar_status2d.c b/patch/bar_status2d.c new file mode 100644 index 0000000..860fd83 --- /dev/null +++ b/patch/bar_status2d.c @@ -0,0 +1,268 @@ +#if BAR_STATUS2D_XRDB_TERMCOLORS_PATCH +static char termcol0[] = "#000000"; /* black */ +static char termcol1[] = "#ff0000"; /* red */ +static char termcol2[] = "#33ff00"; /* green */ +static char termcol3[] = "#ff0099"; /* yellow */ +static char termcol4[] = "#0066ff"; /* blue */ +static char termcol5[] = "#cc00ff"; /* magenta */ +static char termcol6[] = "#00ffff"; /* cyan */ +static char termcol7[] = "#d0d0d0"; /* white */ +static char termcol8[] = "#808080"; /* black */ +static char termcol9[] = "#ff0000"; /* red */ +static char termcol10[] = "#33ff00"; /* green */ +static char termcol11[] = "#ff0099"; /* yellow */ +static char termcol12[] = "#0066ff"; /* blue */ +static char termcol13[] = "#cc00ff"; /* magenta */ +static char termcol14[] = "#00ffff"; /* cyan */ +static char termcol15[] = "#ffffff"; /* white */ +static char *termcolor[] = { + termcol0, termcol1, termcol2, termcol3, termcol4, termcol5, termcol6, termcol7, + termcol8, termcol9, termcol10, termcol11, termcol12, termcol13, termcol14, termcol15, +}; +#endif // BAR_STATUS2D_XRDB_TERMCOLORS_PATCH + +int +width_status2d(Bar *bar, BarArg *a) +{ + int width; + #if BAR_EXTRASTATUS_PATCH || BAR_STATUSCMD_PATCH + width = status2dtextlength(rawstext); + #else + width = status2dtextlength(stext); + #endif // #if BAR_EXTRASTATUS_PATCH | BAR_STATUSCMD_PATCH + return width ? width + lrpad : 0; +} + +#if BAR_EXTRASTATUS_PATCH +int +width_status2d_es(Bar *bar, BarArg *a) +{ + int width; + #if BAR_STATUSCMD_PATCH + width = status2dtextlength(rawestext); + #else + width = status2dtextlength(estext); + #endif // BAR_STATUSCMD_PATCH + return width ? width + lrpad : 0; +} +#endif // BAR_EXTRASTATUS_PATCH + +int +draw_status2d(Bar *bar, BarArg *a) +{ + #if BAR_EXTRASTATUS_PATCH || BAR_STATUSCMD_PATCH + return drawstatusbar(a, rawstext); + #else + return drawstatusbar(a, stext); + #endif // #if BAR_EXTRASTATUS_PATCH | BAR_STATUSCMD_PATCH +} + +#if BAR_EXTRASTATUS_PATCH +int +draw_status2d_es(Bar *bar, BarArg *a) +{ + #if BAR_STATUSCMD_PATCH + return drawstatusbar(a, rawestext); + #else + return drawstatusbar(a, estext); + #endif // BAR_STATUSCMD_PATCH +} +#endif // BAR_EXTRASTATUS_PATCH + +#if !BAR_STATUSCMD_PATCH +int +click_status2d(Bar *bar, Arg *arg, BarArg *a) +{ + return ClkStatusText; +} +#endif // BAR_STATUSCMD_PATCH + +int +drawstatusbar(BarArg *a, char* stext) +{ + int i, w, len; + int x = a->x; + int y = a->y; + short isCode = 0; + char *text; + char *p; + Clr oldbg, oldfg; + len = strlen(stext); + if (!(text = (char*) malloc(sizeof(char)*(len + 1)))) + die("malloc"); + p = text; + #if BAR_STATUSCMD_PATCH + copyvalidchars(text, stext); + #else + memcpy(text, stext, len); + #endif // BAR_STATUSCMD_PATCH + + x += lrpad / 2; + drw_setscheme(drw, scheme[LENGTH(colors)]); + drw->scheme[ColFg] = scheme[SchemeNorm][ColFg]; + drw->scheme[ColBg] = scheme[SchemeNorm][ColBg]; + + /* process status text */ + i = -1; + while (text[++i]) { + if (text[i] == '^' && !isCode) { + isCode = 1; + + text[i] = '\0'; + w = TEXTWM(text) - lrpad; + drw_text(drw, x, y, w, bh, 0, text, 0, True); + + x += w; + + /* process code */ + while (text[++i] != '^') { + if (text[i] == 'c') { + char buf[8]; + if (i + 7 >= len) { + i += 7; + len = 0; + break; + } + memcpy(buf, (char*)text+i+1, 7); + buf[7] = '\0'; + #if BAR_ALPHA_PATCH && BAR_STATUS2D_NO_ALPHA_PATCH + drw_clr_create(drw, &drw->scheme[ColFg], buf, 0xff); + #elif BAR_ALPHA_PATCH + drw_clr_create(drw, &drw->scheme[ColFg], buf, alphas[SchemeNorm][ColFg]); + #else + drw_clr_create(drw, &drw->scheme[ColFg], buf); + #endif // BAR_ALPHA_PATCH + i += 7; + } else if (text[i] == 'b') { + char buf[8]; + if (i + 7 >= len) { + i += 7; + len = 0; + break; + } + memcpy(buf, (char*)text+i+1, 7); + buf[7] = '\0'; + #if BAR_ALPHA_PATCH && BAR_STATUS2D_NO_ALPHA_PATCH + drw_clr_create(drw, &drw->scheme[ColBg], buf, 0xff); + #elif BAR_ALPHA_PATCH + drw_clr_create(drw, &drw->scheme[ColBg], buf, alphas[SchemeNorm][ColBg]); + #else + drw_clr_create(drw, &drw->scheme[ColBg], buf); + #endif // BAR_ALPHA_PATCH + i += 7; + #if BAR_STATUS2D_XRDB_TERMCOLORS_PATCH + } else if (text[i] == 'C') { + int c = atoi(text + ++i) % 16; + #if BAR_ALPHA_PATCH && BAR_STATUS2D_NO_ALPHA_PATCH + drw_clr_create(drw, &drw->scheme[ColFg], termcolor[c], 0xff); + #elif BAR_ALPHA_PATCH + drw_clr_create(drw, &drw->scheme[ColFg], termcolor[c], alphas[SchemeNorm][ColBg]); + #else + drw_clr_create(drw, &drw->scheme[ColFg], termcolor[c]); + #endif // BAR_ALPHA_PATCH + } else if (text[i] == 'B') { + int c = atoi(text + ++i) % 16; + #if BAR_ALPHA_PATCH && BAR_STATUS2D_NO_ALPHA_PATCH + drw_clr_create(drw, &drw->scheme[ColBg], termcolor[c], 0xff); + #elif BAR_ALPHA_PATCH + drw_clr_create(drw, &drw->scheme[ColBg], termcolor[c], alphas[SchemeNorm][ColBg]); + #else + drw_clr_create(drw, &drw->scheme[ColBg], termcolor[c]); + #endif // BAR_ALPHA_PATCH + #endif // BAR_STATUS2D_XRDB_TERMCOLORS_PATCH + } else if (text[i] == 'd') { + drw->scheme[ColFg] = scheme[SchemeNorm][ColFg]; + drw->scheme[ColBg] = scheme[SchemeNorm][ColBg]; + } else if (text[i] == 'w') { + Clr swp; + swp = drw->scheme[ColFg]; + drw->scheme[ColFg] = drw->scheme[ColBg]; + drw->scheme[ColBg] = swp; + } else if (text[i] == 'v') { + oldfg = drw->scheme[ColFg]; + oldbg = drw->scheme[ColBg]; + } else if (text[i] == 't') { + drw->scheme[ColFg] = oldfg; + drw->scheme[ColBg] = oldbg; + } else if (text[i] == 'r') { + int rx = atoi(text + ++i); + while (text[++i] != ','); + int ry = atoi(text + ++i); + while (text[++i] != ','); + int rw = atoi(text + ++i); + while (text[++i] != ','); + int rh = atoi(text + ++i); + + if (ry < 0) + ry = 0; + if (rx < 0) + rx = 0; + + drw_rect(drw, rx + x, y + ry, rw, rh, 1, 0); + } else if (text[i] == 'f') { + x += atoi(text + ++i); + } + } + + text = text + i + 1; + len -= i + 1; + i = -1; + isCode = 0; + if (len <= 0) + break; + } + } + if (!isCode && len > 0) { + w = TEXTWM(text) - lrpad; + drw_text(drw, x, y, w, bh, 0, text, 0, True); + x += w; + } + free(p); + + drw_setscheme(drw, scheme[SchemeNorm]); + return 1; +} + +int +status2dtextlength(char* stext) +{ + int i, w, len; + short isCode = 0; + char *text; + char *p; + + len = strlen(stext) + 1; + if (!(text = (char*) malloc(sizeof(char)*len))) + die("malloc"); + p = text; + #if BAR_STATUSCMD_PATCH + copyvalidchars(text, stext); + #else + memcpy(text, stext, len); + #endif // BAR_STATUSCMD_PATCH + + /* compute width of the status text */ + w = 0; + i = -1; + while (text[++i]) { + if (text[i] == '^') { + if (!isCode) { + isCode = 1; + text[i] = '\0'; + w += TEXTWM(text) - lrpad; + text[i] = '^'; + if (text[++i] == 'f') + w += atoi(text + ++i); + } else { + isCode = 0; + text = text + i + 1; + i = -1; + } + } + } + if (!isCode) + w += TEXTWM(text) - lrpad; + free(p); + return w; +} + diff --git a/patch/bar_status2d.h b/patch/bar_status2d.h new file mode 100644 index 0000000..8e088fb --- /dev/null +++ b/patch/bar_status2d.h @@ -0,0 +1,14 @@ +static int width_status2d(Bar *bar, BarArg *a); +#if BAR_EXTRASTATUS_PATCH +static int width_status2d_es(Bar *bar, BarArg *a); +#endif // BAR_EXTRASTATUS_PATCH +static int draw_status2d(Bar *bar, BarArg *a); +#if BAR_EXTRASTATUS_PATCH +static int draw_status2d_es(Bar *bar, BarArg *a); +#endif // BAR_EXTRASTATUS_PATCH +#if !BAR_STATUSCMD_PATCH +static int click_status2d(Bar *bar, Arg *arg, BarArg *a); +#endif // BAR_STATUSCMD_PATCH +static int drawstatusbar(BarArg *a, char *text); +static int status2dtextlength(char *stext); + diff --git a/patch/bar_statusbutton.c b/patch/bar_statusbutton.c new file mode 100644 index 0000000..5e43fe0 --- /dev/null +++ b/patch/bar_statusbutton.c @@ -0,0 +1,18 @@ +int +width_stbutton(Bar *bar, BarArg *a) +{ + return TEXTW(buttonbar); +} + +int +draw_stbutton(Bar *bar, BarArg *a) +{ + return drw_text(drw, a->x, a->y, a->w, a->h, lrpad / 2, buttonbar, 0, False); +} + +int +click_stbutton(Bar *bar, Arg *arg, BarArg *a) +{ + return ClkButton; +} + diff --git a/patch/bar_statusbutton.h b/patch/bar_statusbutton.h new file mode 100644 index 0000000..ed5beb5 --- /dev/null +++ b/patch/bar_statusbutton.h @@ -0,0 +1,4 @@ +static int width_stbutton(Bar *bar, BarArg *a); +static int draw_stbutton(Bar *bar, BarArg *a); +static int click_stbutton(Bar *bar, Arg *arg, BarArg *a); + diff --git a/patch/bar_statuscmd.c b/patch/bar_statuscmd.c new file mode 100644 index 0000000..5b26932 --- /dev/null +++ b/patch/bar_statuscmd.c @@ -0,0 +1,75 @@ +#if !BAR_DWMBLOCKS_PATCH +static const char statusexport[] = "export BUTTON=-;"; +static int statuscmdn; +static char lastbutton[] = "-"; +#endif // BAR_DWMBLOCKS_PATCH + +int +click_statuscmd(Bar *bar, Arg *arg, BarArg *a) +{ + return click_statuscmd_text(arg, a->x, rawstext); +} + +#if BAR_EXTRASTATUS_PATCH +int +click_statuscmd_es(Bar *bar, Arg *arg, BarArg *a) +{ + return click_statuscmd_text(arg, a->x, rawestext); +} +#endif // BAR_EXTRASTATUS_PATCH + +int +click_statuscmd_text(Arg *arg, int rel_x, char *text) +{ + int i = -1; + int x = 0; + char ch; + #if BAR_DWMBLOCKS_PATCH + statussig = -1; + #else + statuscmdn = 0; + #endif // BAR_DWMBLOCKS_PATCH + while (text[++i]) { + if ((unsigned char)text[i] < ' ') { + ch = text[i]; + text[i] = '\0'; + #if BAR_STATUS2D_PATCH && !BAR_BAR_STATUSCOLORS_PATCH + x += status2dtextlength(text); + #else + x += TEXTWM(text) - lrpad; + #endif // BAR_STATUS2D_PATCH + text[i] = ch; + text += i+1; + i = -1; + #if BAR_DWMBLOCKS_PATCH + if (x >= rel_x && statussig != -1) + break; + statussig = ch; + #else + if (x >= rel_x) + break; + if (ch <= LENGTH(statuscmds)) + statuscmdn = ch; + #endif // BAR_DWMBLOCKS_PATCH + } + } + #if BAR_DWMBLOCKS_PATCH + if (statussig == -1) + statussig = 0; + #endif // BAR_DWMBLOCKS_PATCH + return ClkStatusText; +} + +void +copyvalidchars(char *text, char *rawtext) +{ + int i = -1, j = 0; + + while (rawtext[++i]) { + if ((unsigned char)rawtext[i] >= ' ') { + text[j++] = rawtext[i]; + } + } + text[j] = '\0'; +} + diff --git a/patch/bar_statuscmd.h b/patch/bar_statuscmd.h new file mode 100644 index 0000000..a4c4d24 --- /dev/null +++ b/patch/bar_statuscmd.h @@ -0,0 +1,12 @@ +static int click_statuscmd(Bar *bar, Arg *arg, BarArg *a); +#if BAR_EXTRASTATUS_PATCH +static int click_statuscmd_es(Bar *bar, Arg *arg, BarArg *a); +#endif // BAR_EXTRASTATUS_PATCH +static int click_statuscmd_text(Arg *arg, int rel_x, char *text); +static void copyvalidchars(char *text, char *rawtext); + +typedef struct { + const char *cmd; + int id; +} StatusCmd; + diff --git a/patch/bar_statuscolors.c b/patch/bar_statuscolors.c new file mode 100644 index 0000000..4ade4df --- /dev/null +++ b/patch/bar_statuscolors.c @@ -0,0 +1,24 @@ +int +textw_wosc(char *s) +{ + char *ts = s; + char *tp = s; + int sw = 0; + char ctmp; + while (1) { + if ((unsigned int)*ts > LENGTH(colors)) { + ts++; + continue; + } + ctmp = *ts; + *ts = '\0'; + sw += drw_fontset_getwidth(drw, tp, True); + *ts = ctmp; + if (ctmp == '\0') + break; + tp = ++ts; + } + + return sw; +} + diff --git a/patch/bar_systray.c b/patch/bar_systray.c new file mode 100644 index 0000000..67d1d2d --- /dev/null +++ b/patch/bar_systray.c @@ -0,0 +1,198 @@ +static Systray *systray = NULL; +static unsigned long systrayorientation = _NET_SYSTEM_TRAY_ORIENTATION_HORZ; + +int +width_systray(Bar *bar, BarArg *a) +{ + unsigned int w = 0; + Client *i; + if (!systray) + return 1; + if (showsystray) + for (i = systray->icons; i; w += i->w + systrayspacing, i = i->next); + return w ? w + lrpad - systrayspacing : 0; +} + +int +draw_systray(Bar *bar, BarArg *a) +{ + if (!showsystray) + return 0; + + XSetWindowAttributes wa; + XWindowChanges wc; + Client *i; + unsigned int w; + + if (!systray) { + /* init systray */ + if (!(systray = (Systray *)calloc(1, sizeof(Systray)))) + die("fatal: could not malloc() %u bytes\n", sizeof(Systray)); + + wa.override_redirect = True; + wa.event_mask = ButtonPressMask|ExposureMask; + wa.border_pixel = 0; + systray->h = MIN(a->h, drw->fonts->h); + #if BAR_ALPHA_PATCH + wa.background_pixel = 0; + wa.colormap = cmap; + systray->win = XCreateWindow(dpy, root, bar->bx + a->x + lrpad / 2, bar->by + a->y + (a->h - systray->h) / 2, MAX(a->w + 40, 1), systray->h, 0, depth, + InputOutput, visual, + CWOverrideRedirect|CWBorderPixel|CWBackPixel|CWColormap|CWEventMask, &wa); // CWBackPixmap + #else + wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; + systray->win = XCreateSimpleWindow(dpy, root, bar->bx + a->x + lrpad / 2, bar->by + a->y + (a->h - systray->h) / 2, MIN(a->w, 1), systray->h, 0, 0, scheme[SchemeNorm][ColBg].pixel); + XChangeWindowAttributes(dpy, systray->win, CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWEventMask, &wa); + #endif // BAR_ALPHA_PATCH + + XSelectInput(dpy, systray->win, SubstructureNotifyMask); + XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32, + PropModeReplace, (unsigned char *)&systrayorientation, 1); + #if BAR_ALPHA_PATCH + XChangeProperty(dpy, systray->win, netatom[NetSystemTrayVisual], XA_VISUALID, 32, + PropModeReplace, (unsigned char *)&visual->visualid, 1); + #endif // BAR_ALPHA_PATCH + XChangeProperty(dpy, systray->win, netatom[NetWMWindowType], XA_ATOM, 32, + PropModeReplace, (unsigned char *)&netatom[NetWMWindowTypeDock], 1); + XMapRaised(dpy, systray->win); + XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime); + if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) { + sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0); + XSync(dpy, False); + } else { + fprintf(stderr, "dwm: unable to obtain system tray.\n"); + free(systray); + systray = NULL; + return 0; + } + } + + systray->bar = bar; + + wc.stack_mode = Above; + wc.sibling = bar->win; + XConfigureWindow(dpy, systray->win, CWSibling|CWStackMode, &wc); + + drw_setscheme(drw, scheme[SchemeNorm]); + for (w = 0, i = systray->icons; i; i = i->next) { + #if BAR_ALPHA_PATCH + wa.background_pixel = 0; + #else + wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; + #endif // BAR_ALPHA_PATCH + XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa); + XMapRaised(dpy, i->win); + i->x = w; + XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h); + w += i->w; + if (i->next) + w += systrayspacing; + if (i->mon != bar->mon) + i->mon = bar->mon; + } + + XMoveResizeWindow(dpy, systray->win, bar->bx + a->x + lrpad / 2, (w ? bar->by + a->y + (a->h - systray->h) / 2: -bar->by - a->y), MAX(w, 1), systray->h); + return w; +} + +int +click_systray(Bar *bar, Arg *arg, BarArg *a) +{ + return -1; +} + +void +removesystrayicon(Client *i) +{ + Client **ii; + + if (!showsystray || !i) + return; + for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next); + if (ii) + *ii = i->next; + free(i); + drawbarwin(systray->bar); +} + +void +resizerequest(XEvent *e) +{ + XResizeRequestEvent *ev = &e->xresizerequest; + Client *i; + + if ((i = wintosystrayicon(ev->window))) { + updatesystrayicongeom(i, ev->width, ev->height); + drawbarwin(systray->bar); + } +} + +void +updatesystrayicongeom(Client *i, int w, int h) +{ + if (!systray) + return; + + int icon_height = systray->h; + if (i) { + i->h = icon_height; + if (w == h) + i->w = icon_height; + else if (h == icon_height) + i->w = w; + else + i->w = (int) ((float)icon_height * ((float)w / (float)h)); + applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False); + /* force icons into the systray dimensions if they don't want to */ + if (i->h > icon_height) { + if (i->w == i->h) + i->w = icon_height; + else + i->w = (int) ((float)icon_height * ((float)i->w / (float)i->h)); + i->h = icon_height; + } + if (i->w > 2 * icon_height) + i->w = icon_height; + } +} + +void +updatesystrayiconstate(Client *i, XPropertyEvent *ev) +{ + long flags; + int code = 0; + + if (!showsystray || !systray || !i || ev->atom != xatom[XembedInfo] || + !(flags = getatomprop(i, xatom[XembedInfo]))) + return; + + if (flags & XEMBED_MAPPED && !i->tags) { + i->tags = 1; + code = XEMBED_WINDOW_ACTIVATE; + XMapRaised(dpy, i->win); + setclientstate(i, NormalState); + } + else if (!(flags & XEMBED_MAPPED) && i->tags) { + i->tags = 0; + code = XEMBED_WINDOW_DEACTIVATE; + XUnmapWindow(dpy, i->win); + setclientstate(i, WithdrawnState); + } + else + return; + sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0, + systray->win, XEMBED_EMBEDDED_VERSION); +} + +Client * +wintosystrayicon(Window w) +{ + if (!systray) + return NULL; + Client *i = NULL; + if (!showsystray || !w) + return i; + for (i = systray->icons; i && i->win != w; i = i->next); + return i; +} + diff --git a/patch/bar_systray.h b/patch/bar_systray.h new file mode 100644 index 0000000..a64b971 --- /dev/null +++ b/patch/bar_systray.h @@ -0,0 +1,42 @@ +#define SYSTEM_TRAY_REQUEST_DOCK 0 +#define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0 + +/* XEMBED messages */ +#define XEMBED_EMBEDDED_NOTIFY 0 +#define XEMBED_WINDOW_ACTIVATE 1 +#define XEMBED_FOCUS_IN 4 +#define XEMBED_MODALITY_ON 10 + +#define XEMBED_MAPPED (1 << 0) +#define XEMBED_WINDOW_ACTIVATE 1 +#define XEMBED_WINDOW_DEACTIVATE 2 + +#define VERSION_MAJOR 0 +#define VERSION_MINOR 0 +#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR + +/* enums */ +enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */ + +typedef struct Systray Systray; +struct Systray { + Window win; + Client *icons; + Bar *bar; + int h; +}; + +/* bar integration */ +static int width_systray(Bar *bar, BarArg *a); +static int draw_systray(Bar *bar, BarArg *a); +static int click_systray(Bar *bar, Arg *arg, BarArg *a); + +/* function declarations */ +static Atom getatomprop(Client *c, Atom prop); +static void removesystrayicon(Client *i); +static void resizerequest(XEvent *e); +static void updatesystrayicongeom(Client *i, int w, int h); +static void updatesystrayiconstate(Client *i, XPropertyEvent *ev); +static Client *wintosystrayicon(Window w); + + diff --git a/patch/bar_tabgroups.c b/patch/bar_tabgroups.c new file mode 100644 index 0000000..1498bc2 --- /dev/null +++ b/patch/bar_tabgroups.c @@ -0,0 +1,231 @@ +/* Bartabgroups properties, you can override these in your config.h if you want. */ +#ifndef BARTAB_BORDERS +#define BARTAB_BORDERS 1 // 0 = off, 1 = on +#endif +#ifndef BARTAB_SHOWFLOATING +#define BARTAB_SHOWFLOATING 0 // whether to show titles for floating windows, hidden clients are always shown +#endif +#ifndef BARTAB_STACKWEIGHT +#define BARTAB_STACKWEIGHT 1 // stack weight compared to hidden and floating window titles +#endif +#ifndef BARTAB_HIDDENWEIGHT +#define BARTAB_HIDDENWEIGHT 1 // hidden window title weight +#endif +#ifndef BARTAB_FLOATWEIGHT +#define BARTAB_FLOATWEIGHT 1 // floating window title weight, set to 0 to not show floating windows +#endif + +int +width_bartabgroups(Bar *bar, BarArg *a) +{ + return a->w; +} + +int +draw_bartabgroups(Bar *bar, BarArg *a) +{ + drw_rect(drw, a->x, a->y, a->w, a->h, 1, 1); + return bartabcalculate(bar->mon, a->x, a->w, -1, bartabdraw, NULL, a); +} + +int +click_bartabgroups(Bar *bar, Arg *arg, BarArg *a) +{ + bartabcalculate(bar->mon, 0, a->w, a->x, bartabclick, arg, a); + return ClkWinTitle; +} + +void +bartabdraw(Monitor *m, Client *c, int unused, int x, int w, int groupactive, Arg *arg, BarArg *barg) +{ + if (!c) + return; + int i, nclienttags = 0, nviewtags = 0, pad = lrpad / 2; + drw_setscheme(drw, scheme[ + m->sel == c + #ifdef HIDDEN + && HIDDEN(c) + ? SchemeHidSel + : HIDDEN(c) + ? SchemeHidNorm + : m->sel == c + #endif + ? SchemeSel + : groupactive + ? SchemeTitleSel + : SchemeTitleNorm + ]); + if (w <= TEXTW("A") - lrpad + pad) // reduce text padding if wintitle is too small + pad = (w - TEXTW("A") + lrpad < 0 ? 0 : (w - TEXTW("A") + lrpad) / 2); + #if BAR_CENTEREDWINDOWNAME_PATCH + else if (TEXTW(c->name) < w) + pad = (w - TEXTW(c->name) + lrpad) / 2; + #endif // BAR_CENTEREDWINDOWNAME_PATCH + + #if BAR_WINICON_PATCH + drw_text(drw, x, barg->y, w, barg->h, pad + (c->icon ? c->icon->width + ICONSPACING : 0), c->name, 0, False); + if (c->icon) + drw_img(drw, x + pad, barg->y + (barg->h - c->icon->height) / 2, c->icon, tmpicon); + #else + drw_text(drw, x, barg->y, w, barg->h, pad, c->name, 0, False); + #endif // BAR_WINICON_PATCH + + drawstateindicator(m, c, 1, x, barg->y, w, barg->h, 0, 0, c->isfixed); + + if (BARTAB_BORDERS) { + XSetForeground(drw->dpy, drw->gc, scheme[SchemeSel][ColBorder].pixel); + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, barg->y, 1, barg->h); + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x + w - (x + w >= barg->w ? 1 : 0), barg->y, 1, barg->h); + } + /* Optional tags icons */ + for (i = 0; i < NUMTAGS; i++) { + if ((m->tagset[m->seltags] >> i) & 1) + nviewtags++; + if ((c->tags >> i) & 1) + nclienttags++; + } + + if (TAGSINDICATOR == 2 || nclienttags > 1 || nviewtags > 1) + drawindicator(m, c, 1, x, barg->y, w, barg->h, 0, 0, 0, INDICATOR_RIGHT_TAGS); +} + +#ifndef HIDDEN +#define HIDDEN(C) 0 +#endif + +void +bartabclick(Monitor *m, Client *c, int passx, int x, int w, int unused, Arg *arg, BarArg *barg) +{ + if (passx >= x && passx <= x + w) + arg->v = c; +} + +int +bartabcalculate( + Monitor *m, int offx, int tabw, int passx, + void(*tabfn)(Monitor *, Client *, int, int, int, int, Arg *arg, BarArg *barg), + Arg *arg, BarArg *barg +) { + Client *c; + int + i, clientsnmaster = 0, clientsnstack = 0, clientsnfloating = 0, clientsnhidden = 0, + masteractive = 0, fulllayout = 0, + x = offx, w, r, num = 0, den, tgactive; + + for (i = 0; i < LENGTH(bartabmonfns); i++) + if (m ->lt[m->sellt]->arrange == bartabmonfns[i]) { + fulllayout = 1; + break; + } + + for (i = 0, c = m->clients; c; c = c->next) { + if (!ISVISIBLE(c)) + continue; + if (HIDDEN(c)) { + clientsnhidden++; + continue; + } + if (c->isfloating) { + clientsnfloating++; + continue; + } + if (m->sel == c) + masteractive = i < m->nmaster; + if (i < m->nmaster) + clientsnmaster++; + else + clientsnstack++; + i++; + } + + if (clientsnmaster + clientsnstack + clientsnfloating + clientsnhidden == 0) + return 0; + + tgactive = 1; + num = tabw; + /* floating mode */ + if ((fulllayout && BARTAB_FLOATWEIGHT) || clientsnmaster + clientsnstack == 0 || !m->lt[m->sellt]->arrange) { + den = clientsnmaster + clientsnstack + clientsnfloating + clientsnhidden; + r = num % den; + w = num / den; + for (c = m->clients, i = 0; c; c = c->next) { + if (!ISVISIBLE(c)) + continue; + tabfn(m, c, passx, x, w + (i < r ? 1 : 0), tgactive, arg, barg); + x += w + (i < r ? 1 : 0); + i++; + } + /* no master and stack mode, e.g. monocole, grid layouts, fibonacci */ + } else if (fulllayout) { + den = clientsnmaster + clientsnstack + clientsnhidden; + r = num % den; + w = num / den; + for (c = m->clients, i = 0; c; c = c->next) { + if (!ISVISIBLE(c) || (c->isfloating && !HIDDEN(c))) + continue; + tabfn(m, c, passx, x, w + (i < r ? 1 : 0), tgactive, arg, barg); + x += w + (i < r ? 1 : 0); + i++; + } + /* tiled mode */ + } else { + den = clientsnmaster; + c = m->clients; + i = 0; + if (den) { + if (clientsnstack + clientsnfloating * BARTAB_FLOATWEIGHT + clientsnhidden) { + tgactive = masteractive; + num = tabw * m->mfact; + } + r = num % den; + w = num / den; + for (; c && i < m->nmaster; c = c->next) { // tiled master + if (!ISVISIBLE(c) || c->isfloating || HIDDEN(c)) + continue; + tabfn(m, c, passx, x, w + (i < r ? 1 : 0), tgactive, arg, barg); + x += w + (i < r ? 1 : 0); + i++; + } + tgactive = !tgactive; + num = tabw - num; + } + + den = clientsnstack * BARTAB_STACKWEIGHT + clientsnfloating * BARTAB_FLOATWEIGHT + clientsnhidden * BARTAB_HIDDENWEIGHT; + if (!den) + return 1; + + r = num % den; + w = num / den; + #if BARTAB_STACKWEIGHT + for (; c; c = c->next) { // tiled stack + if (!ISVISIBLE(c) || HIDDEN(c) || c->isfloating) + continue; + tabfn(m, c, passx, x, w * BARTAB_STACKWEIGHT + (i - m->nmaster < r ? 1 : 0), tgactive, arg, barg); + x += w * BARTAB_STACKWEIGHT + (i - m->nmaster < r ? 1 : 0); + i++; + } + #endif // BARTAB_STACKWEIGHT + + #if BARTAB_HIDDENWEIGHT + for (c = m->clients; c; c = c->next) { // hidden windows + if (!ISVISIBLE(c) || !HIDDEN(c)) + continue; + tabfn(m, c, passx, x, w * BARTAB_HIDDENWEIGHT + (i - m->nmaster < r ? 1 : 0), tgactive, arg, barg); + x += w * BARTAB_HIDDENWEIGHT + (i - m->nmaster < r ? 1 : 0); + i++; + } + #endif // BARTAB_HIDDENWEIGHT + + #if BARTAB_FLOATWEIGHT + for (c = m->clients; c; c = c->next) { // floating windows + if (!ISVISIBLE(c) || HIDDEN(c) || !c->isfloating) + continue; + tabfn(m, c, passx, x, w * BARTAB_FLOATWEIGHT + (i - m->nmaster < r ? 1 : 0), tgactive, arg, barg); + x += w * BARTAB_FLOATWEIGHT + (i - m->nmaster < r ? 1 : 0); + i++; + } + #endif // BARTAB_FLOATWEIGHT + } + return 1; +} + diff --git a/patch/bar_tabgroups.h b/patch/bar_tabgroups.h new file mode 100644 index 0000000..75a8512 --- /dev/null +++ b/patch/bar_tabgroups.h @@ -0,0 +1,8 @@ +static int width_bartabgroups(Bar *bar, BarArg *a); +static int draw_bartabgroups(Bar *bar, BarArg *a); +static int click_bartabgroups(Bar *bar, Arg *arg, BarArg *a); + +static void bartabdraw(Monitor *m, Client *c, int unused, int x, int w, int groupactive, Arg *arg, BarArg *barg); +static void bartabclick(Monitor *m, Client *c, int passx, int x, int w, int unused, Arg *arg, BarArg *barg); +static int bartabcalculate(Monitor *m, int offx, int w, int passx, void(*tabfn)(Monitor *, Client *, int, int, int, int, Arg *arg, BarArg *barg), Arg *arg, BarArg *barg); + diff --git a/patch/bar_taggrid.c b/patch/bar_taggrid.c new file mode 100644 index 0000000..675c8de --- /dev/null +++ b/patch/bar_taggrid.c @@ -0,0 +1,150 @@ +int +width_taggrid(Bar *bar, BarArg *a) +{ + return (a->h / 2) * (NUMTAGS / tagrows + ((NUMTAGS % tagrows > 0) ? 1 : 0)) + lrpad; +} + +int +draw_taggrid(Bar *bar, BarArg *a) +{ + unsigned int x, y, h, max_x = 0, columns, occ = 0; + int invert, i,j, k; + Client *c; + + for (c = bar->mon->clients; c; c = c->next) + occ |= c->tags; + + max_x = x = a->x + lrpad / 2; + h = a->h / tagrows - 1; + y = a->y; + columns = NUMTAGS / tagrows + ((NUMTAGS % tagrows > 0) ? 1 : 0); + + /* Firstly we will fill the borders of squares */ + XSetForeground(drw->dpy, drw->gc, scheme[SchemeTagsNorm][ColBg].pixel); + XFillRectangle(dpy, drw->drawable, drw->gc, x, y, h*columns + 1, a->h); + + /* We will draw NUMTAGS squares in tagraws raws. */ + for (j = 0, i = 0; j < tagrows; j++) { + x = a->x + lrpad / 2; + for (k = 0; k < columns; k++, i++) { + if (i < NUMTAGS) { + invert = bar->mon->tagset[bar->mon->seltags] & 1 << i ? 0 : 1; + + /* Select active color for current square */ + XSetForeground(drw->dpy, drw->gc, !invert ? scheme[SchemeTagsSel][ColBg].pixel : + scheme[SchemeTagsNorm][ColFg].pixel); + XFillRectangle(dpy, drw->drawable, drw->gc, x+1, y+1, h-1, h-1); + + /* Mark square if tag has client */ + if (occ & 1 << i) { + XSetForeground(drw->dpy, drw->gc, !invert ? scheme[SchemeTagsSel][ColFg].pixel : + scheme[SchemeTagsNorm][ColBg].pixel); + XFillRectangle(dpy, drw->drawable, drw->gc, x + 1, y + 1, + h / 2, h / 2); + } + } else { + XSetForeground(drw->dpy, drw->gc, scheme[SchemeTagsNorm][ColBg].pixel); + XFillRectangle(dpy, drw->drawable, drw->gc, x+1, y+1, h-1, h); + } + x += h; + if (x > max_x) { + max_x = x; + } + } + y += h; + } + return 1; +} + +int +click_taggrid(Bar *bar, Arg *arg, BarArg *a) +{ + unsigned int i, h, columns; + + h = a->h / tagrows - 1; + columns = NUMTAGS / tagrows + ((NUMTAGS % tagrows > 0) ? 1 : 0); + i = (a->x - lrpad / 2) / h + columns * (a->y / h); + if (i >= NUMTAGS) { + i = NUMTAGS - 1; + } + arg->ui = 1 << i; + return ClkTagBar; +} + +void +switchtag(const Arg *arg) +{ + unsigned int columns; + unsigned int new_tagset = 0; + unsigned int pos, i; + int col, row; + Arg new_arg; + + columns = NUMTAGS / tagrows + ((NUMTAGS % tagrows > 0) ? 1 : 0); + + for (i = 0; i < NUMTAGS; ++i) { + if (!(selmon->tagset[selmon->seltags] & 1 << i)) { + continue; + } + pos = i; + row = pos / columns; + col = pos % columns; + if (arg->ui & SWITCHTAG_UP) { /* UP */ + row --; + if (row < 0) { + row = tagrows - 1; + } + do { + pos = row * columns + col; + row --; + } while (pos >= NUMTAGS); + } + if (arg->ui & SWITCHTAG_DOWN) { /* DOWN */ + row ++; + if (row >= tagrows) { + row = 0; + } + pos = row * columns + col; + if (pos >= NUMTAGS) { + row = 0; + } + pos = row * columns + col; + } + if (arg->ui & SWITCHTAG_LEFT) { /* LEFT */ + col --; + if (col < 0) { + col = columns - 1; + } + do { + pos = row * columns + col; + col --; + } while (pos >= NUMTAGS); + } + if (arg->ui & SWITCHTAG_RIGHT) { /* RIGHT */ + col ++; + if (col >= columns) { + col = 0; + } + pos = row * columns + col; + if (pos >= NUMTAGS) { + col = 0; + pos = row * columns + col; + } + } + new_tagset |= 1 << pos; + } + new_arg.ui = new_tagset; + if (arg->ui & SWITCHTAG_TOGGLETAG) { + toggletag(&new_arg); + } + if (arg->ui & SWITCHTAG_TAG) { + tag(&new_arg); + } + if (arg->ui & SWITCHTAG_VIEW) { + view (&new_arg); + } + if (arg->ui & SWITCHTAG_TOGGLEVIEW) { + toggleview (&new_arg); + } +} + diff --git a/patch/bar_taggrid.h b/patch/bar_taggrid.h new file mode 100644 index 0000000..dfc60de --- /dev/null +++ b/patch/bar_taggrid.h @@ -0,0 +1,5 @@ +static int width_taggrid(Bar *bar, BarArg *a); +static int draw_taggrid(Bar *bar, BarArg *a); +static int click_taggrid(Bar *bar, Arg *arg, BarArg *a); +static void switchtag(const Arg *arg); + diff --git a/patch/bar_tagicons.c b/patch/bar_tagicons.c new file mode 100644 index 0000000..235a6e2 --- /dev/null +++ b/patch/bar_tagicons.c @@ -0,0 +1,21 @@ +char * +tagicon(Monitor *m, int tag) +{ + #if BAR_ALTTAGSDECORATION_PATCH + Client *c; + #endif // BAR_ALTTAGSDECORATION_PATCH + int tagindex = tag + NUMTAGS * m->index; + if (tagindex >= LENGTH(tagicons[DEFAULT_TAGS])) + tagindex = tagindex % LENGTH(tagicons[DEFAULT_TAGS]); + #if BAR_ALTTAGSDECORATION_PATCH + for (c = m->clients; c && (!(c->tags & 1 << tag) || HIDDEN(c)); c = c->next); + if (c) + return tagicons[ALT_TAGS_DECORATION][tagindex]; + #endif // BAR_ALTTAGSDECORATION_PATCH + #if BAR_ALTERNATIVE_TAGS_PATCH + if (m->alttag) + return tagicons[ALTERNATIVE_TAGS][tagindex]; + #endif // BAR_ALTERNATIVE_TAGS_PATCH + return tagicons[DEFAULT_TAGS][tagindex]; +} + diff --git a/patch/bar_tagicons.h b/patch/bar_tagicons.h new file mode 100644 index 0000000..16fad2a --- /dev/null +++ b/patch/bar_tagicons.h @@ -0,0 +1,8 @@ +enum { + DEFAULT_TAGS, + ALTERNATIVE_TAGS, + ALT_TAGS_DECORATION, +}; + +static char * tagicon(Monitor *m, int tag); + diff --git a/patch/bar_tags.c b/patch/bar_tags.c new file mode 100644 index 0000000..c00818d --- /dev/null +++ b/patch/bar_tags.c @@ -0,0 +1,89 @@ +int +width_tags(Bar *bar, BarArg *a) +{ + int w, i; + #if BAR_HIDEVACANTTAGS_PATCH + Client *c; + unsigned int occ = 0; + for (c = bar->mon->clients; c; c = c->next) + occ |= c->tags == 255 ? 0 : c->tags; + #endif // BAR_HIDEVACANTTAGS_PATCH + + for (w = 0, i = 0; i < NUMTAGS; i++) { + #if BAR_HIDEVACANTTAGS_PATCH + if (!(occ & 1 << i || bar->mon->tagset[bar->mon->seltags] & 1 << i)) + continue; + #endif // BAR_HIDEVACANTTAGS_PATCH + w += TEXTW(tagicon(bar->mon, i)); + } + return w; +} + +int +draw_tags(Bar *bar, BarArg *a) +{ + int invert; + int w, x = a->x; + unsigned int i, occ = 0, urg = 0; + char *icon; + Client *c; + Monitor *m = bar->mon; + + for (c = m->clients; c; c = c->next) { + #if BAR_HIDEVACANTTAGS_PATCH + occ |= c->tags == 255 ? 0 : c->tags; + #else + occ |= c->tags; + #endif // BAR_HIDEVACANTTAGS_PATCH + if (c->isurgent) + urg |= c->tags; + } + for (i = 0; i < NUMTAGS; i++) { + #if BAR_HIDEVACANTTAGS_PATCH + /* do not draw vacant tags */ + if (!(occ & 1 << i || m->tagset[m->seltags] & 1 << i)) + continue; + #endif // BAR_HIDEVACANTTAGS_PATCH + + icon = tagicon(bar->mon, i); + invert = 0; + w = TEXTW(icon); + drw_setscheme(drw, scheme[ + m->tagset[m->seltags] & 1 << i + ? SchemeTagsSel + : urg & 1 << i + ? SchemeUrg + : SchemeTagsNorm + ]); + drw_text(drw, x, a->y, w, a->h, lrpad / 2, icon, invert, False); + drawindicator(m, NULL, occ, x, a->y, w, a->h, i, -1, invert, tagindicatortype); + x += w; + } + + return 1; +} + +int +click_tags(Bar *bar, Arg *arg, BarArg *a) +{ + int i = 0, x = lrpad / 2; + #if BAR_HIDEVACANTTAGS_PATCH + Client *c; + unsigned int occ = 0; + for (c = bar->mon->clients; c; c = c->next) + occ |= c->tags == 255 ? 0 : c->tags; + #endif // BAR_HIDEVACANTTAGS_PATCH + + do { + #if BAR_HIDEVACANTTAGS_PATCH + if (!(occ & 1 << i || bar->mon->tagset[bar->mon->seltags] & 1 << i)) + continue; + #endif // BAR_HIDEVACANTTAGS_PATCH + x += TEXTW(tagicon(bar->mon, i)); + } while (a->x >= x && ++i < NUMTAGS); + if (i < NUMTAGS) { + arg->ui = 1 << i; + } + return ClkTagBar; +} + diff --git a/patch/bar_tags.h b/patch/bar_tags.h new file mode 100644 index 0000000..9261261 --- /dev/null +++ b/patch/bar_tags.h @@ -0,0 +1,4 @@ +static int width_tags(Bar *bar, BarArg *a); +static int draw_tags(Bar *bar, BarArg *a); +static int click_tags(Bar *bar, Arg *arg, BarArg *a); + diff --git a/patch/bar_vtcolors.c b/patch/bar_vtcolors.c new file mode 100644 index 0000000..7c66e2d --- /dev/null +++ b/patch/bar_vtcolors.c @@ -0,0 +1,69 @@ +void +get_vt_colors(void) +{ + char *cfs[3] = { + "/sys/module/vt/parameters/default_red", + "/sys/module/vt/parameters/default_grn", + "/sys/module/vt/parameters/default_blu", + }; + char vtcs[16][8]; + char tk[] = ","; + char cl[64]; + char *tp = NULL; + FILE *fp; + size_t r; + int i, c, n, len; + for (i = 0; i < 16; i++) + strcpy(vtcs[i], "#000000"); + + for (i = 0, r = 0; i < 3; i++) { + if ((fp = fopen(cfs[i], "r")) == NULL) + continue; + while ((cl[r] = fgetc(fp)) != EOF && cl[r] != '\n') + r++; + cl[r] = '\0'; + for (c = 0, tp = cl, n = 0; c < 16; c++, tp++) { + if ((r = strcspn(tp, tk)) == -1) + break; + for (n = 0; r && *tp >= 48 && *tp < 58; r--, tp++) + n = n * 10 - 48 + *tp; + vtcs[c][i * 2 + 1] = n / 16 < 10 ? n / 16 + 48 : n / 16 + 87; + vtcs[c][i * 2 + 2] = n % 16 < 10 ? n % 16 + 48 : n % 16 + 87; + } + fclose(fp); + } + + len = LENGTH(colors); + if (len > LENGTH(color_ptrs)) + len = LENGTH(color_ptrs); + for (i = 0; i < len; i++) { + for (c = 0; c < ColCount; c++) { + n = color_ptrs[i][c]; + if (n > -1 && strlen(colors[i][c]) >= strlen(vtcs[n])) + memcpy(colors[i][c], vtcs[n], 7); + } + } +} + +int get_luminance(char *r) +{ + char *c = r; + int n[3] = {0}; + int i = 0; + + while (*c) { + if (*c >= 48 && *c < 58) + n[i / 2] = n[i / 2] * 16 - 48 + *c; + else if (*c >= 65 && *c < 71) + n[i / 2] = n[i / 2] * 16 - 55 + *c; + else if (*c >= 97 && *c < 103) + n[i / 2] = n[i / 2] * 16 - 87 + *c; + else + i--; + i++; + c++; + } + + return (0.299 * n[0] + 0.587 * n[1] + 0.114 * n[2]) / 2.55; +} + diff --git a/patch/bar_vtcolors.h b/patch/bar_vtcolors.h new file mode 100644 index 0000000..8c5d6c1 --- /dev/null +++ b/patch/bar_vtcolors.h @@ -0,0 +1,3 @@ +static void get_vt_colors(void); +static int get_luminance(char *rgb); + diff --git a/patch/bar_winicon.c b/patch/bar_winicon.c new file mode 100644 index 0000000..5ae7fe7 --- /dev/null +++ b/patch/bar_winicon.c @@ -0,0 +1,146 @@ +static uint32_t tmpicon[ICONSIZE * ICONSIZE]; + +static uint32_t prealpha(uint32_t p) { + uint8_t a = p >> 24u; + uint32_t rb = (a * (p & 0xFF00FFu)) >> 8u; + uint32_t g = (a * (p & 0x00FF00u)) >> 8u; + return (rb & 0xFF00FFu) | (g & 0x00FF00u) | ((~a) << 24u); +} + +#if BAR_ALPHA_PATCH +static uint8_t div255(uint16_t x) { return (x*0x8081u) >> 23u; } +static uint32_t blend(uint32_t p1rb, uint32_t p1g, uint8_t p1a, uint32_t p2) { + uint8_t a = p2 >> 24u; + uint32_t rb = (p2 & 0xFF00FFu) + ( (a * p1rb) >> 8u ); + uint32_t g = (p2 & 0x00FF00u) + ( (a * p1g) >> 8u ); + return (rb & 0xFF00FFu) | (g & 0x00FF00u) | div255(~a * 255u + a * p1a) << 24u; +} + +void +drw_img(Drw *drw, int x, int y, XImage *img, uint32_t *tmp) +{ + if (!drw || !drw->scheme) + return; + uint32_t *data = (uint32_t *)img->data, p = drw->scheme[ColBg].pixel, + prb = p & 0xFF00FFu, pg = p & 0x00FF00u; + uint8_t pa = p >> 24u; + int icsz = img->width * img->height, i; + for (i = 0; i < icsz; ++i) tmp[i] = blend(prb, pg, pa, data[i]); + + img->data = (char *) tmp; + XPutImage(drw->dpy, drw->drawable, drw->gc, img, 0, 0, x, y, img->width, img->height); + img->data = (char *) data; +} +#else +static uint32_t blend(uint32_t p1rb, uint32_t p1g, uint32_t p2) { + uint8_t a = p2 >> 24u; + uint32_t rb = (p2 & 0xFF00FFu) + ( (a * p1rb) >> 8u ); + uint32_t g = (p2 & 0x00FF00u) + ( (a * p1g) >> 8u ); + return (rb & 0xFF00FFu) | (g & 0x00FF00u) | ((~a) << 24u); +} + +void +drw_img(Drw *drw, int x, int y, XImage *img, uint32_t *tmp) +{ + if (!drw || !drw->scheme) + return; + uint32_t *data = (uint32_t *)img->data, p = drw->scheme[ColBg].pixel, prb = p & 0xFF00FFu, pg = p & 0x00FF00u; + int icsz = img->width * img->height, i; + for (i = 0; i < icsz; ++i) tmp[i] = blend(prb, pg, data[i]); + img->data = (char *) tmp; + XPutImage(drw->dpy, drw->drawable, drw->gc, img, 0, 0, x, y, img->width, img->height); + img->data = (char *) data; +} +#endif // BAR_ALPHA_PATCH + +XImage * +geticonprop(Window win) +{ + int format; + unsigned long n, extra, *p = NULL; + Atom real; + + if (XGetWindowProperty(dpy, win, netatom[NetWMIcon], 0L, LONG_MAX, False, AnyPropertyType, + &real, &format, &n, &extra, (unsigned char **)&p) != Success) + return NULL; + if (n == 0 || format != 32) { XFree(p); return NULL; } + + unsigned long *bstp = NULL; + uint32_t w, h, sz; + + { + const unsigned long *end = p + n; + unsigned long *i; + uint32_t bstd = UINT32_MAX, d, m; + for (i = p; i < end - 1; i += sz) { + if ((w = *i++) > UINT16_MAX || (h = *i++) > UINT16_MAX) { XFree(p); return NULL; } + if ((sz = w * h) > end - i) break; + if ((m = w > h ? w : h) >= ICONSIZE && (d = m - ICONSIZE) < bstd) { bstd = d; bstp = i; } + } + if (!bstp) { + for (i = p; i < end - 1; i += sz) { + if ((w = *i++) > UINT16_MAX || (h = *i++) > UINT16_MAX) { XFree(p); return NULL; } + if ((sz = w * h) > end - i) break; + if ((d = ICONSIZE - (w > h ? w : h)) < bstd) { bstd = d; bstp = i; } + } + } + if (!bstp) { XFree(p); return NULL; } + } + + if ((w = *(bstp - 2)) == 0 || (h = *(bstp - 1)) == 0) { XFree(p); return NULL; } + + uint32_t icw, ich, icsz; + if (w <= h) { + ich = ICONSIZE; icw = w * ICONSIZE / h; + if (icw == 0) icw = 1; + } + else { + icw = ICONSIZE; ich = h * ICONSIZE / w; + if (ich == 0) ich = 1; + } + icsz = icw * ich; + + uint32_t i; +#if ULONG_MAX > UINT32_MAX + uint32_t *bstp32 = (uint32_t *)bstp; + for (sz = w * h, i = 0; i < sz; ++i) bstp32[i] = bstp[i]; +#endif + uint32_t *icbuf = malloc(icsz << 2); if(!icbuf) { XFree(p); return NULL; } + if (w == icw && h == ich) memcpy(icbuf, bstp, icsz << 2); + else { + Imlib_Image origin = imlib_create_image_using_data(w, h, (DATA32 *)bstp); + if (!origin) { XFree(p); free(icbuf); return NULL; } + imlib_context_set_image(origin); + imlib_image_set_has_alpha(1); + Imlib_Image scaled = imlib_create_cropped_scaled_image(0, 0, w, h, icw, ich); + imlib_free_image_and_decache(); + if (!scaled) { XFree(p); free(icbuf); return NULL; } + imlib_context_set_image(scaled); + imlib_image_set_has_alpha(1); + memcpy(icbuf, imlib_image_get_data_for_reading_only(), icsz << 2); + imlib_free_image_and_decache(); + } + XFree(p); + for (i = 0; i < icsz; ++i) icbuf[i] = prealpha(icbuf[i]); + #if BAR_ALPHA_PATCH + return XCreateImage(dpy, drw->visual, drw->depth, ZPixmap, 0, (char *)icbuf, icw, ich, 32, 0); + #else + return XCreateImage(dpy, DefaultVisual(dpy, screen), DefaultDepth(dpy, screen), ZPixmap, 0, (char *)icbuf, icw, ich, 32, 0); + #endif // BAR_ALPHA_PATCH +} + +void +freeicon(Client *c) +{ + if (c->icon) { + XDestroyImage(c->icon); + c->icon = NULL; + } +} + +void +updateicon(Client *c) +{ + freeicon(c); + c->icon = geticonprop(c->win); +}
\ No newline at end of file diff --git a/patch/bar_winicon.h b/patch/bar_winicon.h new file mode 100644 index 0000000..791182e --- /dev/null +++ b/patch/bar_winicon.h @@ -0,0 +1,8 @@ +#include <Imlib2.h> +#include <limits.h> +#include <stdint.h> + +void drw_img(Drw *drw, int x, int y, XImage *img, uint32_t *tmp); +static XImage *geticonprop(Window win); +static void freeicon(Client *c); +static void updateicon(Client *c);
\ No newline at end of file diff --git a/patch/bar_wintitle.c b/patch/bar_wintitle.c new file mode 100644 index 0000000..d2241d2 --- /dev/null +++ b/patch/bar_wintitle.c @@ -0,0 +1,59 @@ +int +width_wintitle(Bar *bar, BarArg *a) +{ + return a->w; +} + +int +draw_wintitle(Bar *bar, BarArg *a) +{ + #if BAR_TITLE_LEFT_PAD_PATCH && BAR_TITLE_RIGHT_PAD_PATCH + int x = a->x + lrpad / 2, w = a->w - lrpad; + #elif BAR_TITLE_LEFT_PAD_PATCH + int x = a->x + lrpad / 2, w = a->w - lrpad / 2; + #elif BAR_TITLE_RIGHT_PAD_PATCH + int x = a->x, w = a->w - lrpad / 2; + #else + int x = a->x, w = a->w; + #endif // BAR_TITLE_LEFT_PAD_PATCH | BAR_TITLE_RIGHT_PAD_PATCH + Monitor *m = bar->mon; + Client *c = m->sel; + int pad = lrpad / 2; + + if (!c) { + drw_setscheme(drw, scheme[SchemeTitleNorm]); + drw_rect(drw, x, a->y, w, a->h, 1, 1); + return 0; + } + + drw_setscheme(drw, scheme[m == selmon ? SchemeTitleSel : SchemeTitleNorm]); + #if BAR_IGNORE_XFT_ERRORS_WHEN_DRAWING_TEXT_PATCH + XSetErrorHandler(xerrordummy); + #endif // BAR_IGNORE_XFT_ERRORS_WHEN_DRAWING_TEXT_PATCH + #if BAR_CENTEREDWINDOWNAME_PATCH + if (TEXTW(c->name) < w) + pad = (w - TEXTW(c->name) + lrpad) / 2; + #endif // BAR_CENTEREDWINDOWNAME_PATCH + + #if BAR_WINICON_PATCH + drw_text(drw, x, a->y, w, a->h, pad + (c->icon ? c->icon->width + ICONSPACING : 0), c->name, 0, False); + if (c->icon) + drw_img(drw, x + pad, a->y + (a->h - c->icon->height) / 2, c->icon, tmpicon); + #else + drw_text(drw, x, a->y, w, a->h, pad, c->name, 0, False); + #endif // BAR_WINICON_PATCH + + #if BAR_IGNORE_XFT_ERRORS_WHEN_DRAWING_TEXT_PATCH + XSync(dpy, False); + XSetErrorHandler(xerror); + #endif // BAR_IGNORE_XFT_ERRORS_WHEN_DRAWING_TEXT_PATCH + drawstateindicator(m, c, 1, x, a->y, w, a->h, 0, 0, c->isfixed); + return 1; +} + +int +click_wintitle(Bar *bar, Arg *arg, BarArg *a) +{ + return ClkWinTitle; +} + diff --git a/patch/bar_wintitle.h b/patch/bar_wintitle.h new file mode 100644 index 0000000..7e8cce5 --- /dev/null +++ b/patch/bar_wintitle.h @@ -0,0 +1,4 @@ +static int width_wintitle(Bar *bar, BarArg *a); +static int draw_wintitle(Bar *bar, BarArg *a); +static int click_wintitle(Bar *bar, Arg *arg, BarArg *a); + diff --git a/patch/bar_wintitle_floating.c b/patch/bar_wintitle_floating.c new file mode 100644 index 0000000..3a15b8e --- /dev/null +++ b/patch/bar_wintitle_floating.c @@ -0,0 +1,46 @@ +int +width_wintitle_floating(Bar *bar, BarArg *a) +{ + return a->w; +} + +int +draw_wintitle_floating(Bar *bar, BarArg *a) +{ + drw_rect(drw, a->x, a->y, a->w, a->h, 1, 1); + return calc_wintitle_floating(bar->mon, a->x, a->w, -1, flextitledraw, NULL, a); +} + +int +click_wintitle_floating(Bar *bar, Arg *arg, BarArg *a) +{ + calc_wintitle_floating(bar->mon, 0, a->w, a->x, flextitleclick, arg, a); + return ClkWinTitle; +} + +int +calc_wintitle_floating( + Monitor *m, int offx, int tabw, int passx, + void(*tabfn)(Monitor *, Client *, int, int, int, int, Arg *arg, BarArg *barg), + Arg *arg, BarArg *barg +) { + Client *c; + int clientsnfloating = 0, w, r; + int groupactive = GRP_FLOAT; + + for (c = m->clients; c; c = c->next) { + if (!ISVISIBLE(c) || HIDDEN(c)) + continue; + if (c->isfloating) + clientsnfloating++; + } + + if (!clientsnfloating) + return 0; + + w = tabw / clientsnfloating; + r = tabw % clientsnfloating; + c = flextitledrawarea(m, m->clients, offx, r, w, clientsnfloating, SCHEMEFOR(GRP_FLOAT), 0, 0, 1, passx, tabfn, arg, barg); + return 1; +} + diff --git a/patch/bar_wintitle_floating.h b/patch/bar_wintitle_floating.h new file mode 100644 index 0000000..e73d53a --- /dev/null +++ b/patch/bar_wintitle_floating.h @@ -0,0 +1,9 @@ +static int width_wintitle_floating(Bar *bar, BarArg *a); +static int draw_wintitle_floating(Bar *bar, BarArg *a); +static int click_wintitle_floating(Bar *bar, Arg *arg, BarArg *a); +static int calc_wintitle_floating( + Monitor *m, int offx, int tabw, int passx, + void(*tabfn)(Monitor *, Client *, int, int, int, int, Arg *arg, BarArg *barg), + Arg *arg, BarArg *barg +); + diff --git a/patch/bar_wintitle_hidden.c b/patch/bar_wintitle_hidden.c new file mode 100644 index 0000000..2d2414c --- /dev/null +++ b/patch/bar_wintitle_hidden.c @@ -0,0 +1,46 @@ +int +width_wintitle_hidden(Bar *bar, BarArg *a) +{ + return a->w; +} + +int +draw_wintitle_hidden(Bar *bar, BarArg *a) +{ + drw_rect(drw, a->x, a->y, a->w, a->h, 1, 1); + return calc_wintitle_hidden(bar->mon, a->x, a->w, -1, flextitledraw, NULL, a); +} + +int +click_wintitle_hidden(Bar *bar, Arg *arg, BarArg *a) +{ + calc_wintitle_hidden(bar->mon, 0, a->w, a->x, flextitleclick, arg, a); + return ClkWinTitle; +} + +int +calc_wintitle_hidden( + Monitor *m, int offx, int tabw, int passx, + void(*tabfn)(Monitor *, Client *, int, int, int, int, Arg *arg, BarArg *barg), + Arg *arg, BarArg *barg +) { + Client *c; + int clientsnhidden = 0, w, r; + int groupactive = GRP_HIDDEN; + + for (c = m->clients; c; c = c->next) { + if (!ISVISIBLE(c)) + continue; + if (HIDDEN(c)) + clientsnhidden++; + } + + if (!clientsnhidden) + return 0; + + w = tabw / clientsnhidden; + r = tabw % clientsnhidden; + c = flextitledrawarea(m, m->clients, offx, r, w, clientsnhidden, SCHEMEFOR(GRP_HIDDEN), 0, 1, 0, passx, tabfn, arg, barg); + return 1; +} + diff --git a/patch/bar_wintitle_hidden.h b/patch/bar_wintitle_hidden.h new file mode 100644 index 0000000..0495d49 --- /dev/null +++ b/patch/bar_wintitle_hidden.h @@ -0,0 +1,9 @@ +static int width_wintitle_hidden(Bar *bar, BarArg *a); +static int draw_wintitle_hidden(Bar *bar, BarArg *a); +static int click_wintitle_hidden(Bar *bar, Arg *arg, BarArg *a); +static int calc_wintitle_hidden( + Monitor *m, int offx, int tabw, int passx, + void(*tabfn)(Monitor *, Client *, int, int, int, int, Arg *arg, BarArg *barg), + Arg *arg, BarArg *barg +); + diff --git a/patch/bar_wintitleactions.c b/patch/bar_wintitleactions.c new file mode 100644 index 0000000..61a5d21 --- /dev/null +++ b/patch/bar_wintitleactions.c @@ -0,0 +1,94 @@ +void +hide(Client *c) { + + Client *n; + if (!c || HIDDEN(c)) + return; + + Window w = c->win; + static XWindowAttributes ra, ca; + + // more or less taken directly from blackbox's hide() function + XGrabServer(dpy); + XGetWindowAttributes(dpy, root, &ra); + XGetWindowAttributes(dpy, w, &ca); + // prevent UnmapNotify events + XSelectInput(dpy, root, ra.your_event_mask & ~SubstructureNotifyMask); + XSelectInput(dpy, w, ca.your_event_mask & ~StructureNotifyMask); + XUnmapWindow(dpy, w); + setclientstate(c, IconicState); + XSelectInput(dpy, root, ra.your_event_mask); + XSelectInput(dpy, w, ca.your_event_mask); + XUngrabServer(dpy); + + if (c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) { + for (n = c->snext; n && (!ISVISIBLE(n) || HIDDEN(n)); n = n->snext); + if (!n) + for (n = c->mon->stack; n && (!ISVISIBLE(n) || HIDDEN(n)); n = n->snext); + } else { + n = nexttiled(c); + if (!n) + n = prevtiled(c); + } + focus(n); + arrange(c->mon); +} + +void +show(Client *c) +{ + if (!c || !HIDDEN(c)) + return; + + XMapWindow(dpy, c->win); + setclientstate(c, NormalState); + arrange(c->mon); +} + +void +togglewin(const Arg *arg) +{ + Client *c = (Client*)arg->v; + if (!c) + return; + if (c == selmon->sel) + hide(c); + else { + if (HIDDEN(c)) + show(c); + focus(c); + restack(c->mon); + } +} + +Client * +prevtiled(Client *c) +{ + Client *p, *i; + for (p = NULL, i = c->mon->clients; c && i != c; i = i->next) + if (ISVISIBLE(i) && !HIDDEN(i)) + p = i; + return p; +} + +void +showhideclient(const Arg *arg) +{ + Client *c = (Client*)arg->v; + if (!c) + c = selmon->sel; + if (!c) + return; + + #if WARP_PATCH + force_warp = 1; + #endif // WARP_PATCH + if (HIDDEN(c)) { + show(c); + focus(c); + restack(c->mon); + } else { + hide(c); + } +} + diff --git a/patch/bar_wintitleactions.h b/patch/bar_wintitleactions.h new file mode 100644 index 0000000..0c8d5f0 --- /dev/null +++ b/patch/bar_wintitleactions.h @@ -0,0 +1,6 @@ +static void hide(Client *c); +static void show(Client *c); +static void togglewin(const Arg *arg); +static Client * prevtiled(Client *c); +static void showhideclient(const Arg *arg); + diff --git a/patch/cfacts.c b/patch/cfacts.c new file mode 100644 index 0000000..e491a41 --- /dev/null +++ b/patch/cfacts.c @@ -0,0 +1,24 @@ +void +setcfact(const Arg *arg) +{ + float f; + Client *c; + + c = selmon->sel; + + if (!arg || !c || !selmon->lt[selmon->sellt]->arrange) + return; + if (!arg->f) + f = 1.0; + else if (arg->f > 4.0) // set fact absolutely + f = arg->f - 4.0; + else + f = arg->f + c->cfact; + if (f < 0.25) + f = 0.25; + else if (f > 4.0) + f = 4.0; + c->cfact = f; + arrange(selmon); +} + diff --git a/patch/cfacts.h b/patch/cfacts.h new file mode 100644 index 0000000..97f9f1c --- /dev/null +++ b/patch/cfacts.h @@ -0,0 +1,2 @@ +static void setcfact(const Arg *arg); + diff --git a/patch/cmdcustomize.c b/patch/cmdcustomize.c new file mode 100644 index 0000000..9769660 --- /dev/null +++ b/patch/cmdcustomize.c @@ -0,0 +1,6 @@ +char* +help(void) +{ + return "usage: dwm [-hv] [-fn font] [-nb color] [-nf color] [-sb color] [-sf color]\n[-df font] [-dnf color] [-dnb color] [-dsf color] [-dsb color]\n"; +} + diff --git a/patch/cmdcustomize.h b/patch/cmdcustomize.h new file mode 100644 index 0000000..46b75cf --- /dev/null +++ b/patch/cmdcustomize.h @@ -0,0 +1,2 @@ +static char* help(); + diff --git a/patch/combo.c b/patch/combo.c new file mode 100644 index 0000000..058b1f9 --- /dev/null +++ b/patch/combo.c @@ -0,0 +1,50 @@ +static int combo = 0; + +#if !BAR_HOLDBAR_PATCH +void +keyrelease(XEvent *e) +{ + combo = 0; +} +#endif // !BAR_HOLDBAR_PATCH + +void +combotag(const Arg *arg) +{ + if (selmon->sel && arg->ui & TAGMASK) { + #if SWITCHTAG_PATCH + if (selmon->sel->switchtag) + selmon->sel->switchtag = 0; + #endif // SWITCHTAG_PATCH + if (combo) { + selmon->sel->tags |= arg->ui & TAGMASK; + } else { + combo = 1; + selmon->sel->tags = arg->ui & TAGMASK; + } + focus(NULL); + arrange(selmon); + } +} + +void +comboview(const Arg *arg) +{ + unsigned newtags = arg->ui & TAGMASK; + if (combo) { + selmon->tagset[selmon->seltags] |= newtags; + } else { + selmon->seltags ^= 1; /*toggle tagset*/ + combo = 1; + if (newtags) { + #if PERTAG_PATCH + pertagview(&((Arg) { .ui = newtags })); + #else + selmon->tagset[selmon->seltags] = newtags; + #endif // PERTAG_PATCH + } + } + focus(NULL); + arrange(selmon); +} + diff --git a/patch/combo.h b/patch/combo.h new file mode 100644 index 0000000..0c42136 --- /dev/null +++ b/patch/combo.h @@ -0,0 +1,6 @@ +#if !BAR_HOLDBAR_PATCH +static void keyrelease(XEvent *e); +#endif // !BAR_HOLDBAR_PATCH +static void combotag(const Arg *arg); +static void comboview(const Arg *arg); + diff --git a/patch/cool_autostart.c b/patch/cool_autostart.c new file mode 100644 index 0000000..ffd4ba3 --- /dev/null +++ b/patch/cool_autostart.c @@ -0,0 +1,29 @@ +/* dwm will keep pid's of processes from autostart array and kill them at quit */ +static pid_t *autostart_pids; +static size_t autostart_len; + +/* execute command from autostart array */ +static void +autostart_exec() +{ + const char *const *p; + size_t i = 0; + + /* count entries */ + for (p = autostart; *p; autostart_len++, p++) + while (*++p); + + autostart_pids = malloc(autostart_len * sizeof(pid_t)); + for (p = autostart; *p; i++, p++) { + if ((autostart_pids[i] = fork()) == 0) { + setsid(); + execvp(*p, (char *const *)p); + fprintf(stderr, "dwm: execvp %s\n", *p); + perror(" failed"); + _exit(EXIT_FAILURE); + } + /* skip arguments */ + while (*++p); + } +} + diff --git a/patch/cool_autostart.h b/patch/cool_autostart.h new file mode 100644 index 0000000..5534d99 --- /dev/null +++ b/patch/cool_autostart.h @@ -0,0 +1,2 @@ +static void autostart_exec(void); + diff --git a/patch/cyclelayouts.c b/patch/cyclelayouts.c new file mode 100644 index 0000000..81339bc --- /dev/null +++ b/patch/cyclelayouts.c @@ -0,0 +1,18 @@ +void +cyclelayout(const Arg *arg) +{ + Layout *l; + for (l = (Layout *)layouts; l != selmon->lt[selmon->sellt]; l++); + if (arg->i > 0) { + if (l->symbol && (l + 1)->symbol) + setlayout(&((Arg) { .v = (l + 1) })); + else + setlayout(&((Arg) { .v = layouts })); + } else { + if (l != layouts && (l - 1)->symbol) + setlayout(&((Arg) { .v = (l - 1) })); + else + setlayout(&((Arg) { .v = &layouts[LENGTH(layouts) - 2] })); + } +} + diff --git a/patch/cyclelayouts.h b/patch/cyclelayouts.h new file mode 100644 index 0000000..3647d88 --- /dev/null +++ b/patch/cyclelayouts.h @@ -0,0 +1,2 @@ +static void cyclelayout(const Arg *arg); + diff --git a/patch/decorationhints.c b/patch/decorationhints.c new file mode 100644 index 0000000..78be5ea --- /dev/null +++ b/patch/decorationhints.c @@ -0,0 +1,39 @@ +static Atom motifatom; + +void +updatemotifhints(Client *c) +{ + Atom real; + int format; + unsigned char *p = NULL; + unsigned long n, extra; + unsigned long *motif; + int width, height; + + if (!decorhints) + return; + + if (XGetWindowProperty(dpy, c->win, motifatom, 0L, 5L, False, motifatom, + &real, &format, &n, &extra, &p) == Success && p != NULL) { + motif = (unsigned long*)p; + if (motif[MWM_HINTS_FLAGS_FIELD] & MWM_HINTS_DECORATIONS) { + width = WIDTH(c); + height = HEIGHT(c); + + if (motif[MWM_HINTS_DECORATIONS_FIELD] & MWM_DECOR_ALL || + motif[MWM_HINTS_DECORATIONS_FIELD] & MWM_DECOR_BORDER || + motif[MWM_HINTS_DECORATIONS_FIELD] & MWM_DECOR_TITLE) + #if SETBORDERPX_PATCH + c->bw = c->oldbw = c->mon->borderpx; + #else + c->bw = c->oldbw = borderpx; + #endif // SETBORDERPX_PATCH + else + c->bw = c->oldbw = 0; + + resize(c, c->x, c->y, width - (2*c->bw), height - (2*c->bw), 0); + } + XFree(p); + } +} + diff --git a/patch/decorationhints.h b/patch/decorationhints.h new file mode 100644 index 0000000..e28f507 --- /dev/null +++ b/patch/decorationhints.h @@ -0,0 +1,9 @@ +#define MWM_HINTS_FLAGS_FIELD 0 +#define MWM_HINTS_DECORATIONS_FIELD 2 +#define MWM_HINTS_DECORATIONS (1 << 1) +#define MWM_DECOR_ALL (1 << 0) +#define MWM_DECOR_BORDER (1 << 1) +#define MWM_DECOR_TITLE (1 << 3) + +static void updatemotifhints(Client *c); + diff --git a/patch/distributetags.c b/patch/distributetags.c new file mode 100644 index 0000000..284b24c --- /dev/null +++ b/patch/distributetags.c @@ -0,0 +1,17 @@ +void +distributetags(const Arg *arg) +{ + unsigned int ui = 1; + int i = 0; + for (Client *c = selmon->clients; c; c = c->next) { + if (HIDDEN(c)) + continue; + if (!(c->tags & TAGMASK)) + continue; + c->tags = (ui << i) & TAGMASK; + i = (i + 1) % NUMTAGS; + } + focus(NULL); + arrange(selmon); +} + diff --git a/patch/distributetags.h b/patch/distributetags.h new file mode 100644 index 0000000..3b276d9 --- /dev/null +++ b/patch/distributetags.h @@ -0,0 +1,2 @@ +static void distributetags(const Arg *arg); + diff --git a/patch/dragcfact.c b/patch/dragcfact.c new file mode 100644 index 0000000..1b14513 --- /dev/null +++ b/patch/dragcfact.c @@ -0,0 +1,83 @@ +void +dragcfact(const Arg *arg) +{ + int prev_x, prev_y, dist_x, dist_y; + float fact; + Client *c; + XEvent ev; + Time lasttime = 0; + + if (!(c = selmon->sel)) + return; + if (c->isfloating) { + resizemouse(arg); + return; + } + #if !FAKEFULLSCREEN_PATCH + #if FAKEFULLSCREEN_CLIENT_PATCH + if (c->isfullscreen && !c->fakefullscreen) /* no support resizing fullscreen windows by mouse */ + return; + #else + if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ + return; + #endif // FAKEFULLSCREEN_CLIENT_PATCH + #endif // !FAKEFULLSCREEN_PATCH + restack(selmon); + + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurIronCross]->cursor, CurrentTime) != GrabSuccess) + return; + + #if WARP_PATCH + ignore_warp = 1; + #endif // WARP_PATCH + + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w/2, c->h/2); + + prev_x = prev_y = -999999; + + do { + XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); + switch(ev.type) { + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + if ((ev.xmotion.time - lasttime) <= (1000 / 60)) + continue; + lasttime = ev.xmotion.time; + if (prev_x == -999999) { + prev_x = ev.xmotion.x_root; + prev_y = ev.xmotion.y_root; + } + + dist_x = ev.xmotion.x - prev_x; + dist_y = ev.xmotion.y - prev_y; + + if (abs(dist_x) > abs(dist_y)) { + fact = (float) 4.0 * dist_x / c->mon->ww; + } else { + fact = (float) -4.0 * dist_y / c->mon->wh; + } + + if (fact) + setcfact(&((Arg) { .f = fact })); + + prev_x = ev.xmotion.x; + prev_y = ev.xmotion.y; + break; + } + } while (ev.type != ButtonRelease); + + #if WARP_PATCH + ignore_warp = 0; + #endif // WARP_PATCH + + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w/2, c->h/2); + + XUngrabPointer(dpy, CurrentTime); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + diff --git a/patch/dragcfact.h b/patch/dragcfact.h new file mode 100644 index 0000000..6a29783 --- /dev/null +++ b/patch/dragcfact.h @@ -0,0 +1,2 @@ +static void dragcfact(const Arg *arg); + diff --git a/patch/dragmfact.c b/patch/dragmfact.c new file mode 100644 index 0000000..ee0ed9c --- /dev/null +++ b/patch/dragmfact.c @@ -0,0 +1,232 @@ +void +dragmfact(const Arg *arg) +{ + unsigned int n; + int py, px; // pointer coordinates + int ax, ay, aw, ah; // area position, width and height + int center = 0, horizontal = 0, mirror = 0, fixed = 0; // layout configuration + double fact; + Monitor *m; + XEvent ev; + Time lasttime = 0; + + m = selmon; + + #if VANITYGAPS_PATCH + int oh, ov, ih, iv; + getgaps(m, &oh, &ov, &ih, &iv, &n); + #else + Client *c; + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + #endif // VANITYGAPS_PATCH + + ax = m->wx; + ay = m->wy; + ah = m->wh; + aw = m->ww; + + if (!n) + return; + #if FLEXTILE_DELUXE_LAYOUT + else if (m->lt[m->sellt]->arrange == &flextile) { + int layout = m->ltaxis[LAYOUT]; + if (layout < 0) { + mirror = 1; + layout *= -1; + } + if (layout > FLOATING_MASTER) { + layout -= FLOATING_MASTER; + fixed = 1; + } + + if (layout == SPLIT_HORIZONTAL || layout == SPLIT_HORIZONTAL_DUAL_STACK) + horizontal = 1; + else if (layout == SPLIT_CENTERED_VERTICAL && (fixed || n - m->nmaster > 1)) + center = 1; + else if (layout == FLOATING_MASTER) { + center = 1; + if (aw < ah) + horizontal = 1; + } + else if (layout == SPLIT_CENTERED_HORIZONTAL) { + if (fixed || n - m->nmaster > 1) + center = 1; + horizontal = 1; + } + } + #endif // FLEXTILE_DELUXE_LAYOUT + #if CENTEREDMASTER_LAYOUT + else if (m->lt[m->sellt]->arrange == ¢eredmaster && (fixed || n - m->nmaster > 1)) + center = 1; + #endif // CENTEREDMASTER_LAYOUT + #if CENTEREDFLOATINGMASTER_LAYOUT + else if (m->lt[m->sellt]->arrange == ¢eredfloatingmaster) + center = 1; + #endif // CENTEREDFLOATINGMASTER_LAYOUT + #if BSTACK_LAYOUT + else if (m->lt[m->sellt]->arrange == &bstack) + horizontal = 1; + #endif // BSTACK_LAYOUT + #if BSTACKHORIZ_LAYOUT + else if (m->lt[m->sellt]->arrange == &bstackhoriz) + horizontal = 1; + #endif // BSTACKHORIZ_LAYOUT + + /* do not allow mfact to be modified under certain conditions */ + if (!m->lt[m->sellt]->arrange // floating layout + || (!fixed && m->nmaster && n <= m->nmaster) // no master + #if MONOCLE_LAYOUT + || m->lt[m->sellt]->arrange == &monocle + #endif // MONOCLE_LAYOUT + #if GRIDMODE_LAYOUT + || m->lt[m->sellt]->arrange == &grid + #endif // GRIDMODE_LAYOUT + #if HORIZGRID_LAYOUT + || m->lt[m->sellt]->arrange == &horizgrid + #endif // HORIZGRID_LAYOUT + #if GAPPLESSGRID_LAYOUT + || m->lt[m->sellt]->arrange == &gaplessgrid + #endif // GAPPLESSGRID_LAYOUT + #if NROWGRID_LAYOUT + || m->lt[m->sellt]->arrange == &nrowgrid + #endif // NROWGRID_LAYOUT + #if FLEXTILE_DELUXE_LAYOUT + || (m->lt[m->sellt]->arrange == &flextile && m->ltaxis[LAYOUT] == NO_SPLIT) + #endif // FLEXTILE_DELUXE_LAYOUT + ) + return; + + #if VANITYGAPS_PATCH + ay += oh; + ax += ov; + aw -= 2*ov; + ah -= 2*oh; + #endif // VANITYGAPS_PATCH + + if (center) { + if (horizontal) { + px = ax + aw / 2; + #if VANITYGAPS_PATCH + py = ay + ah / 2 + (ah - 2*ih) * (m->mfact / 2.0) + ih / 2; + #else + py = ay + ah / 2 + ah * m->mfact / 2.0; + #endif // VANITYGAPS_PATCH + } else { // vertical split + #if VANITYGAPS_PATCH + px = ax + aw / 2 + (aw - 2*iv) * m->mfact / 2.0 + iv / 2; + #else + px = ax + aw / 2 + aw * m->mfact / 2.0; + #endif // VANITYGAPS_PATCH + py = ay + ah / 2; + } + } else if (horizontal) { + px = ax + aw / 2; + if (mirror) + #if VANITYGAPS_PATCH + py = ay + (ah - ih) * (1.0 - m->mfact) + ih / 2; + #else + py = ay + (ah * (1.0 - m->mfact)); + #endif // VANITYGAPS_PATCH + else + #if VANITYGAPS_PATCH + py = ay + ((ah - ih) * m->mfact) + ih / 2; + #else + py = ay + (ah * m->mfact); + #endif // VANITYGAPS_PATCH + } else { // vertical split + if (mirror) + #if VANITYGAPS_PATCH + px = ax + (aw - iv) * (1.0 - m->mfact) + iv / 2; + #else + px = ax + (aw * m->mfact); + #endif // VANITYGAPS_PATCH + else + #if VANITYGAPS_PATCH + px = ax + ((aw - iv) * m->mfact) + iv / 2; + #else + px = ax + (aw * m->mfact); + #endif // VANITYGAPS_PATCH + py = ay + ah / 2; + } + + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[horizontal ? CurResizeVertArrow : CurResizeHorzArrow]->cursor, CurrentTime) != GrabSuccess) + return; + + #if WARP_PATCH + ignore_warp = 1; + #endif // WARP_PATCH + + XWarpPointer(dpy, None, root, 0, 0, 0, 0, px, py); + + do { + XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); + switch(ev.type) { + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + if ((ev.xmotion.time - lasttime) <= (1000 / 40)) + continue; + if (lasttime != 0) { + px = ev.xmotion.x; + py = ev.xmotion.y; + } + lasttime = ev.xmotion.time; + + #if VANITYGAPS_PATCH + if (center) + if (horizontal) + if (py - ay > ah / 2) + fact = (double) 1.0 - (ay + ah - py - ih / 2) * 2 / (double) (ah - 2*ih); + else + fact = (double) 1.0 - (py - ay - ih / 2) * 2 / (double) (ah - 2*ih); + else + if (px - ax > aw / 2) + fact = (double) 1.0 - (ax + aw - px - iv / 2) * 2 / (double) (aw - 2*iv); + else + fact = (double) 1.0 - (px - ax - iv / 2) * 2 / (double) (aw - 2*iv); + else + if (horizontal) + fact = (double) (py - ay - ih / 2) / (double) (ah - ih); + else + fact = (double) (px - ax - iv / 2) / (double) (aw - iv); + #else + if (center) + if (horizontal) + if (py - ay > ah / 2) + fact = (double) 1.0 - (ay + ah - py) * 2 / (double) ah; + else + fact = (double) 1.0 - (py - ay) * 2 / (double) ah; + else + if (px - ax > aw / 2) + fact = (double) 1.0 - (ax + aw - px) * 2 / (double) aw; + else + fact = (double) 1.0 - (px - ax) * 2 / (double) aw; + else + if (horizontal) + fact = (double) (py - ay) / (double) ah; + else + fact = (double) (px - ax) / (double) aw; + #endif // VANITYGAPS_PATCH + + if (!center && mirror) + fact = 1.0 - fact; + + setmfact(&((Arg) { .f = 1.0 + fact })); + px = ev.xmotion.x; + py = ev.xmotion.y; + break; + } + } while (ev.type != ButtonRelease); + + #if WARP_PATCH + ignore_warp = 0; + #endif // WARP_PATCH + + XUngrabPointer(dpy, CurrentTime); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + diff --git a/patch/dragmfact.h b/patch/dragmfact.h new file mode 100644 index 0000000..a67432a --- /dev/null +++ b/patch/dragmfact.h @@ -0,0 +1,2 @@ +static void dragmfact(const Arg *arg); + diff --git a/patch/dwmc b/patch/dwmc new file mode 100755 index 0000000..3880428 --- /dev/null +++ b/patch/dwmc @@ -0,0 +1,130 @@ +#!/usr/bin/env bash + +signal() { + xsetroot -name "fsignal:$*" +} + +case $# in +1) + case $1 in + focusurgent) ;& + mirrorlayout) ;& + mpdcontrol) ;& + pushdown) ;& + pushup) ;& + self_restart) ;& + setlayout) ;& + setcfact) ;& + switchcol) ;& + view) ;& + viewall) ;& + viewtoleft) ;& + viewtoright) ;& + tagtoleft) ;& + tagtoright) ;& + tagandviewtoleft) ;& + tagandviewtoright) ;& + transfer) ;& + transferall) ;& + togglealttag) ;& + togglebar) ;& + togglefloating) ;& + togglefullscreen) ;& + fullscreen) ;& + togglefakefullscreen) ;& + togglesticky) ;& + togglehorizontalmax) ;& + toggleverticalmax) ;& + togglemax) ;& + togglegaps) ;& + defaultgaps) ;& + unfloatvisible) ;& + winview) ;& + xrdb) ;& + zoom) ;& + killclient) ;& + quit) + signal $1 + ;; + *) + echo "Unknown command ($1) or missing one argument." + exit 1 + ;; + esac + ;; +2) + case $1 in + cyclelayout) ;& + explace) ;& + moveplace) ;& + mpdchange) ;& + setkeymode) ;& + switchtag) ;& + togglescratch) ;& + view) + signal $1 ui $2 + ;; + viewex) ;& + toggleviewex) ;& + tagallmon) ;& + tagswapmon) ;& + tagex) ;& + toggletagex) ;& + setborderpx) ;& + setgaps) ;& + setlayoutex) ;& + setlayoutaxisex) ;& + swapfocus) ;& + focusstack) ;& + pushstack) ;& + inplacerotate) ;& + rotatestack) ;& + rotatelayoutaxis) ;& + incnmaster) ;& + incnstack) ;& + incrgaps) ;& + incrigaps) ;& + incrogaps) ;& + incrihgaps) ;& + incrivgaps) ;& + incrohgaps) ;& + incrovgaps) ;& + movestack) ;& + shiftview) ;& + shiftviewclients) ;& + focusmon) ;& + tagmon) + signal $1 i $2 + ;; + setcfact) ;& + setmfact) + signal $1 f $2 + ;; + *) + echo "Unknown command ($1) or too many arguments" + exit 1 + ;; + esac + ;; +5) + case $1 in + setgaps) + # Expects "setgaps oh ov ih iv" where -1 means to keep existing values + [ $2 = -1 ] && oh=128 || oh=$2 + [ $3 = -1 ] && ov=128 || ov=$3 + [ $4 = -1 ] && ih=128 || ih=$4 + [ $5 = -1 ] && iv=128 || iv=$5 + signal $1 i $(((oh << 24) + (ov << 16) + (ih << 8) + iv)) + ;; + *) + echo "Unknown command ($1) or too many arguments" + exit 1 + ;; + esac + ;; +*) + echo "Unknown command ($1) or too many arguments" + exit 1 + ;; +esac + diff --git a/patch/dwmc.c b/patch/dwmc.c new file mode 100644 index 0000000..b515f87 --- /dev/null +++ b/patch/dwmc.c @@ -0,0 +1,101 @@ +void +setlayoutex(const Arg *arg) +{ + setlayout(&((Arg) { .v = &layouts[arg->i] })); +} + +void +viewex(const Arg *arg) +{ + view(&((Arg) { .ui = 1 << arg->ui })); +} + +void +viewallex(const Arg *arg) +{ + #if SCRATCHPADS_PATCH + view(&((Arg){.ui = ~SPTAGMASK})); + #else + view(&((Arg){.ui = ~0})); + #endif // SCRATCHPADS_PATCH +} + +void +toggleviewex(const Arg *arg) +{ + toggleview(&((Arg) { .ui = 1 << arg->ui })); +} + +void +tagex(const Arg *arg) +{ + tag(&((Arg) { .ui = 1 << arg->ui })); +} + +void +toggletagex(const Arg *arg) +{ + toggletag(&((Arg) { .ui = 1 << arg->ui })); +} + +void +tagallex(const Arg *arg) +{ + #if SCRATCHPADS_PATCH + tag(&((Arg){.ui = ~SPTAGMASK})); + #else + tag(&((Arg){.ui = ~0})); + #endif // SCRATCHPADS_PATCH +} + +int +fake_signal(void) +{ + char fsignal[256]; + char indicator[9] = "fsignal:"; + char str_sig[50]; + char param[16]; + int i, len_str_sig, n, paramn; + size_t len_fsignal, len_indicator = strlen(indicator); + Arg arg; + + // Get root name property + if (gettextprop(root, XA_WM_NAME, fsignal, sizeof(fsignal))) { + len_fsignal = strlen(fsignal); + + // Check if this is indeed a fake signal + if (len_indicator > len_fsignal ? 0 : strncmp(indicator, fsignal, len_indicator) == 0) { + paramn = sscanf(fsignal+len_indicator, "%s%n%s%n", str_sig, &len_str_sig, param, &n); + + if (paramn == 1) arg = (Arg) {0}; + else if (paramn > 2) return 1; + else if (strncmp(param, "i", n - len_str_sig) == 0) + #if IPC_PATCH + sscanf(fsignal + len_indicator + n, "%li", &(arg.i)); + #else + sscanf(fsignal + len_indicator + n, "%i", &(arg.i)); + #endif // IPC_PATCH + else if (strncmp(param, "ui", n - len_str_sig) == 0) + #if IPC_PATCH + sscanf(fsignal + len_indicator + n, "%lu", &(arg.ui)); + #else + sscanf(fsignal + len_indicator + n, "%u", &(arg.ui)); + #endif // IPC_PATCH + else if (strncmp(param, "f", n - len_str_sig) == 0) + sscanf(fsignal + len_indicator + n, "%f", &(arg.f)); + else return 1; + + // Check if a signal was found, and if so handle it + for (i = 0; i < LENGTH(signals); i++) + if (strncmp(str_sig, signals[i].sig, len_str_sig) == 0 && signals[i].func) + signals[i].func(&(arg)); + + // A fake signal was sent + return 1; + } + } + + // No fake signal was sent, so proceed with update + return 0; +} + diff --git a/patch/dwmc.h b/patch/dwmc.h new file mode 100644 index 0000000..66e23a9 --- /dev/null +++ b/patch/dwmc.h @@ -0,0 +1,14 @@ +typedef struct { + const char * sig; + void (*func)(const Arg *); +} Signal; + +static void setlayoutex(const Arg *arg); +static void viewex(const Arg *arg); +static void viewallex(const Arg *arg); +static void toggleviewex(const Arg *arg); +static void tagex(const Arg *arg); +static void toggletagex(const Arg *arg); +static void tagallex(const Arg *arg); +static int fake_signal(void); + diff --git a/patch/exresize.c b/patch/exresize.c new file mode 100644 index 0000000..ac3eab7 --- /dev/null +++ b/patch/exresize.c @@ -0,0 +1,196 @@ +#define EXPAND_LEFT (1 << 0) +#define EXPAND_RIGHT (1 << 2) +#define EXPAND_UP (1 << 4) +#define EXPAND_DOWN (1 << 6) + +#define IS_SET(q, w) ((q & w) != 0) +#define IS_FORCED(q, w) IS_SET((q << 1), w) + +#define EXPANDALL (EXPAND_LEFT | EXPAND_RIGHT | EXPAND_UP | EXPAND_DOWN) +#define UNEXPAND (EXPANDALL << 1) // Force all directions to 0 +#define FORCE_EXPANDALL ~0 // Force expand in all directions + +void +exresize(const Arg *arg) +{ + Client *c; + int x, y, nx, ny, nw, nh; + c = selmon->sel; + + if (!c || !arg) return; + if (selmon->lt[selmon->sellt]->arrange && !c->isfloating) + togglefloating(NULL); + if (c->expandmask != 0) + expand(UNEXPAND); + + x = ((int *)arg->v)[0]; + y = ((int *)arg->v)[1]; + + nw = MIN(selmon->ww - c->bw*2, c->w + x); + nh = MIN(selmon->wh - c->bw*2, c->h + y); + nx = c->x - x/2; + ny = c->y - y/2; + + if (!((abs(c->x + c->w/2 - (selmon->wx + selmon->ww/2)) < snap))) { + if ((nw == selmon->ww) || + (nx < selmon->wx) || + (abs(selmon->wx - c->x) < snap)) + nx = selmon->wx; + else if ((nx+nw > (selmon->wx + selmon->ww)) || + (abs((selmon->wx + selmon->ww) - (c->x + c->w)) < snap)) + nx = (selmon->wx + selmon->ww) - nw - c->bw*2; + } else + nx = selmon->wx + selmon->ww/2 - nw/2; + + if (!((abs(c->y + c->h/2 - (selmon->wy + selmon->wh/2)) < snap))) { + + if ((nh == selmon->wh) || + (ny < selmon->wy) || + (abs(selmon->wy - c->y) < snap)) + ny = selmon->wy; + else if ((ny+nh > (selmon->wy + selmon->wh)) || + (abs((selmon->wy + selmon->wh) - (c->y + c->h)) < snap)) + ny = (selmon->wy + selmon->wh) - nh - c->bw*2; + } else + ny = selmon->wy + selmon->wh/2 - nh/2; + + + resizeclient(c, nx, ny, MAX(nw, 32), MAX(nh, 32)); + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w/2, c->h/2); +} + +void +explace(const Arg *arg) +{ + Client *c; + int nx, ny; + + c = selmon->sel; + if (!c || (arg->ui >= 9)) return; + if (selmon->lt[selmon->sellt]->arrange && !c->isfloating) + togglefloating(NULL); + + nx = (arg->ui % 3) - 1; + ny = (arg->ui / 3) - 1; + + if (nx < 0) nx = selmon->wx; + else if (nx > 0) nx = selmon->wx + selmon->ww - c->w - c->bw*2; + else nx = selmon->wx + selmon->ww/2 - c->w/2; + + if (ny < 0) ny = selmon->wy; + else if (ny > 0) ny = selmon->wy + selmon->wh - c->h - c->bw*2; + else ny = selmon->wy + selmon->wh/2 - c->h/2; + + resize(c, nx, ny, c->w, c->h, True); + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w/2, c->h/2); +} + +int +calculate_expand(unsigned char mask, unsigned char curmask, + unsigned char *newmask, unsigned char key, + int *reset_value, int new_reset_value, + int max_value, int old_value) +{ + if (IS_SET(key, mask) || + (IS_SET(key, curmask) && (!IS_SET(key, mask) && IS_FORCED(key, mask))) || + (!IS_SET(key, curmask) && (IS_SET(key, mask) && IS_FORCED(key, mask)))) { + + if (IS_SET(key, mask) && (!IS_SET(key,curmask) || IS_FORCED(key,mask))) { + if (!IS_SET(key, curmask)) + *reset_value = new_reset_value; + *newmask |= key; + return max_value; + } else if ((IS_SET(key,curmask) && IS_SET(key, mask)) || + (!IS_SET(key, mask) && IS_FORCED(key, mask))) { + *newmask &= ~key; + return *reset_value; + } else { + *newmask &= ~key; + return old_value; + } + } else + return new_reset_value; +} + +void +expand(unsigned char mask) +{ + XEvent ev; + int nx1, ny1, nx2, ny2; + #if SETBORDERPX_PATCH + int bp = selmon->borderpx; + #else + int bp = borderpx; + #endif // SETBORDERPX_PATCH + unsigned char curmask; + unsigned char newmask; + + if (!selmon->sel || selmon->sel->isfixed) + return; + XRaiseWindow(dpy, selmon->sel->win); + newmask = curmask = selmon->sel->expandmask; + + if (curmask == 0) { + if(!selmon->lt[selmon->sellt]->arrange || selmon->sel->isfloating) + selmon->sel->wasfloating = 1; + else { + togglefloating(NULL); + selmon->sel->wasfloating = 0; + } + } + + nx1 = calculate_expand(mask, curmask, &newmask, + EXPAND_LEFT, &selmon->sel->expandx1, + selmon->sel->x, + selmon->wx, + selmon->sel->oldx); + nx2 = calculate_expand(mask, curmask, &newmask, + EXPAND_RIGHT, &selmon->sel->expandx2, + selmon->sel->x + selmon->sel->w, + selmon->wx + selmon->ww - 2*bp, + selmon->sel->oldw + selmon->sel->x); + ny1 = calculate_expand(mask, curmask, &newmask, + EXPAND_UP, &selmon->sel->expandy1, + selmon->sel->y, + selmon->wy, + selmon->sel->oldy); + ny2 = calculate_expand(mask, curmask, &newmask, + EXPAND_DOWN, &selmon->sel->expandy2, + selmon->sel->y + selmon->sel->h, + selmon->wy + selmon->wh - 2*bp, + selmon->sel->oldh + selmon->sel->y); + + + resizeclient(selmon->sel, nx1, ny1, MAX(nx2-nx1, 32), MAX(ny2-ny1, 32)); + + if ((newmask == 0) && (!selmon->sel->wasfloating)) + togglefloating(NULL); + selmon->sel->expandmask = newmask; + drawbar(selmon); + while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + +void +togglemaximize(const Arg *arg) +{ + if (arg->i > 0) expand(FORCE_EXPANDALL); + else if (arg->i < 0) expand(UNEXPAND); + else expand(EXPANDALL); +} + +void +toggleverticalexpand(const Arg *arg) +{ + if (arg->i < 0) expand(EXPAND_DOWN); + else if (arg->i > 0) expand(EXPAND_UP); + else expand(EXPAND_DOWN | EXPAND_UP); +} + +void +togglehorizontalexpand(const Arg *arg) +{ + if (arg->i < 0) expand(EXPAND_LEFT); + else if (arg->i > 0) expand(EXPAND_RIGHT); + else expand(EXPAND_LEFT | EXPAND_RIGHT); +} + diff --git a/patch/exresize.h b/patch/exresize.h new file mode 100644 index 0000000..73a636a --- /dev/null +++ b/patch/exresize.h @@ -0,0 +1,9 @@ +enum { EX_NW, EX_N, EX_NE, EX_W, EX_C, EX_E, EX_SW, EX_S, EX_SE }; + +void expand(unsigned char mask); +void togglemaximize(const Arg *arg); +void toggleverticalexpand(const Arg *arg); +void togglehorizontalexpand(const Arg *arg); +void exresize(const Arg *arg); +void explace(const Arg *arg); + diff --git a/patch/fakefullscreenclient.c b/patch/fakefullscreenclient.c new file mode 100644 index 0000000..509211f --- /dev/null +++ b/patch/fakefullscreenclient.c @@ -0,0 +1,19 @@ +void +togglefakefullscreen(const Arg *arg) +{ + Client *c = selmon->sel; + if (!c) + return; + + if (c->fakefullscreen != 1 && c->isfullscreen) { // exit fullscreen --> fake fullscreen + c->fakefullscreen = 2; + setfullscreen(c, 0); + } else if (c->fakefullscreen == 1) { + setfullscreen(c, 0); + c->fakefullscreen = 0; + } else { + c->fakefullscreen = 1; + setfullscreen(c, 1); + } +} + diff --git a/patch/fakefullscreenclient.h b/patch/fakefullscreenclient.h new file mode 100644 index 0000000..889eed6 --- /dev/null +++ b/patch/fakefullscreenclient.h @@ -0,0 +1,2 @@ +static void togglefakefullscreen(const Arg *arg); + diff --git a/patch/floatpos.c b/patch/floatpos.c new file mode 100644 index 0000000..f5e83a7 --- /dev/null +++ b/patch/floatpos.c @@ -0,0 +1,195 @@ +void +floatpos(const Arg *arg) +{ + Client *c = selmon->sel; + + if (!c || (selmon->lt[selmon->sellt]->arrange && !c->isfloating)) + return; + + setfloatpos(c, (char *)arg->v); + resizeclient(c, c->x, c->y, c->w, c->h); + + #if !FOCUSONCLICK_PATCH + XRaiseWindow(dpy, c->win); + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w/2, c->h/2); + #endif // FOCUSONCLICK_PATCH +} + +void +setfloatpos(Client *c, const char *floatpos) +{ + char xCh, yCh, wCh, hCh; + int x, y, w, h, wx, ww, wy, wh; + #if FLOATPOS_RESPECT_GAPS_PATCH && VANITYGAPS_PATCH + int oh, ov, ih, iv; + unsigned int n; + #endif // FLOATPOS_RESPECT_GAPS_PATCH | VANITYGAPS_PATCH + + if (!c || !floatpos) + return; + if (selmon->lt[selmon->sellt]->arrange && !c->isfloating) + return; + + switch(sscanf(floatpos, "%d%c %d%c %d%c %d%c", &x, &xCh, &y, &yCh, &w, &wCh, &h, &hCh)) { + case 4: + if (xCh == 'w' || xCh == 'W') { + w = x; wCh = xCh; + h = y; hCh = yCh; + x = -1; xCh = 'C'; + y = -1; yCh = 'C'; + } else if (xCh == 'p' || xCh == 'P') { + w = x; wCh = xCh; + h = y; hCh = yCh; + x = 0; xCh = 'G'; + y = 0; yCh = 'G'; + } else if (xCh == 'm' || xCh == 'M') { + getrootptr(&x, &y); + } else { + w = 0; wCh = 0; + h = 0; hCh = 0; + } + break; + case 8: + if (xCh == 'm' || xCh == 'M') + getrootptr(&x, &y); + break; + default: + return; + } + + #if FLOATPOS_RESPECT_GAPS_PATCH && VANITYGAPS_PATCH + getgaps(c->mon, &oh, &ov, &ih, &iv, &n); + wx = c->mon->wx + ov; + wy = c->mon->wy + oh; + ww = c->mon->ww - 2*ov; + wh = c->mon->wh - 2*oh; + #else + wx = c->mon->wx; + wy = c->mon->wy; + ww = c->mon->ww; + wh = c->mon->wh; + #endif // FLOATPOS_RESPECT_GAPS_PATCH | VANITYGAPS_PATCH + + getfloatpos(x, xCh, w, wCh, wx, ww, c->x, c->w, c->bw, floatposgrid_x, &c->x, &c->w); + getfloatpos(y, yCh, h, hCh, wy, wh, c->y, c->h, c->bw, floatposgrid_y, &c->y, &c->h); +} + +/* p - position, s - size, cp and cs represents current position and size */ +void +getfloatpos(int pos, char pCh, int size, char sCh, int min_p, int max_s, int cp, int cs, int cbw, int defgrid, int *out_p, int *out_s) +{ + int abs_p, abs_s, i, delta, rest; + + abs_p = pCh == 'A' || pCh == 'a'; + abs_s = sCh == 'A' || sCh == 'a'; + + cs += 2*cbw; + + switch(pCh) { + case 'A': // absolute position + cp = pos; + break; + case 'a': // absolute relative position + cp += pos; + break; + case 'y': + case 'x': // client relative position + cp = MIN(cp + pos, min_p + max_s); + break; + case 'Y': + case 'X': // client position relative to monitor + cp = min_p + MIN(pos, max_s); + break; + case 'S': // fixed client position (sticky) + case 'C': // fixed client position (center) + case 'Z': // fixed client right-hand position (position + size) + if (pos == -1) + break; + pos = MAX(MIN(pos, max_s), 0); + if (pCh == 'Z') + cs = abs((cp + cs) - (min_p + pos)); + else if (pCh == 'C') + cs = abs((cp + cs / 2) - (min_p + pos)); + else + cs = abs(cp - (min_p + pos)); + cp = min_p + pos; + sCh = 0; // size determined by position, override defined size + break; + case 'G': // grid + if (pos <= 0) + pos = defgrid; // default configurable + if (size == 0 || pos < 2 || (sCh != 'p' && sCh != 'P')) + break; + delta = (max_s - cs) / (pos - 1); + rest = max_s - cs - delta * (pos - 1); + if (sCh == 'P') { + if (size < 1 || size > pos) + break; + cp = min_p + delta * (size - 1); + } else { + for (i = 0; i < pos && cp >= min_p + delta * i + (i > pos - rest ? i + rest - pos + 1 : 0); i++); + cp = min_p + delta * (MAX(MIN(i + size, pos), 1) - 1) + (i > pos - rest ? i + rest - pos + 1 : 0); + } + break; + } + + switch(sCh) { + case 'A': // absolute size + cs = size; + break; + case 'a': // absolute relative size + cs = MAX(1, cs + size); + break; + case '%': // client size percentage in relation to monitor window area size + if (size <= 0) + break; + size = max_s * MIN(size, 100) / 100; + /* falls through */ + case 'h': + case 'w': // size relative to client + if (sCh == 'w' || sCh == 'h') { + if (size == 0) + break; + size += cs; + } + /* falls through */ + case 'H': + case 'W': // normal size, position takes precedence + if (pCh == 'S' && cp + size > min_p + max_s) + size = min_p + max_s - cp; + else if (size > max_s) + size = max_s; + + if (pCh == 'C') { // fixed client center, expand or contract client + delta = size - cs; + if (delta < 0 || (cp - delta / 2 + size <= min_p + max_s)) + cp -= delta / 2; + else if (cp - delta / 2 < min_p) + cp = min_p; + else if (delta) + cp = min_p + max_s; + } else if (pCh == 'Z') + cp -= size - cs; + + cs = size; + break; + } + + if (pCh == '%') // client mid-point position in relation to monitor window area size + cp = min_p + max_s * MAX(MIN(pos, 100), 0) / 100 - (cs) / 2; + if (pCh == 'm' || pCh == 'M') + cp = pos - cs / 2; + + if (!abs_p && cp < min_p) + cp = min_p; + if (cp + cs > min_p + max_s && !(abs_p && abs_s)) { + if (abs_p || cp == min_p) + cs = min_p + max_s - cp; + else + cp = min_p + max_s - cs; + } + + *out_p = cp; + *out_s = MAX(cs - 2*cbw, 1); +} + diff --git a/patch/floatpos.h b/patch/floatpos.h new file mode 100644 index 0000000..ecdcf0b --- /dev/null +++ b/patch/floatpos.h @@ -0,0 +1,4 @@ +static void floatpos(const Arg *arg); +static void setfloatpos(Client *c, const char *floatpos); +static void getfloatpos(int pos, char pCh, int size, char sCh, int min_p, int max_s, int cp, int cs, int cbw, int defgrid, int *out_p, int *out_s); + diff --git a/patch/focusadjacenttag.c b/patch/focusadjacenttag.c new file mode 100644 index 0000000..1665c18 --- /dev/null +++ b/patch/focusadjacenttag.c @@ -0,0 +1,102 @@ +void +tagtoleft(const Arg *arg) +{ + if (selmon->sel != NULL + && __builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1 + && selmon->tagset[selmon->seltags] > 1) { + selmon->sel->tags >>= 1; + focus(NULL); + arrange(selmon); + } +} + +void +tagtoright(const Arg *arg) +{ + if (selmon->sel != NULL + && __builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1 + && selmon->tagset[selmon->seltags] & (TAGMASK >> 1)) { + selmon->sel->tags <<= 1; + focus(NULL); + arrange(selmon); + } +} + +void +viewtoleft(const Arg *arg) +{ + if (__builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1 + && selmon->tagset[selmon->seltags] > 1) { + selmon->seltags ^= 1; /* toggle sel tagset */ + #if PERTAG_PATCH + pertagview(&((Arg) { .ui = selmon->tagset[selmon->seltags ^ 1] >> 1 })); + #else + selmon->tagset[selmon->seltags] = selmon->tagset[selmon->seltags ^ 1] >> 1; + #endif // pertagview + focus(NULL); + arrange(selmon); + #if BAR_EWMHTAGS_PATCH + updatecurrentdesktop(); + #endif // BAR_EWMHTAGS_PATCH + } +} + +void +viewtoright(const Arg *arg) +{ + if (__builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1 + && selmon->tagset[selmon->seltags] & (TAGMASK >> 1)) { + selmon->seltags ^= 1; /* toggle sel tagset */ + #if PERTAG_PATCH + pertagview(&((Arg) { .ui = selmon->tagset[selmon->seltags ^ 1] << 1 })); + #else + selmon->tagset[selmon->seltags] = selmon->tagset[selmon->seltags ^ 1] << 1; + #endif // pertagview + focus(NULL); + arrange(selmon); + #if BAR_EWMHTAGS_PATCH + updatecurrentdesktop(); + #endif // BAR_EWMHTAGS_PATCH + } +} + +void +tagandviewtoleft(const Arg *arg) +{ + if (__builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1 + && selmon->tagset[selmon->seltags] > 1) { + selmon->sel->tags >>= 1; + selmon->seltags ^= 1; /* toggle sel tagset */ + #if PERTAG_PATCH + pertagview(&((Arg) { .ui = selmon->tagset[selmon->seltags ^ 1] >> 1 })); + #else + selmon->tagset[selmon->seltags] = selmon->tagset[selmon->seltags ^ 1] >> 1; + #endif // pertagview + focus(selmon->sel); + arrange(selmon); + #if BAR_EWMHTAGS_PATCH + updatecurrentdesktop(); + #endif // BAR_EWMHTAGS_PATCH + } +} + +void +tagandviewtoright(const Arg *arg) +{ + if (__builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1 + && selmon->tagset[selmon->seltags] & (TAGMASK >> 1)) { + selmon->sel->tags <<= 1; + selmon->seltags ^= 1; /* toggle sel tagset */ + #if PERTAG_PATCH + pertagview(&((Arg) { .ui = selmon->tagset[selmon->seltags ^ 1] << 1 })); + #else + selmon->tagset[selmon->seltags] = selmon->tagset[selmon->seltags ^ 1] << 1; + #endif // pertagview + focus(selmon->sel); + arrange(selmon); + #if BAR_EWMHTAGS_PATCH + updatecurrentdesktop(); + #endif // BAR_EWMHTAGS_PATCH + } +} + diff --git a/patch/focusadjacenttag.h b/patch/focusadjacenttag.h new file mode 100644 index 0000000..abd7439 --- /dev/null +++ b/patch/focusadjacenttag.h @@ -0,0 +1,7 @@ +static void tagtoleft(const Arg *arg); +static void tagtoright(const Arg *arg); +static void viewtoleft(const Arg *arg); +static void viewtoright(const Arg *arg); +static void tagandviewtoleft(const Arg *arg); +static void tagandviewtoright(const Arg *arg); + diff --git a/patch/focusdir.c b/patch/focusdir.c new file mode 100644 index 0000000..65798c5 --- /dev/null +++ b/patch/focusdir.c @@ -0,0 +1,66 @@ +void +focusdir(const Arg *arg) +{ + Client *s = selmon->sel, *f = NULL, *c, *next; + + if (!s) + return; + + unsigned int score = -1; + unsigned int client_score; + int dist; + int dirweight = 20; + int isfloating = s->isfloating; + + next = s->next; + if (!next) + next = s->mon->clients; + for (c = next; c != s; c = next) { + + next = c->next; + if (!next) + next = s->mon->clients; + + if (!ISVISIBLE(c) || c->isfloating != isfloating) // || HIDDEN(c) + continue; + + switch (arg->i) { + case 0: // left + dist = s->x - c->x - c->w; + client_score = + dirweight * MIN(abs(dist), abs(dist + s->mon->ww)) + + abs(s->y - c->y); + break; + case 1: // right + dist = c->x - s->x - s->w; + client_score = + dirweight * MIN(abs(dist), abs(dist + s->mon->ww)) + + abs(c->y - s->y); + break; + case 2: // up + dist = s->y - c->y - c->h; + client_score = + dirweight * MIN(abs(dist), abs(dist + s->mon->wh)) + + abs(s->x - c->x); + break; + default: + case 3: // down + dist = c->y - s->y - s->h; + client_score = + dirweight * MIN(abs(dist), abs(dist + s->mon->wh)) + + abs(c->x - s->x); + break; + } + + if (((arg->i == 0 || arg->i == 2) && client_score <= score) || client_score < score) { + score = client_score; + f = c; + } + } + + if (f && f != s) { + focus(f); + restack(f->mon); + } +} + diff --git a/patch/focusdir.h b/patch/focusdir.h new file mode 100644 index 0000000..0d82ebf --- /dev/null +++ b/patch/focusdir.h @@ -0,0 +1,2 @@ +static void focusdir(const Arg *arg); + diff --git a/patch/focusmaster.c b/patch/focusmaster.c new file mode 100644 index 0000000..13a47e5 --- /dev/null +++ b/patch/focusmaster.c @@ -0,0 +1,14 @@ +void +focusmaster(const Arg *arg) +{ + Client *c; + + if (selmon->nmaster < 1) + return; + + c = nexttiled(selmon->clients); + + if (c) + focus(c); +} + diff --git a/patch/focusmaster.h b/patch/focusmaster.h new file mode 100644 index 0000000..32e7c7c --- /dev/null +++ b/patch/focusmaster.h @@ -0,0 +1,2 @@ +static void focusmaster(const Arg *arg); + diff --git a/patch/focusurgent.c b/patch/focusurgent.c new file mode 100644 index 0000000..86a918e --- /dev/null +++ b/patch/focusurgent.c @@ -0,0 +1,16 @@ +void +focusurgent(const Arg *arg) +{ + Client *c; + int i; + for (c = selmon->clients; c && !c->isurgent; c = c->next); + if (c) { + for (i = 0; i < NUMTAGS && !((1 << i) & c->tags); i++); + if (i < NUMTAGS) { + if (((1 << i) & TAGMASK) != selmon->tagset[selmon->seltags]) + view(&((Arg) { .ui = 1 << i })); + focus(c); + } + } +} + diff --git a/patch/focusurgent.h b/patch/focusurgent.h new file mode 100644 index 0000000..854258d --- /dev/null +++ b/patch/focusurgent.h @@ -0,0 +1,2 @@ +static void focusurgent(const Arg *arg); + diff --git a/patch/fsignal.c b/patch/fsignal.c new file mode 100644 index 0000000..b0ce2ea --- /dev/null +++ b/patch/fsignal.c @@ -0,0 +1,41 @@ +int +fake_signal(void) +{ + char fsignal[256]; + char indicator[9] = "fsignal:"; + char str_signum[16]; + int i, v, signum; + size_t len_fsignal, len_indicator = strlen(indicator); + + // Get root name property + if (gettextprop(root, XA_WM_NAME, fsignal, sizeof(fsignal))) { + len_fsignal = strlen(fsignal); + + // Check if this is indeed a fake signal + if (len_indicator > len_fsignal ? 0 : strncmp(indicator, fsignal, len_indicator) == 0) { + memcpy(str_signum, &fsignal[len_indicator], len_fsignal - len_indicator); + str_signum[len_fsignal - len_indicator] = '\0'; + + // Convert string value into managable integer + for (i = signum = 0; i < strlen(str_signum); i++) { + v = str_signum[i] - '0'; + if (v >= 0 && v <= 9) { + signum = signum * 10 + v; + } + } + + // Check if a signal was found, and if so handle it + if (signum) + for (i = 0; i < LENGTH(signals); i++) + if (signum == signals[i].signum && signals[i].func) + signals[i].func(&(signals[i].arg)); + + // A fake signal was sent + return 1; + } + } + + // No fake signal was sent, so proceed with update + return 0; +} + diff --git a/patch/fsignal.h b/patch/fsignal.h new file mode 100644 index 0000000..55fed6c --- /dev/null +++ b/patch/fsignal.h @@ -0,0 +1,8 @@ +typedef struct { + unsigned int signum; + void (*func)(const Arg *); + const Arg arg; +} Signal; + +static int fake_signal(void); + diff --git a/patch/fullscreen.c b/patch/fullscreen.c new file mode 100644 index 0000000..667cdc9 --- /dev/null +++ b/patch/fullscreen.c @@ -0,0 +1,18 @@ +Layout *last_layout; + +void +fullscreen(const Arg *arg) +{ + int monocle_pos = 0; + if (selmon->showbar || last_layout == NULL) { + #if MONOCLE_LAYOUT + for (monocle_pos = 0, last_layout = (Layout *)layouts; !last_layout->arrange || last_layout->arrange != &monocle; monocle_pos++, last_layout++ ); + #endif // MONOCLE_LAYOUT + for (last_layout = (Layout *)layouts; last_layout != selmon->lt[selmon->sellt]; last_layout++); + setlayout(&((Arg) { .v = &layouts[monocle_pos] })); + } else { + setlayout(&((Arg) { .v = last_layout })); + } + togglebar(arg); +} + diff --git a/patch/fullscreen.h b/patch/fullscreen.h new file mode 100644 index 0000000..72983e1 --- /dev/null +++ b/patch/fullscreen.h @@ -0,0 +1,2 @@ +static void fullscreen(const Arg *arg); + diff --git a/patch/include.c b/patch/include.c new file mode 100644 index 0000000..29dac83 --- /dev/null +++ b/patch/include.c @@ -0,0 +1,359 @@ +/* Bar functionality */ +#include "bar_indicators.c" +#include "bar_tagicons.c" + +#if BAR_ALPHA_PATCH +#include "bar_alpha.c" +#endif +#if BAR_ALTERNATIVE_TAGS_PATCH +#include "bar_alternativetags.c" +#endif +#if BAR_ANYBAR_PATCH +#include "bar_anybar.c" +#endif +#if BAR_DWMBLOCKS_PATCH && BAR_STATUSCMD_PATCH +#include "bar_dwmblocks.c" +#endif +#if BAR_EWMHTAGS_PATCH +#include "bar_ewmhtags.c" +#endif +#if COMBO_PATCH +#include "combo.c" +#endif +#if BAR_HOLDBAR_PATCH +#include "bar_holdbar.c" +#endif +#if BAR_LTSYMBOL_PATCH +#include "bar_ltsymbol.c" +#endif +#if BAR_POWERLINE_STATUS_PATCH +#include "bar_powerline_status.c" +#endif +#if BAR_POWERLINE_TAGS_PATCH +#include "bar_powerline_tags.c" +#endif +#if BAR_STATUS_PATCH +#include "bar_status.c" +#endif +#if BAR_STATUS2D_PATCH +#include "bar_status2d.c" +#endif +#if BAR_STATUSBUTTON_PATCH +#include "bar_statusbutton.c" +#endif +#if BAR_STATUSCMD_PATCH +#include "bar_statuscmd.c" +#endif +#if BAR_STATUSCOLORS_PATCH +#include "bar_statuscolors.c" +#endif +#if BAR_WINICON_PATCH +#include "bar_winicon.c" +#endif +#if BAR_TABGROUPS_PATCH +#include "bar_tabgroups.c" +#endif +#if BAR_TAGS_PATCH +#include "bar_tags.c" +#endif +#if BAR_TAGGRID_PATCH +#include "bar_taggrid.c" +#endif +#if BAR_WINTITLE_PATCH +#include "bar_wintitle.c" +#endif +#if BAR_FANCYBAR_PATCH +#include "bar_fancybar.c" +#endif +#if BAR_FLEXWINTITLE_PATCH +#include "bar_flexwintitle.c" +#if BAR_WINTITLE_FLOATING_PATCH +#include "bar_wintitle_floating.c" +#endif +#if BAR_WINTITLE_HIDDEN_PATCH +#include "bar_wintitle_hidden.c" +#endif +#endif // BAR_FLEXWINTITLE_PATCH +#if BAR_AWESOMEBAR_PATCH +#include "bar_awesomebar.c" +#endif +#if BAR_SYSTRAY_PATCH +#include "bar_systray.c" +#endif +#if BAR_VTCOLORS_PATCH +#include "bar_vtcolors.c" +#endif +#if BAR_WINTITLEACTIONS_PATCH +#include "bar_wintitleactions.c" +#endif +#if BAR_LAYOUTMENU_PATCH +#include "bar_layoutmenu.c" +#endif + +/* Other patches */ +#if ASPECTRESIZE_PATCH +#include "aspectresize.c" +#endif +#if ATTACHABOVE_PATCH || ATTACHASIDE_PATCH || ATTACHBELOW_PATCH || ATTACHBOTTOM_PATCH +#include "attachx.c" +#endif +#if AUTOSTART_PATCH +#include "autostart.c" +#endif +#if CFACTS_PATCH +#include "cfacts.c" +#endif +#if CMDCUSTOMIZE_PATCH +#include "cmdcustomize.c" +#endif +#if COOL_AUTOSTART_PATCH +#include "cool_autostart.c" +#endif +#if CYCLELAYOUTS_PATCH +#include "cyclelayouts.c" +#endif +#if DECORATION_HINTS_PATCH +#include "decorationhints.c" +#endif +#if DISTRIBUTETAGS_PATCH +#include "distributetags.c" +#endif +#if DRAGCFACT_PATCH && CFACTS_PATCH +#include "dragcfact.c" +#endif +#if DWMC_PATCH +#include "dwmc.c" +#elif FSIGNAL_PATCH +#include "fsignal.c" +#endif +#if EXRESIZE_PATCH +#include "exresize.c" +#endif +#if !FAKEFULLSCREEN_PATCH && FAKEFULLSCREEN_CLIENT_PATCH +#include "fakefullscreenclient.c" +#endif +#if FLOATPOS_PATCH +#include "floatpos.c" +#endif +#if FOCUSADJACENTTAG_PATCH +#include "focusadjacenttag.c" +#endif +#if FOCUSDIR_PATCH +#include "focusdir.c" +#endif +#if FOCUSMASTER_PATCH +#include "focusmaster.c" +#endif +#if FOCUSURGENT_PATCH +#include "focusurgent.c" +#endif +#if FULLSCREEN_PATCH +#include "fullscreen.c" +#endif +#if INPLACEROTATE_PATCH +#include "inplacerotate.c" +#endif +#if IPC_PATCH +#include "ipc.c" +#ifdef VERSION +#include "ipc/IPCClient.c" +#include "ipc/yajl_dumps.c" +#include "ipc/ipc.c" +#include "ipc/util.c" +#endif +#endif // IPC_PATCH +#if INSETS_PATCH +#include "insets.c" +#endif +#if KEYMODES_PATCH +#include "keymodes.c" +#endif +#if KILLUNSEL_PATCH +#include "killunsel.c" +#endif +#if MAXIMIZE_PATCH +#include "maximize.c" +#endif +#if MPDCONTROL_PATCH +#include "mpdcontrol.c" +#endif +#if MOVEPLACE_PATCH +#include "moveplace.c" +#endif +#if MOVERESIZE_PATCH +#include "moveresize.c" +#endif +#if MOVESTACK_PATCH +#include "movestack.c" +#endif +#if NO_MOD_BUTTONS_PATCH +#include "nomodbuttons.c" +#endif +#if PERTAG_PATCH +#include "pertag.c" +#endif +#if PLACEMOUSE_PATCH +#include "placemouse.c" +#endif +#if PUSH_NO_MASTER_PATCH +#include "push_no_master.c" +#elif PUSH_PATCH +#include "push.c" +#endif +#if REORGANIZETAGS_PATCH +#include "reorganizetags.c" +#endif +#if RESTARTSIG_PATCH +#include "restartsig.c" +#endif +#if RIODRAW_PATCH +#include "riodraw.c" +#endif +#if ROTATESTACK_PATCH +#include "rotatestack.c" +#endif +#if ROUNDED_CORNERS_PATCH +#include "roundedcorners.c" +#endif +#if SCRATCHPADS_PATCH +#include "scratchpad.c" +#endif +#if SCRATCHPAD_ALT_1_PATCH +#include "scratchpad_alt_1.c" +#endif +#if SELFRESTART_PATCH +#include "selfrestart.c" +#endif +#if SETBORDERPX_PATCH +#include "setborderpx.c" +#endif +#if SHIFTVIEW_PATCH +#include "shiftview.c" +#endif +#if SHIFTVIEW_CLIENTS_PATCH +#include "shiftviewclients.c" +#endif +#if SIZEHINTS_RULED_PATCH +#include "sizehints_ruled.c" +#endif +#if SORTSCREENS_PATCH +#ifdef XINERAMA +#include "sortscreens.c" +#endif // XINERAMA +#endif +#if STACKER_PATCH +#include "stacker.c" +#endif +#if STICKY_PATCH +#include "sticky.c" +#endif +#if SWALLOW_PATCH +#include "swallow.c" +#endif +#if SWAPFOCUS_PATCH && PERTAG_PATCH +#include "swapfocus.c" +#endif +#if SWAPTAGS_PATCH +#include "swaptags.c" +#endif +#if SWITCHCOL_PATCH +#include "switchcol.c" +#endif +#if TAB_PATCH +#include "tab.c" +#endif +#if TAGALL_PATCH +#include "tagall.c" +#endif +#if TAGALLMON_PATCH +#include "tagallmon.c" +#endif +#if TAGOTHERMONITOR_PATCH +#include "tagothermonitor.c" +#endif +#if TAGSWAPMON_PATCH +#include "tagswapmon.c" +#endif +#if TAPRESIZE_PATCH +#include "tapresize.c" +#endif +#if TOGGLEFULLSCREEN_PATCH +#include "togglefullscreen.c" +#endif +#if TRANSFER_PATCH +#include "transfer.c" +#endif +#if TRANSFER_ALL_PATCH +#include "transferall.c" +#endif +#if UNFLOATVISIBLE_PATCH +#include "unfloatvisible.c" +#endif +#if VANITYGAPS_PATCH +#include "vanitygaps.c" +#endif +#if WARP_PATCH +#include "warp.c" +#endif +#if WINVIEW_PATCH +#include "winview.c" +#endif +#if ZOOMSWAP_PATCH +#include "zoomswap.c" +#endif +#if XKB_PATCH +#include "xkb.c" +#endif +#if XRDB_PATCH && !BAR_VTCOLORS_PATCH +#include "xrdb.c" +#endif +#if DRAGMFACT_PATCH +#include "dragmfact.c" +#endif +/* Layouts */ +#if BSTACK_LAYOUT || BSTACKHORIZ_LAYOUT || CENTEREDMASTER_LAYOUT || CENTEREDFLOATINGMASTER_LAYOUT || COLUMNS_LAYOUT || DECK_LAYOUT || TILE_LAYOUT +#include "layout_facts.c" +#endif +#if BSTACK_LAYOUT +#include "layout_bstack.c" +#endif +#if BSTACKHORIZ_LAYOUT +#include "layout_bstackhoriz.c" +#endif +#if CENTEREDMASTER_LAYOUT +#include "layout_centeredmaster.c" +#endif +#if CENTEREDFLOATINGMASTER_LAYOUT +#include "layout_centeredfloatingmaster.c" +#endif +#if COLUMNS_LAYOUT +#include "layout_columns.c" +#endif +#if DECK_LAYOUT +#include "layout_deck.c" +#endif +#if FIBONACCI_DWINDLE_LAYOUT || FIBONACCI_SPIRAL_LAYOUT +#include "layout_fibonacci.c" +#endif +#if FLEXTILE_DELUXE_LAYOUT +#include "layout_flextile-deluxe.c" +#endif +#if GAPPLESSGRID_LAYOUT +#include "layout_gapplessgrid.c" +#endif +#if GRIDMODE_LAYOUT +#include "layout_grid.c" +#endif +#if HORIZGRID_LAYOUT +#include "layout_horizgrid.c" +#endif +#if MONOCLE_LAYOUT +#include "layout_monocle.c" +#endif +#if NROWGRID_LAYOUT +#include "layout_nrowgrid.c" +#endif +#if TILE_LAYOUT +#include "layout_tile.c" +#endif + diff --git a/patch/include.h b/patch/include.h new file mode 100644 index 0000000..d4030a9 --- /dev/null +++ b/patch/include.h @@ -0,0 +1,349 @@ +/* Bar functionality */ +#include "bar_indicators.h" +#include "bar_tagicons.h" + +#if BAR_ALPHA_PATCH +#include "bar_alpha.h" +#endif +#if BAR_ALTERNATIVE_TAGS_PATCH +#include "bar_alternativetags.h" +#endif +#if BAR_ANYBAR_PATCH +#include "bar_anybar.h" +#endif +#if BAR_DWMBLOCKS_PATCH && BAR_STATUSCMD_PATCH +#include "bar_dwmblocks.h" +#endif +#if BAR_EWMHTAGS_PATCH +#include "bar_ewmhtags.h" +#endif +#if COMBO_PATCH +#include "combo.h" +#endif +#if BAR_HOLDBAR_PATCH +#include "bar_holdbar.h" +#endif +#if BAR_LTSYMBOL_PATCH +#include "bar_ltsymbol.h" +#endif +#if BAR_POWERLINE_STATUS_PATCH +#include "bar_powerline_status.h" +#endif +#if BAR_POWERLINE_TAGS_PATCH +#include "bar_powerline_tags.h" +#endif +#if BAR_STATUS_PATCH +#include "bar_status.h" +#endif +#if BAR_STATUS2D_PATCH +#include "bar_status2d.h" +#endif +#if BAR_STATUSBUTTON_PATCH +#include "bar_statusbutton.h" +#endif +#if BAR_STATUSCMD_PATCH +#include "bar_statuscmd.h" +#endif +#if BAR_TABGROUPS_PATCH +#include "bar_tabgroups.h" +#endif +#if BAR_WINICON_PATCH +#include "bar_winicon.h" +#endif +#if BAR_TAGS_PATCH +#include "bar_tags.h" +#endif +#if BAR_TAGGRID_PATCH +#include "bar_taggrid.h" +#endif +#if BAR_WINTITLE_PATCH +#include "bar_wintitle.h" +#endif +#if BAR_FANCYBAR_PATCH +#include "bar_fancybar.h" +#endif +#if BAR_FLEXWINTITLE_PATCH +#include "bar_flexwintitle.h" +#if BAR_WINTITLE_FLOATING_PATCH +#include "bar_wintitle_floating.h" +#endif +#if BAR_WINTITLE_HIDDEN_PATCH +#include "bar_wintitle_hidden.h" +#endif +#endif // BAR_FLEXWINTITLE_PATCH +#if BAR_AWESOMEBAR_PATCH +#include "bar_awesomebar.h" +#endif +#if BAR_SYSTRAY_PATCH +#include "bar_systray.h" +#endif +#if BAR_VTCOLORS_PATCH +#include "bar_vtcolors.h" +#endif +#if BAR_WINTITLEACTIONS_PATCH +#include "bar_wintitleactions.h" +#endif +#if BAR_LAYOUTMENU_PATCH +#include "bar_layoutmenu.h" +#endif + +/* Other patches */ +#if ASPECTRESIZE_PATCH +#include "aspectresize.h" +#endif +#if ATTACHABOVE_PATCH || ATTACHASIDE_PATCH || ATTACHBELOW_PATCH || ATTACHBOTTOM_PATCH +#include "attachx.h" +#endif +#if AUTOSTART_PATCH +#include "autostart.h" +#endif +#if CFACTS_PATCH +#include "cfacts.h" +#endif +#if CMDCUSTOMIZE_PATCH +#include "cmdcustomize.h" +#endif +#if COOL_AUTOSTART_PATCH +#include "cool_autostart.h" +#endif +#if CYCLELAYOUTS_PATCH +#include "cyclelayouts.h" +#endif +#if DECORATION_HINTS_PATCH +#include "decorationhints.h" +#endif +#if DISTRIBUTETAGS_PATCH +#include "distributetags.h" +#endif +#if DRAGCFACT_PATCH && CFACTS_PATCH +#include "dragcfact.h" +#endif +#if DRAGMFACT_PATCH +#include "dragmfact.h" +#endif +#if DWMC_PATCH +#include "dwmc.h" +#elif FSIGNAL_PATCH +#include "fsignal.h" +#endif +#if EXRESIZE_PATCH +#include "exresize.h" +#endif +#if !FAKEFULLSCREEN_PATCH && FAKEFULLSCREEN_CLIENT_PATCH +#include "fakefullscreenclient.h" +#endif +#if FLOATPOS_PATCH +#include "floatpos.h" +#endif +#if FOCUSDIR_PATCH +#include "focusdir.h" +#endif +#if FOCUSADJACENTTAG_PATCH +#include "focusadjacenttag.h" +#endif +#if FOCUSMASTER_PATCH +#include "focusmaster.h" +#endif +#if FOCUSURGENT_PATCH +#include "focusurgent.h" +#endif +#if FULLSCREEN_PATCH +#include "fullscreen.h" +#endif +#if INPLACEROTATE_PATCH +#include "inplacerotate.h" +#endif +#if IPC_PATCH +#include "ipc.h" +#include "ipc/ipc.h" +#include "ipc/util.h" +#endif +#if INSETS_PATCH +#include "insets.h" +#endif +#if KEYMODES_PATCH +#include "keymodes.h" +#endif +#if KILLUNSEL_PATCH +#include "killunsel.h" +#endif +#if MAXIMIZE_PATCH +#include "maximize.h" +#endif +#if MPDCONTROL_PATCH +#include "mpdcontrol.h" +#endif +#if MOVEPLACE_PATCH +#include "moveplace.h" +#endif +#if MOVERESIZE_PATCH +#include "moveresize.h" +#endif +#if MOVESTACK_PATCH +#include "movestack.h" +#endif +#if NO_MOD_BUTTONS_PATCH +#include "nomodbuttons.h" +#endif +#if PERTAG_PATCH +#include "pertag.h" +#endif +#if PLACEMOUSE_PATCH +#include "placemouse.h" +#endif +#if PUSH_NO_MASTER_PATCH +#include "push_no_master.h" +#elif PUSH_PATCH +#include "push.h" +#endif +#if REORGANIZETAGS_PATCH +#include "reorganizetags.h" +#endif +#if RESTARTSIG_PATCH +#include "restartsig.h" +#endif +#if RIODRAW_PATCH +#include "riodraw.h" +#endif +#if ROTATESTACK_PATCH +#include "rotatestack.h" +#endif +#if ROUNDED_CORNERS_PATCH +#include "roundedcorners.h" +#endif +#if SCRATCHPADS_PATCH +#include "scratchpad.h" +#endif +#if SCRATCHPAD_ALT_1_PATCH +#include "scratchpad_alt_1.h" +#endif +#if SELFRESTART_PATCH +#include "selfrestart.h" +#endif +#if SETBORDERPX_PATCH +#include "setborderpx.h" +#endif +#if SHIFTVIEW_PATCH +#include "shiftview.h" +#endif +#if SHIFTVIEW_CLIENTS_PATCH +#include "shiftviewclients.h" +#endif +#if SIZEHINTS_RULED_PATCH +#include "sizehints_ruled.h" +#endif +#if SORTSCREENS_PATCH +#ifdef XINERAMA +#include "sortscreens.h" +#endif // XINERAMA +#endif +#if STACKER_PATCH +#include "stacker.h" +#endif +#if STICKY_PATCH +#include "sticky.h" +#endif +#if SWALLOW_PATCH +#include "swallow.h" +#endif +#if SWAPFOCUS_PATCH && PERTAG_PATCH +#include "swapfocus.h" +#endif +#if SWAPTAGS_PATCH +#include "swaptags.h" +#endif +#if SWITCHCOL_PATCH +#include "switchcol.h" +#endif +#if TAB_PATCH +#include "tab.h" +#endif +#if TAGALL_PATCH +#include "tagall.h" +#endif +#if TAGALLMON_PATCH +#include "tagallmon.h" +#endif +#if TAGOTHERMONITOR_PATCH +#include "tagothermonitor.h" +#endif +#if TAGSWAPMON_PATCH +#include "tagswapmon.h" +#endif +#if TAPRESIZE_PATCH +#include "tapresize.h" +#endif +#if TOGGLEFULLSCREEN_PATCH +#include "togglefullscreen.h" +#endif +#if TRANSFER_PATCH +#include "transfer.h" +#endif +#if TRANSFER_ALL_PATCH +#include "transferall.h" +#endif +#if UNFLOATVISIBLE_PATCH +#include "unfloatvisible.h" +#endif +#if VANITYGAPS_PATCH +#include "vanitygaps.h" +#endif +#if WARP_PATCH +#include "warp.h" +#endif +#if WINVIEW_PATCH +#include "winview.h" +#endif +#if ZOOMSWAP_PATCH +#include "zoomswap.h" +#endif +#if XKB_PATCH +#include "xkb.h" +#endif +#if XRDB_PATCH && !BAR_VTCOLORS_PATCH +#include "xrdb.h" +#endif +/* Layouts */ +#if BSTACK_LAYOUT +#include "layout_bstack.h" +#endif +#if BSTACKHORIZ_LAYOUT +#include "layout_bstackhoriz.h" +#endif +#if CENTEREDMASTER_LAYOUT +#include "layout_centeredmaster.h" +#endif +#if CENTEREDFLOATINGMASTER_LAYOUT +#include "layout_centeredfloatingmaster.h" +#endif +#if COLUMNS_LAYOUT +#include "layout_columns.h" +#endif +#if DECK_LAYOUT +#include "layout_deck.h" +#endif +#if FIBONACCI_DWINDLE_LAYOUT || FIBONACCI_SPIRAL_LAYOUT +#include "layout_fibonacci.h" +#endif +#if FLEXTILE_DELUXE_LAYOUT +#include "layout_flextile-deluxe.h" +#endif +#if GAPPLESSGRID_LAYOUT +#include "layout_gapplessgrid.h" +#endif +#if GRIDMODE_LAYOUT +#include "layout_grid.h" +#endif +#if HORIZGRID_LAYOUT +#include "layout_horizgrid.h" +#endif +#if MONOCLE_LAYOUT +#include "layout_monocle.h" +#endif +#if NROWGRID_LAYOUT +#include "layout_nrowgrid.h" +#endif +#if TILE_LAYOUT +#include "layout_tile.h" +#endif + diff --git a/patch/inplacerotate.c b/patch/inplacerotate.c new file mode 100644 index 0000000..b700e04 --- /dev/null +++ b/patch/inplacerotate.c @@ -0,0 +1,84 @@ +void +insertclient(Client *item, Client *insertItem, int after) +{ + Client *c; + if (item == NULL || insertItem == NULL || item == insertItem) + return; + detach(insertItem); + if (!after && selmon->clients == item) { + attach(insertItem); + return; + } + if (after) { + c = item; + } else { + for (c = selmon->clients; c; c = c->next) { + if (c->next == item) + break; + } + } + insertItem->next = c->next; + c->next = insertItem; +} + +void +inplacerotate(const Arg *arg) +{ + if (!selmon->sel || (selmon->sel->isfloating && !arg->f)) + return; + + unsigned int selidx = 0, i = 0; + Client *c = NULL, *stail = NULL, *mhead = NULL, *mtail = NULL, *shead = NULL; + + // Determine positionings for insertclient + for (c = selmon->clients; c; c = c->next) { + if (ISVISIBLE(c) && !(c->isfloating)) { + if (selmon->sel == c) + selidx = i; + if (i == selmon->nmaster - 1) + mtail = c; + if (i == selmon->nmaster) + shead = c; + if (mhead == NULL) + mhead = c; + stail = c; + i++; + } + } + + switch(arg->i) { + case 1: + if (selidx >= selmon->nmaster) + insertclient(shead, stail, 0); + else + insertclient(mhead, mtail, 0); + break; + case -1: + if (selidx >= selmon->nmaster) + insertclient(stail, shead, 1); + else + insertclient(mtail, mhead, 1); + break; + case 2: + insertclient(selmon->clients, stail, 0); + break; + case -2: + insertclient(stail, selmon->clients, 1); + break; + } + + // Restore focus position + i = 0; + for (c = selmon->clients; c; c = c->next) { + if (!ISVISIBLE(c) || (c->isfloating)) + continue; + if (i == selidx) { + focus(c); + break; + } + i++; + } + arrange(selmon); + focus(c); +} + diff --git a/patch/inplacerotate.h b/patch/inplacerotate.h new file mode 100644 index 0000000..2669375 --- /dev/null +++ b/patch/inplacerotate.h @@ -0,0 +1,2 @@ +static void inplacerotate(const Arg *arg); + diff --git a/patch/insets.c b/patch/insets.c new file mode 100644 index 0000000..8ad99b3 --- /dev/null +++ b/patch/insets.c @@ -0,0 +1,19 @@ +void +setinset(Monitor *m, Inset inset) +{ + Bar *bar; + m->inset = inset; + updatebarpos(m); + for (bar = m->bar; bar; bar = bar->next) + XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + arrange(m); +} + +void +updateinset(const Arg *arg) +{ + Inset *inset = (Inset *)arg->v; + for (Monitor *m = mons; m; m = m->next) + setinset(m, *inset); +} + diff --git a/patch/insets.h b/patch/insets.h new file mode 100644 index 0000000..54635db --- /dev/null +++ b/patch/insets.h @@ -0,0 +1,3 @@ +static void setinset(Monitor *m, Inset inset); +static void updateinset(const Arg *arg); + diff --git a/patch/ipc.c b/patch/ipc.c new file mode 100644 index 0000000..f5ec62c --- /dev/null +++ b/patch/ipc.c @@ -0,0 +1,111 @@ +static int epoll_fd; +static int dpy_fd; +static Monitor *lastselmon; + +int +handlexevent(struct epoll_event *ev) +{ + if (ev->events & EPOLLIN) { + XEvent ev; + while (running && XPending(dpy)) { + XNextEvent(dpy, &ev); + #if XKB_PATCH + /* Unfortunately the xkbEventType is not constant hence it can't be part of the + * normal event handler below */ + if (ev.type == xkbEventType) { + xkbeventnotify(&ev); + continue; + } + #endif // XKB_PATCH + + if (handler[ev.type]) { + handler[ev.type](&ev); /* call handler */ + ipc_send_events(mons, &lastselmon, selmon); + } + } + } else if (ev-> events & EPOLLHUP) + return -1; + + return 0; +} + +void +setlayoutsafe(const Arg *arg) +{ + const Layout *ltptr = (Layout *)arg->v; + if (ltptr == 0) + setlayout(arg); + for (int i = 0; i < LENGTH(layouts); i++) { + if (ltptr == &layouts[i]) + setlayout(arg); + } +} + +void +setupepoll(void) +{ + epoll_fd = epoll_create1(0); + dpy_fd = ConnectionNumber(dpy); + struct epoll_event dpy_event; + + // Initialize struct to 0 + memset(&dpy_event, 0, sizeof(dpy_event)); + + DEBUG("Display socket is fd %d\n", dpy_fd); + + if (epoll_fd == -1) + fputs("Failed to create epoll file descriptor", stderr); + + dpy_event.events = EPOLLIN; + dpy_event.data.fd = dpy_fd; + if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, dpy_fd, &dpy_event)) { + fputs("Failed to add display file descriptor to epoll", stderr); + close(epoll_fd); + exit(1); + } + + if (ipc_init(ipcsockpath, epoll_fd, ipccommands, LENGTH(ipccommands)) < 0) + fputs("Failed to initialize IPC\n", stderr); +} + +void +setstatus(const Arg *arg) +{ + Monitor *m; + #if BAR_EXTRASTATUS_PATCH + if (arg->v == NULL) { + strcpy(stext, "dwm-"VERSION); + estext[0] = '\0'; + } else { + strcpy(rawstext, arg->v); + char *e = strchr(rawstext, statussep); + if (e) { + *e = '\0'; e++; + #if BAR_STATUSCMD_PATCH + strncpy(rawestext, e, sizeof(estext) - 1); + copyvalidchars(estext, rawestext); + #else + strncpy(estext, e, sizeof(estext) - 1); + #endif // BAR_STATUSCMD_PATCH + } else { + estext[0] = '\0'; + } + #if BAR_STATUSCMD_PATCH + copyvalidchars(stext, rawstext); + #else + strncpy(stext, rawstext, sizeof(stext) - 1); + #endif // BAR_STATUSCMD_PATCH + } + #elif BAR_STATUSCMD_PATCH + if (!gettextprop(root, XA_WM_NAME, rawstext, sizeof(rawstext))) + strcpy(stext, "dwm-"VERSION); + else + copyvalidchars(stext, rawstext); + #else + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + strcpy(stext, "dwm-"VERSION); + #endif // BAR_EXTRASTATUS_PATCH | BAR_STATUSCMD_PATCH + for (m = mons; m; m = m->next) + drawbar(m); +} + diff --git a/patch/ipc.h b/patch/ipc.h new file mode 100644 index 0000000..ac8fb73 --- /dev/null +++ b/patch/ipc.h @@ -0,0 +1,7 @@ +#include <sys/epoll.h> + +static int handlexevent(struct epoll_event *ev); +static void setlayoutsafe(const Arg *arg); +static void setupepoll(void); +static void setstatus(const Arg *arg); + diff --git a/patch/ipc/IPCClient.c b/patch/ipc/IPCClient.c new file mode 100644 index 0000000..a157513 --- /dev/null +++ b/patch/ipc/IPCClient.c @@ -0,0 +1,67 @@ +#include "IPCClient.h" + +#include <string.h> +#include <sys/epoll.h> + +#include "util.h" + +IPCClient * +ipc_client_new(int fd) +{ + IPCClient *c = (IPCClient *)malloc(sizeof(IPCClient)); + + if (c == NULL) return NULL; + + // Initialize struct + memset(&c->event, 0, sizeof(struct epoll_event)); + + c->buffer_size = 0; + c->buffer = NULL; + c->fd = fd; + c->event.data.fd = fd; + c->next = NULL; + c->prev = NULL; + c->subscriptions = 0; + + return c; +} + +void +ipc_list_add_client(IPCClientList *list, IPCClient *nc) +{ + DEBUG("Adding client with fd %d to list\n", nc->fd); + + if (*list == NULL) { + // List is empty, point list at first client + *list = nc; + } else { + IPCClient *c; + // Go to last client in list + for (c = *list; c && c->next; c = c->next) + ; + c->next = nc; + nc->prev = c; + } +} + +void +ipc_list_remove_client(IPCClientList *list, IPCClient *c) +{ + IPCClient *cprev = c->prev; + IPCClient *cnext = c->next; + + if (cprev != NULL) cprev->next = c->next; + if (cnext != NULL) cnext->prev = c->prev; + if (c == *list) *list = c->next; +} + +IPCClient * +ipc_list_get_client(IPCClientList list, int fd) +{ + for (IPCClient *c = list; c; c = c->next) { + if (c->fd == fd) return c; + } + + return NULL; +} + diff --git a/patch/ipc/IPCClient.h b/patch/ipc/IPCClient.h new file mode 100644 index 0000000..ee93030 --- /dev/null +++ b/patch/ipc/IPCClient.h @@ -0,0 +1,62 @@ +#ifndef IPC_CLIENT_H_ +#define IPC_CLIENT_H_ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/epoll.h> + +typedef struct IPCClient IPCClient; +/** + * This structure contains the details of an IPC Client and pointers for a + * linked list + */ +struct IPCClient { + int fd; + int subscriptions; + + char *buffer; + uint32_t buffer_size; + + struct epoll_event event; + IPCClient *next; + IPCClient *prev; +}; + +typedef IPCClient *IPCClientList; + +/** + * Allocate memory for new IPCClient with the specified file descriptor and + * initialize struct. + * + * @param fd File descriptor of IPC client + * + * @return Address to allocated IPCClient struct + */ +IPCClient *ipc_client_new(int fd); + +/** + * Add an IPC Client to the specified list + * + * @param list Address of the list to add the client to + * @param nc Address of the IPCClient + */ +void ipc_list_add_client(IPCClientList *list, IPCClient *nc); + +/** + * Remove an IPCClient from the specified list + * + * @param list Address of the list to remove the client from + * @param c Address of the IPCClient + */ +void ipc_list_remove_client(IPCClientList *list, IPCClient *c); + +/** + * Get an IPCClient from the specified IPCClient list + * + * @param list List to remove the client from + * @param fd File descriptor of the IPCClient + */ +IPCClient *ipc_list_get_client(IPCClientList list, int fd); + +#endif // IPC_CLIENT_H_ + diff --git a/patch/ipc/dwm-msg.c b/patch/ipc/dwm-msg.c new file mode 100644 index 0000000..ca1e1a4 --- /dev/null +++ b/patch/ipc/dwm-msg.c @@ -0,0 +1,549 @@ +#include <ctype.h> +#include <errno.h> +#include <inttypes.h> +#include <stdarg.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include <yajl/yajl_gen.h> + +#define IPC_MAGIC "DWM-IPC" +// clang-format off +#define IPC_MAGIC_ARR { 'D', 'W', 'M', '-', 'I', 'P', 'C' } +// clang-format on +#define IPC_MAGIC_LEN 7 // Not including null char + +#define IPC_EVENT_TAG_CHANGE "tag_change_event" +#define IPC_EVENT_CLIENT_FOCUS_CHANGE "client_focus_change_event" +#define IPC_EVENT_LAYOUT_CHANGE "layout_change_event" +#define IPC_EVENT_MONITOR_FOCUS_CHANGE "monitor_focus_change_event" +#define IPC_EVENT_FOCUSED_TITLE_CHANGE "focused_title_change_event" +#define IPC_EVENT_FOCUSED_STATE_CHANGE "focused_state_change_event" + +#define YSTR(str) yajl_gen_string(gen, (unsigned char *)str, strlen(str)) +#define YINT(num) yajl_gen_integer(gen, num) +#define YDOUBLE(num) yajl_gen_double(gen, num) +#define YBOOL(v) yajl_gen_bool(gen, v) +#define YNULL() yajl_gen_null(gen) +#define YARR(body) \ + { \ + yajl_gen_array_open(gen); \ + body; \ + yajl_gen_array_close(gen); \ + } +#define YMAP(body) \ + { \ + yajl_gen_map_open(gen); \ + body; \ + yajl_gen_map_close(gen); \ + } + +typedef unsigned long Window; + +const char *DEFAULT_SOCKET_PATH = "/tmp/dwm.sock"; +static int sock_fd = -1; +static unsigned int ignore_reply = 0; + +typedef enum IPCMessageType { + IPC_TYPE_RUN_COMMAND = 0, + IPC_TYPE_GET_MONITORS = 1, + IPC_TYPE_GET_TAGS = 2, + IPC_TYPE_GET_LAYOUTS = 3, + IPC_TYPE_GET_DWM_CLIENT = 4, + IPC_TYPE_SUBSCRIBE = 5, + IPC_TYPE_EVENT = 6 +} IPCMessageType; + +// Every IPC message must begin with this +typedef struct dwm_ipc_header { + uint8_t magic[IPC_MAGIC_LEN]; + uint32_t size; + uint8_t type; +} __attribute((packed)) dwm_ipc_header_t; + +static int +recv_message(uint8_t *msg_type, uint32_t *reply_size, uint8_t **reply) +{ + uint32_t read_bytes = 0; + const int32_t to_read = sizeof(dwm_ipc_header_t); + char header[to_read]; + char *walk = header; + + // Try to read header + while (read_bytes < to_read) { + ssize_t n = read(sock_fd, header + read_bytes, to_read - read_bytes); + + if (n == 0) { + if (read_bytes == 0) { + fprintf(stderr, "Unexpectedly reached EOF while reading header."); + fprintf(stderr, + "Read %" PRIu32 " bytes, expected %" PRIu32 " total bytes.\n", + read_bytes, to_read); + return -2; + } else { + fprintf(stderr, "Unexpectedly reached EOF while reading header."); + fprintf(stderr, + "Read %" PRIu32 " bytes, expected %" PRIu32 " total bytes.\n", + read_bytes, to_read); + return -3; + } + } else if (n == -1) { + return -1; + } + + read_bytes += n; + } + + // Check if magic string in header matches + if (memcmp(walk, IPC_MAGIC, IPC_MAGIC_LEN) != 0) { + fprintf(stderr, "Invalid magic string. Got '%.*s', expected '%s'\n", + IPC_MAGIC_LEN, walk, IPC_MAGIC); + return -3; + } + + walk += IPC_MAGIC_LEN; + + // Extract reply size + memcpy(reply_size, walk, sizeof(uint32_t)); + walk += sizeof(uint32_t); + + // Extract message type + memcpy(msg_type, walk, sizeof(uint8_t)); + walk += sizeof(uint8_t); + + (*reply) = malloc(*reply_size); + + // Extract payload + read_bytes = 0; + while (read_bytes < *reply_size) { + ssize_t n = read(sock_fd, *reply + read_bytes, *reply_size - read_bytes); + + if (n == 0) { + fprintf(stderr, "Unexpectedly reached EOF while reading payload."); + fprintf(stderr, "Read %" PRIu32 " bytes, expected %" PRIu32 " bytes.\n", + read_bytes, *reply_size); + free(*reply); + return -2; + } else if (n == -1) { + if (errno == EINTR || errno == EAGAIN) continue; + free(*reply); + return -1; + } + + read_bytes += n; + } + + return 0; +} + +static int +read_socket(IPCMessageType *msg_type, uint32_t *msg_size, char **msg) +{ + int ret = -1; + + while (ret != 0) { + ret = recv_message((uint8_t *)msg_type, msg_size, (uint8_t **)msg); + + if (ret < 0) { + // Try again (non-fatal error) + if (ret == -1 && (errno == EINTR || errno == EAGAIN)) continue; + + fprintf(stderr, "Error receiving response from socket. "); + fprintf(stderr, "The connection might have been lost.\n"); + exit(2); + } + } + + return 0; +} + +static ssize_t +write_socket(const void *buf, size_t count) +{ + size_t written = 0; + + while (written < count) { + const ssize_t n = + write(sock_fd, ((uint8_t *)buf) + written, count - written); + + if (n == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) + continue; + else + return n; + } + written += n; + } + return written; +} + +static void +connect_to_socket() +{ + struct sockaddr_un addr; + + int sock = socket(AF_UNIX, SOCK_STREAM, 0); + + // Initialize struct to 0 + memset(&addr, 0, sizeof(struct sockaddr_un)); + + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, DEFAULT_SOCKET_PATH); + + connect(sock, (const struct sockaddr *)&addr, sizeof(struct sockaddr_un)); + + sock_fd = sock; +} + +static int +send_message(IPCMessageType msg_type, uint32_t msg_size, uint8_t *msg) +{ + dwm_ipc_header_t header = { + .magic = IPC_MAGIC_ARR, .size = msg_size, .type = msg_type}; + + size_t header_size = sizeof(dwm_ipc_header_t); + size_t total_size = header_size + msg_size; + + uint8_t buffer[total_size]; + + // Copy header to buffer + memcpy(buffer, &header, header_size); + // Copy message to buffer + memcpy(buffer + header_size, msg, header.size); + + write_socket(buffer, total_size); + + return 0; +} + +static int +is_float(const char *s) +{ + size_t len = strlen(s); + int is_dot_used = 0; + int is_minus_used = 0; + + // Floats can only have one decimal point in between or digits + // Optionally, floats can also be below zero (negative) + for (int i = 0; i < len; i++) { + if (isdigit(s[i])) + continue; + else if (!is_dot_used && s[i] == '.' && i != 0 && i != len - 1) { + is_dot_used = 1; + continue; + } else if (!is_minus_used && s[i] == '-' && i == 0) { + is_minus_used = 1; + continue; + } else + return 0; + } + + return 1; +} + +static int +is_unsigned_int(const char *s) +{ + size_t len = strlen(s); + + // Unsigned int can only have digits + for (int i = 0; i < len; i++) { + if (isdigit(s[i])) + continue; + else + return 0; + } + + return 1; +} + +static int +is_signed_int(const char *s) +{ + size_t len = strlen(s); + + // Signed int can only have digits and a negative sign at the start + for (int i = 0; i < len; i++) { + if (isdigit(s[i])) + continue; + else if (i == 0 && s[i] == '-') { + continue; + } else + return 0; + } + + return 1; +} + +static void +flush_socket_reply() +{ + IPCMessageType reply_type; + uint32_t reply_size; + char *reply; + + read_socket(&reply_type, &reply_size, &reply); + + free(reply); +} + +static void +print_socket_reply() +{ + IPCMessageType reply_type; + uint32_t reply_size; + char *reply; + + read_socket(&reply_type, &reply_size, &reply); + + printf("%.*s\n", reply_size, reply); + fflush(stdout); + free(reply); +} + +static int +run_command(const char *name, char *args[], int argc) +{ + const unsigned char *msg; + size_t msg_size; + + yajl_gen gen = yajl_gen_alloc(NULL); + + // Message format: + // { + // "command": "<name>", + // "args": [ ... ] + // } + // clang-format off + YMAP( + YSTR("command"); YSTR(name); + YSTR("args"); YARR( + for (int i = 0; i < argc; i++) { + if (is_signed_int(args[i])) { + long long num = atoll(args[i]); + YINT(num); + } else if (is_float(args[i])) { + float num = atof(args[i]); + YDOUBLE(num); + } else { + YSTR(args[i]); + } + } + ) + ) + // clang-format on + + yajl_gen_get_buf(gen, &msg, &msg_size); + + send_message(IPC_TYPE_RUN_COMMAND, msg_size, (uint8_t *)msg); + + if (!ignore_reply) + print_socket_reply(); + else + flush_socket_reply(); + + yajl_gen_free(gen); + + return 0; +} + +static int +get_monitors() +{ + send_message(IPC_TYPE_GET_MONITORS, 1, (uint8_t *)""); + print_socket_reply(); + return 0; +} + +static int +get_tags() +{ + send_message(IPC_TYPE_GET_TAGS, 1, (uint8_t *)""); + print_socket_reply(); + + return 0; +} + +static int +get_layouts() +{ + send_message(IPC_TYPE_GET_LAYOUTS, 1, (uint8_t *)""); + print_socket_reply(); + + return 0; +} + +static int +get_dwm_client(Window win) +{ + const unsigned char *msg; + size_t msg_size; + + yajl_gen gen = yajl_gen_alloc(NULL); + + // Message format: + // { + // "client_window_id": "<win>" + // } + // clang-format off + YMAP( + YSTR("client_window_id"); YINT(win); + ) + // clang-format on + + yajl_gen_get_buf(gen, &msg, &msg_size); + + send_message(IPC_TYPE_GET_DWM_CLIENT, msg_size, (uint8_t *)msg); + + print_socket_reply(); + + yajl_gen_free(gen); + + return 0; +} + +static int +subscribe(const char *event) +{ + const unsigned char *msg; + size_t msg_size; + + yajl_gen gen = yajl_gen_alloc(NULL); + + // Message format: + // { + // "event": "<event>", + // "action": "subscribe" + // } + // clang-format off + YMAP( + YSTR("event"); YSTR(event); + YSTR("action"); YSTR("subscribe"); + ) + // clang-format on + + yajl_gen_get_buf(gen, &msg, &msg_size); + + send_message(IPC_TYPE_SUBSCRIBE, msg_size, (uint8_t *)msg); + + if (!ignore_reply) + print_socket_reply(); + else + flush_socket_reply(); + + yajl_gen_free(gen); + + return 0; +} + +static void +usage_error(const char *prog_name, const char *format, ...) +{ + va_list args; + va_start(args, format); + + fprintf(stderr, "Error: "); + vfprintf(stderr, format, args); + fprintf(stderr, "\nusage: %s <command> [...]\n", prog_name); + fprintf(stderr, "Try '%s help'\n", prog_name); + + va_end(args); + exit(1); +} + +static void +print_usage(const char *name) +{ + printf("usage: %s [options] <command> [...]\n", name); + puts(""); + puts("Commands:"); + puts(" run_command <name> [args...] Run an IPC command"); + puts(""); + puts(" get_monitors Get monitor properties"); + puts(""); + puts(" get_tags Get list of tags"); + puts(""); + puts(" get_layouts Get list of layouts"); + puts(""); + puts(" get_dwm_client <window_id> Get dwm client proprties"); + puts(""); + puts(" subscribe [events...] Subscribe to specified events"); + puts(" Options: " IPC_EVENT_TAG_CHANGE ","); + puts(" " IPC_EVENT_LAYOUT_CHANGE ","); + puts(" " IPC_EVENT_CLIENT_FOCUS_CHANGE ","); + puts(" " IPC_EVENT_MONITOR_FOCUS_CHANGE ","); + puts(" " IPC_EVENT_FOCUSED_TITLE_CHANGE ","); + puts(" " IPC_EVENT_FOCUSED_STATE_CHANGE); + puts(""); + puts(" help Display this message"); + puts(""); + puts("Options:"); + puts(" --ignore-reply Don't print reply messages from"); + puts(" run_command and subscribe."); + puts(""); +} + +int +main(int argc, char *argv[]) +{ + const char *prog_name = argv[0]; + + connect_to_socket(); + if (sock_fd == -1) { + fprintf(stderr, "Failed to connect to socket\n"); + return 1; + } + + int i = 1; + if (i < argc && strcmp(argv[i], "--ignore-reply") == 0) { + ignore_reply = 1; + i++; + } + + if (i >= argc) usage_error(prog_name, "Expected an argument, got none"); + + if (!argc || strcmp(argv[i], "help") == 0) + print_usage(prog_name); + else if (strcmp(argv[i], "run_command") == 0) { + if (++i >= argc) usage_error(prog_name, "No command specified"); + // Command name + char *command = argv[i]; + // Command arguments are everything after command name + char **command_args = argv + ++i; + // Number of command arguments + int command_argc = argc - i; + run_command(command, command_args, command_argc); + } else if (strcmp(argv[i], "get_monitors") == 0) { + get_monitors(); + } else if (strcmp(argv[i], "get_tags") == 0) { + get_tags(); + } else if (strcmp(argv[i], "get_layouts") == 0) { + get_layouts(); + } else if (strcmp(argv[i], "get_dwm_client") == 0) { + if (++i < argc) { + if (is_unsigned_int(argv[i])) { + Window win = atol(argv[i]); + get_dwm_client(win); + } else + usage_error(prog_name, "Expected unsigned integer argument"); + } else + usage_error(prog_name, "Expected the window id"); + } else if (strcmp(argv[i], "subscribe") == 0) { + if (++i < argc) { + for (int j = i; j < argc; j++) subscribe(argv[j]); + } else + usage_error(prog_name, "Expected event name"); + // Keep listening for events forever + while (1) { + print_socket_reply(); + } + } else + usage_error(prog_name, "Invalid argument '%s'", argv[i]); + + return 0; +} + diff --git a/patch/ipc/ipc.c b/patch/ipc/ipc.c new file mode 100644 index 0000000..eae9667 --- /dev/null +++ b/patch/ipc/ipc.c @@ -0,0 +1,1202 @@ +#include "ipc.h" + +#include <errno.h> +#include <fcntl.h> +#include <inttypes.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/epoll.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include <yajl/yajl_gen.h> +#include <yajl/yajl_tree.h> + +#include "util.h" +#include "yajl_dumps.h" + +static struct sockaddr_un sockaddr; +static struct epoll_event sock_epoll_event; +static IPCClientList ipc_clients = NULL; +static int epoll_fd = -1; +static int sock_fd = -1; +static IPCCommand *ipc_commands; +static unsigned int ipc_commands_len; +// Max size is 1 MB +static const uint32_t MAX_MESSAGE_SIZE = 1000000; +static const int IPC_SOCKET_BACKLOG = 5; + +/** + * Create IPC socket at specified path and return file descriptor to socket. + * This initializes the static variable sockaddr. + */ +static int +ipc_create_socket(const char *filename) +{ + char *normal_filename; + char *parent; + const size_t addr_size = sizeof(struct sockaddr_un); + const int sock_type = SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC; + + normalizepath(filename, &normal_filename); + + // In case socket file exists + unlink(normal_filename); + + // For portability clear the addr structure, since some implementations have + // nonstandard fields in the structure + memset(&sockaddr, 0, addr_size); + + parentdir(normal_filename, &parent); + // Create parent directories + mkdirp(parent); + free(parent); + + sockaddr.sun_family = AF_LOCAL; + strcpy(sockaddr.sun_path, normal_filename); + free(normal_filename); + + sock_fd = socket(AF_LOCAL, sock_type, 0); + if (sock_fd == -1) { + fputs("Failed to create socket\n", stderr); + return -1; + } + + DEBUG("Created socket at %s\n", sockaddr.sun_path); + + if (bind(sock_fd, (const struct sockaddr *)&sockaddr, addr_size) == -1) { + fputs("Failed to bind socket\n", stderr); + return -1; + } + + DEBUG("Socket binded\n"); + + if (listen(sock_fd, IPC_SOCKET_BACKLOG) < 0) { + fputs("Failed to listen for connections on socket\n", stderr); + return -1; + } + + DEBUG("Now listening for connections on socket\n"); + + return sock_fd; +} + +/** + * Internal function used to receive IPC messages from a given file descriptor. + * + * Returns -1 on error reading (could be EAGAIN or EINTR) + * Returns -2 if EOF before header could be read + * Returns -3 if invalid IPC header + * Returns -4 if message length exceeds MAX_MESSAGE_SIZE + */ +static int +ipc_recv_message(int fd, uint8_t *msg_type, uint32_t *reply_size, + uint8_t **reply) +{ + uint32_t read_bytes = 0; + const int32_t to_read = sizeof(dwm_ipc_header_t); + char header[to_read]; + char *walk = header; + + // Try to read header + while (read_bytes < to_read) { + const ssize_t n = read(fd, header + read_bytes, to_read - read_bytes); + + if (n == 0) { + if (read_bytes == 0) { + fprintf(stderr, "Unexpectedly reached EOF while reading header."); + fprintf(stderr, + "Read %" PRIu32 " bytes, expected %" PRIu32 " total bytes.\n", + read_bytes, to_read); + return -2; + } else { + fprintf(stderr, "Unexpectedly reached EOF while reading header."); + fprintf(stderr, + "Read %" PRIu32 " bytes, expected %" PRIu32 " total bytes.\n", + read_bytes, to_read); + return -3; + } + } else if (n == -1) { + // errno will still be set + return -1; + } + + read_bytes += n; + } + + // Check if magic string in header matches + if (memcmp(walk, IPC_MAGIC, IPC_MAGIC_LEN) != 0) { + fprintf(stderr, "Invalid magic string. Got '%.*s', expected '%s'\n", + IPC_MAGIC_LEN, walk, IPC_MAGIC); + return -3; + } + + walk += IPC_MAGIC_LEN; + + // Extract reply size + memcpy(reply_size, walk, sizeof(uint32_t)); + walk += sizeof(uint32_t); + + if (*reply_size > MAX_MESSAGE_SIZE) { + fprintf(stderr, "Message too long: %" PRIu32 " bytes. ", *reply_size); + fprintf(stderr, "Maximum message size is: %d\n", MAX_MESSAGE_SIZE); + return -4; + } + + // Extract message type + memcpy(msg_type, walk, sizeof(uint8_t)); + walk += sizeof(uint8_t); + + if (*reply_size > 0) + (*reply) = malloc(*reply_size); + else + return 0; + + read_bytes = 0; + while (read_bytes < *reply_size) { + const ssize_t n = read(fd, *reply + read_bytes, *reply_size - read_bytes); + + if (n == 0) { + fprintf(stderr, "Unexpectedly reached EOF while reading payload."); + fprintf(stderr, "Read %" PRIu32 " bytes, expected %" PRIu32 " bytes.\n", + read_bytes, *reply_size); + free(*reply); + return -2; + } else if (n == -1) { + // TODO: Should we return and wait for another epoll event? + // This would require saving the partial read in some way. + if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) continue; + + free(*reply); + return -1; + } + + read_bytes += n; + } + + return 0; +} + +/** + * Internal function used to write a buffer to a file descriptor + * + * Returns number of bytes written if successful write + * Returns 0 if no bytes were written due to EAGAIN or EWOULDBLOCK + * Returns -1 on unknown error trying to write, errno will carry over from + * write() call + */ +static ssize_t +ipc_write_message(int fd, const void *buf, size_t count) +{ + size_t written = 0; + + while (written < count) { + const ssize_t n = write(fd, (uint8_t *)buf + written, count - written); + + if (n == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) + return written; + else if (errno == EINTR) + continue; + else + return n; + } + + written += n; + DEBUG("Wrote %zu/%zu to client at fd %d\n", written, count, fd); + } + + return written; +} + +/** + * Initialization for generic event message. This is used to allocate the yajl + * handle, set yajl options, and in the future any other initialization that + * should occur for event messages. + */ +static void +ipc_event_init_message(yajl_gen *gen) +{ + *gen = yajl_gen_alloc(NULL); + yajl_gen_config(*gen, yajl_gen_beautify, 1); +} + +/** + * Prepares buffers of IPC subscribers of specified event using buffer from yajl + * handle. + */ +static void +ipc_event_prepare_send_message(yajl_gen gen, IPCEvent event) +{ + const unsigned char *buffer; + size_t len = 0; + + yajl_gen_get_buf(gen, &buffer, &len); + len++; // For null char + + for (IPCClient *c = ipc_clients; c; c = c->next) { + if (c->subscriptions & event) { + DEBUG("Sending selected client change event to fd %d\n", c->fd); + ipc_prepare_send_message(c, IPC_TYPE_EVENT, len, (char *)buffer); + } + } + + // Not documented, but this frees temp_buffer + yajl_gen_free(gen); +} + +/** + * Initialization for generic reply message. This is used to allocate the yajl + * handle, set yajl options, and in the future any other initialization that + * should occur for reply messages. + */ +static void +ipc_reply_init_message(yajl_gen *gen) +{ + *gen = yajl_gen_alloc(NULL); + yajl_gen_config(*gen, yajl_gen_beautify, 1); +} + +/** + * Prepares the IPC client's buffer with a message using the buffer of the yajl + * handle. + */ +static void +ipc_reply_prepare_send_message(yajl_gen gen, IPCClient *c, + IPCMessageType msg_type) +{ + const unsigned char *buffer; + size_t len = 0; + + yajl_gen_get_buf(gen, &buffer, &len); + len++; // For null char + + ipc_prepare_send_message(c, msg_type, len, (const char *)buffer); + + // Not documented, but this frees temp_buffer + yajl_gen_free(gen); +} + +/** + * Find the IPCCommand with the specified name + * + * Returns 0 if a command with the specified name was found + * Returns -1 if a command with the specified name could not be found + */ +static int +ipc_get_ipc_command(const char *name, IPCCommand *ipc_command) +{ + for (int i = 0; i < ipc_commands_len; i++) { + if (strcmp(ipc_commands[i].name, name) == 0) { + *ipc_command = ipc_commands[i]; + return 0; + } + } + + return -1; +} + +/** + * Parse a IPC_TYPE_RUN_COMMAND message from a client. This function extracts + * the arguments, argument count, argument types, and command name and returns + * the parsed information as an IPCParsedCommand. If this function returns + * successfully, the parsed_command must be freed using + * ipc_free_parsed_command_members. + * + * Returns 0 if the message was successfully parsed + * Returns -1 otherwise + */ +static int +ipc_parse_run_command(char *msg, IPCParsedCommand *parsed_command) +{ + char error_buffer[1000]; + yajl_val parent = yajl_tree_parse(msg, error_buffer, 1000); + + if (parent == NULL) { + fputs("Failed to parse command from client\n", stderr); + fprintf(stderr, "%s\n", error_buffer); + fprintf(stderr, "Tried to parse: %s\n", msg); + return -1; + } + + // Format: + // { + // "command": "<command name>" + // "args": [ "arg1", "arg2", ... ] + // } + const char *command_path[] = {"command", 0}; + yajl_val command_val = yajl_tree_get(parent, command_path, yajl_t_string); + + if (command_val == NULL) { + fputs("No command key found in client message\n", stderr); + yajl_tree_free(parent); + return -1; + } + + const char *command_name = YAJL_GET_STRING(command_val); + size_t command_name_len = strlen(command_name); + parsed_command->name = (char *)malloc((command_name_len + 1) * sizeof(char)); + strcpy(parsed_command->name, command_name); + + DEBUG("Received command: %s\n", parsed_command->name); + + const char *args_path[] = {"args", 0}; + yajl_val args_val = yajl_tree_get(parent, args_path, yajl_t_array); + + if (args_val == NULL) { + fputs("No args key found in client message\n", stderr); + yajl_tree_free(parent); + return -1; + } + + unsigned int *argc = &parsed_command->argc; + Arg **args = &parsed_command->args; + ArgType **arg_types = &parsed_command->arg_types; + + *argc = args_val->u.array.len; + + // If no arguments are specified, make a dummy argument to pass to the + // function. This is just the way dwm's void(Arg*) functions are setup. + if (*argc == 0) { + *args = (Arg *)malloc(sizeof(Arg)); + *arg_types = (ArgType *)malloc(sizeof(ArgType)); + (*arg_types)[0] = ARG_TYPE_NONE; + (*args)[0].f = 0; + (*argc)++; + } else if (*argc > 0) { + *args = (Arg *)calloc(*argc, sizeof(Arg)); + *arg_types = (ArgType *)malloc(*argc * sizeof(ArgType)); + + for (int i = 0; i < *argc; i++) { + yajl_val arg_val = args_val->u.array.values[i]; + + if (YAJL_IS_NUMBER(arg_val)) { + if (YAJL_IS_INTEGER(arg_val)) { + // Any values below 0 must be a signed int + if (YAJL_GET_INTEGER(arg_val) < 0) { + (*args)[i].i = YAJL_GET_INTEGER(arg_val); + (*arg_types)[i] = ARG_TYPE_SINT; + DEBUG("i=%ld\n", (*args)[i].i); + // Any values above 0 should be an unsigned int + } else if (YAJL_GET_INTEGER(arg_val) >= 0) { + (*args)[i].ui = YAJL_GET_INTEGER(arg_val); + (*arg_types)[i] = ARG_TYPE_UINT; + DEBUG("ui=%ld\n", (*args)[i].i); + } + // If the number is not an integer, it must be a float + } else { + (*args)[i].f = (float)YAJL_GET_DOUBLE(arg_val); + (*arg_types)[i] = ARG_TYPE_FLOAT; + DEBUG("f=%f\n", (*args)[i].f); + // If argument is not a number, it must be a string + } + } else if (YAJL_IS_STRING(arg_val)) { + char *arg_s = YAJL_GET_STRING(arg_val); + size_t arg_s_size = (strlen(arg_s) + 1) * sizeof(char); + (*args)[i].v = (char *)malloc(arg_s_size); + (*arg_types)[i] = ARG_TYPE_STR; + strcpy((char *)(*args)[i].v, arg_s); + } + } + } + + yajl_tree_free(parent); + + return 0; +} + +/** + * Free the members of a IPCParsedCommand struct + */ +static void +ipc_free_parsed_command_members(IPCParsedCommand *command) +{ + for (int i = 0; i < command->argc; i++) { + if (command->arg_types[i] == ARG_TYPE_STR) free((void *)command->args[i].v); + } + free(command->args); + free(command->arg_types); + free(command->name); +} + +/** + * Check if the given arguments are the correct length and type. Also do any + * casting to correct the types. + * + * Returns 0 if the arguments were the correct length and types + * Returns -1 if the argument count doesn't match + * Returns -2 if the argument types don't match + */ +static int +ipc_validate_run_command(IPCParsedCommand *parsed, const IPCCommand actual) +{ + if (actual.argc != parsed->argc) return -1; + + for (int i = 0; i < parsed->argc; i++) { + ArgType ptype = parsed->arg_types[i]; + ArgType atype = actual.arg_types[i]; + + if (ptype != atype) { + if (ptype == ARG_TYPE_UINT && atype == ARG_TYPE_PTR) + // If this argument is supposed to be a void pointer, cast it + parsed->args[i].v = (void *)parsed->args[i].ui; + else if (ptype == ARG_TYPE_UINT && atype == ARG_TYPE_SINT) + // If this argument is supposed to be a signed int, cast it + parsed->args[i].i = parsed->args[i].ui; + else + return -2; + } + } + + return 0; +} + +/** + * Convert event name to their IPCEvent equivalent enum value + * + * Returns 0 if a valid event name was given + * Returns -1 otherwise + */ +static int +ipc_event_stoi(const char *subscription, IPCEvent *event) +{ + if (strcmp(subscription, "tag_change_event") == 0) + *event = IPC_EVENT_TAG_CHANGE; + else if (strcmp(subscription, "client_focus_change_event") == 0) + *event = IPC_EVENT_CLIENT_FOCUS_CHANGE; + else if (strcmp(subscription, "layout_change_event") == 0) + *event = IPC_EVENT_LAYOUT_CHANGE; + else if (strcmp(subscription, "monitor_focus_change_event") == 0) + *event = IPC_EVENT_MONITOR_FOCUS_CHANGE; + else if (strcmp(subscription, "focused_title_change_event") == 0) + *event = IPC_EVENT_FOCUSED_TITLE_CHANGE; + else if (strcmp(subscription, "focused_state_change_event") == 0) + *event = IPC_EVENT_FOCUSED_STATE_CHANGE; + else + return -1; + return 0; +} + +/** + * Parse a IPC_TYPE_SUBSCRIBE message from a client. This function extracts the + * event name and the subscription action from the message. + * + * Returns 0 if message was successfully parsed + * Returns -1 otherwise + */ +static int +ipc_parse_subscribe(const char *msg, IPCSubscriptionAction *subscribe, + IPCEvent *event) +{ + char error_buffer[100]; + yajl_val parent = yajl_tree_parse((char *)msg, error_buffer, 100); + + if (parent == NULL) { + fputs("Failed to parse command from client\n", stderr); + fprintf(stderr, "%s\n", error_buffer); + return -1; + } + + // Format: + // { + // "event": "<event name>" + // "action": "<subscribe|unsubscribe>" + // } + const char *event_path[] = {"event", 0}; + yajl_val event_val = yajl_tree_get(parent, event_path, yajl_t_string); + + if (event_val == NULL) { + fputs("No 'event' key found in client message\n", stderr); + return -1; + } + + const char *event_str = YAJL_GET_STRING(event_val); + DEBUG("Received event: %s\n", event_str); + + if (ipc_event_stoi(event_str, event) < 0) return -1; + + const char *action_path[] = {"action", 0}; + yajl_val action_val = yajl_tree_get(parent, action_path, yajl_t_string); + + if (action_val == NULL) { + fputs("No 'action' key found in client message\n", stderr); + return -1; + } + + const char *action = YAJL_GET_STRING(action_val); + + if (strcmp(action, "subscribe") == 0) + *subscribe = IPC_ACTION_SUBSCRIBE; + else if (strcmp(action, "unsubscribe") == 0) + *subscribe = IPC_ACTION_UNSUBSCRIBE; + else { + fputs("Invalid action specified for subscription\n", stderr); + return -1; + } + + yajl_tree_free(parent); + + return 0; +} + +/** + * Parse an IPC_TYPE_GET_DWM_CLIENT message from a client. This function + * extracts the window id from the message. + * + * Returns 0 if message was successfully parsed + * Returns -1 otherwise + */ +static int +ipc_parse_get_dwm_client(const char *msg, Window *win) +{ + char error_buffer[100]; + + yajl_val parent = yajl_tree_parse(msg, error_buffer, 100); + + if (parent == NULL) { + fputs("Failed to parse message from client\n", stderr); + fprintf(stderr, "%s\n", error_buffer); + return -1; + } + + // Format: + // { + // "client_window_id": <client window id> + // } + const char *win_path[] = {"client_window_id", 0}; + yajl_val win_val = yajl_tree_get(parent, win_path, yajl_t_number); + + if (win_val == NULL) { + fputs("No client window id found in client message\n", stderr); + return -1; + } + + *win = YAJL_GET_INTEGER(win_val); + + yajl_tree_free(parent); + + return 0; +} + +/** + * Called when an IPC_TYPE_RUN_COMMAND message is received from a client. This + * function parses, executes the given command, and prepares a reply message to + * the client indicating success/failure. + * + * NOTE: There is currently no check for argument validity beyond the number of + * arguments given and types of arguments. There is also no way to check if the + * function succeeded based on dwm's void(const Arg*) function types. Pointer + * arguments can cause crashes if they are not validated in the function itself. + * + * Returns 0 if message was successfully parsed + * Returns -1 on failure parsing message + */ +static int +ipc_run_command(IPCClient *ipc_client, char *msg) +{ + IPCParsedCommand parsed_command; + IPCCommand ipc_command; + + // Initialize struct + memset(&parsed_command, 0, sizeof(IPCParsedCommand)); + + if (ipc_parse_run_command(msg, &parsed_command) < 0) { + ipc_prepare_reply_failure(ipc_client, IPC_TYPE_RUN_COMMAND, + "Failed to parse run command"); + return -1; + } + + if (ipc_get_ipc_command(parsed_command.name, &ipc_command) < 0) { + ipc_prepare_reply_failure(ipc_client, IPC_TYPE_RUN_COMMAND, + "Command %s not found", parsed_command.name); + ipc_free_parsed_command_members(&parsed_command); + return -1; + } + + int res = ipc_validate_run_command(&parsed_command, ipc_command); + if (res < 0) { + if (res == -1) + ipc_prepare_reply_failure(ipc_client, IPC_TYPE_RUN_COMMAND, + "%u arguments provided, %u expected", + parsed_command.argc, ipc_command.argc); + else if (res == -2) + ipc_prepare_reply_failure(ipc_client, IPC_TYPE_RUN_COMMAND, + "Type mismatch"); + ipc_free_parsed_command_members(&parsed_command); + return -1; + } + + if (parsed_command.argc == 1) + ipc_command.func.single_param(parsed_command.args); + else if (parsed_command.argc > 1) + ipc_command.func.array_param(parsed_command.args, parsed_command.argc); + + DEBUG("Called function for command %s\n", parsed_command.name); + + ipc_free_parsed_command_members(&parsed_command); + + ipc_prepare_reply_success(ipc_client, IPC_TYPE_RUN_COMMAND); + return 0; +} + +/** + * Called when an IPC_TYPE_GET_MONITORS message is received from a client. It + * prepares a reply with the properties of all of the monitors in JSON. + */ +static void +ipc_get_monitors(IPCClient *c, Monitor *mons, Monitor *selmon) +{ + yajl_gen gen; + ipc_reply_init_message(&gen); + dump_monitors(gen, mons, selmon); + + ipc_reply_prepare_send_message(gen, c, IPC_TYPE_GET_MONITORS); +} + +/** + * Called when an IPC_TYPE_GET_TAGS message is received from a client. It + * prepares a reply with info about all the tags in JSON. + */ +static void +ipc_get_tags(IPCClient *c, const int tags_len) +{ + yajl_gen gen; + ipc_reply_init_message(&gen); + + dump_tags(gen, tags_len); + + ipc_reply_prepare_send_message(gen, c, IPC_TYPE_GET_TAGS); +} + +/** + * Called when an IPC_TYPE_GET_LAYOUTS message is received from a client. It + * prepares a reply with a JSON array of available layouts + */ +static void +ipc_get_layouts(IPCClient *c, const Layout layouts[], const int layouts_len) +{ + yajl_gen gen; + ipc_reply_init_message(&gen); + + dump_layouts(gen, layouts, layouts_len); + + ipc_reply_prepare_send_message(gen, c, IPC_TYPE_GET_LAYOUTS); +} + +/** + * Called when an IPC_TYPE_GET_DWM_CLIENT message is received from a client. It + * prepares a JSON reply with the properties of the client with the specified + * window XID. + * + * Returns 0 if the message was successfully parsed and if the client with the + * specified window XID was found + * Returns -1 if the message could not be parsed + */ +static int +ipc_get_dwm_client(IPCClient *ipc_client, const char *msg, const Monitor *mons) +{ + Window win; + + if (ipc_parse_get_dwm_client(msg, &win) < 0) return -1; + + // Find client with specified window XID + for (const Monitor *m = mons; m; m = m->next) + for (Client *c = m->clients; c; c = c->next) + if (c->win == win) { + yajl_gen gen; + ipc_reply_init_message(&gen); + + dump_client(gen, c); + + ipc_reply_prepare_send_message(gen, ipc_client, + IPC_TYPE_GET_DWM_CLIENT); + + return 0; + } + + ipc_prepare_reply_failure(ipc_client, IPC_TYPE_GET_DWM_CLIENT, + "Client with window id %d not found", win); + return -1; +} + +/** + * Called when an IPC_TYPE_SUBSCRIBE message is received from a client. It + * subscribes/unsubscribes the client from the specified event and replies with + * the result. + * + * Returns 0 if the message was successfully parsed. + * Returns -1 if the message could not be parsed + */ +static int +ipc_subscribe(IPCClient *c, const char *msg) +{ + IPCSubscriptionAction action = IPC_ACTION_SUBSCRIBE; + IPCEvent event = 0; + + if (ipc_parse_subscribe(msg, &action, &event)) { + ipc_prepare_reply_failure(c, IPC_TYPE_SUBSCRIBE, "Event does not exist"); + return -1; + } + + if (action == IPC_ACTION_SUBSCRIBE) { + DEBUG("Subscribing client on fd %d to %d\n", c->fd, event); + c->subscriptions |= event; + } else if (action == IPC_ACTION_UNSUBSCRIBE) { + DEBUG("Unsubscribing client on fd %d to %d\n", c->fd, event); + c->subscriptions ^= event; + } else { + ipc_prepare_reply_failure(c, IPC_TYPE_SUBSCRIBE, + "Invalid subscription action"); + return -1; + } + + ipc_prepare_reply_success(c, IPC_TYPE_SUBSCRIBE); + return 0; +} + +int +ipc_init(const char *socket_path, const int p_epoll_fd, IPCCommand commands[], + const int commands_len) +{ + // Initialize struct to 0 + memset(&sock_epoll_event, 0, sizeof(sock_epoll_event)); + + int socket_fd = ipc_create_socket(socket_path); + if (socket_fd < 0) return -1; + + ipc_commands = commands; + ipc_commands_len = commands_len; + + epoll_fd = p_epoll_fd; + + // Wake up to incoming connection requests + sock_epoll_event.data.fd = socket_fd; + sock_epoll_event.events = EPOLLIN; + if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, socket_fd, &sock_epoll_event)) { + fputs("Failed to add sock file descriptor to epoll", stderr); + return -1; + } + + return socket_fd; +} + +void +ipc_cleanup() +{ + IPCClient *c = ipc_clients; + // Free clients and their buffers + while (c) { + ipc_drop_client(c); + c = ipc_clients; + } + + // Stop waking up for socket events + epoll_ctl(epoll_fd, EPOLL_CTL_DEL, sock_fd, &sock_epoll_event); + + // Uninitialize all static variables + epoll_fd = -1; + sock_fd = -1; + ipc_commands = NULL; + ipc_commands_len = 0; + memset(&sock_epoll_event, 0, sizeof(struct epoll_event)); + memset(&sockaddr, 0, sizeof(struct sockaddr_un)); + + // Delete socket + unlink(sockaddr.sun_path); + + shutdown(sock_fd, SHUT_RDWR); + close(sock_fd); +} + +int +ipc_get_sock_fd() +{ + return sock_fd; +} + +IPCClient * +ipc_get_client(int fd) +{ + return ipc_list_get_client(ipc_clients, fd); +} + +int +ipc_is_client_registered(int fd) +{ + return (ipc_get_client(fd) != NULL); +} + +int +ipc_accept_client() +{ + int fd = -1; + + struct sockaddr_un client_addr; + socklen_t len = 0; + + // For portability clear the addr structure, since some implementations + // have nonstandard fields in the structure + memset(&client_addr, 0, sizeof(struct sockaddr_un)); + + fd = accept(sock_fd, (struct sockaddr *)&client_addr, &len); + if (fd < 0 && errno != EINTR) { + fputs("Failed to accept IPC connection from client", stderr); + return -1; + } + + if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) { + shutdown(fd, SHUT_RDWR); + close(fd); + fputs("Failed to set flags on new client fd", stderr); + } + + IPCClient *nc = ipc_client_new(fd); + if (nc == NULL) return -1; + + // Wake up to messages from this client + nc->event.data.fd = fd; + nc->event.events = EPOLLIN | EPOLLHUP; + epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &nc->event); + + ipc_list_add_client(&ipc_clients, nc); + + DEBUG("%s%d\n", "New client at fd: ", fd); + + return fd; +} + +int +ipc_drop_client(IPCClient *c) +{ + int fd = c->fd; + shutdown(fd, SHUT_RDWR); + int res = close(fd); + + if (res == 0) { + struct epoll_event ev; + + // Stop waking up to messages from this client + epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &ev); + ipc_list_remove_client(&ipc_clients, c); + + free(c->buffer); + free(c); + + DEBUG("Successfully removed client on fd %d\n", fd); + } else if (res < 0 && res != EINTR) { + fprintf(stderr, "Failed to close fd %d\n", fd); + } + + return res; +} + +int +ipc_read_client(IPCClient *c, IPCMessageType *msg_type, uint32_t *msg_size, + char **msg) +{ + int fd = c->fd; + int ret = + ipc_recv_message(fd, (uint8_t *)msg_type, msg_size, (uint8_t **)msg); + + if (ret < 0) { + // This will happen if these errors occur while reading header + if (ret == -1 && + (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) + return -2; + + fprintf(stderr, "Error reading message: dropping client at fd %d\n", fd); + ipc_drop_client(c); + + return -1; + } + + // Make sure receive message is null terminated to avoid parsing issues + if (*msg_size > 0) { + size_t len = *msg_size; + nullterminate(msg, &len); + *msg_size = len; + } + + DEBUG("[fd %d] ", fd); + if (*msg_size > 0) + DEBUG("Received message: '%.*s' ", *msg_size, *msg); + else + DEBUG("Received empty message "); + DEBUG("Message type: %" PRIu8 " ", (uint8_t)*msg_type); + DEBUG("Message size: %" PRIu32 "\n", *msg_size); + + return 0; +} + +ssize_t +ipc_write_client(IPCClient *c) +{ + const ssize_t n = ipc_write_message(c->fd, c->buffer, c->buffer_size); + + if (n < 0) return n; + + // TODO: Deal with client timeouts + + if (n == c->buffer_size) { + c->buffer_size = 0; + free(c->buffer); + // No dangling pointers! + c->buffer = NULL; + // Stop waking up when client is ready to receive messages + if (c->event.events & EPOLLOUT) { + c->event.events -= EPOLLOUT; + epoll_ctl(epoll_fd, EPOLL_CTL_MOD, c->fd, &c->event); + } + return n; + } + + // Shift unwritten buffer to beginning of buffer and reallocate + c->buffer_size -= n; + memmove(c->buffer, c->buffer + n, c->buffer_size); + c->buffer = (char *)realloc(c->buffer, c->buffer_size); + + return n; +} + +void +ipc_prepare_send_message(IPCClient *c, const IPCMessageType msg_type, + const uint32_t msg_size, const char *msg) +{ + dwm_ipc_header_t header = { + .magic = IPC_MAGIC_ARR, .type = msg_type, .size = msg_size}; + + uint32_t header_size = sizeof(dwm_ipc_header_t); + uint32_t packet_size = header_size + msg_size; + + if (c->buffer == NULL) + c->buffer = (char *)malloc(c->buffer_size + packet_size); + else + c->buffer = (char *)realloc(c->buffer, c->buffer_size + packet_size); + + // Copy header to end of client buffer + memcpy(c->buffer + c->buffer_size, &header, header_size); + c->buffer_size += header_size; + + // Copy message to end of client buffer + memcpy(c->buffer + c->buffer_size, msg, msg_size); + c->buffer_size += msg_size; + + // Wake up when client is ready to receive messages + c->event.events |= EPOLLOUT; + epoll_ctl(epoll_fd, EPOLL_CTL_MOD, c->fd, &c->event); +} + +void +ipc_prepare_reply_failure(IPCClient *c, IPCMessageType msg_type, + const char *format, ...) +{ + yajl_gen gen; + va_list args; + + // Get output size + va_start(args, format); + size_t len = vsnprintf(NULL, 0, format, args); + va_end(args); + char *buffer = (char *)malloc((len + 1) * sizeof(char)); + + ipc_reply_init_message(&gen); + + va_start(args, format); + vsnprintf(buffer, len + 1, format, args); + va_end(args); + dump_error_message(gen, buffer); + + ipc_reply_prepare_send_message(gen, c, msg_type); + fprintf(stderr, "[fd %d] Error: %s\n", c->fd, buffer); + + free(buffer); +} + +void +ipc_prepare_reply_success(IPCClient *c, IPCMessageType msg_type) +{ + const char *success_msg = "{\"result\":\"success\"}"; + const size_t msg_len = strlen(success_msg) + 1; // +1 for null char + + ipc_prepare_send_message(c, msg_type, msg_len, success_msg); +} + +void +ipc_tag_change_event(int mon_num, TagState old_state, TagState new_state) +{ + yajl_gen gen; + ipc_event_init_message(&gen); + dump_tag_event(gen, mon_num, old_state, new_state); + ipc_event_prepare_send_message(gen, IPC_EVENT_TAG_CHANGE); +} + +void +ipc_client_focus_change_event(int mon_num, Client *old_client, + Client *new_client) +{ + yajl_gen gen; + ipc_event_init_message(&gen); + dump_client_focus_change_event(gen, old_client, new_client, mon_num); + ipc_event_prepare_send_message(gen, IPC_EVENT_CLIENT_FOCUS_CHANGE); +} + +void +ipc_layout_change_event(const int mon_num, const char *old_symbol, + const Layout *old_layout, const char *new_symbol, + const Layout *new_layout) +{ + yajl_gen gen; + ipc_event_init_message(&gen); + dump_layout_change_event(gen, mon_num, old_symbol, old_layout, new_symbol, + new_layout); + ipc_event_prepare_send_message(gen, IPC_EVENT_LAYOUT_CHANGE); +} + +void +ipc_monitor_focus_change_event(const int last_mon_num, const int new_mon_num) +{ + yajl_gen gen; + ipc_event_init_message(&gen); + dump_monitor_focus_change_event(gen, last_mon_num, new_mon_num); + ipc_event_prepare_send_message(gen, IPC_EVENT_MONITOR_FOCUS_CHANGE); +} + +void +ipc_focused_title_change_event(const int mon_num, const Window client_id, + const char *old_name, const char *new_name) +{ + yajl_gen gen; + ipc_event_init_message(&gen); + dump_focused_title_change_event(gen, mon_num, client_id, old_name, new_name); + ipc_event_prepare_send_message(gen, IPC_EVENT_FOCUSED_TITLE_CHANGE); +} + +void +ipc_focused_state_change_event(const int mon_num, const Window client_id, + const ClientState *old_state, + const ClientState *new_state) +{ + yajl_gen gen; + ipc_event_init_message(&gen); + dump_focused_state_change_event(gen, mon_num, client_id, old_state, + new_state); + ipc_event_prepare_send_message(gen, IPC_EVENT_FOCUSED_STATE_CHANGE); +} + +void +ipc_send_events(Monitor *mons, Monitor **lastselmon, Monitor *selmon) +{ + for (Monitor *m = mons; m; m = m->next) { + unsigned int urg = 0, occ = 0, tagset = 0; + + for (Client *c = m->clients; c; c = c->next) { + occ |= c->tags; + + if (c->isurgent) urg |= c->tags; + } + tagset = m->tagset[m->seltags]; + + TagState new_state = {.selected = tagset, .occupied = occ, .urgent = urg}; + + if (memcmp(&m->tagstate, &new_state, sizeof(TagState)) != 0) { + ipc_tag_change_event(m->num, m->tagstate, new_state); + m->tagstate = new_state; + } + + if (m->lastsel != m->sel) { + ipc_client_focus_change_event(m->num, m->lastsel, m->sel); + m->lastsel = m->sel; + } + + if (strcmp(m->ltsymbol, m->lastltsymbol) != 0 || + m->lastlt != m->lt[m->sellt]) { + ipc_layout_change_event(m->num, m->lastltsymbol, m->lastlt, m->ltsymbol, + m->lt[m->sellt]); + strcpy(m->lastltsymbol, m->ltsymbol); + m->lastlt = m->lt[m->sellt]; + } + + if (*lastselmon != selmon) { + if (*lastselmon != NULL) + ipc_monitor_focus_change_event((*lastselmon)->num, selmon->num); + *lastselmon = selmon; + } + + Client *sel = m->sel; + if (!sel) continue; + ClientState *o = &m->sel->prevstate; + ClientState n = {.oldstate = sel->oldstate, + .isfixed = sel->isfixed, + .isfloating = sel->isfloating, + .isfullscreen = sel->isfullscreen, + .isurgent = sel->isurgent, + .neverfocus = sel->neverfocus}; + if (memcmp(o, &n, sizeof(ClientState)) != 0) { + ipc_focused_state_change_event(m->num, m->sel->win, o, &n); + *o = n; + } + } +} + +int +ipc_handle_client_epoll_event(struct epoll_event *ev, Monitor *mons, + Monitor **lastselmon, Monitor *selmon, const int tags_len, + const Layout *layouts, const int layouts_len) +{ + int fd = ev->data.fd; + IPCClient *c = ipc_get_client(fd); + + if (ev->events & EPOLLHUP) { + DEBUG("EPOLLHUP received from client at fd %d\n", fd); + ipc_drop_client(c); + } else if (ev->events & EPOLLOUT) { + DEBUG("Sending message to client at fd %d...\n", fd); + if (c->buffer_size) ipc_write_client(c); + } else if (ev->events & EPOLLIN) { + IPCMessageType msg_type = 0; + uint32_t msg_size = 0; + char *msg = NULL; + + DEBUG("Received message from fd %d\n", fd); + if (ipc_read_client(c, &msg_type, &msg_size, &msg) < 0) return -1; + + if (msg_type == IPC_TYPE_GET_MONITORS) + ipc_get_monitors(c, mons, selmon); + else if (msg_type == IPC_TYPE_GET_TAGS) + ipc_get_tags(c, tags_len); + else if (msg_type == IPC_TYPE_GET_LAYOUTS) + ipc_get_layouts(c, layouts, layouts_len); + else if (msg_type == IPC_TYPE_RUN_COMMAND) { + if (ipc_run_command(c, msg) < 0) return -1; + ipc_send_events(mons, lastselmon, selmon); + } else if (msg_type == IPC_TYPE_GET_DWM_CLIENT) { + if (ipc_get_dwm_client(c, msg, mons) < 0) return -1; + } else if (msg_type == IPC_TYPE_SUBSCRIBE) { + if (ipc_subscribe(c, msg) < 0) return -1; + } else { + fprintf(stderr, "Invalid message type received from fd %d", fd); + ipc_prepare_reply_failure(c, msg_type, "Invalid message type: %d", + msg_type); + } + free(msg); + } else { + fprintf(stderr, "Epoll event returned %d from fd %d\n", ev->events, fd); + return -1; + } + + return 0; +} + +int +ipc_handle_socket_epoll_event(struct epoll_event *ev) +{ + if (!(ev->events & EPOLLIN)) return -1; + + // EPOLLIN means incoming client connection request + fputs("Received EPOLLIN event on socket\n", stderr); + int new_fd = ipc_accept_client(); + + return new_fd; +} + diff --git a/patch/ipc/ipc.h b/patch/ipc/ipc.h new file mode 100644 index 0000000..4a72fb8 --- /dev/null +++ b/patch/ipc/ipc.h @@ -0,0 +1,320 @@ +#ifndef IPC_H_ +#define IPC_H_ + +#include <stdint.h> +#include <sys/epoll.h> +#include <yajl/yajl_gen.h> + +#include "IPCClient.h" + +// clang-format off +#define IPC_MAGIC "DWM-IPC" +#define IPC_MAGIC_ARR { 'D', 'W', 'M', '-', 'I', 'P', 'C'} +#define IPC_MAGIC_LEN 7 // Not including null char + +#define IPCCOMMAND(FUNC, ARGC, TYPES) \ + { #FUNC, {FUNC }, ARGC, (ArgType[ARGC])TYPES } +// clang-format on + +typedef enum IPCMessageType { + IPC_TYPE_RUN_COMMAND = 0, + IPC_TYPE_GET_MONITORS = 1, + IPC_TYPE_GET_TAGS = 2, + IPC_TYPE_GET_LAYOUTS = 3, + IPC_TYPE_GET_DWM_CLIENT = 4, + IPC_TYPE_SUBSCRIBE = 5, + IPC_TYPE_EVENT = 6 +} IPCMessageType; + +typedef enum IPCEvent { + IPC_EVENT_TAG_CHANGE = 1 << 0, + IPC_EVENT_CLIENT_FOCUS_CHANGE = 1 << 1, + IPC_EVENT_LAYOUT_CHANGE = 1 << 2, + IPC_EVENT_MONITOR_FOCUS_CHANGE = 1 << 3, + IPC_EVENT_FOCUSED_TITLE_CHANGE = 1 << 4, + IPC_EVENT_FOCUSED_STATE_CHANGE = 1 << 5 +} IPCEvent; + +typedef enum IPCSubscriptionAction { + IPC_ACTION_UNSUBSCRIBE = 0, + IPC_ACTION_SUBSCRIBE = 1 +} IPCSubscriptionAction; + +/** + * Every IPC packet starts with this structure + */ +typedef struct dwm_ipc_header { + uint8_t magic[IPC_MAGIC_LEN]; + uint32_t size; + uint8_t type; +} __attribute((packed)) dwm_ipc_header_t; + +typedef enum ArgType { + ARG_TYPE_NONE = 0, + ARG_TYPE_UINT = 1, + ARG_TYPE_SINT = 2, + ARG_TYPE_FLOAT = 3, + ARG_TYPE_PTR = 4, + ARG_TYPE_STR = 5 +} ArgType; + +/** + * An IPCCommand function can have either of these function signatures + */ +typedef union ArgFunction { + void (*single_param)(const Arg *); + void (*array_param)(const Arg *, int); +} ArgFunction; + +typedef struct IPCCommand { + char *name; + ArgFunction func; + unsigned int argc; + ArgType *arg_types; +} IPCCommand; + +typedef struct IPCParsedCommand { + char *name; + Arg *args; + ArgType *arg_types; + unsigned int argc; +} IPCParsedCommand; + +/** + * Initialize the IPC socket and the IPC module + * + * @param socket_path Path to create the socket at + * @param epoll_fd File descriptor for epoll + * @param commands Address of IPCCommands array defined in config.h + * @param commands_len Length of commands[] array + * + * @return int The file descriptor of the socket if it was successfully created, + * -1 otherwise + */ +int ipc_init(const char *socket_path, const int p_epoll_fd, + IPCCommand commands[], const int commands_len); + +/** + * Uninitialize the socket and module. Free allocated memory and restore static + * variables to their state before ipc_init + */ +void ipc_cleanup(); + +/** + * Get the file descriptor of the IPC socket + * + * @return int File descriptor of IPC socket, -1 if socket not created. + */ +int ipc_get_sock_fd(); + +/** + * Get address to IPCClient with specified file descriptor + * + * @param fd File descriptor of IPC Client + * + * @return Address to IPCClient with specified file descriptor, -1 otherwise + */ +IPCClient *ipc_get_client(int fd); + +/** + * Check if an IPC client exists with the specified file descriptor + * + * @param fd File descriptor + * + * @return int 1 if client exists, 0 otherwise + */ +int ipc_is_client_registered(int fd); + +/** + * Disconnect an IPCClient from the socket and remove the client from the list + * of known connected clients + * + * @param c Address of IPCClient + * + * @return 0 if the client's file descriptor was closed successfully, the + * result of executing close() on the file descriptor otherwise. + */ +int ipc_drop_client(IPCClient *c); + +/** + * Accept an IPC Client requesting to connect to the socket and add it to the + * list of clients + * + * @return File descriptor of new client, -1 on error + */ +int ipc_accept_client(); + +/** + * Read an incoming message from an accepted IPC client + * + * @param c Address of IPCClient + * @param msg_type Address to IPCMessageType variable which will be assigned + * the message type of the received message + * @param msg_size Address to uint32_t variable which will be assigned the size + * of the received message + * @param msg Address to char* variable which will be assigned the address of + * the received message. This must be freed using free(). + * + * @return 0 on success, -1 on error reading message, -2 if reading the message + * resulted in EAGAIN, EINTR, or EWOULDBLOCK. + */ +int ipc_read_client(IPCClient *c, IPCMessageType *msg_type, uint32_t *msg_size, + char **msg); + +/** + * Write any pending buffer of the client to the client's socket + * + * @param c Client whose buffer to write + * + * @return Number of bytes written >= 0, -1 otherwise. errno will still be set + * from the write operation. + */ +ssize_t ipc_write_client(IPCClient *c); + +/** + * Prepare a message in the specified client's buffer. + * + * @param c Client to prepare message for + * @param msg_type Type of message to prepare + * @param msg_size Size of the message in bytes. Should not exceed + * MAX_MESSAGE_SIZE + * @param msg Message to prepare (not including header). This pointer can be + * freed after the function invocation. + */ +void ipc_prepare_send_message(IPCClient *c, const IPCMessageType msg_type, + const uint32_t msg_size, const char *msg); + +/** + * Prepare an error message in the specified client's buffer + * + * @param c Client to prepare message for + * @param msg_type Type of message + * @param format Format string following vsprintf + * @param ... Arguments for format string + */ +void ipc_prepare_reply_failure(IPCClient *c, IPCMessageType msg_type, + const char *format, ...); + +/** + * Prepare a success message in the specified client's buffer + * + * @param c Client to prepare message for + * @param msg_type Type of message + */ +void ipc_prepare_reply_success(IPCClient *c, IPCMessageType msg_type); + +/** + * Send a tag_change_event to all subscribers. Should be called only when there + * has been a tag state change. + * + * @param mon_num The index of the monitor (Monitor.num property) + * @param old_state The old tag state + * @param new_state The new (now current) tag state + */ +void ipc_tag_change_event(const int mon_num, TagState old_state, + TagState new_state); + +/** + * Send a client_focus_change_event to all subscribers. Should be called only + * when the client focus changes. + * + * @param mon_num The index of the monitor (Monitor.num property) + * @param old_client The old DWM client selection (Monitor.oldsel) + * @param new_client The new (now current) DWM client selection + */ +void ipc_client_focus_change_event(const int mon_num, Client *old_client, + Client *new_client); + +/** + * Send a layout_change_event to all subscribers. Should be called only + * when there has been a layout change. + * + * @param mon_num The index of the monitor (Monitor.num property) + * @param old_symbol The old layout symbol + * @param old_layout Address to the old Layout + * @param new_symbol The new (now current) layout symbol + * @param new_layout Address to the new Layout + */ +void ipc_layout_change_event(const int mon_num, const char *old_symbol, + const Layout *old_layout, const char *new_symbol, + const Layout *new_layout); + +/** + * Send a monitor_focus_change_event to all subscribers. Should be called only + * when the monitor focus changes. + * + * @param last_mon_num The index of the previously selected monitor + * @param new_mon_num The index of the newly selected monitor + */ +void ipc_monitor_focus_change_event(const int last_mon_num, + const int new_mon_num); + +/** + * Send a focused_title_change_event to all subscribers. Should only be called + * if a selected client has a title change. + * + * @param mon_num Index of the client's monitor + * @param client_id Window XID of client + * @param old_name Old name of the client window + * @param new_name New name of the client window + */ +void ipc_focused_title_change_event(const int mon_num, const Window client_id, + const char *old_name, const char *new_name); + +/** + * Send a focused_state_change_event to all subscribers. Should only be called + * if a selected client has a state change. + * + * @param mon_num Index of the client's monitor + * @param client_id Window XID of client + * @param old_state Old state of the client + * @param new_state New state of the client + */ +void ipc_focused_state_change_event(const int mon_num, const Window client_id, + const ClientState *old_state, + const ClientState *new_state); +/** + * Check to see if an event has occured and call the *_change_event functions + * accordingly + * + * @param mons Address of Monitor pointing to start of linked list + * @param lastselmon Address of pointer to previously selected monitor + * @param selmon Address of selected Monitor + */ +void ipc_send_events(Monitor *mons, Monitor **lastselmon, Monitor *selmon); + +/** + * Handle an epoll event caused by a registered IPC client. Read, process, and + * handle any received messages from clients. Write pending buffer to client if + * the client is ready to receive messages. Drop clients that have sent an + * EPOLLHUP. + * + * @param ev Associated epoll event returned by epoll_wait + * @param mons Address of Monitor pointing to start of linked list + * @param selmon Address of selected Monitor + * @param lastselmon Address of pointer to previously selected monitor + * @param tags Array of tag names + * @param tags_len Length of tags array + * @param layouts Array of available layouts + * @param layouts_len Length of layouts array + * + * @return 0 if event was successfully handled, -1 on any error receiving + * or handling incoming messages or unhandled epoll event. + */ +int ipc_handle_client_epoll_event(struct epoll_event *ev, Monitor *mons, + Monitor **lastselmon, Monitor *selmon, const int tags_len, + const Layout *layouts, const int layouts_len); + +/** + * Handle an epoll event caused by the IPC socket. This function only handles an + * EPOLLIN event indicating a new client requesting to connect to the socket. + * + * @param ev Associated epoll event returned by epoll_wait + * + * @return 0, if the event was successfully handled, -1 if not an EPOLLIN event + * or if a new IPC client connection request could not be accepted. + */ +int ipc_handle_socket_epoll_event(struct epoll_event *ev); + +#endif /* IPC_H_ */ + diff --git a/patch/ipc/util.c b/patch/ipc/util.c new file mode 100644 index 0000000..7ea425e --- /dev/null +++ b/patch/ipc/util.c @@ -0,0 +1,136 @@ +#include <errno.h> +#include <sys/stat.h> + +int +normalizepath(const char *path, char **normal) +{ + size_t len = strlen(path); + *normal = (char *)malloc((len + 1) * sizeof(char)); + const char *walk = path; + const char *match; + size_t newlen = 0; + + while ((match = strchr(walk, '/'))) { + // Copy everything between match and walk + strncpy(*normal + newlen, walk, match - walk); + newlen += match - walk; + walk += match - walk; + + // Skip all repeating slashes + while (*walk == '/') + walk++; + + // If not last character in path + if (walk != path + len) + (*normal)[newlen++] = '/'; + } + + (*normal)[newlen++] = '\0'; + + // Copy remaining path + strcat(*normal, walk); + newlen += strlen(walk); + + *normal = (char *)realloc(*normal, newlen * sizeof(char)); + + return 0; +} + +int +parentdir(const char *path, char **parent) +{ + char *normal; + char *walk; + + normalizepath(path, &normal); + + // Pointer to last '/' + if (!(walk = strrchr(normal, '/'))) { + free(normal); + return -1; + } + + // Get path up to last '/' + size_t len = walk - normal; + *parent = (char *)malloc((len + 1) * sizeof(char)); + + // Copy path up to last '/' + strncpy(*parent, normal, len); + // Add null char + (*parent)[len] = '\0'; + + free(normal); + + return 0; +} + +int +mkdirp(const char *path) +{ + char *normal; + char *walk; + size_t normallen; + + normalizepath(path, &normal); + normallen = strlen(normal); + walk = normal; + + while (walk < normal + normallen + 1) { + // Get length from walk to next / + size_t n = strcspn(walk, "/"); + + // Skip path / + if (n == 0) { + walk++; + continue; + } + + // Length of current path segment + size_t curpathlen = walk - normal + n; + char curpath[curpathlen + 1]; + struct stat s; + + // Copy path segment to stat + strncpy(curpath, normal, curpathlen); + strcpy(curpath + curpathlen, ""); + int res = stat(curpath, &s); + + if (res < 0) { + if (errno == ENOENT) { + DEBUG("Making directory %s\n", curpath); + if (mkdir(curpath, 0700) < 0) { + fprintf(stderr, "Failed to make directory %s\n", curpath); + perror(""); + free(normal); + return -1; + } + } else { + fprintf(stderr, "Error statting directory %s\n", curpath); + perror(""); + free(normal); + return -1; + } + } + + // Continue to next path segment + walk += n; + } + + free(normal); + + return 0; +} + +int +nullterminate(char **str, size_t *len) +{ + if ((*str)[*len - 1] == '\0') + return 0; + + (*len)++; + *str = (char*)realloc(*str, *len * sizeof(char)); + (*str)[*len - 1] = '\0'; + + return 0; +} + diff --git a/patch/ipc/util.h b/patch/ipc/util.h new file mode 100644 index 0000000..8d2a626 --- /dev/null +++ b/patch/ipc/util.h @@ -0,0 +1,5 @@ +int normalizepath(const char *path, char **normal); +int mkdirp(const char *path); +int parentdir(const char *path, char **parent); +int nullterminate(char **str, size_t *len); + diff --git a/patch/ipc/yajl_dumps.c b/patch/ipc/yajl_dumps.c new file mode 100644 index 0000000..b24a429 --- /dev/null +++ b/patch/ipc/yajl_dumps.c @@ -0,0 +1,356 @@ +#include "yajl_dumps.h" + +#include <stdint.h> + +int +dump_tag(yajl_gen gen, const char *name, const int tag_mask) +{ + if (!name) + return 0; + + // clang-format off + YMAP( + YSTR("bit_mask"); YINT(tag_mask); + YSTR("name"); YSTR(name); + ) + // clang-format on + return 0; +} + +int +dump_tags(yajl_gen gen, int tags_len) +{ + // clang-format off + YARR( + for (int i = 0; i < tags_len; i++) + dump_tag(gen, tagicon(mons, i), 1 << i); + ) + // clang-format on + + return 0; +} + +int +dump_client(yajl_gen gen, Client *c) +{ + // clang-format off + YMAP( + YSTR("name"); YSTR(c->name); + YSTR("tags"); YINT(c->tags); + YSTR("window_id"); YINT(c->win); + YSTR("monitor_number"); YINT(c->mon->num); + + YSTR("geometry"); YMAP( + YSTR("current"); YMAP ( + YSTR("x"); YINT(c->x); + YSTR("y"); YINT(c->y); + YSTR("width"); YINT(c->w); + YSTR("height"); YINT(c->h); + ) + YSTR("old"); YMAP( + YSTR("x"); YINT(c->oldx); + YSTR("y"); YINT(c->oldy); + YSTR("width"); YINT(c->oldw); + YSTR("height"); YINT(c->oldh); + ) + ) + + YSTR("size_hints"); YMAP( + YSTR("base"); YMAP( + YSTR("width"); YINT(c->basew); + YSTR("height"); YINT(c->baseh); + ) + YSTR("step"); YMAP( + YSTR("width"); YINT(c->incw); + YSTR("height"); YINT(c->inch); + ) + YSTR("max"); YMAP( + YSTR("width"); YINT(c->maxw); + YSTR("height"); YINT(c->maxh); + ) + YSTR("min"); YMAP( + YSTR("width"); YINT(c->minw); + YSTR("height"); YINT(c->minh); + ) + YSTR("aspect_ratio"); YMAP( + YSTR("min"); YDOUBLE(c->mina); + YSTR("max"); YDOUBLE(c->maxa); + ) + ) + + YSTR("border_width"); YMAP( + YSTR("current"); YINT(c->bw); + YSTR("old"); YINT(c->oldbw); + ) + + YSTR("states"); YMAP( + YSTR("is_fixed"); YBOOL(c->isfixed); + YSTR("is_floating"); YBOOL(c->isfloating); + YSTR("is_urgent"); YBOOL(c->isurgent); + YSTR("never_focus"); YBOOL(c->neverfocus); + YSTR("old_state"); YBOOL(c->oldstate); + YSTR("is_fullscreen"); YBOOL(c->isfullscreen); + ) + ) + // clang-format on + + return 0; +} + +int +dump_monitor(yajl_gen gen, Monitor *mon, int is_selected) +{ + // clang-format off + YMAP( + YSTR("master_factor"); YDOUBLE(mon->mfact); + YSTR("num_master"); YINT(mon->nmaster); + YSTR("num"); YINT(mon->num); + YSTR("is_selected"); YBOOL(is_selected); + + YSTR("monitor_geometry"); YMAP( + YSTR("x"); YINT(mon->mx); + YSTR("y"); YINT(mon->my); + YSTR("width"); YINT(mon->mw); + YSTR("height"); YINT(mon->mh); + ) + + YSTR("window_geometry"); YMAP( + YSTR("x"); YINT(mon->wx); + YSTR("y"); YINT(mon->wy); + YSTR("width"); YINT(mon->ww); + YSTR("height"); YINT(mon->wh); + ) + + YSTR("tagset"); YMAP( + YSTR("current"); YINT(mon->tagset[mon->seltags]); + YSTR("old"); YINT(mon->tagset[mon->seltags ^ 1]); + ) + + YSTR("tag_state"); dump_tag_state(gen, mon->tagstate); + + YSTR("clients"); YMAP( + YSTR("selected"); YINT(mon->sel ? mon->sel->win : 0); + YSTR("stack"); YARR( + for (Client* c = mon->stack; c; c = c->snext) + YINT(c->win); + ) + YSTR("all"); YARR( + for (Client* c = mon->clients; c; c = c->next) + YINT(c->win); + ) + ) + + YSTR("layout"); YMAP( + YSTR("symbol"); YMAP( + YSTR("current"); YSTR(mon->ltsymbol); + YSTR("old"); YSTR(mon->lastltsymbol); + ) + YSTR("address"); YMAP( + YSTR("current"); YINT((uintptr_t)mon->lt[mon->sellt]); + YSTR("old"); YINT((uintptr_t)mon->lt[mon->sellt ^ 1]); + ) + ) + + if (mon->bar) { + YSTR("bar"); YMAP( + YSTR("y"); YINT(mon->bar->by); + YSTR("is_shown"); YBOOL(mon->showbar); + YSTR("is_top"); YBOOL(mon->bar->topbar); + YSTR("window_id"); YINT(mon->bar->win); + ) + } + ) + // clang-format on + + return 0; +} + +int +dump_monitors(yajl_gen gen, Monitor *mons, Monitor *selmon) +{ + // clang-format off + YARR( + for (Monitor *mon = mons; mon; mon = mon->next) { + if (mon == selmon) + dump_monitor(gen, mon, 1); + else + dump_monitor(gen, mon, 0); + } + ) + // clang-format on + + return 0; +} + +int +dump_layouts(yajl_gen gen, const Layout layouts[], const int layouts_len) +{ + // clang-format off + YARR( + for (int i = 0; i < layouts_len; i++) { + YMAP( + // Check for a NULL pointer. The cycle layouts patch adds an entry at + // the end of the layouts array with a NULL pointer for the symbol + YSTR("symbol"); YSTR((layouts[i].symbol ? layouts[i].symbol : "")); + YSTR("address"); YINT((uintptr_t)(layouts + i)); + ) + } + ) + // clang-format on + + return 0; +} + +int +dump_tag_state(yajl_gen gen, TagState state) +{ + // clang-format off + YMAP( + YSTR("selected"); YINT(state.selected); + YSTR("occupied"); YINT(state.occupied); + YSTR("urgent"); YINT(state.urgent); + ) + // clang-format on + + return 0; +} + +int +dump_tag_event(yajl_gen gen, int mon_num, TagState old_state, + TagState new_state) +{ + // clang-format off + YMAP( + YSTR("tag_change_event"); YMAP( + YSTR("monitor_number"); YINT(mon_num); + YSTR("old_state"); dump_tag_state(gen, old_state); + YSTR("new_state"); dump_tag_state(gen, new_state); + ) + ) + // clang-format on + + return 0; +} + +int +dump_client_focus_change_event(yajl_gen gen, Client *old_client, + Client *new_client, int mon_num) +{ + // clang-format off + YMAP( + YSTR("client_focus_change_event"); YMAP( + YSTR("monitor_number"); YINT(mon_num); + YSTR("old_win_id"); old_client == NULL ? YNULL() : YINT(old_client->win); + YSTR("new_win_id"); new_client == NULL ? YNULL() : YINT(new_client->win); + ) + ) + // clang-format on + + return 0; +} + +int +dump_layout_change_event(yajl_gen gen, const int mon_num, + const char *old_symbol, const Layout *old_layout, + const char *new_symbol, const Layout *new_layout) +{ + // clang-format off + YMAP( + YSTR("layout_change_event"); YMAP( + YSTR("monitor_number"); YINT(mon_num); + YSTR("old_symbol"); YSTR(old_symbol); + YSTR("old_address"); YINT((uintptr_t)old_layout); + YSTR("new_symbol"); YSTR(new_symbol); + YSTR("new_address"); YINT((uintptr_t)new_layout); + ) + ) + // clang-format on + + return 0; +} + +int +dump_monitor_focus_change_event(yajl_gen gen, const int last_mon_num, + const int new_mon_num) +{ + // clang-format off + YMAP( + YSTR("monitor_focus_change_event"); YMAP( + YSTR("old_monitor_number"); YINT(last_mon_num); + YSTR("new_monitor_number"); YINT(new_mon_num); + ) + ) + // clang-format on + + return 0; +} + +int +dump_focused_title_change_event(yajl_gen gen, const int mon_num, + const Window client_id, const char *old_name, + const char *new_name) +{ + // clang-format off + YMAP( + YSTR("focused_title_change_event"); YMAP( + YSTR("monitor_number"); YINT(mon_num); + YSTR("client_window_id"); YINT(client_id); + YSTR("old_name"); YSTR(old_name); + YSTR("new_name"); YSTR(new_name); + ) + ) + // clang-format on + + return 0; +} + +int +dump_client_state(yajl_gen gen, const ClientState *state) +{ + // clang-format off + YMAP( + YSTR("old_state"); YBOOL(state->oldstate); + YSTR("is_fixed"); YBOOL(state->isfixed); + YSTR("is_floating"); YBOOL(state->isfloating); + YSTR("is_fullscreen"); YBOOL(state->isfullscreen); + YSTR("is_urgent"); YBOOL(state->isurgent); + YSTR("never_focus"); YBOOL(state->neverfocus); + ) + // clang-format on + + return 0; +} + +int +dump_focused_state_change_event(yajl_gen gen, const int mon_num, + const Window client_id, + const ClientState *old_state, + const ClientState *new_state) +{ + // clang-format off + YMAP( + YSTR("focused_state_change_event"); YMAP( + YSTR("monitor_number"); YINT(mon_num); + YSTR("client_window_id"); YINT(client_id); + YSTR("old_state"); dump_client_state(gen, old_state); + YSTR("new_state"); dump_client_state(gen, new_state); + ) + ) + // clang-format on + + return 0; +} + +int +dump_error_message(yajl_gen gen, const char *reason) +{ + // clang-format off + YMAP( + YSTR("result"); YSTR("error"); + YSTR("reason"); YSTR(reason); + ) + // clang-format on + + return 0; +} + diff --git a/patch/ipc/yajl_dumps.h b/patch/ipc/yajl_dumps.h new file mode 100644 index 0000000..bb57a17 --- /dev/null +++ b/patch/ipc/yajl_dumps.h @@ -0,0 +1,66 @@ +#ifndef YAJL_DUMPS_H_ +#define YAJL_DUMPS_H_ + +#include <string.h> +#include <yajl/yajl_gen.h> + +#define YSTR(str) yajl_gen_string(gen, (unsigned char *)str, strlen(str)) +#define YINT(num) yajl_gen_integer(gen, num) +#define YDOUBLE(num) yajl_gen_double(gen, num) +#define YBOOL(v) yajl_gen_bool(gen, v) +#define YNULL() yajl_gen_null(gen) +#define YARR(body) \ + { \ + yajl_gen_array_open(gen); \ + body; \ + yajl_gen_array_close(gen); \ + } +#define YMAP(body) \ + { \ + yajl_gen_map_open(gen); \ + body; \ + yajl_gen_map_close(gen); \ + } + +int dump_tag(yajl_gen gen, const char *name, const int tag_mask); + +int dump_tags(yajl_gen gen, int tags_len); + +int dump_client(yajl_gen gen, Client *c); + +int dump_monitor(yajl_gen gen, Monitor *mon, int is_selected); + +int dump_monitors(yajl_gen gen, Monitor *mons, Monitor *selmon); + +int dump_layouts(yajl_gen gen, const Layout layouts[], const int layouts_len); + +int dump_tag_state(yajl_gen gen, TagState state); + +int dump_tag_event(yajl_gen gen, int mon_num, TagState old_state, + TagState new_state); + +int dump_client_focus_change_event(yajl_gen gen, Client *old_client, + Client *new_client, int mon_num); + +int dump_layout_change_event(yajl_gen gen, const int mon_num, + const char *old_symbol, const Layout *old_layout, + const char *new_symbol, const Layout *new_layout); + +int dump_monitor_focus_change_event(yajl_gen gen, const int last_mon_num, + const int new_mon_num); + +int dump_focused_title_change_event(yajl_gen gen, const int mon_num, + const Window client_id, + const char *old_name, const char *new_name); + +int dump_client_state(yajl_gen gen, const ClientState *state); + +int dump_focused_state_change_event(yajl_gen gen, const int mon_num, + const Window client_id, + const ClientState *old_state, + const ClientState *new_state); + +int dump_error_message(yajl_gen gen, const char *reason); + +#endif // YAJL_DUMPS_H_ + diff --git a/patch/keymodes.c b/patch/keymodes.c new file mode 100644 index 0000000..6f2f840 --- /dev/null +++ b/patch/keymodes.c @@ -0,0 +1,144 @@ +/* function implementations */ +void +clearcmd(const Arg *arg) +{ + unsigned int i; + + for (i = 0; i < LENGTH(cmdkeysym); i++) { + cmdkeysym[i] = 0; + cmdmod[i] = 0; + } +} + +void +grabkeys(void) +{ + if (keymode == INSERTMODE) { + grabdefkeys(); + } else if (keymode == COMMANDMODE) { + XUngrabKey(dpy, AnyKey, AnyModifier, root); + XGrabKey(dpy, AnyKey, AnyModifier, root, + True, GrabModeAsync, GrabModeAsync); + } +} + +int +isprotodel(Client *c) +{ + int n; + Atom *protocols; + int ret = 0; + + if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { + while (!ret && n--) + ret = protocols[n] == wmatom[WMDelete]; + XFree(protocols); + } + return ret; +} + + +void +keypress(XEvent *e) +{ + unsigned int i, j; + Arg a = {0}; + Bool ismatch = False, maybematch = False; + KeySym keysym; + XKeyEvent *ev; + + if (keymode == INSERTMODE) + keydefpress(e); + else if (keymode == COMMANDMODE) { + ev = &e->xkey; + keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); + if (keysym < XK_Shift_L || keysym > XK_Hyper_R) { + for (i = 0; i < LENGTH(cmdkeys); i++) + if (keysym == cmdkeys[i].keysym + && CLEANMASK(cmdkeys[i].mod) == CLEANMASK(ev->state) + && cmdkeys[i].func) { + cmdkeys[i].func(&(cmdkeys[i].arg)); + ismatch = True; + break; + } + if (!ismatch) { + for (j = 0; j < LENGTH(cmdkeysym); j++) + if (cmdkeysym[j] == 0) { + cmdkeysym[j] = keysym; + cmdmod[j] = ev->state; + break; + } + for (i = 0; i < LENGTH(commands); i++) { + for (j = 0; j < LENGTH(cmdkeysym); j++) { + if (cmdkeysym[j] == commands[i].keysym[j] + && CLEANMASK(cmdmod[j]) == CLEANMASK(commands[i].mod[j])) + ismatch = True; + else if (cmdkeysym[j] == 0 + && cmdmod[j] == 0) { + ismatch = False; + maybematch = True; + break; + } else { + ismatch = False; + break; + } + } + if (ismatch) { + if (commands[i].func) + commands[i].func(&(commands[i].arg)); + clearcmd(&a); + break; + } + + } + if (!maybematch) + clearcmd(&a); + } + } + } +} + +void +onlyclient(const Arg *arg) +{ + Client *c; + XEvent ev; + + if (!selmon->sel) + return; + for (c = selmon->clients; c; c = c->next) { + if (c != selmon->sel && ISVISIBLE(c)) { + if (isprotodel(c)) { + ev.type = ClientMessage; + ev.xclient.window = c->win; + ev.xclient.message_type = wmatom[WMProtocols]; + ev.xclient.format = 32; + ev.xclient.data.l[0] = wmatom[WMDelete]; + ev.xclient.data.l[1] = CurrentTime; + XSendEvent(dpy, c->win, False, NoEventMask, &ev); + } + else { + XGrabServer(dpy); + XSetErrorHandler(xerrordummy); + XSetCloseDownMode(dpy, DestroyAll); + XKillClient(dpy, c->win); + XSync(dpy, False); + XSetErrorHandler(xerror); + XUngrabServer(dpy); + } + } + } +} + +void +setkeymode(const Arg *arg) +{ + Arg a = {0}; + + if (!arg) + return; + keymode = arg->ui; + clearcmd(&a); + grabkeys(); +} + diff --git a/patch/keymodes.h b/patch/keymodes.h new file mode 100644 index 0000000..0a46125 --- /dev/null +++ b/patch/keymodes.h @@ -0,0 +1,22 @@ +#define COMMANDMODE 1 +#define INSERTMODE 2 + +typedef struct { + unsigned int mod[4]; + KeySym keysym[4]; + void (*func)(const Arg *); + const Arg arg; +} Command; + +static void clearcmd(const Arg *arg); +static void grabkeys(void); +static int isprotodel(Client *c); +static void keypress(XEvent *e); +static void onlyclient(const Arg *arg); +static void setkeymode(const Arg *arg); + +/* variables */ +static unsigned int cmdmod[4]; +static unsigned int keymode = INSERTMODE; +static KeySym cmdkeysym[4]; + diff --git a/patch/killunsel.c b/patch/killunsel.c new file mode 100644 index 0000000..8d7a7b8 --- /dev/null +++ b/patch/killunsel.c @@ -0,0 +1,28 @@ +void +killunsel(const Arg *arg) +{ + Client *i = NULL; + + if (!selmon->sel) + return; + + for (i = selmon->clients; i; i = i->next) { + if (ISVISIBLE(i) && i != selmon->sel) { + #if BAR_SYSTRAY_PATCH + if (!sendevent(i->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0, 0, 0)) + #else + if (!sendevent(i, wmatom[WMDelete])) + #endif // BAR_SYSTRAY_PATCH + { + XGrabServer(dpy); + XSetErrorHandler(xerrordummy); + XSetCloseDownMode(dpy, DestroyAll); + XKillClient(dpy, i->win); + XSync(dpy, False); + XSetErrorHandler(xerror); + XUngrabServer(dpy); + } + } + } +} + diff --git a/patch/killunsel.h b/patch/killunsel.h new file mode 100644 index 0000000..4fc154f --- /dev/null +++ b/patch/killunsel.h @@ -0,0 +1,2 @@ +static void killunsel(const Arg *arg); + diff --git a/patch/layout_bstack.c b/patch/layout_bstack.c new file mode 100644 index 0000000..8989975 --- /dev/null +++ b/patch/layout_bstack.c @@ -0,0 +1,75 @@ +static void +bstack(Monitor *m) +{ + unsigned int i, n; + int mx = 0, my = 0, mh = 0, mw = 0; + int sx = 0, sy = 0, sh = 0, sw = 0; + float mfacts, sfacts; + int mrest, srest; + Client *c; + + #if VANITYGAPS_PATCH + int oh, ov, ih, iv; + getgaps(m, &oh, &ov, &ih, &iv, &n); + #else + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + #endif // VANITYGAPS_PATCH + + if (n == 0) + return; + + #if VANITYGAPS_PATCH + sx = mx = m->wx + ov; + sy = my = m->wy + oh; + sh = mh = m->wh - 2*oh; + mw = m->ww - 2*ov - iv * (MIN(n, m->nmaster) - 1); + sw = m->ww - 2*ov - iv * (n - m->nmaster - 1); + + if (m->nmaster && n > m->nmaster) { + sh = (mh - ih) * (1 - m->mfact); + mh = (mh - ih) * m->mfact; + sx = mx; + sy = my + mh + ih; + } + #else + sx = mx = m->wx; + sy = my = m->wy; + sh = mh = m->wh; + sw = mw = m->ww; + + if (m->nmaster && n > m->nmaster) { + sh = mh * (1 - m->mfact); + mh = mh * m->mfact; + sy = my + mh; + } + #endif // VANITYGAPS_PATCH + + getfacts(m, mw, sw, &mfacts, &sfacts, &mrest, &srest); + + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { + if (i < m->nmaster) { + #if CFACTS_PATCH + resize(c, mx, my, (mw / mfacts) * c->cfact + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); + #else + resize(c, mx, my, (mw / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); + #endif // CFACTS_PATCH + #if VANITYGAPS_PATCH + mx += WIDTH(c) + iv; + #else + mx += WIDTH(c); + #endif + } else { + #if CFACTS_PATCH + resize(c, sx, sy, (sw / sfacts) * c->cfact + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), sh - (2*c->bw), 0); + #else + resize(c, sx, sy, (sw / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), sh - (2*c->bw), 0); + #endif // CFACTS_PATCH + #if VANITYGAPS_PATCH + sx += WIDTH(c) + iv; + #else + sx += WIDTH(c); + #endif + } + } +} + diff --git a/patch/layout_bstack.h b/patch/layout_bstack.h new file mode 100644 index 0000000..07e8c0a --- /dev/null +++ b/patch/layout_bstack.h @@ -0,0 +1,2 @@ +static void bstack(Monitor *m); + diff --git a/patch/layout_bstackhoriz.c b/patch/layout_bstackhoriz.c new file mode 100644 index 0000000..462301c --- /dev/null +++ b/patch/layout_bstackhoriz.c @@ -0,0 +1,77 @@ +static void +bstackhoriz(Monitor *m) +{ + unsigned int i, n; + int mx = 0, my = 0, mh = 0, mw = 0; + int sx = 0, sy = 0, sh = 0, sw = 0; + float mfacts, sfacts; + int mrest, srest; + Client *c; + + #if VANITYGAPS_PATCH + int oh, ov, ih, iv; + getgaps(m, &oh, &ov, &ih, &iv, &n); + #else + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + #endif // VANITYGAPS_PATCH + + if (n == 0) + return; + + #if VANITYGAPS_PATCH + sx = mx = m->wx + ov; + sy = my = m->wy + oh; + mh = m->wh - 2*oh; + sh = m->wh - 2*oh - ih * (n - m->nmaster - 1); + mw = m->ww - 2*ov - iv * (MIN(n, m->nmaster) - 1); + sw = m->ww - 2*ov; + + if (m->nmaster && n > m->nmaster) { + sh = (mh - ih) * (1 - m->mfact); + mh = (mh - ih) * m->mfact; + sy = my + mh + ih; + sh = m->wh - mh - 2*oh - ih * (n - m->nmaster); + } + #else + sx = mx = m->wx; + sy = my = m->wy; + sh = mh = m->wh; + sw = mw = m->ww; + + if (m->nmaster && n > m->nmaster) { + sh = mh * (1 - m->mfact); + mh = mh * m->mfact; + sy = my + mh; + sh = m->wh - mh; + } + #endif // VANITYGAPS_PATCH + + getfacts(m, mw, sh, &mfacts, &sfacts, &mrest, &srest); + + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { + if (i < m->nmaster) { + #if CFACTS_PATCH + resize(c, mx, my, (mw / mfacts) * c->cfact + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); + #else + resize(c, mx, my, (mw / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); + #endif // CFACTS_PATCH + #if VANITYGAPS_PATCH + mx += WIDTH(c) + iv; + #else + mx += WIDTH(c); + #endif + } else { + #if CFACTS_PATCH + resize(c, sx, sy, sw - (2*c->bw), (sh / sfacts) * c->cfact + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), 0); + #else + resize(c, sx, sy, sw - (2*c->bw), (sh / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), 0); + #endif // CFACTS_PATCH + #if VANITYGAPS_PATCH + sy += HEIGHT(c) + ih; + #else + sy += HEIGHT(c); + #endif + } + } +} + diff --git a/patch/layout_bstackhoriz.h b/patch/layout_bstackhoriz.h new file mode 100644 index 0000000..a421d6f --- /dev/null +++ b/patch/layout_bstackhoriz.h @@ -0,0 +1,2 @@ +static void bstackhoriz(Monitor *m); + diff --git a/patch/layout_centeredfloatingmaster.c b/patch/layout_centeredfloatingmaster.c new file mode 100644 index 0000000..6d1aff0 --- /dev/null +++ b/patch/layout_centeredfloatingmaster.c @@ -0,0 +1,95 @@ +void +centeredfloatingmaster(Monitor *m) +{ + unsigned int i, n; + float mfacts, sfacts; + int mrest, srest; + int mx = 0, my = 0, mh = 0, mw = 0; + int sx = 0, sy = 0, sh = 0, sw = 0; + Client *c; + + #if VANITYGAPS_PATCH + float mivf = 1.0; // master inner vertical gap factor + int oh, ov, ih, iv; + getgaps(m, &oh, &ov, &ih, &iv, &n); + #else + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + #endif // VANITYGAPS_PATCH + + if (n == 0) + return; + + #if VANITYGAPS_PATCH + sx = mx = m->wx + ov; + sy = my = m->wy + oh; + sh = mh = m->wh - 2*oh; + mw = m->ww - 2*ov - iv*(n - 1); + sw = m->ww - 2*ov - iv*(n - m->nmaster - 1); + + if (m->nmaster && n > m->nmaster) { + mivf = 0.8; + /* go mfact box in the center if more than nmaster clients */ + if (m->ww > m->wh) { + mw = m->ww * m->mfact - iv*mivf*(MIN(n, m->nmaster) - 1); + mh = m->wh * 0.9; + } else { + mw = m->ww * 0.9 - iv*mivf*(MIN(n, m->nmaster) - 1); + mh = m->wh * m->mfact; + } + mx = m->wx + (m->ww - mw) / 2; + my = m->wy + (m->wh - mh - 2*oh) / 2; + + sx = m->wx + ov; + sy = m->wy + oh; + sh = m->wh - 2*oh; + } + #else + sx = mx = m->wx; + sy = my = m->wy; + sh = mh = m->wh; + sw = mw = m->ww; + + if (m->nmaster && n > m->nmaster) { + /* go mfact box in the center if more than nmaster clients */ + if (m->ww > m->wh) { + mw = m->ww * m->mfact; + mh = m->wh * 0.9; + } else { + mw = m->ww * 0.9; + mh = m->wh * m->mfact; + } + mx = m->wx + (m->ww - mw) / 2; + my = m->wy + (m->wh - mh) / 2; + } + #endif // VANITYGAPS_PATCH + + getfacts(m, mw, sw, &mfacts, &sfacts, &mrest, &srest); + + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < m->nmaster) { + /* nmaster clients are stacked horizontally, in the center of the screen */ + #if CFACTS_PATCH + resize(c, mx, my, (mw / mfacts) * c->cfact + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); + #else + resize(c, mx, my, (mw / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); + #endif // CFACTS_PATCH + #if VANITYGAPS_PATCH + mx += WIDTH(c) + iv*mivf; + #else + mx += WIDTH(c); + #endif + } else { + /* stack clients are stacked horizontally */ + #if CFACTS_PATCH + resize(c, sx, sy, (sw / sfacts) * c->cfact + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), sh - (2*c->bw), 0); + #else + resize(c, sx, sy, (sw / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), sh - (2*c->bw), 0); + #endif // CFACTS_PATCH + #if VANITYGAPS_PATCH + sx += WIDTH(c) + iv; + #else + sx += WIDTH(c); + #endif + } +} + diff --git a/patch/layout_centeredfloatingmaster.h b/patch/layout_centeredfloatingmaster.h new file mode 100644 index 0000000..66b0b0e --- /dev/null +++ b/patch/layout_centeredfloatingmaster.h @@ -0,0 +1,2 @@ +static void centeredfloatingmaster(Monitor *m); + diff --git a/patch/layout_centeredmaster.c b/patch/layout_centeredmaster.c new file mode 100644 index 0000000..39aa67f --- /dev/null +++ b/patch/layout_centeredmaster.c @@ -0,0 +1,160 @@ +void +centeredmaster(Monitor *m) +{ + unsigned int i, n; + int mx = 0, my = 0, mh = 0, mw = 0; + int lx = 0, ly = 0, lw = 0, lh = 0; + int rx = 0, ry = 0, rw = 0, rh = 0; + float mfacts = 0, lfacts = 0, rfacts = 0; + int mtotal = 0, ltotal = 0, rtotal = 0; + int mrest = 0, lrest = 0, rrest = 0; + Client *c; + + #if VANITYGAPS_PATCH + int oh, ov, ih, iv; + getgaps(m, &oh, &ov, &ih, &iv, &n); + #else + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + #endif // VANITYGAPS_PATCH + + if (n == 0) + return; + + /* initialize areas */ + #if VANITYGAPS_PATCH + mx = m->wx + ov; + my = m->wy + oh; + mh = m->wh - 2*oh - ih * ((!m->nmaster ? n : MIN(n, m->nmaster)) - 1); + mw = m->ww - 2*ov; + lh = m->wh - 2*oh - ih * (((n - m->nmaster) / 2) - 1); + rh = m->wh - 2*oh - ih * (((n - m->nmaster) / 2) - ((n - m->nmaster) % 2 ? 0 : 1)); + + if (m->nmaster && n > m->nmaster) { + /* go mfact box in the center if more than nmaster clients */ + if (n - m->nmaster > 1) { + /* ||<-S->|<---M--->|<-S->|| */ + mw = (m->ww - 2*ov - 2*iv) * m->mfact; + lw = (m->ww - mw - 2*ov - 2*iv) / 2; + mx += lw + iv; + } else { + /* ||<---M--->|<-S->|| */ + mw = (mw - iv) * m->mfact; + lw = m->ww - mw - iv - 2*ov; + } + rw = lw; + lx = m->wx + ov; + ly = m->wy + oh; + rx = mx + mw + iv; + ry = m->wy + oh; + } + #else + mx = m->wx; + my = m->wy; + mh = m->wh; + mw = m->ww; + lh = m->wh; + rh = m->wh; + + if (m->nmaster && n > m->nmaster) { + /* go mfact box in the center if more than nmaster clients */ + if (n - m->nmaster > 1) { + /* ||<-S->|<---M--->|<-S->|| */ + mw = m->ww * m->mfact; + lw = (m->ww - mw) / 2; + mx += lw; + } else { + /* ||<---M--->|<-S->|| */ + mw = mw * m->mfact; + lw = m->ww - mw; + } + rw = lw; + lx = m->wx; + ly = m->wy; + rx = mx + mw; + ry = m->wy; + } + #endif // VANITYGAPS_PATCH + + /* calculate facts */ + #if CFACTS_PATCH + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { + if (!m->nmaster || n < m->nmaster) + mfacts += c->cfact; // total factor of master area + else if ((n - m->nmaster) % 2) + lfacts += c->cfact; // total factor of left hand stack area + else + rfacts += c->cfact; // total factor of right hand stack area + } + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) + if (!m->nmaster || n < m->nmaster) + mtotal += mh / mfacts; + else if ((n - m->nmaster) % 2) + ltotal += lh * (c->cfact / lfacts); + else + rtotal += rh * (c->cfact / rfacts); + #else + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { + if (!m->nmaster || n < m->nmaster) + mfacts += 1; + else if ((n - m->nmaster) % 2) + lfacts += 1; // total factor of left hand stack area + else + rfacts += 1; // total factor of right hand stack area + } + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) + if (!m->nmaster || n < m->nmaster) + mtotal += mh / mfacts; + else if ((n - m->nmaster) % 2) + ltotal += lh / lfacts; + else + rtotal += rh / rfacts; + #endif // CFACTS_PATCH + + mrest = mh - mtotal; + lrest = lh - ltotal; + rrest = rh - rtotal; + + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { + if (!m->nmaster || i < m->nmaster) { + /* nmaster clients are stacked vertically, in the center of the screen */ + #if CFACTS_PATCH + resize(c, mx, my, mw - (2*c->bw), (mh / mfacts) * c->cfact + (i < mrest ? 1 : 0) - (2*c->bw), 0); + #else + resize(c, mx, my, mw - (2*c->bw), (mh / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), 0); + #endif // CFACTS_PATCH + #if VANITYGAPS_PATCH + my += HEIGHT(c) + ih; + #else + my += HEIGHT(c); + #endif + } else { + /* stack clients are stacked vertically */ + if ((i - m->nmaster) % 2 ) { + #if CFACTS_PATCH + resize(c, lx, ly, lw - (2*c->bw), (lh / lfacts) * c->cfact + ((i - 2*m->nmaster) < 2*lrest ? 1 : 0) - (2*c->bw), 0); + #else + resize(c, lx, ly, lw - (2*c->bw), (lh / lfacts) + ((i - 2*m->nmaster) < 2*lrest ? 1 : 0) - (2*c->bw), 0); + #endif // CFACTS_PATCH + #if VANITYGAPS_PATCH + ly += HEIGHT(c) + ih; + #else + ly += HEIGHT(c); + #endif + } else { + #if CFACTS_PATCH + resize(c, rx, ry, rw - (2*c->bw), (rh / rfacts) * c->cfact + ((i - 2*m->nmaster) < 2*rrest ? 1 : 0) - (2*c->bw), 0); + #else + resize(c, rx, ry, rw - (2*c->bw), (rh / rfacts) + ((i - 2*m->nmaster) < 2*rrest ? 1 : 0) - (2*c->bw), 0); + #endif // CFACTS_PATCH + #if VANITYGAPS_PATCH + ry += HEIGHT(c) + ih; + #else + ry += HEIGHT(c); + #endif + } + } + } +} + diff --git a/patch/layout_centeredmaster.h b/patch/layout_centeredmaster.h new file mode 100644 index 0000000..703f8b8 --- /dev/null +++ b/patch/layout_centeredmaster.h @@ -0,0 +1,2 @@ +static void centeredmaster(Monitor *m); + diff --git a/patch/layout_columns.c b/patch/layout_columns.c new file mode 100644 index 0000000..8a0e551 --- /dev/null +++ b/patch/layout_columns.c @@ -0,0 +1,74 @@ +static void +col(Monitor *m) +{ + unsigned int i, n; + int mx = 0, my = 0, mh = 0, mw = 0; + int sx = 0, sy = 0, sh = 0, sw = 0; + float mfacts, sfacts; + int mrest, srest; + Client *c; + + #if VANITYGAPS_PATCH + int oh, ov, ih, iv; + getgaps(m, &oh, &ov, &ih, &iv, &n); + #else + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + #endif // VANITYGAPS_PATCH + + if (n == 0) + return; + + #if VANITYGAPS_PATCH + sx = mx = m->wx + ov; + sy = my = m->wy + oh; + mh = m->wh - 2*oh; + sh = m->wh - 2*oh - ih * (n - m->nmaster - 1); + mw = m->ww - 2*ov - iv * (MIN(n, m->nmaster) - 1); + sw = m->ww - 2*ov; + + if (m->nmaster && n > m->nmaster) { + sw = (mw - iv) * (1 - m->mfact); + mw = (mw - iv) * m->mfact; + sx = mx + mw + iv * m->nmaster; + } + #else + sx = mx = m->wx; + sy = my = m->wy; + sh = mh = m->wh; + sw = mw = m->ww; + + if (m->nmaster && n > m->nmaster) { + sw = mw * (1 - m->mfact); + mw = mw * m->mfact; + sx = mx + mw; + } + #endif // VANITYGAPS_PATCH + + getfacts(m, mw, sh, &mfacts, &sfacts, &mrest, &srest); + + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < m->nmaster) { + #if CFACTS_PATCH + resize(c, mx, my, (mw / mfacts) * c->cfact + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); + #else + resize(c, mx, my, (mw / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); + #endif // CFACTS_PATCH + #if VANITYGAPS_PATCH + mx += WIDTH(c) + iv; + #else + mx += WIDTH(c); + #endif + } else { + #if CFACTS_PATCH + resize(c, sx, sy, sw - (2*c->bw), (sh / sfacts) * c->cfact + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), 0); + #else + resize(c, sx, sy, sw - (2*c->bw), (sh / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), 0); + #endif // CFACTS_PATCH + #if VANITYGAPS_PATCH + sy += HEIGHT(c) + ih; + #else + sy += HEIGHT(c); + #endif + } +} + diff --git a/patch/layout_columns.h b/patch/layout_columns.h new file mode 100644 index 0000000..a589371 --- /dev/null +++ b/patch/layout_columns.h @@ -0,0 +1,2 @@ +static void col(Monitor *); + diff --git a/patch/layout_deck.c b/patch/layout_deck.c new file mode 100644 index 0000000..1cabf0e --- /dev/null +++ b/patch/layout_deck.c @@ -0,0 +1,67 @@ +static void +deck(Monitor *m) +{ + unsigned int i, n; + int mx = 0, my = 0, mh = 0, mw = 0; + int sx = 0, sy = 0, sh = 0, sw = 0; + float mfacts, sfacts; + int mrest, srest; + Client *c; + + #if VANITYGAPS_PATCH + int oh, ov, ih, iv; + getgaps(m, &oh, &ov, &ih, &iv, &n); + #else + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + #endif // VANITYGAPS_PATCH + + if (n == 0) + return; + + #if VANITYGAPS_PATCH + sx = mx = m->wx + ov; + sy = my = m->wy + oh; + sh = mh = m->wh - 2*oh - ih * (MIN(n, m->nmaster) - 1); + sw = mw = m->ww - 2*ov; + + if (m->nmaster && n > m->nmaster) { + sw = (mw - iv) * (1 - m->mfact); + mw = (mw - iv) * m->mfact; + sx = mx + mw + iv; + sh = m->wh - 2*oh; + } + #else + sx = mx = m->wx; + sy = my = m->wy; + sh = mh = m->wh; + sw = mw = m->ww; + + if (m->nmaster && n > m->nmaster) { + sw = mw * (1 - m->mfact); + mw = mw * m->mfact; + sx = mx + mw; + } + #endif // VANITYGAPS_PATCH + + getfacts(m, mh, sh, &mfacts, &sfacts, &mrest, &srest); + + if (n - m->nmaster > 0) /* override layout symbol */ + snprintf(m->ltsymbol, sizeof m->ltsymbol, "D %d", n - m->nmaster); + + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < m->nmaster) { + #if CFACTS_PATCH + resize(c, mx, my, mw - (2*c->bw), (mh / mfacts) * c->cfact + (i < mrest ? 1 : 0) - (2*c->bw), 0); + #else + resize(c, mx, my, mw - (2*c->bw), (mh / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), 0); + #endif // CFACTS_PATCH + #if VANITYGAPS_PATCH + my += HEIGHT(c) + ih; + #else + my += HEIGHT(c); + #endif + } else { + resize(c, sx, sy, sw - (2*c->bw), sh - (2*c->bw), 0); + } +} + diff --git a/patch/layout_deck.h b/patch/layout_deck.h new file mode 100644 index 0000000..b6e3c41 --- /dev/null +++ b/patch/layout_deck.h @@ -0,0 +1,2 @@ +static void deck(Monitor *m); + diff --git a/patch/layout_facts.c b/patch/layout_facts.c new file mode 100644 index 0000000..611e89e --- /dev/null +++ b/patch/layout_facts.c @@ -0,0 +1,52 @@ +#if CFACTS_PATCH +void +getfacts(Monitor *m, int msize, int ssize, float *mf, float *sf, int *mr, int *sr) +{ + unsigned int n; + float mfacts = 0, sfacts = 0; + int mtotal = 0, stotal = 0; + Client *c; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) + if (n < m->nmaster) + mfacts += c->cfact; + else + sfacts += c->cfact; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) + if (n < m->nmaster) + mtotal += msize * (c->cfact / mfacts); + else + stotal += ssize * (c->cfact / sfacts); + + *mf = mfacts; // total factor of master area + *sf = sfacts; // total factor of stack area + *mr = msize - mtotal; // the remainder (rest) of pixels after a cfacts master split + *sr = ssize - stotal; // the remainder (rest) of pixels after a cfacts stack split +} +#else +void +getfacts(Monitor *m, int msize, int ssize, float *mf, float *sf, int *mr, int *sr) +{ + unsigned int n; + float mfacts, sfacts; + int mtotal = 0, stotal = 0; + Client *c; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + mfacts = MIN(n, m->nmaster); + sfacts = n - m->nmaster; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) + if (n < m->nmaster) + mtotal += msize / mfacts; + else + stotal += ssize / sfacts; + + *mf = mfacts; // total factor of master area + *sf = sfacts; // total factor of stack area + *mr = msize - mtotal; // the remainder (rest) of pixels after an even master split + *sr = ssize - stotal; // the remainder (rest) of pixels after an even stack split +} +#endif // CFACTS_PATCH + diff --git a/patch/layout_fibonacci.c b/patch/layout_fibonacci.c new file mode 100644 index 0000000..4c5eb72 --- /dev/null +++ b/patch/layout_fibonacci.c @@ -0,0 +1,191 @@ +#if VANITYGAPS_PATCH +void +fibonacci(Monitor *m, int s) +{ + unsigned int i, n; + int nx, ny, nw, nh; + int oh, ov, ih, iv; + int nv, hrest = 0, wrest = 0, r = 1; + Client *c; + + getgaps(m, &oh, &ov, &ih, &iv, &n); + if (n == 0) + return; + + nx = m->wx + ov; + ny = oh; + nw = m->ww - 2*ov; + nh = m->wh - 2*oh; + + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next)) { + if (r) { + if ((i % 2 && (nh - ih) / 2 <= (bh + 2*c->bw)) + || (!(i % 2) && (nw - iv) / 2 <= (bh + 2*c->bw))) { + r = 0; + } + if (r && i < n - 1) { + if (i % 2) { + nv = (nh - ih) / 2; + hrest = nh - 2*nv - ih; + nh = nv; + } else { + nv = (nw - iv) / 2; + wrest = nw - 2*nv - iv; + nw = nv; + } + + if ((i % 4) == 2 && !s) + nx += nw + iv; + else if ((i % 4) == 3 && !s) + ny += nh + ih; + } + + if ((i % 4) == 0) { + if (s) { + ny += nh + ih; + nh += hrest; + } + else { + nh -= hrest; + ny -= nh + ih; + } + } + else if ((i % 4) == 1) { + nx += nw + iv; + nw += wrest; + } + else if ((i % 4) == 2) { + ny += nh + ih; + nh += hrest; + if (i < n - 1) + nw += wrest; + } + else if ((i % 4) == 3) { + if (s) { + nx += nw + iv; + nw -= wrest; + } else { + nw -= wrest; + nx -= nw + iv; + nh += hrest; + } + } + if (i == 0) { + if (n != 1) { + nw = (m->ww - iv - 2*ov) - (m->ww - iv - 2*ov) * (1 - m->mfact); + wrest = 0; + } + ny = m->wy + oh; + } + else if (i == 1) + nw = m->ww - nw - iv - 2*ov; + i++; + } + + resize(c, nx, ny, nw - (2*c->bw), nh - (2*c->bw), False); + } +} +#else +void +fibonacci(Monitor *m, int s) +{ + unsigned int i, n; + int nx, ny, nw, nh; + int nv, hrest = 0, wrest = 0, r = 1; + Client *c; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + if (n == 0) + return; + + nx = m->wx; + ny = m->wy; + nw = m->ww; + nh = m->wh; + + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next)) { + if (r) { + if ((i % 2 && nh / 2 <= (bh + 2*c->bw)) + || (!(i % 2) && nw / 2 <= (bh + 2*c->bw))) { + r = 0; + } + if (r && i < n - 1) { + if (i % 2) { + nv = nh / 2; + hrest = nh - 2*nv; + nh = nv; + } else { + nv = nw / 2; + wrest = nw - 2*nv; + nw = nv; + } + + if ((i % 4) == 2 && !s) + nx += nw; + else if ((i % 4) == 3 && !s) + ny += nh; + } + + if ((i % 4) == 0) { + if (s) { + ny += nh; + nh += hrest; + } + else { + nh -= hrest; + ny -= nh; + } + } + else if ((i % 4) == 1) { + nx += nw; + nw += wrest; + } + else if ((i % 4) == 2) { + ny += nh; + nh += hrest; + if (i < n - 1) + nw += wrest; + } + else if ((i % 4) == 3) { + if (s) { + nx += nw; + nw -= wrest; + } else { + nw -= wrest; + nx -= nw; + nh += hrest; + } + } + if (i == 0) { + if (n != 1) { + nw = m->ww - m->ww * (1 - m->mfact); + wrest = 0; + } + ny = m->wy; + } + else if (i == 1) + nw = m->ww - nw; + i++; + } + + resize(c, nx, ny, nw - (2*c->bw), nh - (2*c->bw), False); + } +} +#endif + +#if FIBONACCI_DWINDLE_LAYOUT +static void +dwindle(Monitor *m) +{ + fibonacci(m, 1); +} +#endif + +#if FIBONACCI_SPIRAL_LAYOUT +static void +spiral(Monitor *m) +{ + fibonacci(m, 0); +} +#endif + diff --git a/patch/layout_fibonacci.h b/patch/layout_fibonacci.h new file mode 100644 index 0000000..3d6f77f --- /dev/null +++ b/patch/layout_fibonacci.h @@ -0,0 +1,8 @@ +#if FIBONACCI_DWINDLE_LAYOUT +static void dwindle(Monitor *m); +#endif // FIBONACCI_DWINDLE_LAYOUT +static void fibonacci(Monitor *m, int s); +#if FIBONACCI_SPIRAL_LAYOUT +static void spiral(Monitor *m); +#endif // FIBONACCI_SPIRAL_LAYOUT + diff --git a/patch/layout_flextile-deluxe.c b/patch/layout_flextile-deluxe.c new file mode 100644 index 0000000..c7429f4 --- /dev/null +++ b/patch/layout_flextile-deluxe.c @@ -0,0 +1,891 @@ +typedef struct { + void (*arrange)(Monitor *, int, int, int, int, int, int, int); +} LayoutArranger; + +typedef struct { + void (*arrange)(Monitor *, int, int, int, int, int, int, int, int, int); +} TileArranger; + +static const LayoutArranger flexlayouts[] = { + { layout_no_split }, + { layout_split_vertical }, + { layout_split_horizontal }, + { layout_split_centered_vertical }, + { layout_split_centered_horizontal }, + { layout_split_vertical_dual_stack }, + { layout_split_horizontal_dual_stack }, + { layout_floating_master }, + { layout_split_vertical_fixed }, + { layout_split_horizontal_fixed }, + { layout_split_centered_vertical_fixed }, + { layout_split_centered_horizontal_fixed }, + { layout_split_vertical_dual_stack_fixed }, + { layout_split_horizontal_dual_stack_fixed }, + { layout_floating_master_fixed }, +}; + +static const TileArranger flextiles[] = { + { arrange_top_to_bottom }, + { arrange_left_to_right }, + { arrange_monocle }, + { arrange_gapplessgrid }, + { arrange_gapplessgrid_alt1 }, + { arrange_gapplessgrid_alt2 }, + { arrange_gridmode }, + { arrange_horizgrid }, + { arrange_dwindle }, + { arrange_spiral }, + { arrange_tatami }, +}; + +static void +getfactsforrange(Monitor *m, int an, int ai, int size, int *rest, float *fact) +{ + int i; + float facts; + Client *c; + int total = 0; + + facts = 0; + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i >= ai && i < (ai + an)) + #if CFACTS_PATCH + facts += c->cfact; + #else + facts += 1; + #endif // CFACTS_PATCH + + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i >= ai && i < (ai + an)) + #if CFACTS_PATCH + total += size * (c->cfact / facts); + #else + total += size / facts; + #endif // CFACTS_PATCH + + *rest = size - total; + *fact = facts; +} + +#if IPC_PATCH || DWMC_PATCH +static void +setlayoutaxisex(const Arg *arg) +{ + int axis, arr; + + axis = arg->i & 0x3; // lower two bytes indicates layout, master or stack1-2 + arr = ((arg->i & 0xFC) >> 2); // remaining six upper bytes indicate arrangement + + if ((axis == 0 && abs(arr) > LAYOUT_LAST) + || (axis > 0 && (arr > AXIS_LAST || arr < 0))) + arr = 0; + + selmon->ltaxis[axis] = arr; + #if PERTAG_PATCH + selmon->pertag->ltaxis[selmon->pertag->curtag][axis] = selmon->ltaxis[axis]; + #endif // PERTAG_PATCH + arrange(selmon); +} +#endif // IPC_PATCH | DWMC_PATCH + +static void +layout_no_split(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) +{ + (&flextiles[m->ltaxis[m->nmaster >= n ? MASTER : STACK]])->arrange(m, x, y, h, w, ih, iv, n, n, 0); +} + +static void +layout_split_vertical(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) +{ + /* Split master into master + stack if we have enough clients */ + if (m->nmaster && n > m->nmaster) { + layout_split_vertical_fixed(m, x, y, h, w, ih, iv, n); + } else { + layout_no_split(m, x, y, h, w, ih, iv, n); + } +} + +static void +layout_split_vertical_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) +{ + int sw, sx; + + sw = (w - iv) * (1 - m->mfact); + w = (w - iv) * m->mfact; + if (m->ltaxis[LAYOUT] < 0) { // mirror + sx = x; + x += sw + iv; + } else { + sx = x + w + iv; + } + + (&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, h, w, ih, iv, n, m->nmaster, 0); + (&flextiles[m->ltaxis[STACK]])->arrange(m, sx, y, h, sw, ih, iv, n, n - m->nmaster, m->nmaster); +} + +static void +layout_split_vertical_dual_stack(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) +{ + /* Split master into master + stack if we have enough clients */ + if (!m->nmaster || n <= m->nmaster) { + layout_no_split(m, x, y, h, w, ih, iv, n); + } else if (n <= m->nmaster + (m->nstack ? m->nstack : 1)) { + layout_split_vertical(m, x, y, h, w, ih, iv, n); + } else { + layout_split_vertical_dual_stack_fixed(m, x, y, h, w, ih, iv, n); + } +} + +static void +layout_split_vertical_dual_stack_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) +{ + int sh, sw, sx, oy, sc; + + if (m->nstack) + sc = m->nstack; + else + sc = (n - m->nmaster) / 2 + ((n - m->nmaster) % 2 > 0 ? 1 : 0); + + sw = (w - iv) * (1 - m->mfact); + sh = (h - ih) / 2; + w = (w - iv) * m->mfact; + oy = y + sh + ih; + if (m->ltaxis[LAYOUT] < 0) { // mirror + sx = x; + x += sw + iv; + } else { + sx = x + w + iv; + } + + (&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, h, w, ih, iv, n, m->nmaster, 0); + (&flextiles[m->ltaxis[STACK]])->arrange(m, sx, y, sh, sw, ih, iv, n, sc, m->nmaster); + (&flextiles[m->ltaxis[STACK2]])->arrange(m, sx, oy, sh, sw, ih, iv, n, n - m->nmaster - sc, m->nmaster + sc); +} + +static void +layout_split_horizontal(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) +{ + /* Split master into master + stack if we have enough clients */ + if (m->nmaster && n > m->nmaster) { + layout_split_horizontal_fixed(m, x, y, h, w, ih, iv, n); + } else { + layout_no_split(m, x, y, h, w, ih, iv, n); + } +} + +static void +layout_split_horizontal_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) +{ + int sh, sy; + + sh = (h - ih) * (1 - m->mfact); + h = (h - ih) * m->mfact; + if (m->ltaxis[LAYOUT] < 0) { // mirror + sy = y; + y += sh + ih; + } else { + sy = y + h + ih; + } + + (&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, h, w, ih, iv, n, m->nmaster, 0); + (&flextiles[m->ltaxis[STACK]])->arrange(m, x, sy, sh, w, ih, iv, n, n - m->nmaster, m->nmaster); +} + +static void +layout_split_horizontal_dual_stack(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) +{ + /* Split master into master + stack if we have enough clients */ + if (!m->nmaster || n <= m->nmaster) { + layout_no_split(m, x, y, h, w, ih, iv, n); + } else if (n <= m->nmaster + (m->nstack ? m->nstack : 1)) { + layout_split_horizontal(m, x, y, h, w, ih, iv, n); + } else { + layout_split_horizontal_dual_stack_fixed(m, x, y, h, w, ih, iv, n); + } +} + +static void +layout_split_horizontal_dual_stack_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) +{ + int sh, sy, ox, sc; + + if (m->nstack) + sc = m->nstack; + else + sc = (n - m->nmaster) / 2 + ((n - m->nmaster) % 2 > 0 ? 1 : 0); + + sh = (h - ih) * (1 - m->mfact); + h = (h - ih) * m->mfact; + sw = (w - iv) / 2; + ox = x + sw + iv; + if (m->ltaxis[LAYOUT] < 0) { // mirror + sy = y; + y += sh + ih; + } else { + sy = y + h + ih; + } + + (&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, h, w, ih, iv, n, m->nmaster, 0); + (&flextiles[m->ltaxis[STACK]])->arrange(m, x, sy, sh, sw, ih, iv, n, sc, m->nmaster); + (&flextiles[m->ltaxis[STACK2]])->arrange(m, ox, sy, sh, sw, ih, iv, n, n - m->nmaster - sc, m->nmaster + sc); +} + +static void +layout_split_centered_vertical(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) +{ + /* Split master into master + stack if we have enough clients */ + if (!m->nmaster || n <= m->nmaster) { + layout_no_split(m, x, y, h, w, ih, iv, n); + } else if (n <= m->nmaster + (m->nstack ? m->nstack : 1)) { + layout_split_vertical(m, x, y, h, w, ih, iv, n); + } else { + layout_split_centered_vertical_fixed(m, x, y, h, w, ih, iv, n); + } +} + +static void +layout_split_centered_vertical_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) +{ + int sw, sx, ox, sc; + + if (m->nstack) + sc = m->nstack; + else + sc = (n - m->nmaster) / 2 + ((n - m->nmaster) % 2 > 0 ? 1 : 0); + + sw = (w - 2*iv) * (1 - m->mfact) / 2; + w = (w - 2*iv) * m->mfact; + if (m->ltaxis[LAYOUT] < 0) { // mirror + sx = x; + x += sw + iv; + ox = x + w + iv; + } else { + ox = x; + x += sw + iv; + sx = x + w + iv; + } + + (&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, h, w, ih, iv, n, m->nmaster, 0); + (&flextiles[m->ltaxis[STACK]])->arrange(m, sx, y, h, sw, ih, iv, n, sc, m->nmaster); + (&flextiles[m->ltaxis[STACK2]])->arrange(m, ox, y, h, sw, ih, iv, n, n - m->nmaster - sc, m->nmaster + sc); +} + +static void +layout_split_centered_horizontal(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) +{ + /* Split master into master + stack if we have enough clients */ + if (!m->nmaster || n <= m->nmaster) { + layout_no_split(m, x, y, h, w, ih, iv, n); + } else if (n <= m->nmaster + (m->nstack ? m->nstack : 1)) { + layout_split_horizontal(m, x, y, h, w, ih, iv, n); + } else { + layout_split_centered_horizontal_fixed(m, x, y, h, w, ih, iv, n); + } +} + +static void +layout_split_centered_horizontal_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) +{ + int sh, sy, oy, sc; + + if (m->nstack) + sc = m->nstack; + else + sc = (n - m->nmaster) / 2 + ((n - m->nmaster) % 2 > 0 ? 1 : 0); + + sh = (h - 2*ih) * (1 - m->mfact) / 2; + h = (h - 2*ih) * m->mfact; + if (m->ltaxis[LAYOUT] < 0) { // mirror + sy = y; + y += sh + ih; + oy = y + h + ih; + } else { + oy = y; + y += sh + ih; + sy = y + h + ih; + } + + (&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, h, w, ih, iv, n, m->nmaster, 0); + (&flextiles[m->ltaxis[STACK]])->arrange(m, x, sy, sh, w, ih, iv, n, sc, m->nmaster); + (&flextiles[m->ltaxis[STACK2]])->arrange(m, x, oy, sh, w, ih, iv, n, n - m->nmaster - sc, m->nmaster + sc); +} + +static void +layout_floating_master(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) +{ + /* Split master into master + stack if we have enough clients */ + if (!m->nmaster || n <= m->nmaster) { + layout_no_split(m, x, y, h, w, ih, iv, n); + } else { + layout_floating_master_fixed(m, x, y, h, w, ih, iv, n); + } +} + +static void +layout_floating_master_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n) +{ + int mh, mw; + + /* Draw stack area first */ + (&flextiles[m->ltaxis[STACK]])->arrange(m, x, y, h, w, ih, iv, n, n - m->nmaster, m->nmaster); + + if (w > h) { + mw = w * m->mfact; + mh = h * 0.9; + } else { + mw = w * 0.9; + mh = h * m->mfact; + } + x = x + (w - mw) / 2; + y = y + (h - mh) / 2; + + (&flextiles[m->ltaxis[MASTER]])->arrange(m, x, y, mh, mw, ih, iv, n, m->nmaster, 0); +} + +static void +arrange_left_to_right(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) +{ + int i, rest; + float facts, fact = 1; + Client *c; + + if (ai + an > n) + an = n - ai; + + w -= iv * (an - 1); + getfactsforrange(m, an, ai, w, &rest, &facts); + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { + if (i >= ai && i < (ai + an)) { + #if CFACTS_PATCH + fact = c->cfact; + #endif // CFACTS_PATCH + resize(c, x, y, w * (fact / facts) + ((i - ai) < rest ? 1 : 0) - (2*c->bw), h - (2*c->bw), 0); + x += WIDTH(c) + iv; + } + } +} + +static void +arrange_top_to_bottom(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) +{ + int i, rest; + float facts, fact = 1; + Client *c; + + if (ai + an > n) + an = n - ai; + + h -= ih * (an - 1); + getfactsforrange(m, an, ai, h, &rest, &facts); + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { + if (i >= ai && i < (ai + an)) { + #if CFACTS_PATCH + fact = c->cfact; + #endif // CFACTS_PATCH + resize(c, x, y, w - (2*c->bw), h * (fact / facts) + ((i - ai) < rest ? 1 : 0) - (2*c->bw), 0); + y += HEIGHT(c) + ih; + } + } +} + +static void +arrange_monocle(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) +{ + int i; + Client *c; + + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i >= ai && i < (ai + an)) + resize(c, x, y, w - (2*c->bw), h - (2*c->bw), 0); +} + +static void +arrange_gridmode(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) +{ + int i, cols, rows, ch, cw, cx, cy, cc, cr, chrest, cwrest; // counters + Client *c; + + /* grid dimensions */ + for (rows = 0; rows <= an/2; rows++) + if (rows*rows >= an) + break; + cols = (rows && (rows - 1) * rows >= an) ? rows - 1 : rows; + + /* window geoms (cell height/width) */ + ch = (h - ih * (rows - 1)) / (rows ? rows : 1); + cw = (w - iv * (cols - 1)) / (cols ? cols : 1); + chrest = h - ih * (rows - 1) - ch * rows; + cwrest = w - iv * (cols - 1) - cw * cols; + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { + if (i >= ai && i < (ai + an)) { + cc = ((i - ai) / rows); // client column number + cr = ((i - ai) % rows); // client row number + cx = x + cc * (cw + iv) + MIN(cc, cwrest); + cy = y + cr * (ch + ih) + MIN(cr, chrest); + resize(c, cx, cy, cw + (cc < cwrest ? 1 : 0) - 2*c->bw, ch + (cr < chrest ? 1 : 0) - 2*c->bw, False); + } + } +} + +static void +arrange_horizgrid(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) +{ + int ntop, nbottom, rh, rest; + + /* Exception when there is only one client; don't split into two rows */ + if (an == 1) { + arrange_monocle(m, x, y, h, w, ih, iv, n, an, ai); + return; + } + + ntop = an / 2; + nbottom = an - ntop; + rh = (h - ih) / 2; + rest = h - ih - rh * 2; + arrange_left_to_right(m, x, y, rh + rest, w, ih, iv, n, ntop, ai); + arrange_left_to_right(m, x, y + rh + ih + rest, rh, w, ih, iv, n, nbottom, ai + ntop); +} + +static void +arrange_gapplessgrid(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) +{ + int i, cols, rows, ch, cw, cn, rn, cc, rrest, crest; // counters + Client *c; + + /* grid dimensions */ + for (cols = 1; cols <= an/2; cols++) + if (cols*cols >= an) + break; + if (an == 5) /* set layout against the general calculation: not 1:2:2, but 2:3 */ + cols = 2; + rows = an/cols; + cn = rn = cc = 0; // reset column no, row no, client count + + ch = (h - ih * (rows - 1)) / rows; + rrest = (h - ih * (rows - 1)) - ch * rows; + cw = (w - iv * (cols - 1)) / cols; + crest = (w - iv * (cols - 1)) - cw * cols; + + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { + if (i >= ai && i < (ai + an)) { + if (cc/rows + 1 > cols - an%cols) { + rows = an/cols + 1; + ch = (h - ih * (rows - 1)) / rows; + rrest = (h - ih * (rows - 1)) - ch * rows; + } + resize(c, + x, + y + rn*(ch + ih) + MIN(rn, rrest), + cw + (cn < crest ? 1 : 0) - 2*c->bw, + ch + (rn < rrest ? 1 : 0) - 2*c->bw, + 0); + rn++; + cc++; + if (rn >= rows) { + rn = 0; + x += cw + ih + (cn < crest ? 1 : 0); + cn++; + } + } + } +} + +/* This version of gappless grid fills rows first */ +static void +arrange_gapplessgrid_alt1(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) +{ + int i, cols, rows, rest, ch; + + /* grid dimensions */ + for (cols = 1; cols <= an/2; cols++) + if (cols*cols >= an) + break; + rows = (cols && (cols - 1) * cols >= an) ? cols - 1 : cols; + ch = (h - ih * (rows - 1)) / (rows ? rows : 1); + rest = (h - ih * (rows - 1)) - ch * rows; + + for (i = 0; i < rows; i++) { + arrange_left_to_right(m, x, y, ch + (i < rest ? 1 : 0), w, ih, iv, n, MIN(cols, an - i*cols), ai + i*cols); + y += ch + (i < rest ? 1 : 0) + ih; + } +} + +/* This version of gappless grid fills columns first */ +static void +arrange_gapplessgrid_alt2(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) +{ + int i, cols, rows, rest, cw; + + /* grid dimensions */ + for (rows = 0; rows <= an/2; rows++) + if (rows*rows >= an) + break; + cols = (rows && (rows - 1) * rows >= an) ? rows - 1 : rows; + cw = (w - iv * (cols - 1)) / (cols ? cols : 1); + rest = (w - iv * (cols - 1)) - cw * cols; + + for (i = 0; i < cols; i++) { + arrange_top_to_bottom(m, x, y, h, cw + (i < rest ? 1 : 0), ih, iv, n, MIN(rows, an - i*rows), ai + i*rows); + x += cw + (i < rest ? 1 : 0) + iv; + } +} + +static void +arrange_fibonacci(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai, int s) +{ + int i, j, nv, hrest = 0, wrest = 0, nx = x, ny = y, nw = w, nh = h, r = 1; + Client *c; + + for (i = 0, j = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), j++) { + if (j >= ai && j < (ai + an)) { + if (r) { + if ((i % 2 && ((nh - ih) / 2) <= (bh + 2*c->bw)) || (!(i % 2) && ((nw - iv) / 2) <= (bh + 2*c->bw))) { + r = 0; + } + if (r && i < an - 1) { + if (i % 2) { + nv = (nh - ih) / 2; + hrest = nh - 2*nv - ih; + nh = nv; + } else { + nv = (nw - iv) / 2; + wrest = nw - 2*nv - iv; + nw = nv; + } + + if ((i % 4) == 2 && !s) + nx += nw + iv; + else if ((i % 4) == 3 && !s) + ny += nh + ih; + } + if ((i % 4) == 0) { + if (s) { + ny += nh + ih; + nh += hrest; + } else { + nh -= hrest; + ny -= nh + ih; + } + } else if ((i % 4) == 1) { + nx += nw + iv; + nw += wrest; + } else if ((i % 4) == 2) { + ny += nh + ih; + nh += hrest; + if (i < n - 1) + nw += wrest; + } else if ((i % 4) == 3) { + if (s) { + nx += nw + iv; + nw -= wrest; + } else { + nw -= wrest; + nx -= nw + iv; + nh += hrest; + } + } + if (i == 0) { + if (an != 1) { + nw = (w - iv) - (w - iv) * (1 - m->mfact); + wrest = 0; + } + ny = y; + } else if (i == 1) + nw = w - nw - iv; + i++; + } + + resize(c, nx, ny, nw - 2 * c->bw, nh - 2*c->bw, False); + } + } +} + +static void +arrange_dwindle(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) +{ + arrange_fibonacci(m, x, y, h, w, ih, iv, n, an, ai, 1); +} + +static void +arrange_spiral(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) +{ + arrange_fibonacci(m, x, y, h, w, ih, iv, n, an, ai, 0); +} + +static void +arrange_tatami(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, int an, int ai) +{ + unsigned int i, j, nx, ny, nw, nh, tnx, tny, tnw, tnh, nhrest, hrest, wrest, areas, mats, cats; + Client *c; + + nx = x; + ny = y; + nw = w; + nh = h; + + mats = an / 5; + cats = an % 5; + hrest = 0; + wrest = 0; + + areas = mats + (cats > 0); + nh = (h - ih * (areas - 1)) / areas; + nhrest = (h - ih * (areas - 1)) % areas; + + for (i = 0, j = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), j++) { + if (j >= ai && j < (ai + an)) { + + tnw = nw; + tnx = nx; + tnh = nh; + tny = ny; + + if (j < ai + cats) { + /* Arrange cats (all excess clients that can't be tiled as mats). Cats sleep on mats. */ + + switch (cats) { + case 1: // fill + break; + case 2: // up and down + if ((i % 5) == 0) //up + tnh = (nh - ih) / 2 + (nh - ih) % 2; + else if ((i % 5) == 1) { //down + tny += (nh - ih) / 2 + (nh - ih) % 2 + ih; + tnh = (nh - ih) / 2; + } + break; + case 3: //bottom, up-left and up-right + if ((i % 5) == 0) { // up-left + tnw = (nw - iv) / 2 + (nw - iv) % 2; + tnh = (nh - ih) * 2 / 3 + (nh - ih) * 2 % 3; + } else if ((i % 5) == 1) { // up-right + tnx += (nw - iv) / 2 + (nw - iv) % 2 + iv; + tnw = (nw - iv) / 2; + tnh = (nh - ih) * 2 / 3 + (nh - ih) * 2 % 3; + } else if ((i % 5) == 2) { //bottom + tnh = (nh - ih) / 3; + tny += (nh - ih) * 2 / 3 + (nh - ih) * 2 % 3 + ih; + } + break; + case 4: // bottom, left, right and top + if ((i % 5) == 0) { //top + hrest = (nh - 2 * ih) % 4; + tnh = (nh - 2 * ih) / 4 + (hrest ? 1 : 0); + } else if ((i % 5) == 1) { // left + tnw = (nw - iv) / 2 + (nw - iv) % 2; + tny += (nh - 2 * ih) / 4 + (hrest ? 1 : 0) + ih; + tnh = (nh - 2 * ih) * 2 / 4 + (hrest > 1 ? 1 : 0); + } else if ((i % 5) == 2) { // right + tnx += (nw - iv) / 2 + (nw - iv) % 2 + iv; + tnw = (nw - iv) / 2; + tny += (nh - 2 * ih) / 4 + (hrest ? 1 : 0) + ih; + tnh = (nh - 2 * ih) * 2 / 4 + (hrest > 1 ? 1 : 0); + } else if ((i % 5) == 3) { // bottom + tny += (nh - 2 * ih) / 4 + (hrest ? 1 : 0) + (nh - 2 * ih) * 2 / 4 + (hrest > 1 ? 1 : 0) + 2 * ih; + tnh = (nh - 2 * ih) / 4 + (hrest > 2 ? 1 : 0); + } + break; + } + + } else { + /* Arrange mats. One mat is a collection of five clients arranged tatami style */ + + if (((i - cats) % 5) == 0) { + if ((cats > 0) || ((i - cats) >= 5)) { + tny = ny = ny + nh + (nhrest > 0 ? 1 : 0) + ih; + --nhrest; + } + } + + switch ((i - cats) % 5) { + case 0: // top-left-vert + wrest = (nw - 2 * iv) % 3; + hrest = (nh - 2 * ih) % 3; + tnw = (nw - 2 * iv) / 3 + (wrest ? 1 : 0); + tnh = (nh - 2 * ih) * 2 / 3 + hrest + iv; + break; + case 1: // top-right-hor + tnx += (nw - 2 * iv) / 3 + (wrest ? 1 : 0) + iv; + tnw = (nw - 2 * iv) * 2 / 3 + (wrest > 1 ? 1 : 0) + iv; + tnh = (nh - 2 * ih) / 3 + (hrest ? 1 : 0); + break; + case 2: // center + tnx += (nw - 2 * iv) / 3 + (wrest ? 1 : 0) + iv; + tnw = (nw - 2 * iv) / 3 + (wrest > 1 ? 1 : 0); + tny += (nh - 2 * ih) / 3 + (hrest ? 1 : 0) + ih; + tnh = (nh - 2 * ih) / 3 + (hrest > 1 ? 1 : 0); + break; + case 3: // bottom-right-vert + tnx += (nw - 2 * iv) * 2 / 3 + wrest + 2 * iv; + tnw = (nw - 2 * iv) / 3; + tny += (nh - 2 * ih) / 3 + (hrest ? 1 : 0) + ih; + tnh = (nh - 2 * ih) * 2 / 3 + hrest + iv; + break; + case 4: // (oldest) bottom-left-hor + tnw = (nw - 2 * iv) * 2 / 3 + wrest + iv; + tny += (nh - 2 * ih) * 2 / 3 + hrest + 2 * iv; + tnh = (nh - 2 * ih) / 3; + break; + } + + } + + resize(c, tnx, tny, tnw - 2 * c->bw, tnh - 2 * c->bw, False); + ++i; + } + } +} + +static void +flextile(Monitor *m) +{ + unsigned int n; + int oh = 0, ov = 0, ih = 0, iv = 0; // gaps outer/inner horizontal/vertical + + #if VANITYGAPS_PATCH + getgaps(m, &oh, &ov, &ih, &iv, &n); + #else + Client *c; + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + #endif // VANITYGAPS_PATCH + + if (m->lt[m->sellt]->preset.layout != m->ltaxis[LAYOUT] || + m->lt[m->sellt]->preset.masteraxis != m->ltaxis[MASTER] || + m->lt[m->sellt]->preset.stack1axis != m->ltaxis[STACK] || + m->lt[m->sellt]->preset.stack2axis != m->ltaxis[STACK2]) + setflexsymbols(m, n); + else if (m->lt[m->sellt]->preset.symbolfunc != NULL) + m->lt[m->sellt]->preset.symbolfunc(m, n); + + if (n == 0) + return; + + #if VANITYGAPS_PATCH && !VANITYGAPS_MONOCLE_PATCH + /* No outer gap if full screen monocle */ + if (abs(m->ltaxis[MASTER]) == MONOCLE && (abs(m->ltaxis[LAYOUT]) == NO_SPLIT || n <= m->nmaster)) { + oh = 0; + ov = 0; + } + #endif // VANITYGAPS_PATCH && !VANITYGAPS_MONOCLE_PATCH + + (&flexlayouts[abs(m->ltaxis[LAYOUT])])->arrange(m, m->wx + ov, m->wy + oh, m->wh - 2*oh, m->ww - 2*ov, ih, iv, n); + return; +} + +static void +setflexsymbols(Monitor *m, unsigned int n) +{ + int l; + char sym1, sym2, sym3; + Client *c; + + if (n == 0) + for (c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + + l = abs(m->ltaxis[LAYOUT]); + if (m->ltaxis[MASTER] == MONOCLE && (l == NO_SPLIT || !m->nmaster || n <= m->nmaster)) { + monoclesymbols(m, n); + return; + } + + if (m->ltaxis[STACK] == MONOCLE && (l == SPLIT_VERTICAL || l == SPLIT_HORIZONTAL_FIXED)) { + decksymbols(m, n); + return; + } + + /* Layout symbols */ + if (l == NO_SPLIT || !m->nmaster) { + sym1 = sym2 = sym3 = (int)tilesymb[m->ltaxis[MASTER]]; + } else { + sym2 = layoutsymb[l]; + if (m->ltaxis[LAYOUT] < 0) { + sym1 = tilesymb[m->ltaxis[STACK]]; + sym3 = tilesymb[m->ltaxis[MASTER]]; + } else { + sym1 = tilesymb[m->ltaxis[MASTER]]; + sym3 = tilesymb[m->ltaxis[STACK]]; + } + } + + snprintf(m->ltsymbol, sizeof m->ltsymbol, "%c%c%c", sym1, sym2, sym3); +} + +static void +monoclesymbols(Monitor *m, unsigned int n) +{ + if (n > 0) + snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); + else + snprintf(m->ltsymbol, sizeof m->ltsymbol, "[M]"); +} + +static void +decksymbols(Monitor *m, unsigned int n) +{ + if (n > m->nmaster) + snprintf(m->ltsymbol, sizeof m->ltsymbol, "[]%d", n); + else + snprintf(m->ltsymbol, sizeof m->ltsymbol, "[D]"); +} + +/* Mirror layout axis for flextile */ +void +mirrorlayout(const Arg *arg) +{ + if (!selmon->lt[selmon->sellt]->arrange) + return; + selmon->ltaxis[LAYOUT] *= -1; + #if PERTAG_PATCH + selmon->pertag->ltaxis[selmon->pertag->curtag][0] = selmon->ltaxis[LAYOUT]; + #endif // PERTAG_PATCH + arrange(selmon); +} + +/* Rotate layout axis for flextile */ +void +rotatelayoutaxis(const Arg *arg) +{ + int incr = (arg->i > 0 ? 1 : -1); + int axis = abs(arg->i) - 1; + + if (!selmon->lt[selmon->sellt]->arrange) + return; + if (axis == LAYOUT) { + if (selmon->ltaxis[LAYOUT] >= 0) { + selmon->ltaxis[LAYOUT] += incr; + if (selmon->ltaxis[LAYOUT] >= LAYOUT_LAST) + selmon->ltaxis[LAYOUT] = 0; + else if (selmon->ltaxis[LAYOUT] < 0) + selmon->ltaxis[LAYOUT] = LAYOUT_LAST - 1; + } else { + selmon->ltaxis[LAYOUT] -= incr; + if (selmon->ltaxis[LAYOUT] <= -LAYOUT_LAST) + selmon->ltaxis[LAYOUT] = 0; + else if (selmon->ltaxis[LAYOUT] > 0) + selmon->ltaxis[LAYOUT] = -LAYOUT_LAST + 1; + } + } else { + selmon->ltaxis[axis] += incr; + if (selmon->ltaxis[axis] >= AXIS_LAST) + selmon->ltaxis[axis] = 0; + else if (selmon->ltaxis[axis] < 0) + selmon->ltaxis[axis] = AXIS_LAST - 1; + } + #if PERTAG_PATCH + selmon->pertag->ltaxis[selmon->pertag->curtag][axis] = selmon->ltaxis[axis]; + #endif // PERTAG_PATCH + arrange(selmon); + setflexsymbols(selmon, 0); +} + +void +incnstack(const Arg *arg) +{ + #if PERTAG_PATCH + selmon->nstack = selmon->pertag->nstacks[selmon->pertag->curtag] = MAX(selmon->nstack + arg->i, 0); + #else + selmon->nstack = MAX(selmon->nstack + arg->i, 0); + #endif // PERTAG_PATCH + arrange(selmon); +} + diff --git a/patch/layout_flextile-deluxe.h b/patch/layout_flextile-deluxe.h new file mode 100644 index 0000000..92df5c5 --- /dev/null +++ b/patch/layout_flextile-deluxe.h @@ -0,0 +1,121 @@ +static void flextile(Monitor *m); +static void getfactsforrange(Monitor *m, int an, int ai, int size, int *rest, float *fact); +static void mirrorlayout(const Arg *arg); +static void rotatelayoutaxis(const Arg *arg); +#if IPC_PATCH || DWMC_PATCH +static void setlayoutaxisex(const Arg *arg); +#endif // IPC_PATCH | DWMC_PATCH +static void incnstack(const Arg *arg); + +/* Symbol handlers */ +static void setflexsymbols(Monitor *m, unsigned int n); +static void monoclesymbols(Monitor *m, unsigned int n); +static void decksymbols(Monitor *m, unsigned int n); + +/* Layout split */ +static void layout_no_split(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); +static void layout_split_vertical(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); +static void layout_split_horizontal(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); +static void layout_split_vertical_dual_stack(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); +static void layout_split_horizontal_dual_stack(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); +static void layout_split_centered_vertical(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); +static void layout_split_centered_horizontal(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); +static void layout_floating_master(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); +static void layout_split_vertical_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); +static void layout_split_horizontal_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); +static void layout_split_vertical_dual_stack_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); +static void layout_split_horizontal_dual_stack_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); +static void layout_split_centered_vertical_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); +static void layout_split_centered_horizontal_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); +static void layout_floating_master_fixed(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n); + +/* Layout tile arrangements */ +static void arrange_left_to_right(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); +static void arrange_top_to_bottom(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); +static void arrange_monocle(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); +static void arrange_gapplessgrid(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); +static void arrange_gapplessgrid_alt1(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); +static void arrange_gapplessgrid_alt2(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); +static void arrange_gridmode(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); +static void arrange_horizgrid(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); +static void arrange_dwindle(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); +static void arrange_spiral(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); +static void arrange_tatami(Monitor *m, int ax, int ay, int ah, int aw, int ih, int iv, int n, int an, int ai); + +/* Named flextile constants */ +enum { + LAYOUT, // controls overall layout arrangement / split + MASTER, // indicates the tile arrangement for the master area + STACK, // indicates the tile arrangement for the stack area + STACK2, // indicates the tile arrangement for the secondary stack area + LTAXIS_LAST, +}; + +/* Layout arrangements */ +enum { + NO_SPLIT, + SPLIT_VERTICAL, // master stack vertical split + SPLIT_HORIZONTAL, // master stack horizontal split + SPLIT_CENTERED_VERTICAL, // centered master vertical split + SPLIT_CENTERED_HORIZONTAL, // centered master horizontal split + SPLIT_VERTICAL_DUAL_STACK, // master stack vertical split with dual stack + SPLIT_HORIZONTAL_DUAL_STACK, // master stack vertical split with dual stack + FLOATING_MASTER, // (fake) floating master + SPLIT_VERTICAL_FIXED, // master stack vertical fixed split + SPLIT_HORIZONTAL_FIXED, // master stack horizontal fixed split + SPLIT_CENTERED_VERTICAL_FIXED, // centered master vertical fixed split + SPLIT_CENTERED_HORIZONTAL_FIXED, // centered master horizontal fixed split + SPLIT_VERTICAL_DUAL_STACK_FIXED, // master stack vertical split with fixed dual stack + SPLIT_HORIZONTAL_DUAL_STACK_FIXED, // master stack vertical split with fixed dual stack + FLOATING_MASTER_FIXED, // (fake) fixed floating master + LAYOUT_LAST, +}; + +static char layoutsymb[] = { + 32, // " ", + 124, // "|", + 61, // "=", + 94, // "^", + 126, // "~", + 58, // ":", + 59, // ";", + 43, // "+", + 124, // "¦", + 61, // "=", + 94, // "^", + 126, // "~", + 58, // ":", + 59, // ";", + 43, // "+", +}; + +/* Tile arrangements */ +enum { + TOP_TO_BOTTOM, // clients are arranged vertically + LEFT_TO_RIGHT, // clients are arranged horizontally + MONOCLE, // clients are arranged in deck / monocle mode + GAPPLESSGRID, // clients are arranged in a gappless grid (original formula) + GAPPLESSGRID_ALT1, // clients are arranged in a gappless grid (alt. 1, fills rows first) + GAPPLESSGRID_ALT2, // clients are arranged in a gappless grid (alt. 2, fills columns first) + GRIDMODE, // clients are arranged in a grid + HORIZGRID, // clients are arranged in a horizontal grid + DWINDLE, // clients are arranged in fibonacci dwindle mode + SPIRAL, // clients are arranged in fibonacci spiral mode + TATAMI, // clients are arranged as tatami mats + AXIS_LAST, +}; + +static char tilesymb[] = { + 61, // "=", + 124, // "|", + 68, // "D", + 71, // "G", + 49, // "1", + 50, // "2" + 35, // "#", + 126, // "~", + 92, // "\\", + 64, // "@", + 84, // "T", +}; + diff --git a/patch/layout_gapplessgrid.c b/patch/layout_gapplessgrid.c new file mode 100644 index 0000000..79f236a --- /dev/null +++ b/patch/layout_gapplessgrid.c @@ -0,0 +1,99 @@ +#if VANITYGAPS_PATCH +void +gaplessgrid(Monitor *m) +{ + unsigned int i, n; + int x, y, cols, rows, ch, cw, cn, rn, rrest, crest; // counters + int oh, ov, ih, iv; + Client *c; + + getgaps(m, &oh, &ov, &ih, &iv, &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; + rows = n/cols; + cn = rn = 0; // reset column no, row no, client count + + ch = (m->wh - 2*oh - ih * (rows - 1)) / rows; + cw = (m->ww - 2*ov - iv * (cols - 1)) / cols; + rrest = (m->wh - 2*oh - ih * (rows - 1)) - ch * rows; + crest = (m->ww - 2*ov - iv * (cols - 1)) - cw * cols; + x = m->wx + ov; + y = m->wy + oh; + + for (i = 0, c = nexttiled(m->clients); c; i++, c = nexttiled(c->next)) { + if (i/rows + 1 > cols - n%cols) { + rows = n/cols + 1; + ch = (m->wh - 2*oh - ih * (rows - 1)) / rows; + rrest = (m->wh - 2*oh - ih * (rows - 1)) - ch * rows; + } + resize(c, + x, + y + rn*(ch + ih) + MIN(rn, rrest), + cw + (cn < crest ? 1 : 0) - 2*c->bw, + ch + (rn < rrest ? 1 : 0) - 2*c->bw, + 0); + rn++; + if (rn >= rows) { + rn = 0; + x += cw + ih + (cn < crest ? 1 : 0); + cn++; + } + } +} +#else +void +gaplessgrid(Monitor *m) +{ + unsigned int i, n; + int x, y, cols, rows, ch, cw, cn, rn, rrest, crest; // counters + Client *c; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), 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; + rows = n/cols; + cn = rn = 0; // reset column no, row no, client count + + ch = m->wh / rows; + cw = m->ww / cols; + rrest = m->wh - ch * rows; + crest = m->ww - cw * cols; + x = m->wx; + y = m->wy; + + for (i = 0, c = nexttiled(m->clients); c; i++, c = nexttiled(c->next)) { + if (i/rows + 1 > cols - n%cols) { + rows = n/cols + 1; + ch = m->wh / rows; + rrest = m->wh - ch * rows; + } + resize(c, + x, + y + rn*ch + MIN(rn, rrest), + cw + (cn < crest ? 1 : 0) - 2*c->bw, + ch + (rn < rrest ? 1 : 0) - 2*c->bw, + 0); + rn++; + if (rn >= rows) { + rn = 0; + x += cw + (cn < crest ? 1 : 0); + cn++; + } + } +} +#endif + diff --git a/patch/layout_gapplessgrid.h b/patch/layout_gapplessgrid.h new file mode 100644 index 0000000..1a4ffc2 --- /dev/null +++ b/patch/layout_gapplessgrid.h @@ -0,0 +1,2 @@ +static void gaplessgrid(Monitor *m); + diff --git a/patch/layout_grid.c b/patch/layout_grid.c new file mode 100644 index 0000000..7ac832f --- /dev/null +++ b/patch/layout_grid.c @@ -0,0 +1,61 @@ +#if VANITYGAPS_PATCH +void +grid(Monitor *m) +{ + unsigned int i, n; + int cx, cy, cw, ch, cc, cr, chrest, cwrest, cols, rows; + int oh, ov, ih, iv; + Client *c; + + getgaps(m, &oh, &ov, &ih, &iv, &n); + + /* grid dimensions */ + for (rows = 0; rows <= n/2; rows++) + if (rows*rows >= n) + break; + cols = (rows && (rows - 1) * rows >= n) ? rows - 1 : rows; + + /* window geoms (cell height/width) */ + ch = (m->wh - 2*oh - ih * (rows - 1)) / (rows ? rows : 1); + cw = (m->ww - 2*ov - iv * (cols - 1)) / (cols ? cols : 1); + chrest = (m->wh - 2*oh - ih * (rows - 1)) - ch * rows; + cwrest = (m->ww - 2*ov - iv * (cols - 1)) - cw * cols; + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { + cc = i / rows; + cr = i % rows; + cx = m->wx + ov + cc * (cw + iv) + MIN(cc, cwrest); + cy = m->wy + oh + cr * (ch + ih) + MIN(cr, chrest); + resize(c, cx, cy, cw + (cc < cwrest ? 1 : 0) - 2*c->bw, ch + (cr < chrest ? 1 : 0) - 2*c->bw, False); + } +} +#else +void +grid(Monitor *m) +{ + unsigned int i, n; + int cx, cy, cw, ch, cc, cr, chrest, cwrest, cols, rows; + Client *c; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + + /* grid dimensions */ + for (rows = 0; rows <= n/2; rows++) + if (rows*rows >= n) + break; + cols = (rows && (rows - 1) * rows >= n) ? rows - 1 : rows; + + /* window geoms (cell height/width) */ + ch = m->wh / (rows ? rows : 1); + cw = m->ww / (cols ? cols : 1); + chrest = m->wh - ch * rows; + cwrest = m->ww - cw * cols; + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { + cc = i / rows; + cr = i % rows; + cx = m->wx + cc * cw + MIN(cc, cwrest); + cy = m->wy + cr * ch + MIN(cr, chrest); + resize(c, cx, cy, cw + (cc < cwrest ? 1 : 0) - 2*c->bw, ch + (cr < chrest ? 1 : 0) - 2*c->bw, False); + } +} +#endif + diff --git a/patch/layout_grid.h b/patch/layout_grid.h new file mode 100644 index 0000000..2c03abb --- /dev/null +++ b/patch/layout_grid.h @@ -0,0 +1,2 @@ +static void grid(Monitor *m); + diff --git a/patch/layout_horizgrid.c b/patch/layout_horizgrid.c new file mode 100644 index 0000000..beb8e0b --- /dev/null +++ b/patch/layout_horizgrid.c @@ -0,0 +1,104 @@ +void +horizgrid(Monitor *m) { + Client *c; + unsigned int n, i; + int mx = 0, my = 0, mh = 0, mw = 0; + int sx = 0, sy = 0, sh = 0, sw = 0; + int ntop; + float mfacts = 0, sfacts = 0; + int mrest, srest, mtotal = 0, stotal = 0; + + #if VANITYGAPS_PATCH + int oh, ov, ih, iv; + getgaps(m, &oh, &ov, &ih, &iv, &n); + #else + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + #endif // VANITYGAPS_PATCH + if (n == 0) + return; + + if (n <= 2) + ntop = n; + else { + ntop = n / 2; + } + + #if VANITYGAPS_PATCH + sx = mx = m->wx + ov; + sy = my = m->wy + oh; + sh = mh = m->wh - 2*oh; + sw = mw = m->ww - 2*ov; + + if (n > ntop) { + sh = (mh - ih) / 2; + mh = mh - ih - sh; + sy = my + mh + ih; + mw = m->ww - 2*ov - iv * (ntop - 1); + sw = m->ww - 2*ov - iv * (n - ntop - 1); + } + #else + sx = mx = m->wx; + sy = my = m->wy; + sh = mh = m->wh; + sw = mw = m->ww; + + if (n > ntop) { + sh = mh / 2; + mh = mh - sh; + sy = my + mh; + } + #endif // VANITYGAPS_PATCH + + /* calculate facts */ + #if CFACTS_PATCH + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < ntop) + mfacts += c->cfact; + else + sfacts += c->cfact; + + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < ntop) + mtotal += mh * (c->cfact / mfacts); + else + stotal += sw * (c->cfact / sfacts); + #else + mfacts = ntop; + sfacts = n - ntop; + + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < ntop) + mtotal += mh / mfacts; + else + stotal += sw / sfacts; + #endif // CFACTS_PATCH + + mrest = mh - mtotal; + srest = sw - stotal; + + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < ntop) { + #if CFACTS_PATCH + resize(c, mx, my, mw * (c->cfact / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); + #else + resize(c, mx, my, mw / mfacts + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); + #endif // CFACTS_PATCH + #if VANITYGAPS_PATCH + mx += WIDTH(c) + iv; + #else + mx += WIDTH(c); + #endif // VANITYGAPS_PATCH + } else { + #if CFACTS_PATCH + resize(c, sx, sy, sw * (c->cfact / sfacts) + ((i - ntop) < srest ? 1 : 0) - (2*c->bw), sh - (2*c->bw), 0); + #else + resize(c, sx, sy, sw / sfacts + ((i - ntop) < srest ? 1 : 0) - (2*c->bw), sh - (2*c->bw), 0); + #endif // CFACTS_PATCH + #if VANITYGAPS_PATCH + sx += WIDTH(c) + iv; + #else + sx += WIDTH(c); + #endif // VANITYGAPS_PATCH + } +} + diff --git a/patch/layout_horizgrid.h b/patch/layout_horizgrid.h new file mode 100644 index 0000000..799fa9c --- /dev/null +++ b/patch/layout_horizgrid.h @@ -0,0 +1,2 @@ +static void horizgrid(Monitor *m); + diff --git a/patch/layout_monocle.c b/patch/layout_monocle.c new file mode 100644 index 0000000..f7b5bc3 --- /dev/null +++ b/patch/layout_monocle.c @@ -0,0 +1,38 @@ +#if VANITYGAPS_PATCH && VANITYGAPS_MONOCLE_PATCH +void +monocle(Monitor *m) +{ + unsigned int n; + int oh, ov, ih, iv; + Client *c; + + getgaps(m, &oh, &ov, &ih, &iv, &n); + + #if !MONOCLESYMBOL_PATCH + if (n > 0) /* override layout symbol */ + snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); + #endif // MONOCLESYMBOL_PATCH + for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) + resize(c, m->wx + ov, m->wy + oh, m->ww - 2 * c->bw - 2 * ov, m->wh - 2 * c->bw - 2 * oh, 0); +} +#else +void +monocle(Monitor *m) +{ + #if !MONOCLESYMBOL_PATCH + unsigned int n = 0; + #endif // MONOCLESYMBOL_PATCH + Client *c; + + #if !MONOCLESYMBOL_PATCH + for (c = m->clients; c; c = c->next) + if (ISVISIBLE(c)) + n++; + if (n > 0) /* override layout symbol */ + snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); + #endif // MONOCLESYMBOL_PATCH + for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) + resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0); +} +#endif // VANITYGAPS_PATCH + diff --git a/patch/layout_monocle.h b/patch/layout_monocle.h new file mode 100644 index 0000000..f32e49f --- /dev/null +++ b/patch/layout_monocle.h @@ -0,0 +1,2 @@ +static void monocle(Monitor *m); + diff --git a/patch/layout_nrowgrid.c b/patch/layout_nrowgrid.c new file mode 100644 index 0000000..7fada74 --- /dev/null +++ b/patch/layout_nrowgrid.c @@ -0,0 +1,104 @@ +#if VANITYGAPS_PATCH +void +nrowgrid(Monitor *m) +{ + unsigned int n = 0, i = 0, ri = 0, ci = 0; /* counters */ + int oh, ov, ih, iv; /* vanitygap settings */ + unsigned int cx, cy, cw, ch; /* client geometry */ + unsigned int uw = 0, uh = 0, uc = 0; /* utilization trackers */ + unsigned int cols, rows = m->nmaster + 1; + Client *c; + + /* count clients */ + getgaps(m, &oh, &ov, &ih, &iv, &n); + + /* nothing to do here */ + if (n == 0) + return; + + /* force 2 clients to always split vertically */ + if (FORCE_VSPLIT && n == 2) + rows = 1; + + /* never allow empty rows */ + if (n < rows) + rows = n; + + /* define first row */ + cols = n / rows; + uc = cols; + cy = m->wy + oh; + ch = (m->wh - 2*oh - ih*(rows - 1)) / rows; + uh = ch; + + for (c = nexttiled(m->clients); c; c = nexttiled(c->next), i++, ci++) { + if (ci == cols) { + uw = 0; + ci = 0; + ri++; + + /* next row */ + cols = (n - uc) / (rows - ri); + uc += cols; + cy = m->wy + oh + uh + ih; + uh += ch + ih; + } + + cx = m->wx + ov + uw; + cw = (m->ww - 2*ov - uw) / (cols - ci); + uw += cw + iv; + + resize(c, cx, cy, cw - (2*c->bw), ch - (2*c->bw), 0); + } +} +#else +void +nrowgrid(Monitor *m) +{ + unsigned int n = 0, i = 0, ri = 0, ci = 0; /* counters */ + unsigned int cx, cy, cw, ch; /* client geometry */ + unsigned int uw = 0, uh = 0, uc = 0; /* utilization trackers */ + unsigned int cols, rows = m->nmaster + 1; + Client *c; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + if (n == 0) + return; + + /* force 2 clients to always split vertically */ + if (FORCE_VSPLIT && n == 2) + rows = 1; + + /* never allow empty rows */ + if (n < rows) + rows = n; + + /* define first row */ + cols = n / rows; + uc = cols; + cy = m->wy; + ch = m->wh / rows; + uh = ch; + + for (c = nexttiled(m->clients); c; c = nexttiled(c->next), i++, ci++) { + if (ci == cols) { + uw = 0; + ci = 0; + ri++; + + /* next row */ + cols = (n - uc) / (rows - ri); + uc += cols; + cy = m->wy + uh; + uh += ch; + } + + cx = m->wx + uw; + cw = (m->ww - uw) / (cols - ci); + uw += cw; + + resize(c, cx, cy, cw - (2*c->bw), ch - (2*c->bw), 0); + } +} +#endif + diff --git a/patch/layout_nrowgrid.h b/patch/layout_nrowgrid.h new file mode 100644 index 0000000..9cce593 --- /dev/null +++ b/patch/layout_nrowgrid.h @@ -0,0 +1,2 @@ +static void nrowgrid(Monitor *m); + diff --git a/patch/layout_tile.c b/patch/layout_tile.c new file mode 100644 index 0000000..cd4f1ae --- /dev/null +++ b/patch/layout_tile.c @@ -0,0 +1,74 @@ +static void +tile(Monitor *m) +{ + unsigned int i, n; + int mx = 0, my = 0, mh = 0, mw = 0; + int sx = 0, sy = 0, sh = 0, sw = 0; + float mfacts, sfacts; + int mrest, srest; + Client *c; + + + #if VANITYGAPS_PATCH + int oh, ov, ih, iv; + getgaps(m, &oh, &ov, &ih, &iv, &n); + #else + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + #endif // VANITYGAPS_PATCH + + if (n == 0) + return; + + #if VANITYGAPS_PATCH + sx = mx = m->wx + ov; + sy = my = m->wy + oh; + mh = m->wh - 2*oh - ih * (MIN(n, m->nmaster) - 1); + sh = m->wh - 2*oh - ih * (n - m->nmaster - 1); + sw = mw = m->ww - 2*ov; + + if (m->nmaster && n > m->nmaster) { + sw = (mw - iv) * (1 - m->mfact); + mw = (mw - iv) * m->mfact; + sx = mx + mw + iv; + } + #else + sx = mx = m->wx; + sy = my = m->wy; + sh = mh = m->wh; + sw = mw = m->ww; + + if (m->nmaster && n > m->nmaster) { + sw = mw * (1 - m->mfact); + mw = mw * m->mfact; + sx = mx + mw; + } + #endif // VANITYGAPS_PATCH + + getfacts(m, mh, sh, &mfacts, &sfacts, &mrest, &srest); + + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < m->nmaster) { + #if CFACTS_PATCH + resize(c, mx, my, mw - (2*c->bw), (mh / mfacts) * c->cfact + (i < mrest ? 1 : 0) - (2*c->bw), 0); + #else + resize(c, mx, my, mw - (2*c->bw), (mh / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), 0); + #endif // CFACTS_PATCH + #if VANITYGAPS_PATCH + my += HEIGHT(c) + ih; + #else + my += HEIGHT(c); + #endif + } else { + #if CFACTS_PATCH + resize(c, sx, sy, sw - (2*c->bw), (sh / sfacts) * c->cfact + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), 0); + #else + resize(c, sx, sy, sw - (2*c->bw), (sh / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), 0); + #endif // CFACTS_PATCH + #if VANITYGAPS_PATCH + sy += HEIGHT(c) + ih; + #else + sy += HEIGHT(c); + #endif + } +} + diff --git a/patch/layout_tile.h b/patch/layout_tile.h new file mode 100644 index 0000000..78cafc8 --- /dev/null +++ b/patch/layout_tile.h @@ -0,0 +1,2 @@ +static void tile(Monitor *); + diff --git a/patch/layoutmenu.sh b/patch/layoutmenu.sh new file mode 100755 index 0000000..0bb0456 --- /dev/null +++ b/patch/layoutmenu.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +cat <<EOF | xmenu +[]= Tiled Layout 0 +><> Floating Layout 1 +[M] Monocle Layout 2 +EOF + diff --git a/patch/maximize.c b/patch/maximize.c new file mode 100644 index 0000000..c89dbda --- /dev/null +++ b/patch/maximize.c @@ -0,0 +1,70 @@ +void +maximize(int x, int y, int w, int h) +{ + XEvent ev; + + if (!selmon->sel || selmon->sel->isfixed) + return; + XRaiseWindow(dpy, selmon->sel->win); + if (!selmon->sel->ismax) { + if (!selmon->lt[selmon->sellt]->arrange || selmon->sel->isfloating) + selmon->sel->wasfloating = True; + else { + togglefloating(NULL); + selmon->sel->wasfloating = False; + } + selmon->sel->oldx = selmon->sel->x; + selmon->sel->oldy = selmon->sel->y; + selmon->sel->oldw = selmon->sel->w; + selmon->sel->oldh = selmon->sel->h; + resize(selmon->sel, x, y, w, h, True); + selmon->sel->ismax = True; + } + else { + resize(selmon->sel, selmon->sel->oldx, selmon->sel->oldy, selmon->sel->oldw, selmon->sel->oldh, True); + if (!selmon->sel->wasfloating) + togglefloating(NULL); + selmon->sel->ismax = False; + } + drawbar(selmon); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + +#if SETBORDERPX_PATCH +void +togglemax(const Arg *arg) +{ + maximize(selmon->wx, selmon->wy, selmon->ww - 2 * selmon->borderpx, selmon->wh - 2 * selmon->borderpx); +} + +void +toggleverticalmax(const Arg *arg) +{ + maximize(selmon->sel->x, selmon->wy, selmon->sel->w, selmon->wh - 2 * selmon->borderpx); +} + +void +togglehorizontalmax(const Arg *arg) +{ + maximize(selmon->wx, selmon->sel->y, selmon->ww - 2 * selmon->borderpx, selmon->sel->h); +} +#else +void +togglemax(const Arg *arg) +{ + maximize(selmon->wx, selmon->wy, selmon->ww - 2 * borderpx, selmon->wh - 2 * borderpx); +} + +void +toggleverticalmax(const Arg *arg) +{ + maximize(selmon->sel->x, selmon->wy, selmon->sel->w, selmon->wh - 2 * borderpx); +} + +void +togglehorizontalmax(const Arg *arg) +{ + maximize(selmon->wx, selmon->sel->y, selmon->ww - 2 * borderpx, selmon->sel->h); +} +#endif // SETBORDERPX_PATCH + diff --git a/patch/maximize.h b/patch/maximize.h new file mode 100644 index 0000000..f3c6c9a --- /dev/null +++ b/patch/maximize.h @@ -0,0 +1,5 @@ +static void maximize(int x, int y, int w, int h); +static void togglemax(const Arg *arg); +static void toggleverticalmax(const Arg *arg); +static void togglehorizontalmax(const Arg *arg); + diff --git a/patch/moveplace.c b/patch/moveplace.c new file mode 100644 index 0000000..7211417 --- /dev/null +++ b/patch/moveplace.c @@ -0,0 +1,30 @@ +void +moveplace(const Arg *arg) +{ + Client *c; + int nh, nw, nx, ny; + c = selmon->sel; + if (!c || (arg->ui >= 9)) + return; + if (selmon->lt[selmon->sellt]->arrange && !c->isfloating) + togglefloating(NULL); + nh = (selmon->wh / 3) - (c->bw * 2); + nw = (selmon->ww / 3) - (c->bw * 2); + nx = (arg->ui % 3) -1; + ny = (arg->ui / 3) -1; + if (nx < 0) + nx = selmon->wx; + else if (nx > 0) + nx = selmon->wx + selmon->ww - nw - c->bw*2; + else + nx = selmon->wx + selmon->ww/2 - nw/2 - c->bw; + if (ny <0) + ny = selmon->wy; + else if (ny > 0) + ny = selmon->wy + selmon->wh - nh - c->bw*2; + else + ny = selmon->wy + selmon->wh/2 - nh/2 - c->bw; + resize(c, nx, ny, nw, nh, True); + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, nw/2, nh/2); +} + diff --git a/patch/moveplace.h b/patch/moveplace.h new file mode 100644 index 0000000..57bc924 --- /dev/null +++ b/patch/moveplace.h @@ -0,0 +1,4 @@ +enum { WIN_NW, WIN_N, WIN_NE, WIN_W, WIN_C, WIN_E, WIN_SW, WIN_S, WIN_SE }; + +static void moveplace(const Arg *arg); + diff --git a/patch/moveresize.c b/patch/moveresize.c new file mode 100644 index 0000000..75d58e2 --- /dev/null +++ b/patch/moveresize.c @@ -0,0 +1,65 @@ +void +moveresize(const Arg *arg) { + /* only floating windows can be moved */ + Client *c; + c = selmon->sel; + int x, y, w, h, nx, ny, nw, nh, ox, oy, ow, oh; + char xAbs, yAbs, wAbs, hAbs; + int msx, msy, dx, dy, nmx, nmy; + unsigned int dui; + Window dummy; + + if (!c || !arg) + return; + if (selmon->lt[selmon->sellt]->arrange && !c->isfloating) + return; + if (sscanf((char *)arg->v, "%d%c %d%c %d%c %d%c", &x, &xAbs, &y, &yAbs, &w, &wAbs, &h, &hAbs) != 8) + return; + + /* compute new window position; prevent window from be positioned outside the current monitor */ + nw = c->w + w; + if (wAbs == 'W') + nw = w < selmon->mw - 2 * c->bw ? w : selmon->mw - 2 * c->bw; + + nh = c->h + h; + if (hAbs == 'H') + nh = h < selmon->mh - 2 * c->bw ? h : selmon->mh - 2 * c->bw; + + nx = c->x + x; + if (xAbs == 'X') { + if (x < selmon->mx) + nx = selmon->mx; + else if (x > selmon->mx + selmon->mw) + nx = selmon->mx + selmon->mw - nw - 2 * c->bw; + else + nx = x; + } + + ny = c->y + y; + if (yAbs == 'Y') { + if (y < selmon->my) + ny = selmon->my; + else if (y > selmon->my + selmon->mh) + ny = selmon->my + selmon->mh - nh - 2 * c->bw; + else + ny = y; + } + + ox = c->x; + oy = c->y; + ow = c->w; + oh = c->h; + + XRaiseWindow(dpy, c->win); + Bool xqp = XQueryPointer(dpy, root, &dummy, &dummy, &msx, &msy, &dx, &dy, &dui); + resize(c, nx, ny, nw, nh, True); + + /* move cursor along with the window to avoid problems caused by the sloppy focus */ + if (xqp && ox <= msx && (ox + ow) >= msx && oy <= msy && (oy + oh) >= msy) + { + nmx = c->x - ox + c->w - ow; + nmy = c->y - oy + c->h - oh; + XWarpPointer(dpy, None, None, 0, 0, 0, 0, nmx, nmy); + } +} + diff --git a/patch/moveresize.h b/patch/moveresize.h new file mode 100644 index 0000000..919ebad --- /dev/null +++ b/patch/moveresize.h @@ -0,0 +1,2 @@ +static void moveresize(const Arg *arg); + diff --git a/patch/movestack.c b/patch/movestack.c new file mode 100644 index 0000000..fe97f1d --- /dev/null +++ b/patch/movestack.c @@ -0,0 +1,51 @@ +void +movestack(const Arg *arg) +{ + Client *c = NULL, *p = NULL, *pc = NULL, *i; + if (arg->i > 0) { + if (!selmon->sel) + return; + /* find the client after selmon->sel */ + for (c = selmon->sel->next; c && (!ISVISIBLE(c) || c->isfloating); c = c->next); + if (!c) + for (c = selmon->clients; c && (!ISVISIBLE(c) || c->isfloating); c = c->next); + } + else { + /* find the client before selmon->sel */ + for (i = selmon->clients; i != selmon->sel; i = i->next) + if(ISVISIBLE(i) && !i->isfloating) + c = i; + if (!c) + for (; i; i = i->next) + if (ISVISIBLE(i) && !i->isfloating) + c = i; + } + + /* find the client before selmon->sel and c */ + for (i = selmon->clients; i && (!p || !pc); i = i->next) { + if (i->next == selmon->sel) + p = i; + if (i->next == c) + pc = i; + } + + /* swap c and selmon->sel selmon->clients in the selmon->clients list */ + if (c && c != selmon->sel) { + Client *temp = selmon->sel->next==c?selmon->sel:selmon->sel->next; + selmon->sel->next = c->next==selmon->sel?c:c->next; + c->next = temp; + + if (p && p != c) + p->next = c; + if (pc && pc != selmon->sel) + pc->next = selmon->sel; + + if (selmon->sel == selmon->clients) + selmon->clients = c; + else if (c == selmon->clients) + selmon->clients = selmon->sel; + + arrange(selmon); + } +} + diff --git a/patch/movestack.h b/patch/movestack.h new file mode 100644 index 0000000..25f198f --- /dev/null +++ b/patch/movestack.h @@ -0,0 +1,2 @@ +static void movestack(const Arg *arg); + diff --git a/patch/mpdcontrol.c b/patch/mpdcontrol.c new file mode 100644 index 0000000..fc81e07 --- /dev/null +++ b/patch/mpdcontrol.c @@ -0,0 +1,145 @@ +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <regex.h> + +#include <mpd/client.h> + +#define MPDHOST "localhost" +#define MPDPORT 6600 + +struct mpd_connection *get_conn() +{ + struct mpd_connection *conn; + + conn = mpd_connection_new(MPDHOST, MPDPORT, 1000); + + if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS) { + fprintf(stderr, "Could not connect to mpd: %s\n", mpd_connection_get_error_message(conn)); + + mpd_connection_free(conn); + return NULL; + } + + return conn; +} + +void mpdchange(const Arg *direction) +{ + struct mpd_connection *conn; + + conn = get_conn(); + + if (conn == NULL) { + return; + } + + if (direction->i > 0) { + mpd_run_next(conn); + } + else { + mpd_run_previous(conn); + } + + mpd_connection_free(conn); +} + +char *get_regerror(int errcode, regex_t *compiled) +{ + size_t length = regerror(errcode, compiled, NULL, 0); + char *buffer = malloc(length); + (void) regerror(errcode, compiled, buffer, length); + + return buffer; +} + +void mpdcontrol() +{ + struct mpd_connection *conn; + struct mpd_status *status; + struct mpd_song *song; + enum mpd_state state; + + const char *filename; + + regex_t expr; + + conn = get_conn(); + + if (conn == NULL) { + return; + } + + status = mpd_run_status(conn); + + if (status == NULL) { + fprintf(stderr, "Could not get mpd status: %s\n", mpd_status_get_error(status)); + + mpd_status_free(status); + mpd_connection_free(conn); + return; + } + + state = mpd_status_get_state(status); + + if (state == MPD_STATE_STOP || state == MPD_STATE_PAUSE) { + mpd_run_play(conn); + mpd_status_free(status); + mpd_connection_free(conn); + } + else if (state != MPD_STATE_UNKNOWN) { //playing some music + song = mpd_run_current_song(conn); + + if (song == NULL){ + fprintf(stderr, "Error fetching current song!\n"); + + mpd_song_free(song); + mpd_status_free(status); + mpd_connection_free(conn); + return; + } + + filename = mpd_song_get_uri(song); + + int errcode = regcomp(&expr, "^[[:alnum:]]+://", REG_EXTENDED|REG_NOSUB); + if (errcode != 0) { + char *err = get_regerror(errcode, &expr); + fprintf(stderr, "Could not compile regexp: %s\n", err); + + mpd_song_free(song); + mpd_status_free(status); + mpd_connection_free(conn); + free(err); + regfree(&expr); + return; + } + + int matchcode = regexec(&expr, filename, 0, NULL, 0); + + if (matchcode == 0) { + if (strstr(filename, "file://") == filename) { //match just at the start of the filename + //this means that mpd is playing a file outside the music_dir, + //but on disk, so we can safely pause + mpd_run_toggle_pause(conn); + } + else { + mpd_run_stop(conn); + } + } + else if (matchcode == REG_NOMATCH) { + mpd_run_toggle_pause(conn); + } + else { + char *err = get_regerror(matchcode, &expr); + fprintf(stderr, "Error while matching regexp: %s\n", err); + + free(err); + } + + regfree(&expr); + mpd_song_free(song); + mpd_status_free(status); + mpd_connection_free(conn); + } +} + diff --git a/patch/mpdcontrol.h b/patch/mpdcontrol.h new file mode 100644 index 0000000..1ce304e --- /dev/null +++ b/patch/mpdcontrol.h @@ -0,0 +1,3 @@ +static void mpdchange(const Arg *direction); +static void mpdcontrol(); + diff --git a/patch/nomodbuttons.c b/patch/nomodbuttons.c new file mode 100644 index 0000000..9c196de --- /dev/null +++ b/patch/nomodbuttons.c @@ -0,0 +1,8 @@ +void +togglenomodbuttons(const Arg *arg) +{ + nomodbuttons = !nomodbuttons; + if (selmon->sel) + grabbuttons(selmon->sel, 1); +} + diff --git a/patch/nomodbuttons.h b/patch/nomodbuttons.h new file mode 100644 index 0000000..a613ed4 --- /dev/null +++ b/patch/nomodbuttons.h @@ -0,0 +1,2 @@ +static void togglenomodbuttons(const Arg *arg); + diff --git a/patch/pertag.c b/patch/pertag.c new file mode 100644 index 0000000..f534023 --- /dev/null +++ b/patch/pertag.c @@ -0,0 +1,78 @@ +struct Pertag { + unsigned int curtag, prevtag; /* current and previous tag */ + int nmasters[NUMTAGS + 1]; /* number of windows in master area */ + #if FLEXTILE_DELUXE_LAYOUT + int nstacks[NUMTAGS + 1]; /* number of windows in primary stack area */ + int ltaxis[NUMTAGS + 1][LTAXIS_LAST]; + const Layout *ltidxs[NUMTAGS + 1][3]; /* matrix of tags and layouts indexes */ + #else + const Layout *ltidxs[NUMTAGS + 1][2]; /* matrix of tags and layouts indexes */ + #endif // FLEXTILE_DELUXE_LAYOUT + float mfacts[NUMTAGS + 1]; /* mfacts per tag */ + unsigned int sellts[NUMTAGS + 1]; /* selected layouts */ + #if PERTAGBAR_PATCH + int showbars[NUMTAGS + 1]; /* display bar for the current tag */ + #endif // PERTAGBAR_PATCH + #if SWAPFOCUS_PATCH + Client *prevclient[NUMTAGS + 1]; + #endif // SWAPFOCUS_PATCH + #if ZOOMSWAP_PATCH + Client *prevzooms[NUMTAGS + 1]; /* store zoom information */ + #endif // ZOOMSWAP_PATCH + #if PERTAG_VANITYGAPS_PATCH && VANITYGAPS_PATCH + int enablegaps[NUMTAGS + 1]; + unsigned int gaps[NUMTAGS + 1]; + #endif // PERTAG_VANITYGAPS_PATCH | VANITYGAPS_PATCH +}; + +void +pertagview(const Arg *arg) +{ + int i; + unsigned int tmptag; + if (arg->ui & TAGMASK) { + selmon->pertag->prevtag = selmon->pertag->curtag; + selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; + #if SCRATCHPADS_PATCH + if (arg->ui == ~SPTAGMASK) + #else + if (arg->ui == ~0) + #endif // SCRATCHPADS_PATCH + selmon->pertag->curtag = 0; + else { + for (i = 0; !(arg->ui & 1 << i); i++) ; + selmon->pertag->curtag = i + 1; + } + } else { + tmptag = selmon->pertag->prevtag; + selmon->pertag->prevtag = selmon->pertag->curtag; + selmon->pertag->curtag = tmptag; + } + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; + #if FLEXTILE_DELUXE_LAYOUT + selmon->nstack = selmon->pertag->nstacks[selmon->pertag->curtag]; + #endif // FLEXTILE_DELUXE_LAYOUT + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; + selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; + + #if PERTAG_VANITYGAPS_PATCH && VANITYGAPS_PATCH + selmon->gappoh = (selmon->pertag->gaps[selmon->pertag->curtag] & 0xff) >> 0; + selmon->gappov = (selmon->pertag->gaps[selmon->pertag->curtag] & 0xff00) >> 8; + selmon->gappih = (selmon->pertag->gaps[selmon->pertag->curtag] & 0xff0000) >> 16; + selmon->gappiv = (selmon->pertag->gaps[selmon->pertag->curtag] & 0xff000000) >> 24; + #endif // PERTAG_VANITYGAPS_PATCH | VANITYGAPS_PATCH + + #if FLEXTILE_DELUXE_LAYOUT + selmon->ltaxis[LAYOUT] = selmon->pertag->ltaxis[selmon->pertag->curtag][LAYOUT]; + selmon->ltaxis[MASTER] = selmon->pertag->ltaxis[selmon->pertag->curtag][MASTER]; + selmon->ltaxis[STACK] = selmon->pertag->ltaxis[selmon->pertag->curtag][STACK]; + selmon->ltaxis[STACK2] = selmon->pertag->ltaxis[selmon->pertag->curtag][STACK2]; + #endif // FLEXTILE_DELUXE_LAYOUT + #if PERTAGBAR_PATCH + if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) + togglebar(NULL); + #endif // PERTAGBAR_PATCH +} + diff --git a/patch/pertag.h b/patch/pertag.h new file mode 100644 index 0000000..5c53ac9 --- /dev/null +++ b/patch/pertag.h @@ -0,0 +1,2 @@ +static void pertagview(const Arg *arg); + diff --git a/patch/placemouse.c b/patch/placemouse.c new file mode 100644 index 0000000..0208baf --- /dev/null +++ b/patch/placemouse.c @@ -0,0 +1,153 @@ +void +moveorplace(const Arg *arg) { + if ((!selmon->lt[selmon->sellt]->arrange || (selmon->sel && selmon->sel->isfloating))) + movemouse(arg); + else + placemouse(arg); +} + +void +placemouse(const Arg *arg) +{ + int x, y, px, py, ocx, ocy, nx = -9999, ny = -9999, freemove = 0; + Client *c, *r = NULL, *at, *prevr; + Monitor *m; + XEvent ev; + XWindowAttributes wa; + Time lasttime = 0; + int attachmode, prevattachmode; + attachmode = prevattachmode = -1; + + if (!(c = selmon->sel) || !c->mon->lt[c->mon->sellt]->arrange) /* no support for placemouse when floating layout is used */ + return; + if (c->isfullscreen) /* no support placing fullscreen windows by mouse */ + return; + restack(selmon); + prevr = c; + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) + return; + + c->isfloating = 0; + c->beingmoved = 1; + + XGetWindowAttributes(dpy, c->win, &wa); + ocx = wa.x; + ocy = wa.y; + + if (arg->i == 2) // warp cursor to client center + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, WIDTH(c) / 2, HEIGHT(c) / 2); + + if (!getrootptr(&x, &y)) + return; + + do { + XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); + switch (ev.type) { + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + if ((ev.xmotion.time - lasttime) <= (1000 / 60)) + continue; + lasttime = ev.xmotion.time; + + nx = ocx + (ev.xmotion.x - x); + ny = ocy + (ev.xmotion.y - y); + + if (!freemove && (abs(nx - ocx) > snap || abs(ny - ocy) > snap)) + freemove = 1; + + if (freemove) + XMoveWindow(dpy, c->win, nx, ny); + + if ((m = recttomon(ev.xmotion.x, ev.xmotion.y, 1, 1)) && m != selmon) + selmon = m; + + if (arg->i == 1) { // tiled position is relative to the client window center point + px = nx + wa.width / 2; + py = ny + wa.height / 2; + } else { // tiled position is relative to the mouse cursor + px = ev.xmotion.x; + py = ev.xmotion.y; + } + + r = recttoclient(px, py, 1, 1); + + if (!r || r == c) + break; + + if ((((float)(r->y + r->h - py) / r->h) > ((float)(r->x + r->w - px) / r->w) + && (abs(r->y - py) < r->h / 2)) || (abs(r->x - px) < r->w / 2)) + attachmode = 1; // above + else + attachmode = 0; // below + + if ((r && r != prevr) || (attachmode != prevattachmode)) { + detachstack(c); + detach(c); + if (c->mon != r->mon) + arrangemon(c->mon); + + c->mon = r->mon; + r->mon->sel = r; + + if (attachmode) { + if (r == r->mon->clients) + attach(c); + else { + for (at = r->mon->clients; at->next != r; at = at->next); + c->next = at->next; + at->next = c; + } + } else { + c->next = r->next; + r->next = c; + } + + attachstack(c); + arrangemon(r->mon); + prevr = r; + prevattachmode = attachmode; + } + break; + } + } while (ev.type != ButtonRelease); + XUngrabPointer(dpy, CurrentTime); + + if ((m = recttomon(ev.xmotion.x, ev.xmotion.y, 1, 1)) && m != c->mon) { + detach(c); + detachstack(c); + arrangemon(c->mon); + c->mon = m; + c->tags = m->tagset[m->seltags]; + attach(c); + attachstack(c); + selmon = m; + } + + focus(c); + c->beingmoved = 0; + + if (nx != -9999) + resize(c, nx, ny, c->w, c->h, 0); + arrangemon(c->mon); +} + +Client * +recttoclient(int x, int y, int w, int h) +{ + Client *c, *r = NULL; + int a, area = 0; + + for (c = nexttiled(selmon->clients); c; c = nexttiled(c->next)) { + if ((a = INTERSECTC(x, y, w, h, c)) > area) { + area = a; + r = c; + } + } + return r; +} + diff --git a/patch/placemouse.h b/patch/placemouse.h new file mode 100644 index 0000000..20c3eca --- /dev/null +++ b/patch/placemouse.h @@ -0,0 +1,7 @@ +#define INTERSECTC(x,y,w,h,z) (MAX(0, MIN((x)+(w),(z)->x+(z)->w) - MAX((x),(z)->x)) \ + * MAX(0, MIN((y)+(h),(z)->y+(z)->h) - MAX((y),(z)->y))) + +static void moveorplace(const Arg *arg); +static void placemouse(const Arg *arg); +static Client *recttoclient(int x, int y, int w, int h); + diff --git a/patch/push.c b/patch/push.c new file mode 100644 index 0000000..9410e61 --- /dev/null +++ b/patch/push.c @@ -0,0 +1,72 @@ +static Client * +nextc(Client *c, float f) +{ + if (!f) + return nexttiled(c); + + for (; c && !ISVISIBLE(c); c = c->next); + return c; +} + +static Client * +prevc(Client *c, float f) +{ + Client *p, *r; + + for (p = selmon->clients, r = NULL; c && p && p != c; p = p->next) + if ((f || !p->isfloating) && ISVISIBLE(p)) + r = p; + return r; +} + +static void +pushup(const Arg *arg) +{ + Client *sel = selmon->sel; + Client *c; + + if (!sel || (sel->isfloating && !arg->f)) + return; + if ((c = prevc(sel, arg->f))) { + /* attach before c */ + detach(sel); + sel->next = c; + if (selmon->clients == c) + selmon->clients = sel; + else { + for (c = selmon->clients; c->next != sel->next; c = c->next); + c->next = sel; + } + } else { + /* move to the end */ + for (c = sel; c->next; c = c->next); + detach(sel); + sel->next = NULL; + c->next = sel; + } + focus(sel); + arrange(selmon); +} + +static void +pushdown(const Arg *arg) +{ + Client *sel = selmon->sel; + Client *c; + + if (!sel || (sel->isfloating && !arg->f)) + return; + if ((c = nextc(sel->next, arg->f))) { + /* attach after c */ + detach(sel); + sel->next = c->next; + c->next = sel; + } else { + /* move to the front */ + detach(sel); + attach(sel); + } + focus(sel); + arrange(selmon); +} + diff --git a/patch/push.h b/patch/push.h new file mode 100644 index 0000000..6415f80 --- /dev/null +++ b/patch/push.h @@ -0,0 +1,5 @@ +static Client * nextc(Client *c, float f); +static Client * prevc(Client *c, float f); +static void pushup(const Arg *arg); +static void pushdown(const Arg *arg); + diff --git a/patch/push_no_master.c b/patch/push_no_master.c new file mode 100644 index 0000000..2d78cf4 --- /dev/null +++ b/patch/push_no_master.c @@ -0,0 +1,44 @@ +Client * +prevt(Client *c) +{ + Client *p, *r; + + for (p = selmon->clients, r = NULL; p && p != c; p = p->next) + if (!p->isfloating && ISVISIBLE(p)) + r = p; + return r; +} + +void +pushup(const Arg *arg) +{ + Client *sel = selmon->sel, *c; + + if (!sel || sel->isfloating) + return; + if ((c = prevt(sel)) && c != nexttiled(selmon->clients)) { + detach(sel); + sel->next = c; + for (c = selmon->clients; c->next != sel->next; c = c->next); + c->next = sel; + } + focus(sel); + arrange(selmon); +} + +void +pushdown(const Arg *arg) +{ + Client *sel = selmon->sel, *c; + + if (!sel || sel->isfloating || sel == nexttiled(selmon->clients)) + return; + if ((c = nexttiled(sel->next))) { + detach(sel); + sel->next = c->next; + c->next = sel; + } + focus(sel); + arrange(selmon); +} + diff --git a/patch/push_no_master.h b/patch/push_no_master.h new file mode 100644 index 0000000..6904fb0 --- /dev/null +++ b/patch/push_no_master.h @@ -0,0 +1,4 @@ +Client * prevt(Client *c); +static void pushup(const Arg *arg); +static void pushdown(const Arg *arg); + diff --git a/patch/reorganizetags.c b/patch/reorganizetags.c new file mode 100644 index 0000000..1aff54a --- /dev/null +++ b/patch/reorganizetags.c @@ -0,0 +1,28 @@ +void +reorganizetags(const Arg *arg) +{ + Client *c; + unsigned int occ, unocc, i; + unsigned int tagdest[NUMTAGS]; + + occ = 0; + for (c = selmon->clients; c; c = c->next) + occ |= (1 << (ffs(c->tags)-1)); + unocc = 0; + for (i = 0; i < NUMTAGS; ++i) { + while (unocc < i && (occ & (1 << unocc))) + unocc++; + if (occ & (1 << i)) { + tagdest[i] = unocc; + occ &= ~(1 << i); + occ |= 1 << unocc; + } + } + + for (c = selmon->clients; c; c = c->next) + c->tags = 1 << tagdest[ffs(c->tags)-1]; + if (selmon->sel) + selmon->tagset[selmon->seltags] = selmon->sel->tags; + arrange(selmon); +} + diff --git a/patch/reorganizetags.h b/patch/reorganizetags.h new file mode 100644 index 0000000..c4917af --- /dev/null +++ b/patch/reorganizetags.h @@ -0,0 +1,2 @@ +static void reorganizetags(const Arg *arg); + diff --git a/patch/restartsig.c b/patch/restartsig.c new file mode 100644 index 0000000..adb61b5 --- /dev/null +++ b/patch/restartsig.c @@ -0,0 +1,16 @@ +static int restart = 0; + +void +sighup(int unused) +{ + Arg a = {.i = 1}; + quit(&a); +} + +void +sigterm(int unused) +{ + Arg a = {.i = 0}; + quit(&a); +} + diff --git a/patch/restartsig.h b/patch/restartsig.h new file mode 100644 index 0000000..b16975b --- /dev/null +++ b/patch/restartsig.h @@ -0,0 +1,3 @@ +static void sighup(int unused); +static void sigterm(int unused); + diff --git a/patch/riodraw.c b/patch/riodraw.c new file mode 100644 index 0000000..c430f5c --- /dev/null +++ b/patch/riodraw.c @@ -0,0 +1,104 @@ +/* drag out an area using slop and resize the selected window to it. */ +int +riodraw(Client *c, const char slopstyle[]) +{ + int i; + char str[100]; + char strout[100]; + char tmpstring[30] = {0}; + char slopcmd[100] = "slop -f x%xx%yx%wx%hx "; + int firstchar = 0; + int counter = 0; + + strcat(slopcmd, slopstyle); + FILE *fp = popen(slopcmd, "r"); + + while (fgets(str, 100, fp) != NULL) + strcat(strout, str); + + pclose(fp); + + if (strlen(strout) < 6) + return 0; + + for (i = 0; i < strlen(strout); i++){ + if (!firstchar) { + if (strout[i] == 'x') + firstchar = 1; + continue; + } + + if (strout[i] != 'x') + tmpstring[strlen(tmpstring)] = strout[i]; + else { + riodimensions[counter] = atoi(tmpstring); + counter++; + memset(tmpstring,0,strlen(tmpstring)); + } + } + + if (riodimensions[0] <= -40 || riodimensions[1] <= -40 || riodimensions[2] <= 50 || riodimensions[3] <= 50) { + riodimensions[3] = -1; + return 0; + } + + if (c) { + rioposition(c, riodimensions[0], riodimensions[1], riodimensions[2], riodimensions[3]); + return 0; + } + + return 1; +} + +void +rioposition(Client *c, int x, int y, int w, int h) +{ + Monitor *m; + if ((m = recttomon(x, y, w, h)) && m != c->mon) { + detach(c); + detachstack(c); + c->mon = m; + c->tags = m->tagset[m->seltags]; + attach(c); + attachstack(c); + selmon = m; + focus(c); + } + + c->isfloating = 1; + if (riodraw_borders) + resizeclient(c, x, y, w - (c->bw * 2), h - (c->bw * 2)); + else + resizeclient(c, x - c->bw, y - c->bw, w, h); + drawbar(c->mon); + arrange(c->mon); + + riodimensions[3] = -1; + riopid = 0; +} + +/* drag out an area using slop and resize the selected window to it */ +void +rioresize(const Arg *arg) +{ + Client *c = (arg && arg->v ? (Client*)arg->v : selmon->sel); + if (c) + riodraw(c, slopresizestyle); +} + +/* Spawn a new window and drag out an area using slop to position it while the window is + * initialising in the background. */ +void +riospawn(const Arg *arg) +{ + riopid = spawncmd(arg); + riodraw(NULL, slopspawnstyle); +} + +void +riospawnsync(const Arg *arg) +{ + if (riodraw(NULL, slopspawnstyle)) + riopid = spawncmd(arg); +} + diff --git a/patch/riodraw.h b/patch/riodraw.h new file mode 100644 index 0000000..7756aeb --- /dev/null +++ b/patch/riodraw.h @@ -0,0 +1,6 @@ +static int riodraw(Client *c, const char slopstyle[]); +static void rioposition(Client *c, int x, int y, int w, int h); +static void rioresize(const Arg *arg); +static void riospawn(const Arg *arg); +static void riospawnsync(const Arg *arg); + diff --git a/patch/rotatestack.c b/patch/rotatestack.c new file mode 100644 index 0000000..41df8b1 --- /dev/null +++ b/patch/rotatestack.c @@ -0,0 +1,53 @@ +void +enqueue(Client *c) +{ + Client *l; + for (l = c->mon->clients; l && l->next; l = l->next); + if (l) { + l->next = c; + c->next = NULL; + } +} + +void +enqueuestack(Client *c) +{ + Client *l; + for (l = c->mon->stack; l && l->snext; l = l->snext); + if (l) { + l->snext = c; + c->snext = NULL; + } +} + +void +rotatestack(const Arg *arg) +{ + Client *c = NULL, *f; + + if (!selmon->sel) + return; + f = selmon->sel; + if (arg->i > 0) { + for (c = nexttiled(selmon->clients); c && nexttiled(c->next); c = nexttiled(c->next)); + if (c){ + detach(c); + attach(c); + detachstack(c); + attachstack(c); + } + } else { + if ((c = nexttiled(selmon->clients))){ + detach(c); + enqueue(c); + detachstack(c); + enqueuestack(c); + } + } + if (c){ + arrange(selmon); + focus(f); + restack(selmon); + } +} + diff --git a/patch/rotatestack.h b/patch/rotatestack.h new file mode 100644 index 0000000..ea64e68 --- /dev/null +++ b/patch/rotatestack.h @@ -0,0 +1,4 @@ +static void enqueue(Client *c); +static void enqueuestack(Client *c); +static void rotatestack(const Arg *arg); + diff --git a/patch/roundedcorners.c b/patch/roundedcorners.c new file mode 100644 index 0000000..881dbab --- /dev/null +++ b/patch/roundedcorners.c @@ -0,0 +1,51 @@ +#include <X11/extensions/shape.h> + +void drawroundedcorners(Client *c) +{ + if (corner_radius <= 0 || !c || c->isfullscreen) + return; + + Window win; + win = c->win; + if (!win) + return; + + XWindowAttributes win_attr; + if (!XGetWindowAttributes(dpy, win, &win_attr)) + return; + + int dia = 2 * corner_radius; + int w = c->w; + int h = c->h; + if (w < dia || h < dia) + return; + + Pixmap mask; + mask = XCreatePixmap(dpy, win, w, h, 1); + if (!mask) + return; + + XGCValues xgcv; + GC shape_gc; + shape_gc = XCreateGC(dpy, mask, 0, &xgcv); + + if (!shape_gc) { + XFreePixmap(dpy, mask); + free(shape_gc); + return; + } + + XSetForeground(dpy, shape_gc, 0); + XFillRectangle(dpy, mask, shape_gc, 0, 0, w, h); + XSetForeground(dpy, shape_gc, 1); + XFillArc(dpy, mask, shape_gc, 0, 0, dia, dia, 0, 23040); + XFillArc(dpy, mask, shape_gc, w-dia-1, 0, dia, dia, 0, 23040); + XFillArc(dpy, mask, shape_gc, 0, h-dia-1, dia, dia, 0, 23040); + XFillArc(dpy, mask, shape_gc, w-dia-1, h-dia-1, dia, dia, 0, 23040); + XFillRectangle(dpy, mask, shape_gc, corner_radius, 0, w-dia, h); + XFillRectangle(dpy, mask, shape_gc, 0, corner_radius, w, h-dia); + XShapeCombineMask(dpy, win, ShapeBounding, 0, 0, mask, ShapeSet); + XFreePixmap(dpy, mask); + XFreeGC(dpy, shape_gc); +} + diff --git a/patch/roundedcorners.h b/patch/roundedcorners.h new file mode 100644 index 0000000..31a1f31 --- /dev/null +++ b/patch/roundedcorners.h @@ -0,0 +1,2 @@ +static void drawroundedcorners(Client *c); + diff --git a/patch/scratchpad.c b/patch/scratchpad.c new file mode 100644 index 0000000..9e24ff6 --- /dev/null +++ b/patch/scratchpad.c @@ -0,0 +1,77 @@ +void +removescratch(const Arg *arg) +{ + Client *c = selmon->sel; + if (!c) + return; + unsigned int scratchtag = SPTAG(arg->ui); + c->tags = c->mon->tagset[c->mon->seltags] ^ scratchtag; + arrange(c->mon); +} + +void +setscratch(const Arg *arg) +{ + Client *c = selmon->sel; + if (!c) + return; + unsigned int scratchtag = SPTAG(arg->ui); + c->tags = scratchtag; + arrange(c->mon); +} + +void +togglescratch(const Arg *arg) +{ + Client *c = NULL, *next = NULL, *found = NULL; + Monitor *mon; + unsigned int scratchtag = SPTAG(arg->ui); + unsigned int newtagset = 0; + int nh = 0, nw = 0; + Arg sparg = {.v = scratchpads[arg->ui].cmd}; + + for (mon = mons; mon; mon = mon->next) { + for (c = mon->clients; c; c = next) { + next = c->next; + if (!(c->tags & scratchtag)) + continue; + + found = c; + + if (HIDDEN(c)) { + XMapWindow(dpy, c->win); + setclientstate(c, NormalState); + newtagset = 0; + } else + newtagset = selmon->tagset[selmon->seltags] ^ scratchtag; + + if (c->mon != selmon) { + if (c->mon->tagset[c->mon->seltags] & SPTAGMASK) + c->mon->tagset[c->mon->seltags] ^= scratchtag; + if (c->w > selmon->ww) + nw = selmon->ww - c->bw * 2; + if (c->h > selmon->wh) + nh = selmon->wh - c->bw * 2; + if (nw > 0 || nh > 0) + resizeclient(c, c->x, c->y, nw ? nw : c->w, nh ? nh : c->h); + sendmon(c, selmon); + } + } + } + + if (found) { + if (newtagset) { + selmon->tagset[selmon->seltags] = newtagset; + focus(NULL); + arrange(selmon); + } + if (ISVISIBLE(found)) { + focus(found); + restack(selmon); + } + } else { + selmon->tagset[selmon->seltags] |= scratchtag; + spawn(&sparg); + } +} + diff --git a/patch/scratchpad.h b/patch/scratchpad.h new file mode 100644 index 0000000..6230266 --- /dev/null +++ b/patch/scratchpad.h @@ -0,0 +1,9 @@ +typedef struct { + const char *name; + const void *cmd; +} Sp; + +static void removescratch(const Arg *arg); +static void setscratch(const Arg *arg); +static void togglescratch(const Arg *arg); + diff --git a/patch/scratchpad_alt_1.c b/patch/scratchpad_alt_1.c new file mode 100644 index 0000000..6724d5c --- /dev/null +++ b/patch/scratchpad_alt_1.c @@ -0,0 +1,72 @@ +static Client * scratchpad_last_showed = NULL; + +void +scratchpad_hide() +{ + if (selmon->sel) { + selmon->sel->tags = SCRATCHPAD_MASK; + selmon->sel->isfloating = 1; + focus(NULL); + arrange(selmon); + } +} + +_Bool +scratchpad_last_showed_is_killed(void) +{ + Client *c; + for (c = selmon->clients; c && c != scratchpad_last_showed; c = c->next); + return (c == NULL); +} + +void +scratchpad_remove() +{ + if (selmon->sel && scratchpad_last_showed != NULL && selmon->sel == scratchpad_last_showed) + scratchpad_last_showed = NULL; +} + +void +scratchpad_show() +{ + if (scratchpad_last_showed == NULL || scratchpad_last_showed_is_killed()) { + scratchpad_show_first(); + return; + } + + if (scratchpad_last_showed->tags != SCRATCHPAD_MASK) { + scratchpad_last_showed->tags = SCRATCHPAD_MASK; + focus(NULL); + arrange(selmon); + return; + } + + Client *c; + + for (c = selmon->clients; c && c != scratchpad_last_showed; c = c->next); + for (c = (c ? c->next : NULL); c && c->tags != SCRATCHPAD_MASK; c = c->next); + + if (c) + scratchpad_show_client(c); + else + scratchpad_show_first(); +} + +void +scratchpad_show_client(Client* c) +{ + scratchpad_last_showed = c; + c->tags = selmon->tagset[selmon->seltags]; + focus(c); + arrange(selmon); +} + +void +scratchpad_show_first(void) +{ + Client *c; + for (c = selmon->clients; c && c->tags != SCRATCHPAD_MASK; c = c->next); + if (c) + scratchpad_show_client(c); +} + diff --git a/patch/scratchpad_alt_1.h b/patch/scratchpad_alt_1.h new file mode 100644 index 0000000..4e25f11 --- /dev/null +++ b/patch/scratchpad_alt_1.h @@ -0,0 +1,9 @@ +#define SCRATCHPAD_MASK (1u << NUMTAGS) + +static void scratchpad_hide(); +static _Bool scratchpad_last_showed_is_killed(void); +static void scratchpad_remove(); +static void scratchpad_show(); +static void scratchpad_show_client(Client *c); +static void scratchpad_show_first(void); + diff --git a/patch/selfrestart.c b/patch/selfrestart.c new file mode 100644 index 0000000..f733867 --- /dev/null +++ b/patch/selfrestart.c @@ -0,0 +1,69 @@ +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <stdlib.h> + +/** + * Magically finds the current's executable path + * + * I'm doing the do{}while(); trick because Linux (what I'm running) is not + * POSIX compilant and so lstat() cannot be trusted on /proc entries + * + * @return char* the path of the current executable + */ +char *get_dwm_path() +{ + struct stat s; + int r, length, rate = 42; + char *path = NULL; + + if (lstat("/proc/self/exe", &s) == -1) { + perror("lstat:"); + return NULL; + } + + length = s.st_size + 1 - rate; + + do + { + length+=rate; + + free(path); + path = malloc(sizeof(char) * length); + + if (path == NULL){ + perror("malloc:"); + return NULL; + } + + r = readlink("/proc/self/exe", path, length); + + if (r == -1){ + perror("readlink:"); + return NULL; + } + } while (r >= length); + + path[r] = '\0'; + + return path; +} + +/** + * self-restart + * + * Initially inspired by: Yu-Jie Lin + * https://sites.google.com/site/yjlnotes/notes/dwm + */ +void self_restart(const Arg *arg) +{ + char *const argv[] = {get_dwm_path(), NULL}; + + if (argv[0] == NULL) { + return; + } + + execv(argv[0], argv); +} + diff --git a/patch/selfrestart.h b/patch/selfrestart.h new file mode 100644 index 0000000..3fd0b81 --- /dev/null +++ b/patch/selfrestart.h @@ -0,0 +1,3 @@ +char *get_dwm_path(); +void self_restart(const Arg *arg); + diff --git a/patch/setborderpx.c b/patch/setborderpx.c new file mode 100644 index 0000000..948d6f6 --- /dev/null +++ b/patch/setborderpx.c @@ -0,0 +1,45 @@ +void +setborderpx(const Arg *arg) +{ + Client *c; + int prev_borderpx = selmon->borderpx; + + if (arg->i == 0) + selmon->borderpx = borderpx; + else if (selmon->borderpx + arg->i < 0) + selmon->borderpx = 0; + else + selmon->borderpx += arg->i; + + #if BAR_BORDER_PATCH + for (bar = selmon->bar; bar; bar = bar->next) { + bar->bh = bar->bh - 2 * bar->borderpx + 2 * selmon->borderpx; + bar->borderpx = selmon->borderpx; + } + updatebarpos(selmon); + for (bar = selmon->bar; bar; bar = bar->next) + XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + #endif // BAR_BORDER_PATCH + + for (c = selmon->clients; c; c = c->next) + { + if (c->bw + arg->i < 0) + c->bw = 0; + else + c->bw = selmon->borderpx; + + if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) + { + if (arg->i != 0 && prev_borderpx + arg->i >= 0) + resize(c, c->x, c->y, c->w-(arg->i*2), c->h-(arg->i*2), 0); + else if (arg->i != 0) + resizeclient(c, c->x, c->y, c->w, c->h); + else if (prev_borderpx > borderpx) + resize(c, c->x, c->y, c->w + 2*(prev_borderpx - borderpx), c->h + 2*(prev_borderpx - borderpx), 0); + else if (prev_borderpx < borderpx) + resize(c, c->x, c->y, c->w - 2*(borderpx - prev_borderpx), c->h - 2*(borderpx - prev_borderpx), 0); + } + } + arrange(selmon); +} + diff --git a/patch/setborderpx.h b/patch/setborderpx.h new file mode 100644 index 0000000..a92ae0b --- /dev/null +++ b/patch/setborderpx.h @@ -0,0 +1,2 @@ +static void setborderpx(const Arg *arg); + diff --git a/patch/shiftview.c b/patch/shiftview.c new file mode 100644 index 0000000..423d74b --- /dev/null +++ b/patch/shiftview.c @@ -0,0 +1,19 @@ +void +shiftview(const Arg *arg) +{ + Arg shifted; + #if SCRATCHPADS_PATCH + unsigned int seltagset = selmon->tagset[selmon->seltags] & ~SPTAGMASK; + #else + unsigned int seltagset = selmon->tagset[selmon->seltags]; + #endif // SCRATCHPADS_PATCH + if (arg->i > 0) // left circular shift + shifted.ui = (seltagset << arg->i) + | (seltagset >> (NUMTAGS - arg->i)); + else // right circular shift + shifted.ui = seltagset >> -arg->i + | seltagset << (NUMTAGS + arg->i); + + view(&shifted); +} + diff --git a/patch/shiftview.h b/patch/shiftview.h new file mode 100644 index 0000000..1d7fd1f --- /dev/null +++ b/patch/shiftview.h @@ -0,0 +1,2 @@ +static void shiftview(const Arg *arg); + diff --git a/patch/shiftviewclients.c b/patch/shiftviewclients.c new file mode 100644 index 0000000..1bf9de8 --- /dev/null +++ b/patch/shiftviewclients.c @@ -0,0 +1,43 @@ +void +shiftviewclients(const Arg *arg) +{ + Arg shifted; + Client *c; + unsigned int tagmask = 0; + + for (c = selmon->clients; c; c = c->next) + #if SCRATCHPADS_PATCH + if (!(c->tags & SPTAGMASK)) + tagmask = tagmask | c->tags; + #elif SCRATCHPAD_ALT_1_PATCH + if (!(c->tags & SCRATCHPAD_MASK)) + tagmask = tagmask | c->tags; + #else + tagmask = tagmask | c->tags; + #endif // SCRATCHPADS_PATCH + + #if SCRATCHPADS_PATCH + shifted.ui = selmon->tagset[selmon->seltags] & ~SPTAGMASK; + #else + shifted.ui = selmon->tagset[selmon->seltags]; + #endif // SCRATCHPADS_PATCH + if (arg->i > 0) // left circular shift + do { + shifted.ui = (shifted.ui << arg->i) + | (shifted.ui >> (NUMTAGS - arg->i)); + #if SCRATCHPADS_PATCH + shifted.ui &= ~SPTAGMASK; + #endif // SCRATCHPADS_PATCH + } while (tagmask && !(shifted.ui & tagmask)); + else // right circular shift + do { + shifted.ui = (shifted.ui >> (- arg->i) + | shifted.ui << (NUMTAGS + arg->i)); + #if SCRATCHPADS_PATCH + shifted.ui &= ~SPTAGMASK; + #endif // SCRATCHPADS_PATCH + } while (tagmask && !(shifted.ui & tagmask)); + + view(&shifted); +} + diff --git a/patch/shiftviewclients.h b/patch/shiftviewclients.h new file mode 100644 index 0000000..49aeb51 --- /dev/null +++ b/patch/shiftviewclients.h @@ -0,0 +1,2 @@ +static void shiftviewclients(const Arg *arg); + diff --git a/patch/sizehints_ruled.c b/patch/sizehints_ruled.c new file mode 100644 index 0000000..99c7e62 --- /dev/null +++ b/patch/sizehints_ruled.c @@ -0,0 +1,25 @@ +void +checkfloatingrules(Client *c) +{ + const char *class, *instance; + unsigned int i; + const Rule *r; + XClassHint ch = { NULL, NULL }; + + XGetClassHint(dpy, c->win, &ch); + class = ch.res_class ? ch.res_class : broken; + instance = ch.res_name ? ch.res_name : broken; + + for (i = 0; i < LENGTH(rules); i++) { + r = &rules[i]; + if ((!r->title || strstr(c->name, r->title)) + && (!r->class || strstr(class, r->class)) + && (!r->instance || strstr(instance, r->instance))) + c->isfloating = r->isfloating; + } + if (ch.res_class) + XFree(ch.res_class); + if (ch.res_name) + XFree(ch.res_name); +} + diff --git a/patch/sizehints_ruled.h b/patch/sizehints_ruled.h new file mode 100644 index 0000000..14c6719 --- /dev/null +++ b/patch/sizehints_ruled.h @@ -0,0 +1,2 @@ +static void checkfloatingrules(Client *c); + diff --git a/patch/sortscreens.c b/patch/sortscreens.c new file mode 100644 index 0000000..538e2c1 --- /dev/null +++ b/patch/sortscreens.c @@ -0,0 +1,16 @@ +void +sortscreens(XineramaScreenInfo *screens, int n) +{ + int i, j; + XineramaScreenInfo *screen = ecalloc(1, sizeof(XineramaScreenInfo)); + + for (i = 0; i < n; i++) + for (j = i + 1; j < n; j++) + if (RIGHTOF(screens[i], screens[j])) { + memcpy(&screen[0], &screens[i], sizeof(XineramaScreenInfo)); + memcpy(&screens[i], &screens[j], sizeof(XineramaScreenInfo)); + memcpy(&screens[j], &screen[0], sizeof(XineramaScreenInfo)); + } + XFree(screen); +} + diff --git a/patch/sortscreens.h b/patch/sortscreens.h new file mode 100644 index 0000000..82915f4 --- /dev/null +++ b/patch/sortscreens.h @@ -0,0 +1,4 @@ +#define RIGHTOF(a,b) (a.y_org > b.y_org) || ((a.y_org == b.y_org) && (a.x_org > b.x_org)) + +static void sortscreens(XineramaScreenInfo *screens, int n); + diff --git a/patch/stacker.c b/patch/stacker.c new file mode 100644 index 0000000..ed8b76b --- /dev/null +++ b/patch/stacker.c @@ -0,0 +1,113 @@ +void +focusstack(const Arg *arg) +{ + int i = stackpos(arg); + Client *c, *p; + + if (i < 0) + return; + + #if LOSEFULLSCREEN_PATCH + if (!selmon->sel) + return; + #elif FAKEFULLSCREEN_CLIENT_PATCH + if (!selmon->sel || (selmon->sel->isfullscreen && !selmon->sel->fakefullscreen)) + return; + #else + if (!selmon->sel || selmon->sel->isfullscreen) + return; + #endif // LOSEFULLSCREEN_PATCH + + #if BAR_WINTITLEACTIONS_PATCH + for (p = NULL, c = selmon->clients; c && (i || !ISVISIBLE(c) || HIDDEN(c)); + i -= (ISVISIBLE(c) && !HIDDEN(c) ? 1 : 0), p = c, c = c->next); + #else + for (p = NULL, c = selmon->clients; c && (i || !ISVISIBLE(c)); + i -= (ISVISIBLE(c) ? 1 : 0), p = c, c = c->next); + #endif // BAR_WINTITLEACTIONS_PATCH + focus(c ? c : p); + restack(selmon); +} + +void +pushstack(const Arg *arg) +{ + int i = stackpos(arg); + Client *sel = selmon->sel, *c, *p; + + if (i < 0) + return; + else if (i == 0) { + detach(sel); + attach(sel); + } + else { + for (p = NULL, c = selmon->clients; c; p = c, c = c->next) + #if BAR_WINTITLEACTIONS_PATCH + if (!(i -= (ISVISIBLE(c) && !HIDDEN(c) && c != sel))) + #else + if (!(i -= (ISVISIBLE(c) && c != sel))) + #endif // BAR_WINTITLEACTIONS_PATCH + break; + c = c ? c : p; + detach(sel); + sel->next = c->next; + c->next = sel; + } + arrange(selmon); +} + +int +stackpos(const Arg *arg) +{ + int n, i; + Client *c, *l; + + if (!selmon->clients) + return -1; + + #if BAR_WINTITLEACTIONS_PATCH + if (arg->i == PREVSEL) { + for (l = selmon->stack; l && (!ISVISIBLE(l) || HIDDEN(l) || l == selmon->sel); l = l->snext); + if (!l) + return -1; + for (i = 0, c = selmon->clients; c != l; i += (ISVISIBLE(c) && !HIDDEN(c) ? 1 : 0), c = c->next); + return i; + } + else if (ISINC(arg->i)) { + if (!selmon->sel) + return -1; + for (i = 0, c = selmon->clients; c != selmon->sel; i += (ISVISIBLE(c) && !HIDDEN(c) ? 1 : 0), c = c->next); + for (n = i; c; n += (ISVISIBLE(c) && !HIDDEN(c) ? 1 : 0), c = c->next); + return MOD(i + GETINC(arg->i), n); + } + else if (arg->i < 0) { + for (i = 0, c = selmon->clients; c; i += (ISVISIBLE(c) && !HIDDEN(c) ? 1 : 0), c = c->next); + return MAX(i + arg->i, 0); + } + else + return arg->i; + #else // !BAR_WINTITLEACTIONS_PATCH + if (arg->i == PREVSEL) { + for (l = selmon->stack; l && (!ISVISIBLE(l) || l == selmon->sel); l = l->snext); + if (!l) + return -1; + for (i = 0, c = selmon->clients; c != l; i += (ISVISIBLE(c) ? 1 : 0), c = c->next); + return i; + } + else if (ISINC(arg->i)) { + if (!selmon->sel) + return -1; + for (i = 0, c = selmon->clients; c != selmon->sel; i += (ISVISIBLE(c) ? 1 : 0), c = c->next); + for (n = i; c; n += (ISVISIBLE(c) ? 1 : 0), c = c->next); + return MOD(i + GETINC(arg->i), n); + } + else if (arg->i < 0) { + for (i = 0, c = selmon->clients; c; i += (ISVISIBLE(c) ? 1 : 0), c = c->next); + return MAX(i + arg->i, 0); + } + else + return arg->i; + #endif // BAR_WINTITLEACTIONS_PATCH +} + diff --git a/patch/stacker.h b/patch/stacker.h new file mode 100644 index 0000000..ee420bd --- /dev/null +++ b/patch/stacker.h @@ -0,0 +1,11 @@ +#define GETINC(X) ((X) - 2000) +#define INC(X) ((X) + 2000) +#define ISINC(X) ((X) > 1000 && (X) < 3000) +#define PREVSEL 3000 +#define MOD(N,M) ((N)%(M) < 0 ? (N)%(M) + (M) : (N)%(M)) +#define TRUNC(X,A,B) (MAX((A), MIN((X), (B)))) + +static void focusstack(const Arg *arg); +static void pushstack(const Arg *arg); +static int stackpos(const Arg *arg); + diff --git a/patch/sticky.c b/patch/sticky.c new file mode 100644 index 0000000..420c3e0 --- /dev/null +++ b/patch/sticky.c @@ -0,0 +1,9 @@ +void +togglesticky(const Arg *arg) +{ + if (!selmon->sel) + return; + selmon->sel->issticky = !selmon->sel->issticky; + arrange(selmon); +} + diff --git a/patch/sticky.h b/patch/sticky.h new file mode 100644 index 0000000..277bb34 --- /dev/null +++ b/patch/sticky.h @@ -0,0 +1,2 @@ +static void togglesticky(const Arg *arg); + diff --git a/patch/swallow.c b/patch/swallow.c new file mode 100644 index 0000000..824ca83 --- /dev/null +++ b/patch/swallow.c @@ -0,0 +1,206 @@ +#include <X11/Xlib-xcb.h> +#include <xcb/res.h> +#ifdef __OpenBSD__ +#include <sys/sysctl.h> +#include <kvm.h> +#endif /* __OpenBSD__ */ + +static int scanner; +static xcb_connection_t *xcon; + +int +swallow(Client *p, Client *c) +{ + Client *s; + + if (c->noswallow > 0 || c->isterminal) + return 0; + if (c->noswallow < 0 && !swallowfloating && c->isfloating) + return 0; + + XMapWindow(dpy, c->win); + + detach(c); + detachstack(c); + + setclientstate(c, WithdrawnState); + XUnmapWindow(dpy, p->win); + + p->swallowing = c; + c->mon = p->mon; + + Window w = p->win; + p->win = c->win; + c->win = w; + + XChangeProperty(dpy, c->win, netatom[NetClientList], XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &(p->win), 1); + + updatetitle(p); + s = scanner ? c : p; + #if BAR_EWMHTAGS_PATCH + setfloatinghint(s); + #endif // BAR_EWMHTAGS_PATCH + XMoveResizeWindow(dpy, p->win, s->x, s->y, s->w, s->h); + arrange(p->mon); + configure(p); + updateclientlist(); + + return 1; +} + +void +unswallow(Client *c) +{ + c->win = c->swallowing->win; + + free(c->swallowing); + c->swallowing = NULL; + + XDeleteProperty(dpy, c->win, netatom[NetClientList]); + + /* unfullscreen the client */ + setfullscreen(c, 0); + updatetitle(c); + arrange(c->mon); + XMapWindow(dpy, c->win); + XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); + #if BAR_EWMHTAGS_PATCH + setfloatinghint(c); + #endif // BAR_EWMHTAGS_PATCH + setclientstate(c, NormalState); + focus(NULL); + arrange(c->mon); +} + +pid_t +winpid(Window w) +{ + pid_t result = 0; + + #ifdef __linux__ + xcb_res_client_id_spec_t spec = {0}; + spec.client = w; + spec.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID; + + xcb_generic_error_t *e = NULL; + xcb_res_query_client_ids_cookie_t c = xcb_res_query_client_ids(xcon, 1, &spec); + xcb_res_query_client_ids_reply_t *r = xcb_res_query_client_ids_reply(xcon, c, &e); + + if (!r) + return (pid_t)0; + + xcb_res_client_id_value_iterator_t i = xcb_res_query_client_ids_ids_iterator(r); + for (; i.rem; xcb_res_client_id_value_next(&i)) { + spec = i.data->spec; + if (spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) { + uint32_t *t = xcb_res_client_id_value_value(i.data); + result = *t; + break; + } + } + + free(r); + + if (result == (pid_t)-1) + result = 0; + + #endif /* __linux__ */ + #ifdef __OpenBSD__ + Atom type; + int format; + unsigned long len, bytes; + unsigned char *prop; + pid_t ret; + + if (XGetWindowProperty(dpy, w, XInternAtom(dpy, "_NET_WM_PID", 1), 0, 1, False, AnyPropertyType, &type, &format, &len, &bytes, &prop) != Success || !prop) + return 0; + + ret = *(pid_t*)prop; + XFree(prop); + result = ret; + #endif /* __OpenBSD__ */ + + return result; +} + +pid_t +getparentprocess(pid_t p) +{ + unsigned int v = 0; + +#ifdef __linux__ + FILE *f; + char buf[256]; + snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)p); + + if (!(f = fopen(buf, "r"))) + return (pid_t)0; + + if (fscanf(f, "%*u %*s %*c %u", (unsigned *)&v) != 1) + v = (pid_t)0; + fclose(f); +#endif /* __linux__ */ +#ifdef __OpenBSD__ + int n; + kvm_t *kd; + struct kinfo_proc *kp; + + kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, NULL); + if (!kd) + return 0; + + kp = kvm_getprocs(kd, KERN_PROC_PID, p, sizeof(*kp), &n); + v = kp->p_ppid; +#endif /* __OpenBSD__ */ + return (pid_t)v; +} + +int +isdescprocess(pid_t p, pid_t c) +{ + while (p != c && c != 0) + c = getparentprocess(c); + + return (int)c; +} + +Client * +termforwin(const Client *w) +{ + Client *c; + Monitor *m; + + if (!w->pid || w->isterminal) + return NULL; + + c = selmon->sel; + if (c && c->isterminal && !c->swallowing && c->pid && isdescprocess(c->pid, w->pid)) + return c; + + for (m = mons; m; m = m->next) { + for (c = m->clients; c; c = c->next) { + if (c->isterminal && !c->swallowing && c->pid && isdescprocess(c->pid, w->pid)) + return c; + } + } + + return NULL; +} + +Client * +swallowingclient(Window w) +{ + Client *c; + Monitor *m; + + for (m = mons; m; m = m->next) { + for (c = m->clients; c; c = c->next) { + if (c->swallowing && c->swallowing->win == w) + return c; + } + } + + return NULL; +} + diff --git a/patch/swallow.h b/patch/swallow.h new file mode 100644 index 0000000..529fea9 --- /dev/null +++ b/patch/swallow.h @@ -0,0 +1,8 @@ +static pid_t getparentprocess(pid_t p); +static int isdescprocess(pid_t p, pid_t c); +static int swallow(Client *p, Client *c); +static Client *swallowingclient(Window w); +static Client *termforwin(const Client *c); +static void unswallow(Client *c); +static pid_t winpid(Window w); + diff --git a/patch/swapfocus.c b/patch/swapfocus.c new file mode 100644 index 0000000..ec85c2e --- /dev/null +++ b/patch/swapfocus.c @@ -0,0 +1,22 @@ +void +swapfocus(const Arg *arg) +{ + if (!selmon->sel) + return; + if (selmon->pertag->prevclient[selmon->pertag->curtag] != NULL + && ISVISIBLE(selmon->pertag->prevclient[selmon->pertag->curtag])) { + focus(selmon->pertag->prevclient[selmon->pertag->curtag]); + restack(selmon->pertag->prevclient[selmon->pertag->curtag]->mon); + } + else { + Client *c = NULL; + for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); + if (!c) + for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next); + if (c) { + focus(c); + restack(selmon); + } + } +} + diff --git a/patch/swapfocus.h b/patch/swapfocus.h new file mode 100644 index 0000000..e547d2f --- /dev/null +++ b/patch/swapfocus.h @@ -0,0 +1,2 @@ +static void swapfocus(const Arg *arg); + diff --git a/patch/swaptags.c b/patch/swaptags.c new file mode 100644 index 0000000..d6a72d7 --- /dev/null +++ b/patch/swaptags.c @@ -0,0 +1,23 @@ +void +swaptags(const Arg *arg) +{ + unsigned int newtag = arg->ui & TAGMASK; + unsigned int curtag = selmon->tagset[selmon->seltags]; + + if (newtag == curtag || !curtag || (curtag & (curtag-1))) + return; + + for (Client *c = selmon->clients; c != NULL; c = c->next) { + if ((c->tags & newtag) || (c->tags & curtag)) + c->tags ^= curtag ^ newtag; + + if (!c->tags) + c->tags = newtag; + } + + selmon->tagset[selmon->seltags] = newtag; + + focus(NULL); + arrange(selmon); +} + diff --git a/patch/swaptags.h b/patch/swaptags.h new file mode 100644 index 0000000..fdadf3a --- /dev/null +++ b/patch/swaptags.h @@ -0,0 +1,2 @@ +static void swaptags(const Arg *arg); + diff --git a/patch/switchcol.c b/patch/switchcol.c new file mode 100644 index 0000000..6caf8ba --- /dev/null +++ b/patch/switchcol.c @@ -0,0 +1,29 @@ +void +switchcol(const Arg *arg) +{ + Client *c, *t; + int col = 0; + int i; + + if (!selmon->sel) + return; + for (i = 0, c = nexttiled(selmon->clients); c ; + c = nexttiled(c->next), i++) { + if (c == selmon->sel) + col = (i + 1) > selmon->nmaster; + } + if (i <= selmon->nmaster) + return; + for (c = selmon->stack; c; c = c->snext) { + if (!ISVISIBLE(c)) + continue; + for (i = 0, t = nexttiled(selmon->clients); t && t != c; + t = nexttiled(t->next), i++); + if (t && (i + 1 > selmon->nmaster) != col) { + focus(c); + restack(selmon); + break; + } + } +} + diff --git a/patch/switchcol.h b/patch/switchcol.h new file mode 100644 index 0000000..8c7cded --- /dev/null +++ b/patch/switchcol.h @@ -0,0 +1,2 @@ +static void switchcol(const Arg *arg); + diff --git a/patch/tab.c b/patch/tab.c new file mode 100644 index 0000000..30a14d1 --- /dev/null +++ b/patch/tab.c @@ -0,0 +1,133 @@ +static int th = 0; /* tab bar geometry */ + +void +drawtabs(void) +{ + Monitor *m; + + for (m = mons; m; m = m->next) + drawtab(m); +} + +static int +cmpint(const void *p1, const void *p2) +{ + /* The actual arguments to this function are "pointers to + pointers to char", but strcmp(3) arguments are "pointers + to char", hence the following cast plus dereference */ + return *((int*) p1) > * (int*) p2; +} + +void +drawtab(Monitor *m) +{ + Client *c; + int i; + int itag = -1; + char view_info[50]; + int view_info_w = 0; + int sorted_label_widths[MAXTABS]; + int tot_width; + int maxsize = bh; + int x = 0; + int w = 0; + + // view_info: indicate the tag which is displayed in the view + for (i = 0; i < NUMTAGS; ++i) { + if ((selmon->tagset[selmon->seltags] >> i) & 1) { + if (itag >=0) { // more than one tag selected + itag = -1; + break; + } + itag = i; + } + } + + if (0 <= itag && itag < NUMTAGS) { + snprintf(view_info, sizeof view_info, "[%s]", tagicon(m, itag)); + } else { + strncpy(view_info, "[...]", sizeof view_info); + } + view_info[sizeof(view_info) - 1 ] = 0; + view_info_w = TEXTW(view_info); + tot_width = view_info_w; + + /* Calculates number of labels and their width */ + m->ntabs = 0; + for (c = m->clients; c; c = c->next) { + if (!ISVISIBLE(c) || HIDDEN(c)) + continue; + m->tab_widths[m->ntabs] = TEXTW(c->name); + tot_width += m->tab_widths[m->ntabs]; + ++m->ntabs; + if (m->ntabs >= MAXTABS) + break; + } + + if (tot_width > m->ww) { // not enough space to display the labels, they need to be truncated + memcpy(sorted_label_widths, m->tab_widths, sizeof(int) * m->ntabs); + qsort(sorted_label_widths, m->ntabs, sizeof(int), cmpint); + tot_width = view_info_w; + for (i = 0; i < m->ntabs; ++i) { + if (tot_width + (m->ntabs - i) * sorted_label_widths[i] > m->ww) + break; + tot_width += sorted_label_widths[i]; + } + maxsize = (m->ww - tot_width) / (m->ntabs - i); + } else { + maxsize = m->ww; + } + i = 0; + for (c = m->clients; c; c = c->next) { + if (!ISVISIBLE(c) || HIDDEN(c)) + continue; + if (i >= m->ntabs) + break; + if (m->tab_widths[i] > maxsize) + m->tab_widths[i] = maxsize; + w = m->tab_widths[i]; + drw_setscheme(drw, scheme[(c == m->sel) ? SchemeSel : SchemeNorm]); + drw_text(drw, x, 0, w, th, 0, c->name, 0, False); + x += w; + ++i; + } + + drw_setscheme(drw, scheme[SchemeNorm]); + + /* cleans interspace between window names and current viewed tag label */ + w = m->ww - view_info_w - x; + drw_text(drw, x, 0, w, th, 0, "", 0, False); + + /* view info */ + x += w; + w = view_info_w; + drw_text(drw, x, 0, w, th, 0, view_info, 0, False); + + drw_map(drw, m->tabwin, 0, 0, m->ww, th); +} + +void +focuswin(const Arg* arg) +{ + int iwin = arg->i; + Client* c = NULL; + for (c = selmon->clients; c && (iwin || !ISVISIBLE(c)) ; c = c->next) { + if (ISVISIBLE(c) && !HIDDEN(c)) + --iwin; + }; + if (c) { + focus(c); + restack(selmon); + } +} + +void +tabmode(const Arg *arg) +{ + if (arg && arg->i >= 0) + selmon->showtab = arg->ui % showtab_nmodes; + else + selmon->showtab = (selmon->showtab + 1 ) % showtab_nmodes; + arrange(selmon); +} + diff --git a/patch/tab.h b/patch/tab.h new file mode 100644 index 0000000..44161d7 --- /dev/null +++ b/patch/tab.h @@ -0,0 +1,5 @@ +static void drawtab(Monitor *m); +static void drawtabs(void); +static void focuswin(const Arg* arg); +static void tabmode(const Arg *arg); + diff --git a/patch/tagall.c b/patch/tagall.c new file mode 100644 index 0000000..7abe7cc --- /dev/null +++ b/patch/tagall.c @@ -0,0 +1,26 @@ +void +tagall(const Arg *arg) +{ + if (!selmon->clients) + return; + /* if parameter starts with F, just move floating windows */ + int floating_only = (char *)arg->v && ((char *)arg->v)[0] == 'F' ? 1 : 0; + int tag = (char *)arg->v ? atoi(((char *)arg->v) + floating_only) : 0; + int j; + Client* c; + if (tag >= 0 && tag < NUMTAGS) + for (c = selmon->clients; c; c = c->next) + { + if (!floating_only || c->isfloating) + for (j = 0; j < NUMTAGS; j++) + { + if (c->tags & 1 << j && selmon->tagset[selmon->seltags] & 1 << j) + { + c->tags = c->tags ^ (1 << j & TAGMASK); + c->tags = c->tags | 1 << (tag-1); + } + } + } + arrange(selmon); +} + diff --git a/patch/tagall.h b/patch/tagall.h new file mode 100644 index 0000000..f36c9f8 --- /dev/null +++ b/patch/tagall.h @@ -0,0 +1,2 @@ +static void tagall(const Arg *arg); + diff --git a/patch/tagallmon.c b/patch/tagallmon.c new file mode 100644 index 0000000..d6b879a --- /dev/null +++ b/patch/tagallmon.c @@ -0,0 +1,49 @@ +void +tagallmon(const Arg *arg) +{ + Monitor *m; + Client *c, *last, *slast, *next; + + if (!mons->next) + return; + + m = dirtomon(arg->i); + for (last = m->clients; last && last->next; last = last->next); + for (slast = m->stack; slast && slast->snext; slast = slast->snext); + + for (c = selmon->clients; c; c = next) { + next = c->next; + if (!ISVISIBLE(c)) + continue; + unfocus(c, 1, NULL); + detach(c); + detachstack(c); + c->mon = m; + c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ + c->next = NULL; + c->snext = NULL; + if (last) + last = last->next = c; + else + m->clients = last = c; + if (slast) + slast = slast->snext = c; + else + m->stack = slast = c; + if (c->isfullscreen) { + #if !FAKEFULLSCREEN_PATCH && FAKEFULLSCREEN_CLIENT_PATCH + if (c->fakefullscreen != 1) { + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); + XRaiseWindow(dpy, c->win); + } + #elif !FAKEFULLSCREEN_PATCH + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); + XRaiseWindow(dpy, c->win); + #endif // FAKEFULLSCREEN_CLIENT_PATCH + } + } + + focus(NULL); + arrange(NULL); +} + diff --git a/patch/tagallmon.h b/patch/tagallmon.h new file mode 100644 index 0000000..9abf535 --- /dev/null +++ b/patch/tagallmon.h @@ -0,0 +1,2 @@ +static void tagallmon(const Arg *arg); + diff --git a/patch/tagothermonitor.c b/patch/tagothermonitor.c new file mode 100644 index 0000000..d12f6ff --- /dev/null +++ b/patch/tagothermonitor.c @@ -0,0 +1,44 @@ +#if IPC_PATCH || DWMC_PATCH +void +tagnextmonex(const Arg *arg) +{ + tagnextmon(&((Arg) { .ui = 1 << arg->ui })); +} + +void +tagprevmonex(const Arg *arg) +{ + tagprevmon(&((Arg) { .ui = 1 << arg->ui })); +} +#endif // IPC_PATCH | DWMC_PATCH + +void +tagnextmon(const Arg *arg) +{ + tagothermon(arg, 1); +} + +void +tagprevmon(const Arg *arg) +{ + tagothermon(arg, -1); +} + +void +tagothermon(const Arg *arg, int dir) +{ + Client *sel; + Monitor *newmon; + + if (!selmon->sel || !mons->next) + return; + sel = selmon->sel; + newmon = dirtomon(dir); + sendmon(sel, newmon); + if (arg->ui & TAGMASK) { + sel->tags = arg->ui & TAGMASK; + focus(NULL); + arrange(newmon); + } +} + diff --git a/patch/tagothermonitor.h b/patch/tagothermonitor.h new file mode 100644 index 0000000..8ebe975 --- /dev/null +++ b/patch/tagothermonitor.h @@ -0,0 +1,9 @@ +#if IPC_PATCH || DWMC_PATCH +static void tagnextmonex(const Arg *arg); +static void tagprevmonex(const Arg *arg); +#endif // IPC_PATCH | DWMC_PATCH + +static void tagnextmon(const Arg *arg); +static void tagprevmon(const Arg *arg); +static void tagothermon(const Arg *arg, int dir); + diff --git a/patch/tagswapmon.c b/patch/tagswapmon.c new file mode 100644 index 0000000..4719b8c --- /dev/null +++ b/patch/tagswapmon.c @@ -0,0 +1,75 @@ +void +tagswapmon(const Arg *arg) +{ + Monitor *m; + Client *c, *sc = NULL, *mc = NULL, *next; + + if (!mons->next) + return; + + m = dirtomon(arg->i); + + for (c = selmon->clients; c; c = next) { + next = c->next; + if (!ISVISIBLE(c)) + continue; + unfocus(c, 1, NULL); + detach(c); + detachstack(c); + c->next = sc; + sc = c; + } + + for (c = m->clients; c; c = next) { + next = c->next; + if (!ISVISIBLE(c)) + continue; + unfocus(c, 1, NULL); + detach(c); + detachstack(c); + c->next = mc; + mc = c; + } + + for (c = sc; c; c = next) { + next = c->next; + c->mon = m; + c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ + attach(c); + attachstack(c); + if (c->isfullscreen) { + #if !FAKEFULLSCREEN_PATCH && FAKEFULLSCREEN_CLIENT_PATCH + if (c->fakefullscreen != 1) { + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); + XRaiseWindow(dpy, c->win); + } + #elif !FAKEFULLSCREEN_PATCH + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); + XRaiseWindow(dpy, c->win); + #endif // FAKEFULLSCREEN_CLIENT_PATCH + } + } + + for (c = mc; c; c = next) { + next = c->next; + c->mon = selmon; + c->tags = selmon->tagset[selmon->seltags]; /* assign tags of target monitor */ + attach(c); + attachstack(c); + if (c->isfullscreen) { + #if !FAKEFULLSCREEN_PATCH && FAKEFULLSCREEN_CLIENT_PATCH + if (c->fakefullscreen != 1) { + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); + XRaiseWindow(dpy, c->win); + } + #elif !FAKEFULLSCREEN_PATCH + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); + XRaiseWindow(dpy, c->win); + #endif // FAKEFULLSCREEN_CLIENT_PATCH + } + } + + focus(NULL); + arrange(NULL); +} + diff --git a/patch/tagswapmon.h b/patch/tagswapmon.h new file mode 100644 index 0000000..949f591 --- /dev/null +++ b/patch/tagswapmon.h @@ -0,0 +1,2 @@ +static void tagswapmon(const Arg *arg); + diff --git a/patch/tapresize.c b/patch/tapresize.c new file mode 100644 index 0000000..c236b5d --- /dev/null +++ b/patch/tapresize.c @@ -0,0 +1,39 @@ +void +resizemousescroll(const Arg *arg) +{ + int nw, nh; + Client *c; + Monitor *m; + XEvent ev; + int dw = *((int*)arg->v + 1); + int dh = *(int*)arg->v; + + if (!(c = selmon->sel)) + return; + if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ + return; + restack(selmon); + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) + return; + nw = MAX(c->w + dw, 1); + nh = MAX(c->h + dh, 1); + if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww + && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh) + { + if (!c->isfloating && selmon->lt[selmon->sellt]->arrange + && (abs(nw - c->w) > snap || abs(nh - c->h) > snap)) + togglefloating(NULL); + } + if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) + resize(c, c->x, c->y, nw, nh, 1); + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); + XUngrabPointer(dpy, CurrentTime); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { + sendmon(c, m); + selmon = m; + focus(NULL); + } +} + diff --git a/patch/tapresize.h b/patch/tapresize.h new file mode 100644 index 0000000..fe6d227 --- /dev/null +++ b/patch/tapresize.h @@ -0,0 +1,2 @@ +static void resizemousescroll(const Arg *arg); + diff --git a/patch/togglefullscreen.c b/patch/togglefullscreen.c new file mode 100644 index 0000000..18857f5 --- /dev/null +++ b/patch/togglefullscreen.c @@ -0,0 +1,18 @@ +void +togglefullscreen(const Arg *arg) +{ + Client *c = selmon->sel; + if (!c) + return; + + #if FAKEFULLSCREEN_CLIENT_PATCH && !FAKEFULLSCREEN_PATCH + if (c->fakefullscreen == 1) { // fake fullscreen --> fullscreen + c->fakefullscreen = 2; + setfullscreen(c, 1); + } else + setfullscreen(c, !c->isfullscreen); + #else + setfullscreen(c, !c->isfullscreen); + #endif // FAKEFULLSCREEN_CLIENT_PATCH +} + diff --git a/patch/togglefullscreen.h b/patch/togglefullscreen.h new file mode 100644 index 0000000..96a6770 --- /dev/null +++ b/patch/togglefullscreen.h @@ -0,0 +1,2 @@ +static void togglefullscreen(const Arg *arg); + diff --git a/patch/transfer.c b/patch/transfer.c new file mode 100644 index 0000000..ce64319 --- /dev/null +++ b/patch/transfer.c @@ -0,0 +1,34 @@ +void +transfer(const Arg *arg) +{ + Client *c, *mtail = selmon->clients, *stail = NULL, *insertafter; + int transfertostack = 0, i, nmasterclients; + + for (i = 0, c = selmon->clients; c; c = c->next) { + if (!ISVISIBLE(c) || c->isfloating) continue; + if (selmon->sel == c) { transfertostack = i < selmon->nmaster && selmon->nmaster != 0; } + if (i < selmon->nmaster) { nmasterclients++; mtail = c; } + stail = c; + i++; + } + if (!selmon->sel || selmon->sel->isfloating || i == 0) { + return; + } else if (transfertostack) { + selmon->nmaster = MIN(i, selmon->nmaster) - 1; + insertafter = stail; + } else { + selmon->nmaster = selmon->nmaster + 1; + insertafter = mtail; + } + if (insertafter != selmon->sel) { + detach(selmon->sel); + if (selmon->nmaster == 1 && !transfertostack) { + attach(selmon->sel); // Head prepend case + } else { + selmon->sel->next = insertafter->next; + insertafter->next = selmon->sel; + } + } + arrange(selmon); +} + diff --git a/patch/transfer.h b/patch/transfer.h new file mode 100644 index 0000000..e887dae --- /dev/null +++ b/patch/transfer.h @@ -0,0 +1,2 @@ +static void transfer(const Arg *arg); + diff --git a/patch/transferall.c b/patch/transferall.c new file mode 100644 index 0000000..ee823b1 --- /dev/null +++ b/patch/transferall.c @@ -0,0 +1,26 @@ +void +transferall(const Arg *arg) +{ + Client *c, *n = selmon->clients, *attachfrom = NULL; + int i = 0, nstackclients = 0; + while (n) { + c = n; + n = c->next; + if (!ISVISIBLE(c) || c->isfloating) continue; + if (i >= selmon->nmaster) { + detach(c); + if (!attachfrom) { + attach(c); + } else { + c->next = attachfrom->next; + attachfrom->next = c; + } + attachfrom = c; + nstackclients++; + } + i++; + } + selmon->nmaster = nstackclients; + arrange(selmon); +} + diff --git a/patch/transferall.h b/patch/transferall.h new file mode 100644 index 0000000..a424ec8 --- /dev/null +++ b/patch/transferall.h @@ -0,0 +1,2 @@ +static void transferall(const Arg *arg); + diff --git a/patch/unfloatvisible.c b/patch/unfloatvisible.c new file mode 100644 index 0000000..fb8888f --- /dev/null +++ b/patch/unfloatvisible.c @@ -0,0 +1,15 @@ +void +unfloatvisible(const Arg *arg) +{ + Client *c; + + for (c = selmon->clients; c; c = c->next) + if (ISVISIBLE(c) && c->isfloating) + c->isfloating = c->isfixed; + + if (arg && arg->v) + setlayout(arg); + else + arrange(selmon); +} + diff --git a/patch/unfloatvisible.h b/patch/unfloatvisible.h new file mode 100644 index 0000000..f15bc66 --- /dev/null +++ b/patch/unfloatvisible.h @@ -0,0 +1,2 @@ +static void unfloatvisible(const Arg *arg); + diff --git a/patch/vanitygaps.c b/patch/vanitygaps.c new file mode 100644 index 0000000..be2ef08 --- /dev/null +++ b/patch/vanitygaps.c @@ -0,0 +1,199 @@ +/* Settings */ +#if !PERTAG_VANITYGAPS_PATCH +static int enablegaps = 1; +#endif // PERTAG_VANITYGAPS_PATCH + +static void +setgaps(int oh, int ov, int ih, int iv) +{ + if (oh < 0) oh = 0; + if (ov < 0) ov = 0; + if (ih < 0) ih = 0; + if (iv < 0) iv = 0; + + selmon->gappoh = oh; + selmon->gappov = ov; + selmon->gappih = ih; + selmon->gappiv = iv; + + #if PERTAG_VANITYGAPS_PATCH && PERTAG_PATCH + selmon->pertag->gaps[selmon->pertag->curtag] = + ((oh & 0xFF) << 0) | ((ov & 0xFF) << 8) | ((ih & 0xFF) << 16) | ((iv & 0xFF) << 24); + #endif // PERTAG_VANITYGAPS_PATCH + + arrange(selmon); +} + +#if IPC_PATCH || DWMC_PATCH +/* External function that takes one integer and splits it + * into four gap values: + * - outer horizontal (oh) + * - outer vertical (ov) + * - inner horizontal (ih) + * - inner vertical (iv) + * + * Each value is represented as one byte with the uppermost + * bit of each byte indicating whether or not to keep the + * current value. + * + * Example: + * + * 10000000 10000000 00001111 00001111 + * | | | | + * + keep oh + keep ov + ih 15px + iv 15px + * + * This gives an int of: + * 10000000100000000000111100001111 = 2155876111 + * + * Thus this command should set inner gaps to 15: + * xsetroot -name "fsignal:setgaps i 2155876111" + */ +static void +setgapsex(const Arg *arg) +{ + int oh = selmon->gappoh; + int ov = selmon->gappov; + int ih = selmon->gappih; + int iv = selmon->gappiv; + + if (!(arg->i & (1 << 31))) + oh = (arg->i & 0x7f000000) >> 24; + if (!(arg->i & (1 << 23))) + ov = (arg->i & 0x7f0000) >> 16; + if (!(arg->i & (1 << 15))) + ih = (arg->i & 0x7f00) >> 8; + if (!(arg->i & (1 << 7))) + iv = (arg->i & 0x7f); + + /* Auto enable gaps if disabled */ + #if PERTAG_VANITYGAPS_PATCH && PERTAG_PATCH + if (!selmon->pertag->enablegaps[selmon->pertag->curtag]) + selmon->pertag->enablegaps[selmon->pertag->curtag] = 1; + #else + if (!enablegaps) + enablegaps = 1; + #endif // PERTAG_VANITYGAPS_PATCH + + setgaps(oh, ov, ih, iv); +} +#endif // IPC_PATCH | DWMC_PATCH + +static void +togglegaps(const Arg *arg) +{ + #if PERTAG_VANITYGAPS_PATCH && PERTAG_PATCH + selmon->pertag->enablegaps[selmon->pertag->curtag] = !selmon->pertag->enablegaps[selmon->pertag->curtag]; + #else + enablegaps = !enablegaps; + #endif // PERTAG_VANITYGAPS_PATCH + arrange(NULL); +} + +static void +defaultgaps(const Arg *arg) +{ + setgaps(gappoh, gappov, gappih, gappiv); +} + +static void +incrgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh + arg->i, + selmon->gappov + arg->i, + selmon->gappih + arg->i, + selmon->gappiv + arg->i + ); +} + +static void +incrigaps(const Arg *arg) +{ + setgaps( + selmon->gappoh, + selmon->gappov, + selmon->gappih + arg->i, + selmon->gappiv + arg->i + ); +} + +static void +incrogaps(const Arg *arg) +{ + setgaps( + selmon->gappoh + arg->i, + selmon->gappov + arg->i, + selmon->gappih, + selmon->gappiv + ); +} + +static void +incrohgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh + arg->i, + selmon->gappov, + selmon->gappih, + selmon->gappiv + ); +} + +static void +incrovgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh, + selmon->gappov + arg->i, + selmon->gappih, + selmon->gappiv + ); +} + +static void +incrihgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh, + selmon->gappov, + selmon->gappih + arg->i, + selmon->gappiv + ); +} + +static void +incrivgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh, + selmon->gappov, + selmon->gappih, + selmon->gappiv + arg->i + ); +} + +#if DRAGMFACT_PATCH || CENTEREDMASTER_LAYOUT || CENTEREDFLOATINGMASTER_LAYOUT || COLUMNS_LAYOUT || DECK_LAYOUT || FIBONACCI_DWINDLE_LAYOUT || FIBONACCI_SPIRAL_LAYOUT || GAPPLESSGRID_LAYOUT || NROWGRID_LAYOUT || HORIZGRID_LAYOUT || BSTACK_LAYOUT || BSTACKHORIZ_LAYOUT || GRIDMODE_LAYOUT || FLEXTILE_DELUXE_LAYOUT || TILE_LAYOUT || (VANITYGAPS_MONOCLE_PATCH && MONOCLE_LAYOUT) +static void +getgaps(Monitor *m, int *oh, int *ov, int *ih, int *iv, unsigned int *nc) +{ + unsigned int n, oe, ie; + #if PERTAG_VANITYGAPS_PATCH && PERTAG_PATCH + oe = ie = selmon->pertag->enablegaps[selmon->pertag->curtag]; + #else + oe = ie = enablegaps; + #endif // PERTAG_VANITYGAPS_PATCH + Client *c; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + if (n == 1) { + oe *= smartgaps_fact; // outer gaps disabled or multiplied when only one client + } + + *oh = m->gappoh*oe; // outer horizontal gap + *ov = m->gappov*oe; // outer vertical gap + *ih = m->gappih*ie; // inner horizontal gap + *iv = m->gappiv*ie; // inner vertical gap + *nc = n; // number of clients +} +#endif + diff --git a/patch/vanitygaps.h b/patch/vanitygaps.h new file mode 100644 index 0000000..b8f2109 --- /dev/null +++ b/patch/vanitygaps.h @@ -0,0 +1,20 @@ +/* Key binding functions */ +static void defaultgaps(const Arg *arg); +static void incrgaps(const Arg *arg); +static void incrigaps(const Arg *arg); +static void incrogaps(const Arg *arg); +static void incrohgaps(const Arg *arg); +static void incrovgaps(const Arg *arg); +static void incrihgaps(const Arg *arg); +static void incrivgaps(const Arg *arg); +static void togglegaps(const Arg *arg); + +/* Internals */ +#if DRAGMFACT_PATCH || CENTEREDMASTER_LAYOUT || CENTEREDFLOATINGMASTER_LAYOUT || COLUMNS_LAYOUT || DECK_LAYOUT || FIBONACCI_DWINDLE_LAYOUT || FIBONACCI_SPIRAL_LAYOUT || GAPPLESSGRID_LAYOUT || NROWGRID_LAYOUT || HORIZGRID_LAYOUT || BSTACK_LAYOUT || BSTACKHORIZ_LAYOUT || GRIDMODE_LAYOUT || FLEXTILE_DELUXE_LAYOUT || TILE_LAYOUT || (VANITYGAPS_MONOCLE_PATCH && MONOCLE_LAYOUT) +static void getgaps(Monitor *m, int *oh, int *ov, int *ih, int *iv, unsigned int *nc); +#endif +static void setgaps(int oh, int ov, int ih, int iv); +#if IPC_PATCH || DWMC_PATCH +static void setgapsex(const Arg *arg); +#endif // IPC_PATCH | DWMC_PATCH + diff --git a/patch/warp.c b/patch/warp.c new file mode 100644 index 0000000..16f1605 --- /dev/null +++ b/patch/warp.c @@ -0,0 +1,38 @@ +void +warp(const Client *c) +{ + Monitor *m; + Bar *bar; + int x, y; + + if (ignore_warp) + return; + + if (!c) { + XWarpPointer(dpy, None, root, 0, 0, 0, 0, selmon->wx + selmon->ww / 2, selmon->wy + selmon->wh / 2); + return; + } + + if (!getrootptr(&x, &y)) + return; + + if (!force_warp && + (x > c->x - c->bw && + y > c->y - c->bw && + x < c->x + c->w + c->bw*2 && + y < c->y + c->h + c->bw*2)) + return; + + force_warp = 0; + + for (m = mons; m; m = m->next) + for (bar = m->bar; bar; bar = bar->next) + if (x > bar->bx && + x < bar->bx + bar->bw && + y > bar->by && + y < bar->by + bar->bh) + return; + + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w / 2, c->h / 2); +} + diff --git a/patch/warp.h b/patch/warp.h new file mode 100644 index 0000000..d79d75f --- /dev/null +++ b/patch/warp.h @@ -0,0 +1,2 @@ +static void warp(const Client *c); + diff --git a/patch/winview.c b/patch/winview.c new file mode 100644 index 0000000..f54c20c --- /dev/null +++ b/patch/winview.c @@ -0,0 +1,21 @@ +/* Selects for the view of the focused window. The list of tags */ +/* to be displayed is matched to the focused window tag list. */ +void +winview(const Arg* arg) +{ + Window win, win_r, win_p, *win_c; + unsigned nc; + int unused; + Client* c; + Arg a; + + if (!XGetInputFocus(dpy, &win, &unused)) return; + while (XQueryTree(dpy, win, &win_r, &win_p, &win_c, &nc) + && win_p != win_r) win = win_p; + + if (!(c = wintoclient(win))) return; + + a.ui = c->tags; + view(&a); +} + diff --git a/patch/winview.h b/patch/winview.h new file mode 100644 index 0000000..9c2ec97 --- /dev/null +++ b/patch/winview.h @@ -0,0 +1,2 @@ +static void winview(const Arg* arg); + diff --git a/patch/xkb.c b/patch/xkb.c new file mode 100644 index 0000000..d779733 --- /dev/null +++ b/patch/xkb.c @@ -0,0 +1,68 @@ +static XkbInfo xkbGlobal; +static XkbInfo *xkbSaved = NULL; + +static XkbInfo * +createxkb(Window w) +{ + XkbInfo *xkb; + + xkb = malloc(sizeof *xkb); + if (xkb == NULL) + die("fatal: could not malloc() %u bytes\n", sizeof *xkb); + xkb->group = xkbGlobal.group; + xkb->w = w; + xkb->next = xkbSaved; + if (xkbSaved != NULL) + xkbSaved->prev = xkb; + xkb->prev = NULL; + xkbSaved = xkb; + + return xkb; +} + +XkbInfo * +findxkb(Window w) +{ + XkbInfo *xkb; + for (xkb = xkbSaved; xkb != NULL; xkb = xkb->next) + if (xkb->w == w) + return xkb; + return NULL; +} + +void +xkbeventnotify(XEvent *e) +{ + XkbEvent *ev; + + ev = (XkbEvent *) e; + switch (ev->any.xkb_type) { + case XkbStateNotify: + xkbGlobal.group = ev->state.locked_group; + if (selmon != NULL && selmon->sel != NULL) + selmon->sel->xkb->group = xkbGlobal.group; + drawbars(); + break; + } +} + +/* xkb bar module */ +int +width_xkb(Bar *bar, BarArg *a) +{ + return TEXTW(xkb_layouts[xkbGlobal.group]); +} + +int +draw_xkb(Bar *bar, BarArg *a) +{ + drw_text(drw, a->x, a->y, a->w, a->h, lrpad / 2, xkb_layouts[xkbGlobal.group], 0, False); + return 1; +} + +int +click_xkb(Bar *bar, Arg *arg, BarArg *a) +{ + return ClkXKB; +} + diff --git a/patch/xkb.h b/patch/xkb.h new file mode 100644 index 0000000..2c6d33a --- /dev/null +++ b/patch/xkb.h @@ -0,0 +1,8 @@ +static XkbInfo *createxkb(Window w); +static XkbInfo *findxkb(Window w); +static void xkbeventnotify(XEvent *e); + +static int width_xkb(Bar *bar, BarArg *a); +static int draw_xkb(Bar *bar, BarArg *a); +static int click_xkb(Bar *bar, Arg *arg, BarArg *a); + diff --git a/patch/xrdb.c b/patch/xrdb.c new file mode 100644 index 0000000..1490ace --- /dev/null +++ b/patch/xrdb.c @@ -0,0 +1,128 @@ +void +loadxrdb() +{ + Display *display; + char * resm; + XrmDatabase xrdb; + char *type; + XrmValue value; + + display = XOpenDisplay(NULL); + + if (display != NULL) { + resm = XResourceManagerString(display); + + if (resm != NULL) { + xrdb = XrmGetStringDatabase(resm); + + if (xrdb != NULL) { + XRDB_LOAD_COLOR("dwm.normfgcolor", normfgcolor); + XRDB_LOAD_COLOR("dwm.normbgcolor", normbgcolor); + XRDB_LOAD_COLOR("dwm.normbordercolor", normbordercolor); + XRDB_LOAD_COLOR("dwm.normfloatcolor", normfloatcolor); + XRDB_LOAD_COLOR("dwm.selfgcolor", selfgcolor); + XRDB_LOAD_COLOR("dwm.selbgcolor", selbgcolor); + XRDB_LOAD_COLOR("dwm.selbordercolor", selbordercolor); + XRDB_LOAD_COLOR("dwm.selfloatcolor", selfloatcolor); + XRDB_LOAD_COLOR("dwm.titlenormfgcolor", titlenormfgcolor); + XRDB_LOAD_COLOR("dwm.titlenormbgcolor", titlenormbgcolor); + XRDB_LOAD_COLOR("dwm.titlenormbordercolor", titlenormbordercolor); + XRDB_LOAD_COLOR("dwm.titlenormfloatcolor", titlenormfloatcolor); + XRDB_LOAD_COLOR("dwm.titleselfgcolor", titleselfgcolor); + XRDB_LOAD_COLOR("dwm.titleselbgcolor", titleselbgcolor); + XRDB_LOAD_COLOR("dwm.titleselbordercolor", titleselbordercolor); + XRDB_LOAD_COLOR("dwm.titleselfloatcolor", titleselfloatcolor); + XRDB_LOAD_COLOR("dwm.tagsnormfgcolor", tagsnormfgcolor); + XRDB_LOAD_COLOR("dwm.tagsnormbgcolor", tagsnormbgcolor); + XRDB_LOAD_COLOR("dwm.tagsnormbordercolor", tagsnormbordercolor); + XRDB_LOAD_COLOR("dwm.tagsnormfloatcolor", tagsnormfloatcolor); + XRDB_LOAD_COLOR("dwm.tagsselfgcolor", tagsselfgcolor); + XRDB_LOAD_COLOR("dwm.tagsselbgcolor", tagsselbgcolor); + XRDB_LOAD_COLOR("dwm.tagsselbordercolor", tagsselbordercolor); + XRDB_LOAD_COLOR("dwm.tagsselfloatcolor", tagsselfloatcolor); + XRDB_LOAD_COLOR("dwm.hidnormfgcolor", hidnormfgcolor); + XRDB_LOAD_COLOR("dwm.hidnormbgcolor", hidnormbgcolor); + XRDB_LOAD_COLOR("dwm.hidselfgcolor", hidselfgcolor); + XRDB_LOAD_COLOR("dwm.hidselbgcolor", hidselbgcolor); + XRDB_LOAD_COLOR("dwm.urgfgcolor", urgfgcolor); + XRDB_LOAD_COLOR("dwm.urgbgcolor", urgbgcolor); + XRDB_LOAD_COLOR("dwm.urgbordercolor", urgbordercolor); + XRDB_LOAD_COLOR("dwm.urgfloatcolor", urgfloatcolor); + #if BAR_FLEXWINTITLE_PATCH + XRDB_LOAD_COLOR("dwm.normTTBbgcolor", normTTBbgcolor); + XRDB_LOAD_COLOR("dwm.normLTRbgcolor", normLTRbgcolor); + XRDB_LOAD_COLOR("dwm.normMONObgcolor", normMONObgcolor); + XRDB_LOAD_COLOR("dwm.normGRIDbgcolor", normGRIDbgcolor); + XRDB_LOAD_COLOR("dwm.normGRD1bgcolor", normGRD1bgcolor); + XRDB_LOAD_COLOR("dwm.normGRD2bgcolor", normGRD2bgcolor); + XRDB_LOAD_COLOR("dwm.normGRDMbgcolor", normGRDMbgcolor); + XRDB_LOAD_COLOR("dwm.normHGRDbgcolor", normHGRDbgcolor); + XRDB_LOAD_COLOR("dwm.normDWDLbgcolor", normDWDLbgcolor); + XRDB_LOAD_COLOR("dwm.normSPRLbgcolor", normSPRLbgcolor); + XRDB_LOAD_COLOR("dwm.normfloatbgcolor", normfloatbgcolor); + XRDB_LOAD_COLOR("dwm.actTTBbgcolor", actTTBbgcolor); + XRDB_LOAD_COLOR("dwm.actLTRbgcolor", actLTRbgcolor); + XRDB_LOAD_COLOR("dwm.actMONObgcolor", actMONObgcolor); + XRDB_LOAD_COLOR("dwm.actGRIDbgcolor", actGRIDbgcolor); + XRDB_LOAD_COLOR("dwm.actGRD1bgcolor", actGRD1bgcolor); + XRDB_LOAD_COLOR("dwm.actGRD2bgcolor", actGRD2bgcolor); + XRDB_LOAD_COLOR("dwm.actGRDMbgcolor", actGRDMbgcolor); + XRDB_LOAD_COLOR("dwm.actHGRDbgcolor", actHGRDbgcolor); + XRDB_LOAD_COLOR("dwm.actDWDLbgcolor", actDWDLbgcolor); + XRDB_LOAD_COLOR("dwm.actSPRLbgcolor", actSPRLbgcolor); + XRDB_LOAD_COLOR("dwm.actfloatbgcolor", actfloatbgcolor); + XRDB_LOAD_COLOR("dwm.selTTBbgcolor", selTTBbgcolor); + XRDB_LOAD_COLOR("dwm.selLTRbgcolor", selLTRbgcolor); + XRDB_LOAD_COLOR("dwm.selMONObgcolor", selMONObgcolor); + XRDB_LOAD_COLOR("dwm.selGRIDbgcolor", selGRIDbgcolor); + XRDB_LOAD_COLOR("dwm.selGRD1bgcolor", selGRD1bgcolor); + XRDB_LOAD_COLOR("dwm.selGRD2bgcolor", selGRD2bgcolor); + XRDB_LOAD_COLOR("dwm.selGRDMbgcolor", selGRDMbgcolor); + XRDB_LOAD_COLOR("dwm.selHGRDbgcolor", selHGRDbgcolor); + XRDB_LOAD_COLOR("dwm.selDWDLbgcolor", selDWDLbgcolor); + XRDB_LOAD_COLOR("dwm.selSPRLbgcolor", selSPRLbgcolor); + XRDB_LOAD_COLOR("dwm.selfloatbgcolor", selfloatbgcolor); + #endif // BAR_FLEXWINTITLE_PATCH + #if BAR_STATUS2D_XRDB_TERMCOLORS_PATCH && BAR_STATUS2D_PATCH + XRDB_LOAD_COLOR("color0", termcol0); + XRDB_LOAD_COLOR("color1", termcol1); + XRDB_LOAD_COLOR("color2", termcol2); + XRDB_LOAD_COLOR("color3", termcol3); + XRDB_LOAD_COLOR("color4", termcol4); + XRDB_LOAD_COLOR("color5", termcol5); + XRDB_LOAD_COLOR("color6", termcol6); + XRDB_LOAD_COLOR("color7", termcol7); + XRDB_LOAD_COLOR("color8", termcol8); + XRDB_LOAD_COLOR("color9", termcol9); + XRDB_LOAD_COLOR("color10", termcol10); + XRDB_LOAD_COLOR("color11", termcol11); + XRDB_LOAD_COLOR("color12", termcol12); + XRDB_LOAD_COLOR("color13", termcol13); + XRDB_LOAD_COLOR("color14", termcol14); + XRDB_LOAD_COLOR("color15", termcol15); + #endif // BAR_STATUS2D_XRDB_TERMCOLORS_PATCH + + XrmDestroyDatabase(xrdb); + } + } + } + + XCloseDisplay(display); +} + +void +xrdb(const Arg *arg) +{ + loadxrdb(); + int i; + for (i = 0; i < LENGTH(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], + #if BAR_ALPHA_PATCH + alphas[i], + #endif // BAR_ALPHA_PATCH + ColCount + ); + focus(NULL); + arrange(NULL); +} + diff --git a/patch/xrdb.h b/patch/xrdb.h new file mode 100644 index 0000000..3787bec --- /dev/null +++ b/patch/xrdb.h @@ -0,0 +1,22 @@ +#include <X11/Xresource.h> + +#define XRDB_LOAD_COLOR(R,V) if (XrmGetResource(xrdb, R, NULL, &type, &value) == True) { \ + if (value.addr != NULL && strnlen(value.addr, 8) == 7 && value.addr[0] == '#') { \ + int i = 1; \ + for (; i <= 6; i++) { \ + if (value.addr[i] < 48) break; \ + if (value.addr[i] > 57 && value.addr[i] < 65) break; \ + if (value.addr[i] > 70 && value.addr[i] < 97) break; \ + if (value.addr[i] > 102) break; \ + } \ + if (i == 7) { \ + strncpy(V, value.addr, 7); \ + V[7] = '\0'; \ + } \ + } \ + } + +static void loadxrdb(void); +static void xrdb(const Arg *arg); + + diff --git a/patch/zoomswap.c b/patch/zoomswap.c new file mode 100644 index 0000000..58ee9d4 --- /dev/null +++ b/patch/zoomswap.c @@ -0,0 +1,14 @@ + +#if !PERTAG_PATCH +static Client *prevzoom = NULL; +#endif // PERTAG_PATCH + +Client * +findbefore(Client *c) { + Client *p; + if (!c || c == c->mon->clients) + return NULL; + for (p = c->mon->clients; p && p->next != c; p = p->next); + return p; +} + diff --git a/patch/zoomswap.h b/patch/zoomswap.h new file mode 100644 index 0000000..22c752a --- /dev/null +++ b/patch/zoomswap.h @@ -0,0 +1,2 @@ +static Client *findbefore(Client *c); + |
