diff --git a/dmenu-5.0/config.def.h.orig b/dmenu-5.0/config.def.h.orig deleted file mode 100644 index f94e71c..0000000 --- a/dmenu-5.0/config.def.h.orig +++ /dev/null @@ -1,31 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -/* Default settings; can be overriden by command line. */ - -static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */ -static int centered = 0; /* -c option; centers dmenu on screen */ -static int min_width = 500; /* minimum width when centered */ -/* -fn option overrides fonts[0]; default X11 font or font set */ -static const char *fonts[] = { - "monospace:size=10" -}; -static const char *prompt = NULL; /* -p option; prompt to the left of input field */ -static const char *colors[SchemeLast][2] = { - /* fg bg */ - [SchemeNorm] = { "#bbbbbb", "#222222" }, - [SchemeSel] = { "#eeeeee", "#005577" }, - [SchemeSelHighlight] = { "#ffc978", "#005577" }, - [SchemeNormHighlight] = { "#ffc978", "#222222" }, - [SchemeOut] = { "#000000", "#00ffff" }, - [SchemeOutHighlight] = { "#ffc978", "#00ffff" }, -}; -/* -l option; if nonzero, dmenu uses vertical list with given number of lines */ -static unsigned int lines = 0; - -/* - * Characters not considered part of a word while deleting words - * for example: " /?\"&[]" - */ -static const char worddelimiters[] = " "; - -/* Size of the window border */ -static const unsigned int border_width = 5; diff --git a/dmenu-5.0/dmenu.1.orig b/dmenu-5.0/dmenu.1.orig deleted file mode 100644 index c036baa..0000000 --- a/dmenu-5.0/dmenu.1.orig +++ /dev/null @@ -1,197 +0,0 @@ -.TH DMENU 1 dmenu\-VERSION -.SH NAME -dmenu \- dynamic menu -.SH SYNOPSIS -.B dmenu -.RB [ \-bfiv ] -.RB [ \-l -.IR lines ] -.RB [ \-m -.IR monitor ] -.RB [ \-p -.IR prompt ] -.RB [ \-fn -.IR font ] -.RB [ \-nb -.IR color ] -.RB [ \-nf -.IR color ] -.RB [ \-sb -.IR color ] -.RB [ \-sf -.IR color ] -.RB [ \-w -.IR windowid ] -.P -.BR dmenu_run " ..." -.SH DESCRIPTION -.B dmenu -is a dynamic menu for X, which reads a list of newline\-separated items from -stdin. When the user selects an item and presses Return, their choice is printed -to stdout and dmenu terminates. Entering text will narrow the items to those -matching the tokens in the input. -.P -.B dmenu_run -is a script used by -.IR dwm (1) -which lists programs in the user's $PATH and runs the result in their $SHELL. -.SH OPTIONS -.TP -.B \-b -dmenu appears at the bottom of the screen. -.TP -.B \-c -dmenu appears centered on the screen. -.TP -.B \-f -dmenu grabs the keyboard before reading stdin if not reading from a tty. This -is faster, but will lock up X until stdin reaches end\-of\-file. -.TP -.B \-i -dmenu matches menu items case insensitively. -.TP -.BI \-l " lines" -dmenu lists items vertically, with the given number of lines. -.TP -.BI \-m " monitor" -dmenu is displayed on the monitor number supplied. Monitor numbers are starting -from 0. -.TP -.BI \-p " prompt" -defines the prompt to be displayed to the left of the input field. -.TP -.BI \-fn " font" -defines the font or font set used. -.TP -.BI \-nb " color" -defines the normal background color. -.IR #RGB , -.IR #RRGGBB , -and X color names are supported. -.TP -.BI \-nf " color" -defines the normal foreground color. -.TP -.BI \-sb " color" -defines the selected background color. -.TP -.BI \-sf " color" -defines the selected foreground color. -.TP -.B \-v -prints version information to stdout, then exits. -.TP -.BI \-w " windowid" -embed into windowid. -.SH USAGE -dmenu is completely controlled by the keyboard. Items are selected using the -arrow keys, page up, page down, home, and end. -.TP -.B Tab -Copy the selected item to the input field. -.TP -.B Return -Confirm selection. Prints the selected item to stdout and exits, returning -success. -.TP -.B Ctrl-Return -Confirm selection. Prints the selected item to stdout and continues. -.TP -.B Shift\-Return -Confirm input. Prints the input text to stdout and exits, returning success. -.TP -.B Escape -Exit without selecting an item, returning failure. -.TP -.B Ctrl-Left -Move cursor to the start of the current word -.TP -.B Ctrl-Right -Move cursor to the end of the current word -.TP -.B C\-a -Home -.TP -.B C\-b -Left -.TP -.B C\-c -Escape -.TP -.B C\-d -Delete -.TP -.B C\-e -End -.TP -.B C\-f -Right -.TP -.B C\-g -Escape -.TP -.B C\-h -Backspace -.TP -.B C\-i -Tab -.TP -.B C\-j -Return -.TP -.B C\-J -Shift-Return -.TP -.B C\-k -Delete line right -.TP -.B C\-m -Return -.TP -.B C\-M -Shift-Return -.TP -.B C\-n -Down -.TP -.B C\-p -Up -.TP -.B C\-u -Delete line left -.TP -.B C\-w -Delete word left -.TP -.B C\-y -Paste from primary X selection -.TP -.B C\-Y -Paste from X clipboard -.TP -.B M\-b -Move cursor to the start of the current word -.TP -.B M\-f -Move cursor to the end of the current word -.TP -.B M\-g -Home -.TP -.B M\-G -End -.TP -.B M\-h -Up -.TP -.B M\-j -Page down -.TP -.B M\-k -Page up -.TP -.B M\-l -Down -.SH SEE ALSO -.IR dwm (1), -.IR stest (1) diff --git a/dmenu-5.0/dmenu.c.orig b/dmenu-5.0/dmenu.c.orig deleted file mode 100644 index 04b601e..0000000 --- a/dmenu-5.0/dmenu.c.orig +++ /dev/null @@ -1,835 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#ifdef XINERAMA -#include -#endif -#include - -#include "drw.h" -#include "util.h" - -/* macros */ -#define INTERSECT(x,y,w,h,r) (MAX(0, MIN((x)+(w),(r).x_org+(r).width) - MAX((x),(r).x_org)) \ - * MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org))) -#define LENGTH(X) (sizeof X / sizeof X[0]) -#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) - -/* enums */ -enum { SchemeNorm, SchemeSel, SchemeOut, SchemeNormHighlight, SchemeSelHighlight, SchemeOutHighlight, SchemeLast }; /* color schemes */ -struct item { - char *text; - struct item *left, *right; - int out; -}; - -static char text[BUFSIZ] = ""; -static char *embed; -static int bh, mw, mh; -static int inputw = 0, promptw; -static int lrpad; /* sum of left and right padding */ -static size_t cursor; -static struct item *items = NULL; -static struct item *matches, *matchend; -static struct item *prev, *curr, *next, *sel; -static int mon = -1, screen; - -static Atom clip, utf8; -static Display *dpy; -static Window root, parentwin, win; -static XIC xic; - -static Drw *drw; -static Clr *scheme[SchemeLast]; - -#include "config.h" - -static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; -static char *(*fstrstr)(const char *, const char *) = strstr; - -static void -appenditem(struct item *item, struct item **list, struct item **last) -{ - if (*last) - (*last)->right = item; - else - *list = item; - - item->left = *last; - item->right = NULL; - *last = item; -} - -static void -calcoffsets(void) -{ - int i, n; - - if (lines > 0) - n = lines * bh; - else - n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">")); - /* calculate which items will begin the next page and previous page */ - for (i = 0, next = curr; next; next = next->right) - if ((i += (lines > 0) ? bh : MIN(TEXTW(next->text), n)) > n) - break; - for (i = 0, prev = curr; prev && prev->left; prev = prev->left) - if ((i += (lines > 0) ? bh : MIN(TEXTW(prev->left->text), n)) > n) - break; -} - -static int -max_textw(void) -{ - int len = 0; - for (struct item *item = items; item && item->text; item++) - len = MAX(TEXTW(item->text), len); - return len; -} - -static void -cleanup(void) -{ - size_t i; - - XUngrabKey(dpy, AnyKey, AnyModifier, root); - for (i = 0; i < SchemeLast; i++) - free(scheme[i]); - drw_free(drw); - XSync(dpy, False); - XCloseDisplay(dpy); -} - -static char * -cistrstr(const char *s, const char *sub) -{ - size_t len; - - for (len = strlen(sub); *s; s++) - if (!strncasecmp(s, sub, len)) - return (char *)s; - return NULL; -} - -static void -drawhighlights(struct item *item, int x, int y, int maxw) -{ - char restorechar, tokens[sizeof text], *highlight, *token; - int indentx, highlightlen; - - drw_setscheme(drw, scheme[item == sel ? SchemeSelHighlight : item->out ? SchemeOutHighlight : SchemeNormHighlight]); - strcpy(tokens, text); - for (token = strtok(tokens, " "); token; token = strtok(NULL, " ")) { - highlight = fstrstr(item->text, token); - while (highlight) { - // Move item str end, calc width for highlight indent, & restore - highlightlen = highlight - item->text; - restorechar = *highlight; - item->text[highlightlen] = '\0'; - indentx = TEXTW(item->text); - item->text[highlightlen] = restorechar; - - // Move highlight str end, draw highlight, & restore - restorechar = highlight[strlen(token)]; - highlight[strlen(token)] = '\0'; - if (indentx - (lrpad / 2) - 1 < maxw) - drw_text( - drw, - x + indentx - (lrpad / 2) - 1, - y, - MIN(maxw - indentx, TEXTW(highlight) - lrpad), - bh, 0, highlight, 0 - ); - highlight[strlen(token)] = restorechar; - - if (strlen(highlight) - strlen(token) < strlen(token)) break; - highlight = fstrstr(highlight + strlen(token), token); - } - } -} - -static int -drawitem(struct item *item, int x, int y, int w) -{ - if (item == sel) - drw_setscheme(drw, scheme[SchemeSel]); - else if (item->out) - drw_setscheme(drw, scheme[SchemeOut]); - else - drw_setscheme(drw, scheme[SchemeNorm]); - - int r = drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); - drawhighlights(item, x, y, w); - return r; -} - -static void -drawmenu(void) -{ - unsigned int curpos; - struct item *item; - int x = 0, y = 0, w; - - drw_setscheme(drw, scheme[SchemeNorm]); - drw_rect(drw, 0, 0, mw, mh, 1, 1); - - if (prompt && *prompt) { - drw_setscheme(drw, scheme[SchemeSel]); - x = drw_text(drw, x, 0, promptw, bh, lrpad / 2, prompt, 0); - } - /* draw input field */ - w = (lines > 0 || !matches) ? mw - x : inputw; - drw_setscheme(drw, scheme[SchemeNorm]); - drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0); - - curpos = TEXTW(text) - TEXTW(&text[cursor]); - if ((curpos += lrpad / 2 - 1) < w) { - drw_setscheme(drw, scheme[SchemeNorm]); - drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0); - } - - if (lines > 0) { - /* draw vertical list */ - for (item = curr; item != next; item = item->right) - drawitem(item, x, y += bh, mw - x); - } else if (matches) { - /* draw horizontal list */ - x += inputw; - w = TEXTW("<"); - if (curr->left) { - drw_setscheme(drw, scheme[SchemeNorm]); - drw_text(drw, x, 0, w, bh, lrpad / 2, "<", 0); - } - x += w; - for (item = curr; item != next; item = item->right) - x = drawitem(item, x, 0, MIN(TEXTW(item->text), mw - x - TEXTW(">"))); - if (next) { - w = TEXTW(">"); - drw_setscheme(drw, scheme[SchemeNorm]); - drw_text(drw, mw - w, 0, w, bh, lrpad / 2, ">", 0); - } - } - drw_map(drw, win, 0, 0, mw, mh); -} - -static void -grabfocus(void) -{ - struct timespec ts = { .tv_sec = 0, .tv_nsec = 10000000 }; - Window focuswin; - int i, revertwin; - - for (i = 0; i < 100; ++i) { - XGetInputFocus(dpy, &focuswin, &revertwin); - if (focuswin == win) - return; - XSetInputFocus(dpy, win, RevertToParent, CurrentTime); - nanosleep(&ts, NULL); - } - die("cannot grab focus"); -} - -static void -grabkeyboard(void) -{ - struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 }; - int i; - - if (embed) - return; - /* try to grab keyboard, we may have to wait for another process to ungrab */ - for (i = 0; i < 1000; i++) { - if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync, - GrabModeAsync, CurrentTime) == GrabSuccess) - return; - nanosleep(&ts, NULL); - } - die("cannot grab keyboard"); -} - -static void -match(void) -{ - static char **tokv = NULL; - static int tokn = 0; - - char buf[sizeof text], *s; - int i, tokc = 0; - size_t len, textsize; - struct item *item, *lprefix, *lsubstr, *prefixend, *substrend; - - strcpy(buf, text); - /* separate input text into tokens to be matched individually */ - for (s = strtok(buf, " "); s; tokv[tokc - 1] = s, s = strtok(NULL, " ")) - if (++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv))) - die("cannot realloc %u bytes:", tokn * sizeof *tokv); - len = tokc ? strlen(tokv[0]) : 0; - - matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL; - textsize = strlen(text) + 1; - for (item = items; item && item->text; item++) { - for (i = 0; i < tokc; i++) - if (!fstrstr(item->text, tokv[i])) - break; - if (i != tokc) /* not all tokens match */ - continue; - /* exact matches go first, then prefixes, then substrings */ - if (!tokc || !fstrncmp(text, item->text, textsize)) - appenditem(item, &matches, &matchend); - else if (!fstrncmp(tokv[0], item->text, len)) - appenditem(item, &lprefix, &prefixend); - else - appenditem(item, &lsubstr, &substrend); - } - if (lprefix) { - if (matches) { - matchend->right = lprefix; - lprefix->left = matchend; - } else - matches = lprefix; - matchend = prefixend; - } - if (lsubstr) { - if (matches) { - matchend->right = lsubstr; - lsubstr->left = matchend; - } else - matches = lsubstr; - matchend = substrend; - } - curr = sel = matches; - calcoffsets(); -} - -static void -insert(const char *str, ssize_t n) -{ - if (strlen(text) + n > sizeof text - 1) - return; - /* move existing text out of the way, insert new text, and update cursor */ - memmove(&text[cursor + n], &text[cursor], sizeof text - cursor - MAX(n, 0)); - if (n > 0) - memcpy(&text[cursor], str, n); - cursor += n; - match(); -} - -static size_t -nextrune(int inc) -{ - ssize_t n; - - /* return location of next utf8 rune in the given direction (+1 or -1) */ - for (n = cursor + inc; n + inc >= 0 && (text[n] & 0xc0) == 0x80; n += inc) - ; - return n; -} - -static void -movewordedge(int dir) -{ - if (dir < 0) { /* move cursor to the start of the word*/ - while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)])) - cursor = nextrune(-1); - while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)])) - cursor = nextrune(-1); - } else { /* move cursor to the end of the word */ - while (text[cursor] && strchr(worddelimiters, text[cursor])) - cursor = nextrune(+1); - while (text[cursor] && !strchr(worddelimiters, text[cursor])) - cursor = nextrune(+1); - } -} - -static void -keypress(XKeyEvent *ev) -{ - char buf[32]; - int len; - KeySym ksym; - Status status; - - len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status); - switch (status) { - default: /* XLookupNone, XBufferOverflow */ - return; - case XLookupChars: - goto insert; - case XLookupKeySym: - case XLookupBoth: - break; - } - - if (ev->state & ControlMask) { - switch(ksym) { - case XK_a: ksym = XK_Home; break; - case XK_b: ksym = XK_Left; break; - case XK_c: ksym = XK_Escape; break; - case XK_d: ksym = XK_Delete; break; - case XK_e: ksym = XK_End; break; - case XK_f: ksym = XK_Right; break; - case XK_g: ksym = XK_Escape; break; - case XK_h: ksym = XK_BackSpace; break; - case XK_i: ksym = XK_Tab; break; - case XK_j: /* fallthrough */ - case XK_J: /* fallthrough */ - case XK_m: /* fallthrough */ - case XK_M: ksym = XK_Return; ev->state &= ~ControlMask; break; - case XK_n: ksym = XK_Down; break; - case XK_p: ksym = XK_Up; break; - - case XK_k: /* delete right */ - text[cursor] = '\0'; - match(); - break; - case XK_u: /* delete left */ - insert(NULL, 0 - cursor); - break; - case XK_w: /* delete word */ - while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)])) - insert(NULL, nextrune(-1) - cursor); - while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)])) - insert(NULL, nextrune(-1) - cursor); - break; - case XK_y: /* paste selection */ - case XK_Y: - XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, - utf8, utf8, win, CurrentTime); - return; - case XK_Left: - movewordedge(-1); - goto draw; - case XK_Right: - movewordedge(+1); - goto draw; - case XK_Return: - case XK_KP_Enter: - break; - case XK_bracketleft: - cleanup(); - exit(1); - default: - return; - } - } else if (ev->state & Mod1Mask) { - switch(ksym) { - case XK_b: - movewordedge(-1); - goto draw; - case XK_f: - movewordedge(+1); - goto draw; - case XK_g: ksym = XK_Home; break; - case XK_G: ksym = XK_End; break; - case XK_h: ksym = XK_Up; break; - case XK_j: ksym = XK_Next; break; - case XK_k: ksym = XK_Prior; break; - case XK_l: ksym = XK_Down; break; - default: - return; - } - } - - switch(ksym) { - default: -insert: - if (!iscntrl(*buf)) - insert(buf, len); - break; - case XK_Delete: - if (text[cursor] == '\0') - return; - cursor = nextrune(+1); - /* fallthrough */ - case XK_BackSpace: - if (cursor == 0) - return; - insert(NULL, nextrune(-1) - cursor); - break; - case XK_End: - if (text[cursor] != '\0') { - cursor = strlen(text); - break; - } - if (next) { - /* jump to end of list and position items in reverse */ - curr = matchend; - calcoffsets(); - curr = prev; - calcoffsets(); - while (next && (curr = curr->right)) - calcoffsets(); - } - sel = matchend; - break; - case XK_Escape: - cleanup(); - exit(1); - case XK_Home: - if (sel == matches) { - cursor = 0; - break; - } - sel = curr = matches; - calcoffsets(); - break; - case XK_Left: - if (cursor > 0 && (!sel || !sel->left || lines > 0)) { - cursor = nextrune(-1); - break; - } - if (lines > 0) - return; - /* fallthrough */ - case XK_Up: - if (sel && sel->left && (sel = sel->left)->right == curr) { - curr = prev; - calcoffsets(); - } - break; - case XK_Next: - if (!next) - return; - sel = curr = next; - calcoffsets(); - break; - case XK_Prior: - if (!prev) - return; - sel = curr = prev; - calcoffsets(); - break; - case XK_Return: - case XK_KP_Enter: - puts((sel && !(ev->state & ShiftMask)) ? sel->text : text); - if (!(ev->state & ControlMask)) { - cleanup(); - exit(0); - } - if (sel) - sel->out = 1; - break; - case XK_Right: - if (text[cursor] != '\0') { - cursor = nextrune(+1); - break; - } - if (lines > 0) - return; - /* fallthrough */ - case XK_Down: - if (sel && sel->right && (sel = sel->right) == next) { - curr = next; - calcoffsets(); - } - break; - case XK_Tab: - if (!sel) - return; - strncpy(text, sel->text, sizeof text - 1); - text[sizeof text - 1] = '\0'; - cursor = strlen(text); - match(); - break; - } - -draw: - drawmenu(); -} - -static void -paste(void) -{ - char *p, *q; - int di; - unsigned long dl; - Atom da; - - /* we have been given the current selection, now insert it into input */ - if (XGetWindowProperty(dpy, win, utf8, 0, (sizeof text / 4) + 1, False, - utf8, &da, &di, &dl, &dl, (unsigned char **)&p) - == Success && p) { - insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t)strlen(p)); - XFree(p); - } - drawmenu(); -} - -static void -readstdin(void) -{ - char buf[sizeof text], *p; - size_t i, imax = 0, size = 0; - unsigned int tmpmax = 0; - - /* read each line from stdin and add it to the item list */ - for (i = 0; fgets(buf, sizeof buf, stdin); i++) { - if (i + 1 >= size / sizeof *items) - if (!(items = realloc(items, (size += BUFSIZ)))) - die("cannot realloc %u bytes:", size); - if ((p = strchr(buf, '\n'))) - *p = '\0'; - if (!(items[i].text = strdup(buf))) - die("cannot strdup %u bytes:", strlen(buf) + 1); - items[i].out = 0; - drw_font_getexts(drw->fonts, buf, strlen(buf), &tmpmax, NULL); - if (tmpmax > inputw) { - inputw = tmpmax; - imax = i; - } - } - if (items) - items[i].text = NULL; - inputw = items ? TEXTW(items[imax].text) : 0; - lines = MIN(lines, i); -} - -static void -run(void) -{ - XEvent ev; - - while (!XNextEvent(dpy, &ev)) { - if (XFilterEvent(&ev, win)) - continue; - switch(ev.type) { - case DestroyNotify: - if (ev.xdestroywindow.window != win) - break; - cleanup(); - exit(1); - case Expose: - if (ev.xexpose.count == 0) - drw_map(drw, win, 0, 0, mw, mh); - break; - case FocusIn: - /* regrab focus from parent window */ - if (ev.xfocus.window != win) - grabfocus(); - break; - case KeyPress: - keypress(&ev.xkey); - break; - case SelectionNotify: - if (ev.xselection.property == utf8) - paste(); - break; - case VisibilityNotify: - if (ev.xvisibility.state != VisibilityUnobscured) - XRaiseWindow(dpy, win); - break; - } - } -} - -static void -setup(void) -{ - int x, y, i, j; - unsigned int du; - XSetWindowAttributes swa; - XIM xim; - Window w, dw, *dws; - XWindowAttributes wa; - XClassHint ch = {"dmenu", "dmenu"}; -#ifdef XINERAMA - XineramaScreenInfo *info; - Window pw; - int a, di, n, area = 0; -#endif - /* init appearance */ - for (j = 0; j < SchemeLast; j++) - scheme[j] = drw_scm_create(drw, colors[j], 2); - - clip = XInternAtom(dpy, "CLIPBOARD", False); - utf8 = XInternAtom(dpy, "UTF8_STRING", False); - - /* calculate menu geometry */ - bh = drw->fonts->h + 2; - lines = MAX(lines, 0); - mh = (lines + 1) * bh; - promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; -#ifdef XINERAMA - i = 0; - if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) { - XGetInputFocus(dpy, &w, &di); - if (mon >= 0 && mon < n) - i = mon; - else if (w != root && w != PointerRoot && w != None) { - /* find top-level window containing current input focus */ - do { - if (XQueryTree(dpy, (pw = w), &dw, &w, &dws, &du) && dws) - XFree(dws); - } while (w != root && w != pw); - /* find xinerama screen with which the window intersects most */ - if (XGetWindowAttributes(dpy, pw, &wa)) - for (j = 0; j < n; j++) - if ((a = INTERSECT(wa.x, wa.y, wa.width, wa.height, info[j])) > area) { - area = a; - i = j; - } - } - /* no focused window is on screen, so use pointer location instead */ - if (mon < 0 && !area && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du)) - for (i = 0; i < n; i++) - if (INTERSECT(x, y, 1, 1, info[i])) - break; - - if (centered) { - mw = MIN(MAX(max_textw() + promptw, min_width), info[i].width); - x = info[i].x_org + ((info[i].width - mw) / 2); - y = info[i].y_org + ((info[i].height - mh) / 2); - } else { - x = info[i].x_org; - y = info[i].y_org + (topbar ? 0 : info[i].height - mh); - mw = info[i].width; - } - - XFree(info); - } else -#endif - { - if (!XGetWindowAttributes(dpy, parentwin, &wa)) - die("could not get embedding window attributes: 0x%lx", - parentwin); - - if (centered) { - mw = MIN(MAX(max_textw() + promptw, min_width), wa.width); - x = (wa.width - mw) / 2; - y = (wa.height - mh) / 2; - } else { - x = 0; - y = topbar ? 0 : wa.height - mh; - mw = wa.width; - } - } - inputw = MIN(inputw, mw/3); - match(); - - /* create menu window */ - swa.override_redirect = True; - swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; - swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; - win = XCreateWindow(dpy, parentwin, x, y, mw, mh, border_width, - CopyFromParent, CopyFromParent, CopyFromParent, - CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); - XSetWindowBorder(dpy, win, scheme[SchemeSel][ColBg].pixel); - XSetClassHint(dpy, win, &ch); - - - /* input methods */ - if ((xim = XOpenIM(dpy, NULL, NULL, NULL)) == NULL) - die("XOpenIM failed: could not open input device"); - - xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, - XNClientWindow, win, XNFocusWindow, win, NULL); - - XMapRaised(dpy, win); - if (embed) { - XSelectInput(dpy, parentwin, FocusChangeMask | SubstructureNotifyMask); - if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) { - for (i = 0; i < du && dws[i] != win; ++i) - XSelectInput(dpy, dws[i], FocusChangeMask); - XFree(dws); - } - grabfocus(); - } - drw_resize(drw, mw, mh); - drawmenu(); -} - -static void -usage(void) -{ - fputs("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" - " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n", stderr); - exit(1); -} - -int -main(int argc, char *argv[]) -{ - XWindowAttributes wa; - int i, fast = 0; - - for (i = 1; i < argc; i++) - /* these options take no arguments */ - if (!strcmp(argv[i], "-v")) { /* prints version information */ - puts("dmenu-"VERSION); - exit(0); - } else if (!strcmp(argv[i], "-b")) /* appears at the bottom of the screen */ - topbar = 0; - else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */ - fast = 1; - else if (!strcmp(argv[i], "-c")) /* centers dmenu on screen */ - centered = 1; - else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */ - fstrncmp = strncasecmp; - fstrstr = cistrstr; - } else if (i + 1 == argc) - usage(); - /* these options take one argument */ - else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */ - lines = atoi(argv[++i]); - else if (!strcmp(argv[i], "-m")) - mon = atoi(argv[++i]); - else if (!strcmp(argv[i], "-p")) /* adds prompt to left of input field */ - prompt = argv[++i]; - else if (!strcmp(argv[i], "-fn")) /* font or font set */ - fonts[0] = argv[++i]; - else if (!strcmp(argv[i], "-nb")) /* normal background color */ - colors[SchemeNorm][ColBg] = argv[++i]; - else if (!strcmp(argv[i], "-nf")) /* normal foreground color */ - colors[SchemeNorm][ColFg] = argv[++i]; - else if (!strcmp(argv[i], "-sb")) /* selected background color */ - colors[SchemeSel][ColBg] = argv[++i]; - else if (!strcmp(argv[i], "-sf")) /* selected foreground color */ - colors[SchemeSel][ColFg] = argv[++i]; - else if (!strcmp(argv[i], "-w")) /* embedding window id */ - embed = argv[++i]; - else - usage(); - - if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) - fputs("warning: no locale support\n", stderr); - if (!(dpy = XOpenDisplay(NULL))) - die("cannot open display"); - screen = DefaultScreen(dpy); - root = RootWindow(dpy, screen); - if (!embed || !(parentwin = strtol(embed, NULL, 0))) - parentwin = root; - if (!XGetWindowAttributes(dpy, parentwin, &wa)) - die("could not get embedding window attributes: 0x%lx", - parentwin); - drw = drw_create(dpy, screen, root, wa.width, wa.height); - if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) - die("no fonts could be loaded."); - lrpad = drw->fonts->h; - -#ifdef __OpenBSD__ - if (pledge("stdio rpath", NULL) == -1) - die("pledge"); -#endif - - if (fast && !isatty(0)) { - grabkeyboard(); - readstdin(); - } else { - readstdin(); - grabkeyboard(); - } - setup(); - run(); - - return 1; /* unreachable */ -} diff --git a/dwm-6.2/config.def.h b/dwm-6.2/config.def.h index 854b92a..a392d1b 100644 --- a/dwm-6.2/config.def.h +++ b/dwm-6.2/config.def.h @@ -4,6 +4,11 @@ static const unsigned int borderpx = 2; /* border pixel of windows */ static const unsigned int gappx = 8; /* gaps between windows */ static const unsigned int snap = 32; /* snap pixel */ +static const unsigned int systraypinning = 0; /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */ +static const unsigned int systrayonleft = 0; /* 0: systray in the right corner, >0: systray on left of status text */ +static const unsigned int systrayspacing = 3; /* systray spacing */ +static const int systraypinningfailfirst = 1; /* 1: if pinning fails, display systray on the first monitor, False: display systray on the last monitor*/ +static const int showsystray = 1; /* 0 means no systray */ static const int showbar = 1; /* 0 means no bar */ static const int topbar = 1; /* 0 means bottom bar */ static const char *fonts[] = { "MesloLGS NF:size=11" }; @@ -25,8 +30,9 @@ static const char *colors[][3] = { }; /* tagging */ -static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; - +static const char *tags[] = { "", "爵", "", "", "ﭮ", "ﱘ", "", }; +/*static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; +*/ static const Rule rules[] = { /* xprop(1): * WM_CLASS(STRING) = instance, class @@ -35,6 +41,7 @@ static const Rule rules[] = { /* class instance title tags mask isfloating monitor */ { "Gimp", NULL, NULL, 0, 1, -1 }, { "Firefox", NULL, NULL, 1 << 8, 0, -1 }, + { "zoom", NULL, NULL, 0, 1, -1 }, }; /* layout(s) */ diff --git a/dwm-6.2/config.def.h.orig b/dwm-6.2/config.def.h.orig deleted file mode 100644 index b5c5b7e..0000000 --- a/dwm-6.2/config.def.h.orig +++ /dev/null @@ -1,119 +0,0 @@ -/* See LICENSE file for copyright and license details. */ - -/* appearance */ -static const unsigned int borderpx = 1; /* border pixel of windows */ -static const unsigned int snap = 32; /* snap pixel */ -static const int showbar = 1; /* 0 means no bar */ -static const int topbar = 1; /* 0 means bottom bar */ -static const char *fonts[] = { "MesloLGS NF:size=11" }; -static const char dmenufont[] = "MesloLGS NF:size=11"; -/* -static const char *fonts[] = { "monospace:size=10" }; -static const char dmenufont[] = "monospace:size=10"; -*/ -static const char col_gray1[] = "#222222"; -static const char col_gray2[] = "#444444"; -static const char col_gray3[] = "#bbbbbb"; -static const char col_gray4[] = "#eeeeee"; -static const char col_teal[] = "#408977"; -static const char *colors[][3] = { - /* fg bg border */ - [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, - [SchemeSel] = { col_gray4, col_teal, col_teal }, -}; - -/* tagging */ -static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; - -static const Rule rules[] = { - /* xprop(1): - * WM_CLASS(STRING) = instance, class - * WM_NAME(STRING) = title - */ - /* class instance title tags mask isfloating monitor */ - { "Gimp", NULL, NULL, 0, 1, -1 }, - { "Firefox", NULL, NULL, 1 << 8, 0, -1 }, -}; - -/* layout(s) */ -static const float mfact = 0.5; /* factor of master area size [0.05..0.95] */ -static const int nmaster = 1; /* number of clients in master area */ -static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ - -static const Layout layouts[] = { - /* symbol arrange function */ - { "[]=", tile }, /* first entry is default */ - { "><>", NULL }, /* no layout function means floating behavior */ - { "[M]", monocle }, -}; - -/* key definitions */ -#define MODKEY Mod1Mask -#define TAGKEYS(KEY,TAG) \ - { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ - { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ - { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ - { MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, - -/* helper for spawning shell commands in the pre dwm-5.0 fashion */ -#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } - -/* commands */ -static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */ -static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_teal, "-sf", col_gray4, NULL }; -static const char *termcmd[] = { "st", NULL }; - -static Key keys[] = { - /* modifier key function argument */ - { MODKEY, XK_p, spawn, {.v = dmenucmd } }, - { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, - { MODKEY, XK_b, togglebar, {0} }, - { MODKEY, XK_j, focusstack, {.i = +1 } }, - { MODKEY, XK_k, focusstack, {.i = -1 } }, - { MODKEY, XK_i, incnmaster, {.i = +1 } }, - { MODKEY, XK_d, incnmaster, {.i = -1 } }, - { MODKEY, XK_h, setmfact, {.f = -0.05} }, - { MODKEY, XK_l, setmfact, {.f = +0.05} }, - { MODKEY, XK_Return, zoom, {0} }, - { MODKEY, XK_Tab, view, {0} }, - { MODKEY|ShiftMask, XK_c, killclient, {0} }, - { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, - { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, - { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, - { MODKEY, XK_space, setlayout, {0} }, - { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, - { MODKEY, XK_0, view, {.ui = ~0 } }, - { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, - { MODKEY, XK_comma, focusmon, {.i = -1 } }, - { MODKEY, XK_period, focusmon, {.i = +1 } }, - { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, - { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, - TAGKEYS( XK_1, 0) - TAGKEYS( XK_2, 1) - TAGKEYS( XK_3, 2) - TAGKEYS( XK_4, 3) - TAGKEYS( XK_5, 4) - TAGKEYS( XK_6, 5) - TAGKEYS( XK_7, 6) - TAGKEYS( XK_8, 7) - TAGKEYS( XK_9, 8) - { MODKEY|ShiftMask, XK_q, quit, {0} }, -}; - -/* button definitions */ -/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ -static Button buttons[] = { - /* click event mask button function argument */ - { ClkLtSymbol, 0, Button1, setlayout, {0} }, - { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, - { ClkWinTitle, 0, Button2, zoom, {0} }, - { ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, - { ClkClientWin, MODKEY, Button1, movemouse, {0} }, - { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, - { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, - { ClkTagBar, 0, Button1, view, {0} }, - { ClkTagBar, 0, Button3, toggleview, {0} }, - { ClkTagBar, MODKEY, Button1, tag, {0} }, - { ClkTagBar, MODKEY, Button3, toggletag, {0} }, -}; - diff --git a/dwm-6.2/dwm.c.orig b/dwm-6.2/dwm.c.orig deleted file mode 100644 index 4363627..0000000 --- a/dwm-6.2/dwm.c.orig +++ /dev/null @@ -1,2162 +0,0 @@ -/* See LICENSE file for copyright and license details. - * - * dynamic window manager is designed like any other X client as well. It is - * driven through handling X events. In contrast to other X clients, a window - * manager selects for SubstructureRedirectMask on the root window, to receive - * events about window (dis-)appearance. Only one X connection at a time is - * allowed to select for this event mask. - * - * The event handlers of dwm are organized in an array which is accessed - * whenever a new event has been fetched. This allows event dispatching - * in O(1) time. - * - * Each child of the root window is called a client, except windows which have - * set the override_redirect flag. Clients are organized in a linked client - * list on each monitor, the focus history is remembered through a stack list - * on each monitor. Each client contains a bit array to indicate the tags of a - * client. - * - * Keys and tagging rules are organized as arrays and defined in config.h. - * - * To understand everything else, start reading main(). - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef XINERAMA -#include -#endif /* XINERAMA */ -#include - -#include "drw.h" -#include "util.h" - -/* macros */ -#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) -#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) -#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ - * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) -#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) -#define LENGTH(X) (sizeof X / sizeof X[0]) -#define MOUSEMASK (BUTTONMASK|PointerMotionMask) -#define WIDTH(X) ((X)->w + 2 * (X)->bw) -#define HEIGHT(X) ((X)->h + 2 * (X)->bw) -#define TAGMASK ((1 << LENGTH(tags)) - 1) -#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) - -/* enums */ -enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ -enum { SchemeNorm, SchemeSel }; /* color schemes */ -enum { NetSupported, NetWMName, NetWMState, NetWMCheck, - NetWMFullscreen, NetActiveWindow, NetWMWindowType, - NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ -enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ -enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, - ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ - -typedef union { - int i; - unsigned int ui; - float f; - const void *v; -} Arg; - -typedef struct { - unsigned int click; - unsigned int mask; - unsigned int button; - void (*func)(const Arg *arg); - const Arg arg; -} Button; - -typedef struct Monitor Monitor; -typedef struct Client Client; -struct Client { - char name[256]; - float mina, maxa; - int x, y, w, h; - int oldx, oldy, oldw, oldh; - int basew, baseh, incw, inch, maxw, maxh, minw, minh; - int bw, oldbw; - unsigned int tags; - int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; - Client *next; - Client *snext; - Monitor *mon; - Window win; -}; - -typedef struct { - unsigned int mod; - KeySym keysym; - void (*func)(const Arg *); - const Arg arg; -} Key; - -typedef struct { - const char *symbol; - void (*arrange)(Monitor *); -} Layout; - -struct Monitor { - char ltsymbol[16]; - float mfact; - int nmaster; - int num; - int by; /* bar geometry */ - int mx, my, mw, mh; /* screen size */ - int wx, wy, ww, wh; /* window area */ - int gappx; /* gaps between windows */ - unsigned int seltags; - unsigned int sellt; - unsigned int tagset[2]; - int showbar; - int topbar; - Client *clients; - Client *sel; - Client *stack; - Monitor *next; - Window barwin; - const Layout *lt[2]; -}; - -typedef struct { - const char *class; - const char *instance; - const char *title; - unsigned int tags; - int isfloating; - int monitor; -} Rule; - -/* function declarations */ -static void applyrules(Client *c); -static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); -static void arrange(Monitor *m); -static void arrangemon(Monitor *m); -static void attach(Client *c); -static void attachstack(Client *c); -static void buttonpress(XEvent *e); -static void checkotherwm(void); -static void cleanup(void); -static void cleanupmon(Monitor *mon); -static void clientmessage(XEvent *e); -static void configure(Client *c); -static void configurenotify(XEvent *e); -static void configurerequest(XEvent *e); -static Monitor *createmon(void); -static void destroynotify(XEvent *e); -static void detach(Client *c); -static void detachstack(Client *c); -static Monitor *dirtomon(int dir); -static void drawbar(Monitor *m); -static void drawbars(void); -static void enternotify(XEvent *e); -static void expose(XEvent *e); -static void focus(Client *c); -static void focusin(XEvent *e); -static void focusmon(const Arg *arg); -static void focusstack(const Arg *arg); -static int getrootptr(int *x, int *y); -static long getstate(Window w); -static int gettextprop(Window w, Atom atom, char *text, unsigned int size); -static void grabbuttons(Client *c, int focused); -static void grabkeys(void); -static void incnmaster(const Arg *arg); -static void keypress(XEvent *e); -static void killclient(const Arg *arg); -static void manage(Window w, XWindowAttributes *wa); -static void mappingnotify(XEvent *e); -static void maprequest(XEvent *e); -static void monocle(Monitor *m); -static void motionnotify(XEvent *e); -static void movemouse(const Arg *arg); -static Client *nexttiled(Client *c); -static void pop(Client *); -static void propertynotify(XEvent *e); -static void quit(const Arg *arg); -static Monitor *recttomon(int x, int y, int w, int h); -static void resize(Client *c, int x, int y, int w, int h, int interact); -static void resizeclient(Client *c, int x, int y, int w, int h); -static void resizemouse(const Arg *arg); -static void restack(Monitor *m); -static void run(void); -static void scan(void); -static int sendevent(Client *c, Atom proto); -static void sendmon(Client *c, Monitor *m); -static void setclientstate(Client *c, long state); -static void setfocus(Client *c); -static void setfullscreen(Client *c, int fullscreen); -static void setgaps(const Arg *arg); -static void setlayout(const Arg *arg); -static void setmfact(const Arg *arg); -static void setup(void); -static void seturgent(Client *c, int urg); -static void showhide(Client *c); -static void sigchld(int unused); -static void spawn(const Arg *arg); -static void tag(const Arg *arg); -static void tagmon(const Arg *arg); -static void tile(Monitor *); -static void togglebar(const Arg *arg); -static void togglefloating(const Arg *arg); -static void toggletag(const Arg *arg); -static void toggleview(const Arg *arg); -static void unfocus(Client *c, int setfocus); -static void unmanage(Client *c, int destroyed); -static void unmapnotify(XEvent *e); -static void updatebarpos(Monitor *m); -static void updatebars(void); -static void updateclientlist(void); -static int updategeom(void); -static void updatenumlockmask(void); -static void updatesizehints(Client *c); -static void updatestatus(void); -static void updatetitle(Client *c); -static void updatewindowtype(Client *c); -static void updatewmhints(Client *c); -static void view(const Arg *arg); -static Client *wintoclient(Window w); -static Monitor *wintomon(Window w); -static int xerror(Display *dpy, XErrorEvent *ee); -static int xerrordummy(Display *dpy, XErrorEvent *ee); -static int xerrorstart(Display *dpy, XErrorEvent *ee); -static void zoom(const Arg *arg); - -/* variables */ -static const char broken[] = "broken"; -static char stext[256]; -static int screen; -static int sw, sh; /* X display screen geometry width, height */ -static int bh, blw = 0; /* bar geometry */ -static int lrpad; /* sum of left and right padding for text */ -static int (*xerrorxlib)(Display *, XErrorEvent *); -static unsigned int numlockmask = 0; -static void (*handler[LASTEvent]) (XEvent *) = { - [ButtonPress] = buttonpress, - [ClientMessage] = clientmessage, - [ConfigureRequest] = configurerequest, - [ConfigureNotify] = configurenotify, - [DestroyNotify] = destroynotify, - [EnterNotify] = enternotify, - [Expose] = expose, - [FocusIn] = focusin, - [KeyPress] = keypress, - [MappingNotify] = mappingnotify, - [MapRequest] = maprequest, - [MotionNotify] = motionnotify, - [PropertyNotify] = propertynotify, - [UnmapNotify] = unmapnotify -}; -static Atom wmatom[WMLast], netatom[NetLast]; -static int running = 1; -static Cur *cursor[CurLast]; -static Clr **scheme; -static Display *dpy; -static Drw *drw; -static Monitor *mons, *selmon; -static Window root, wmcheckwin; - -/* configuration, allows nested code to access above variables */ -#include "config.h" - -/* compile-time check if all tags fit into an unsigned int bit array. */ -struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; - -/* function implementations */ -void -applyrules(Client *c) -{ - const char *class, *instance; - unsigned int i; - const Rule *r; - Monitor *m; - XClassHint ch = { NULL, NULL }; - - /* rule matching */ - c->isfloating = 0; - c->tags = 0; - 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; - c->tags |= r->tags; - for (m = mons; m && m->num != r->monitor; m = m->next); - if (m) - c->mon = m; - } - } - if (ch.res_class) - XFree(ch.res_class); - if (ch.res_name) - XFree(ch.res_name); - c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags]; -} - -int -applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact) -{ - int baseismin; - Monitor *m = c->mon; - - /* set minimum possible */ - *w = MAX(1, *w); - *h = MAX(1, *h); - if (interact) { - if (*x > sw) - *x = sw - WIDTH(c); - if (*y > sh) - *y = sh - HEIGHT(c); - if (*x + *w + 2 * c->bw < 0) - *x = 0; - if (*y + *h + 2 * c->bw < 0) - *y = 0; - } else { - if (*x >= m->wx + m->ww) - *x = m->wx + m->ww - WIDTH(c); - if (*y >= m->wy + m->wh) - *y = m->wy + m->wh - HEIGHT(c); - if (*x + *w + 2 * c->bw <= m->wx) - *x = m->wx; - if (*y + *h + 2 * c->bw <= m->wy) - *y = m->wy; - } - if (*h < bh) - *h = bh; - if (*w < bh) - *w = bh; - if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) { - /* see last two sentences in ICCCM 4.1.2.3 */ - baseismin = c->basew == c->minw && c->baseh == c->minh; - if (!baseismin) { /* temporarily remove base dimensions */ - *w -= c->basew; - *h -= c->baseh; - } - /* adjust for aspect limits */ - if (c->mina > 0 && c->maxa > 0) { - if (c->maxa < (float)*w / *h) - *w = *h * c->maxa + 0.5; - else if (c->mina < (float)*h / *w) - *h = *w * c->mina + 0.5; - } - if (baseismin) { /* increment calculation requires this */ - *w -= c->basew; - *h -= c->baseh; - } - /* adjust for increment value */ - if (c->incw) - *w -= *w % c->incw; - if (c->inch) - *h -= *h % c->inch; - /* restore base dimensions */ - *w = MAX(*w + c->basew, c->minw); - *h = MAX(*h + c->baseh, c->minh); - if (c->maxw) - *w = MIN(*w, c->maxw); - if (c->maxh) - *h = MIN(*h, c->maxh); - } - return *x != c->x || *y != c->y || *w != c->w || *h != c->h; -} - -void -arrange(Monitor *m) -{ - if (m) - showhide(m->stack); - else for (m = mons; m; m = m->next) - showhide(m->stack); - if (m) { - arrangemon(m); - restack(m); - } else for (m = mons; m; m = m->next) - arrangemon(m); -} - -void -arrangemon(Monitor *m) -{ - strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); - if (m->lt[m->sellt]->arrange) - m->lt[m->sellt]->arrange(m); -} - -void -attach(Client *c) -{ - c->next = c->mon->clients; - c->mon->clients = c; -} - -void -attachstack(Client *c) -{ - c->snext = c->mon->stack; - c->mon->stack = c; -} - -void -buttonpress(XEvent *e) -{ - unsigned int i, x, click; - Arg arg = {0}; - Client *c; - Monitor *m; - XButtonPressedEvent *ev = &e->xbutton; - - click = ClkRootWin; - /* focus monitor if necessary */ - if ((m = wintomon(ev->window)) && m != selmon) { - unfocus(selmon->sel, 1); - selmon = m; - focus(NULL); - } - if (ev->window == selmon->barwin) { - i = x = 0; - do - x += TEXTW(tags[i]); - while (ev->x >= x && ++i < LENGTH(tags)); - if (i < LENGTH(tags)) { - click = ClkTagBar; - arg.ui = 1 << i; - } else if (ev->x < x + blw) - click = ClkLtSymbol; - else if (ev->x > selmon->ww - TEXTW(stext)) - click = ClkStatusText; - else - click = ClkWinTitle; - } else if ((c = wintoclient(ev->window))) { - focus(c); - restack(selmon); - XAllowEvents(dpy, ReplayPointer, CurrentTime); - click = ClkClientWin; - } - for (i = 0; i < LENGTH(buttons); i++) - if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button - && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) - buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); -} - -void -checkotherwm(void) -{ - xerrorxlib = XSetErrorHandler(xerrorstart); - /* this causes an error if some other window manager is running */ - XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask); - XSync(dpy, False); - XSetErrorHandler(xerror); - XSync(dpy, False); -} - -void -cleanup(void) -{ - Arg a = {.ui = ~0}; - Layout foo = { "", NULL }; - Monitor *m; - size_t i; - - view(&a); - selmon->lt[selmon->sellt] = &foo; - for (m = mons; m; m = m->next) - while (m->stack) - unmanage(m->stack, 0); - XUngrabKey(dpy, AnyKey, AnyModifier, root); - while (mons) - cleanupmon(mons); - for (i = 0; i < CurLast; i++) - drw_cur_free(drw, cursor[i]); - for (i = 0; i < LENGTH(colors); i++) - free(scheme[i]); - XDestroyWindow(dpy, wmcheckwin); - drw_free(drw); - XSync(dpy, False); - XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); - XDeleteProperty(dpy, root, netatom[NetActiveWindow]); -} - -void -cleanupmon(Monitor *mon) -{ - Monitor *m; - - if (mon == mons) - mons = mons->next; - else { - for (m = mons; m && m->next != mon; m = m->next); - m->next = mon->next; - } - XUnmapWindow(dpy, mon->barwin); - XDestroyWindow(dpy, mon->barwin); - free(mon); -} - -void -clientmessage(XEvent *e) -{ - XClientMessageEvent *cme = &e->xclient; - Client *c = wintoclient(cme->window); - - if (!c) - return; - if (cme->message_type == netatom[NetWMState]) { - if (cme->data.l[1] == netatom[NetWMFullscreen] - || cme->data.l[2] == netatom[NetWMFullscreen]) - setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */ - || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen))); - } else if (cme->message_type == netatom[NetActiveWindow]) { - if (c != selmon->sel && !c->isurgent) - seturgent(c, 1); - } -} - -void -configure(Client *c) -{ - XConfigureEvent ce; - - ce.type = ConfigureNotify; - ce.display = dpy; - ce.event = c->win; - ce.window = c->win; - ce.x = c->x; - ce.y = c->y; - ce.width = c->w; - ce.height = c->h; - ce.border_width = c->bw; - ce.above = None; - ce.override_redirect = False; - XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); -} - -void -configurenotify(XEvent *e) -{ - Monitor *m; - Client *c; - XConfigureEvent *ev = &e->xconfigure; - int dirty; - - /* TODO: updategeom handling sucks, needs to be simplified */ - if (ev->window == root) { - dirty = (sw != ev->width || sh != ev->height); - sw = ev->width; - sh = ev->height; - if (updategeom() || dirty) { - drw_resize(drw, sw, bh); - updatebars(); - for (m = mons; m; m = m->next) { - for (c = m->clients; c; c = c->next) - if (c->isfullscreen) - resizeclient(c, m->mx, m->my, m->mw, m->mh); - XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); - } - focus(NULL); - arrange(NULL); - } - } -} - -void -configurerequest(XEvent *e) -{ - Client *c; - Monitor *m; - XConfigureRequestEvent *ev = &e->xconfigurerequest; - XWindowChanges wc; - - if ((c = wintoclient(ev->window))) { - if (ev->value_mask & CWBorderWidth) - c->bw = ev->border_width; - else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) { - m = c->mon; - if (ev->value_mask & CWX) { - c->oldx = c->x; - c->x = m->mx + ev->x; - } - if (ev->value_mask & CWY) { - c->oldy = c->y; - c->y = m->my + ev->y; - } - if (ev->value_mask & CWWidth) { - c->oldw = c->w; - c->w = ev->width; - } - if (ev->value_mask & CWHeight) { - c->oldh = c->h; - c->h = ev->height; - } - if ((c->x + c->w) > m->mx + m->mw && c->isfloating) - c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */ - if ((c->y + c->h) > m->my + m->mh && c->isfloating) - c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */ - if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight))) - configure(c); - if (ISVISIBLE(c)) - XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); - } else - configure(c); - } else { - wc.x = ev->x; - wc.y = ev->y; - wc.width = ev->width; - wc.height = ev->height; - wc.border_width = ev->border_width; - wc.sibling = ev->above; - wc.stack_mode = ev->detail; - XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); - } - XSync(dpy, False); -} - -Monitor * -createmon(void) -{ - Monitor *m; - - m = ecalloc(1, sizeof(Monitor)); - m->tagset[0] = m->tagset[1] = 1; - m->mfact = mfact; - m->nmaster = nmaster; - m->showbar = showbar; - m->topbar = topbar; - m->gappx = gappx; - m->lt[0] = &layouts[0]; - m->lt[1] = &layouts[1 % LENGTH(layouts)]; - strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); - return m; -} - -void -destroynotify(XEvent *e) -{ - Client *c; - XDestroyWindowEvent *ev = &e->xdestroywindow; - - if ((c = wintoclient(ev->window))) - unmanage(c, 1); -} - -void -detach(Client *c) -{ - Client **tc; - - for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next); - *tc = c->next; -} - -void -detachstack(Client *c) -{ - Client **tc, *t; - - for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext); - *tc = c->snext; - - if (c == c->mon->sel) { - for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext); - c->mon->sel = t; - } -} - -Monitor * -dirtomon(int dir) -{ - Monitor *m = NULL; - - if (dir > 0) { - if (!(m = selmon->next)) - m = mons; - } else if (selmon == mons) - for (m = mons; m->next; m = m->next); - else - for (m = mons; m->next != selmon; m = m->next); - return m; -} - -void -drawbar(Monitor *m) -{ - int x, w, sw = 0; - int boxs = drw->fonts->h / 9; - int boxw = drw->fonts->h / 6 + 2; - unsigned int i, occ = 0, urg = 0; - Client *c; - - /* draw status first so it can be overdrawn by tags later */ - if (m == selmon) { /* status is only drawn on selected monitor */ - drw_setscheme(drw, scheme[SchemeNorm]); - sw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ - drw_text(drw, m->ww - sw, 0, sw, bh, 0, stext, 0); - } - - for (c = m->clients; c; c = c->next) { - occ |= c->tags; - if (c->isurgent) - urg |= c->tags; - } - x = 0; - for (i = 0; i < LENGTH(tags); i++) { - w = TEXTW(tags[i]); - drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); - drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); - if (occ & 1 << i) - drw_rect(drw, x + boxs, boxs, boxw, boxw, - m == selmon && selmon->sel && selmon->sel->tags & 1 << i, - urg & 1 << i); - x += w; - } - w = blw = TEXTW(m->ltsymbol); - drw_setscheme(drw, scheme[SchemeNorm]); - x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); - - if ((w = m->ww - sw - x) > bh) { - if (m->sel) { - drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); - drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); - if (m->sel->isfloating) - drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); - } else { - drw_setscheme(drw, scheme[SchemeNorm]); - drw_rect(drw, x, 0, w, bh, 1, 1); - } - } - drw_map(drw, m->barwin, 0, 0, m->ww, bh); -} - -void -drawbars(void) -{ - Monitor *m; - - for (m = mons; m; m = m->next) - drawbar(m); -} - -void -enternotify(XEvent *e) -{ - Client *c; - Monitor *m; - XCrossingEvent *ev = &e->xcrossing; - - if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root) - return; - c = wintoclient(ev->window); - m = c ? c->mon : wintomon(ev->window); - if (m != selmon) { - unfocus(selmon->sel, 1); - selmon = m; - } else if (!c || c == selmon->sel) - return; - focus(c); -} - -void -expose(XEvent *e) -{ - Monitor *m; - XExposeEvent *ev = &e->xexpose; - - if (ev->count == 0 && (m = wintomon(ev->window))) - drawbar(m); -} - -void -focus(Client *c) -{ - if (!c || !ISVISIBLE(c)) - for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); - if (selmon->sel && selmon->sel != c) - unfocus(selmon->sel, 0); - if (c) { - if (c->mon != selmon) - selmon = c->mon; - if (c->isurgent) - seturgent(c, 0); - detachstack(c); - attachstack(c); - grabbuttons(c, 1); - XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); - setfocus(c); - } else { - XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); - XDeleteProperty(dpy, root, netatom[NetActiveWindow]); - } - selmon->sel = c; - drawbars(); -} - -/* there are some broken focus acquiring clients needing extra handling */ -void -focusin(XEvent *e) -{ - XFocusChangeEvent *ev = &e->xfocus; - - if (selmon->sel && ev->window != selmon->sel->win) - setfocus(selmon->sel); -} - -void -focusmon(const Arg *arg) -{ - Monitor *m; - - if (!mons->next) - return; - if ((m = dirtomon(arg->i)) == selmon) - return; - unfocus(selmon->sel, 0); - selmon = m; - focus(NULL); -} - -void -focusstack(const Arg *arg) -{ - Client *c = NULL, *i; - - if (!selmon->sel) - return; - if (arg->i > 0) { - for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); - if (!c) - for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next); - } else { - for (i = selmon->clients; i != selmon->sel; i = i->next) - if (ISVISIBLE(i)) - c = i; - if (!c) - for (; i; i = i->next) - if (ISVISIBLE(i)) - c = i; - } - if (c) { - focus(c); - restack(selmon); - } -} - -Atom -getatomprop(Client *c, Atom prop) -{ - int di; - unsigned long dl; - unsigned char *p = NULL; - Atom da, atom = None; - - if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, - &da, &di, &dl, &dl, &p) == Success && p) { - atom = *(Atom *)p; - XFree(p); - } - return atom; -} - -int -getrootptr(int *x, int *y) -{ - int di; - unsigned int dui; - Window dummy; - - return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui); -} - -long -getstate(Window w) -{ - int format; - long result = -1; - unsigned char *p = NULL; - unsigned long n, extra; - Atom real; - - if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], - &real, &format, &n, &extra, (unsigned char **)&p) != Success) - return -1; - if (n != 0) - result = *p; - XFree(p); - return result; -} - -int -gettextprop(Window w, Atom atom, char *text, unsigned int size) -{ - char **list = NULL; - int n; - XTextProperty name; - - if (!text || size == 0) - return 0; - text[0] = '\0'; - if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems) - return 0; - if (name.encoding == XA_STRING) - strncpy(text, (char *)name.value, size - 1); - else { - if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { - strncpy(text, *list, size - 1); - XFreeStringList(list); - } - } - text[size - 1] = '\0'; - XFree(name.value); - return 1; -} - -void -grabbuttons(Client *c, int focused) -{ - updatenumlockmask(); - { - unsigned int i, j; - unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; - XUngrabButton(dpy, AnyButton, AnyModifier, c->win); - if (!focused) - XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, - BUTTONMASK, GrabModeSync, GrabModeSync, None, None); - for (i = 0; i < LENGTH(buttons); i++) - if (buttons[i].click == ClkClientWin) - for (j = 0; j < LENGTH(modifiers); j++) - XGrabButton(dpy, buttons[i].button, - buttons[i].mask | modifiers[j], - c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - } -} - -void -grabkeys(void) -{ - updatenumlockmask(); - { - unsigned int i, j; - unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; - KeyCode code; - - XUngrabKey(dpy, AnyKey, AnyModifier, root); - for (i = 0; i < LENGTH(keys); i++) - if ((code = XKeysymToKeycode(dpy, keys[i].keysym))) - for (j = 0; j < LENGTH(modifiers); j++) - XGrabKey(dpy, code, keys[i].mod | modifiers[j], root, - True, GrabModeAsync, GrabModeAsync); - } -} - -void -incnmaster(const Arg *arg) -{ - selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); - arrange(selmon); -} - -#ifdef XINERAMA -static int -isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) -{ - while (n--) - if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org - && unique[n].width == info->width && unique[n].height == info->height) - return 0; - return 1; -} -#endif /* XINERAMA */ - -void -keypress(XEvent *e) -{ - unsigned int i; - KeySym keysym; - XKeyEvent *ev; - - ev = &e->xkey; - keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); - for (i = 0; i < LENGTH(keys); i++) - if (keysym == keys[i].keysym - && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) - && keys[i].func) - keys[i].func(&(keys[i].arg)); -} - -void -killclient(const Arg *arg) -{ - if (!selmon->sel) - return; - if (!sendevent(selmon->sel, wmatom[WMDelete])) { - XGrabServer(dpy); - XSetErrorHandler(xerrordummy); - XSetCloseDownMode(dpy, DestroyAll); - XKillClient(dpy, selmon->sel->win); - XSync(dpy, False); - XSetErrorHandler(xerror); - XUngrabServer(dpy); - } -} - -void -manage(Window w, XWindowAttributes *wa) -{ - Client *c, *t = NULL; - Window trans = None; - XWindowChanges wc; - - c = ecalloc(1, sizeof(Client)); - c->win = w; - /* geometry */ - c->x = c->oldx = wa->x; - c->y = c->oldy = wa->y; - c->w = c->oldw = wa->width; - c->h = c->oldh = wa->height; - c->oldbw = wa->border_width; - - updatetitle(c); - if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { - c->mon = t->mon; - c->tags = t->tags; - } else { - c->mon = selmon; - applyrules(c); - } - - if (c->x + WIDTH(c) > c->mon->mx + c->mon->mw) - c->x = c->mon->mx + c->mon->mw - WIDTH(c); - if (c->y + HEIGHT(c) > c->mon->my + c->mon->mh) - c->y = c->mon->my + c->mon->mh - HEIGHT(c); - c->x = MAX(c->x, c->mon->mx); - /* only fix client y-offset, if the client center might cover the bar */ - c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) - && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); - c->bw = borderpx; - - wc.border_width = c->bw; - XConfigureWindow(dpy, w, CWBorderWidth, &wc); - XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); - configure(c); /* propagates border_width, if size doesn't change */ - updatewindowtype(c); - updatesizehints(c); - updatewmhints(c); - XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); - grabbuttons(c, 0); - if (!c->isfloating) - c->isfloating = c->oldstate = trans != None || c->isfixed; - if (c->isfloating) - XRaiseWindow(dpy, c->win); - attach(c); - attachstack(c); - XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, - (unsigned char *) &(c->win), 1); - XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ - setclientstate(c, NormalState); - if (c->mon == selmon) - unfocus(selmon->sel, 0); - c->mon->sel = c; - arrange(c->mon); - XMapWindow(dpy, c->win); - focus(NULL); -} - -void -mappingnotify(XEvent *e) -{ - XMappingEvent *ev = &e->xmapping; - - XRefreshKeyboardMapping(ev); - if (ev->request == MappingKeyboard) - grabkeys(); -} - -void -maprequest(XEvent *e) -{ - static XWindowAttributes wa; - XMapRequestEvent *ev = &e->xmaprequest; - - if (!XGetWindowAttributes(dpy, ev->window, &wa)) - return; - if (wa.override_redirect) - return; - if (!wintoclient(ev->window)) - manage(ev->window, &wa); -} - -void -monocle(Monitor *m) -{ - unsigned int n = 0; - Client *c; - - 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); - 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); -} - -void -motionnotify(XEvent *e) -{ - static Monitor *mon = NULL; - Monitor *m; - XMotionEvent *ev = &e->xmotion; - - if (ev->window != root) - return; - if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { - unfocus(selmon->sel, 1); - selmon = m; - focus(NULL); - } - mon = m; -} - -void -movemouse(const Arg *arg) -{ - int x, y, ocx, ocy, nx, ny; - Client *c; - Monitor *m; - XEvent ev; - Time lasttime = 0; - - if (!(c = selmon->sel)) - return; - if (c->isfullscreen) /* no support moving fullscreen windows by mouse */ - return; - restack(selmon); - ocx = c->x; - ocy = c->y; - if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, - None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) - return; - 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 (abs(selmon->wx - nx) < snap) - nx = selmon->wx; - else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap) - nx = selmon->wx + selmon->ww - WIDTH(c); - if (abs(selmon->wy - ny) < snap) - ny = selmon->wy; - else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap) - ny = selmon->wy + selmon->wh - HEIGHT(c); - if (!c->isfloating && selmon->lt[selmon->sellt]->arrange - && (abs(nx - c->x) > snap || abs(ny - c->y) > snap)) - togglefloating(NULL); - if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) - resize(c, nx, ny, c->w, c->h, 1); - break; - } - } while (ev.type != ButtonRelease); - XUngrabPointer(dpy, CurrentTime); - if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { - sendmon(c, m); - selmon = m; - focus(NULL); - } -} - -Client * -nexttiled(Client *c) -{ - for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); - return c; -} - -void -pop(Client *c) -{ - detach(c); - attach(c); - focus(c); - arrange(c->mon); -} - -void -propertynotify(XEvent *e) -{ - Client *c; - Window trans; - XPropertyEvent *ev = &e->xproperty; - - if ((ev->window == root) && (ev->atom == XA_WM_NAME)) - updatestatus(); - else if (ev->state == PropertyDelete) - return; /* ignore */ - else if ((c = wintoclient(ev->window))) { - switch(ev->atom) { - default: break; - case XA_WM_TRANSIENT_FOR: - if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) && - (c->isfloating = (wintoclient(trans)) != NULL)) - arrange(c->mon); - break; - case XA_WM_NORMAL_HINTS: - updatesizehints(c); - break; - case XA_WM_HINTS: - updatewmhints(c); - drawbars(); - break; - } - if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { - updatetitle(c); - if (c == c->mon->sel) - drawbar(c->mon); - } - if (ev->atom == netatom[NetWMWindowType]) - updatewindowtype(c); - } -} - -void -quit(const Arg *arg) -{ - running = 0; -} - -Monitor * -recttomon(int x, int y, int w, int h) -{ - Monitor *m, *r = selmon; - int a, area = 0; - - for (m = mons; m; m = m->next) - if ((a = INTERSECT(x, y, w, h, m)) > area) { - area = a; - r = m; - } - return r; -} - -void -resize(Client *c, int x, int y, int w, int h, int interact) -{ - if (applysizehints(c, &x, &y, &w, &h, interact)) - resizeclient(c, x, y, w, h); -} - -void -resizeclient(Client *c, int x, int y, int w, int h) -{ - XWindowChanges wc; - - c->oldx = c->x; c->x = wc.x = x; - c->oldy = c->y; c->y = wc.y = y; - c->oldw = c->w; c->w = wc.width = w; - c->oldh = c->h; c->h = wc.height = h; - wc.border_width = c->bw; - XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); - configure(c); - XSync(dpy, False); -} - -void -resizemouse(const Arg *arg) -{ - int ocx, ocy, nw, nh; - Client *c; - Monitor *m; - XEvent ev; - Time lasttime = 0; - - if (!(c = selmon->sel)) - return; - if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ - return; - restack(selmon); - ocx = c->x; - ocy = c->y; - if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, - None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) - return; - XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); - 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; - - nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1); - nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 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); - break; - } - } while (ev.type != ButtonRelease); - 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); - } -} - -void -restack(Monitor *m) -{ - Client *c; - XEvent ev; - XWindowChanges wc; - - drawbar(m); - if (!m->sel) - return; - if (m->sel->isfloating || !m->lt[m->sellt]->arrange) - XRaiseWindow(dpy, m->sel->win); - if (m->lt[m->sellt]->arrange) { - wc.stack_mode = Below; - wc.sibling = m->barwin; - for (c = m->stack; c; c = c->snext) - if (!c->isfloating && ISVISIBLE(c)) { - XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); - wc.sibling = c->win; - } - } - XSync(dpy, False); - while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); -} - -void -run(void) -{ - XEvent ev; - /* main event loop */ - XSync(dpy, False); - while (running && !XNextEvent(dpy, &ev)) - if (handler[ev.type]) - handler[ev.type](&ev); /* call handler */ -} - -void -scan(void) -{ - unsigned int i, num; - Window d1, d2, *wins = NULL; - XWindowAttributes wa; - - if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { - for (i = 0; i < num; i++) { - if (!XGetWindowAttributes(dpy, wins[i], &wa) - || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) - continue; - if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) - manage(wins[i], &wa); - } - for (i = 0; i < num; i++) { /* now the transients */ - if (!XGetWindowAttributes(dpy, wins[i], &wa)) - continue; - if (XGetTransientForHint(dpy, wins[i], &d1) - && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) - manage(wins[i], &wa); - } - if (wins) - XFree(wins); - } -} - -void -sendmon(Client *c, Monitor *m) -{ - if (c->mon == m) - return; - unfocus(c, 1); - detach(c); - detachstack(c); - c->mon = m; - c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ - attach(c); - attachstack(c); - focus(NULL); - arrange(NULL); -} - -void -setclientstate(Client *c, long state) -{ - long data[] = { state, None }; - - XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, - PropModeReplace, (unsigned char *)data, 2); -} - -int -sendevent(Client *c, Atom proto) -{ - int n; - Atom *protocols; - int exists = 0; - XEvent ev; - - if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { - while (!exists && n--) - exists = protocols[n] == proto; - XFree(protocols); - } - if (exists) { - ev.type = ClientMessage; - ev.xclient.window = c->win; - ev.xclient.message_type = wmatom[WMProtocols]; - ev.xclient.format = 32; - ev.xclient.data.l[0] = proto; - ev.xclient.data.l[1] = CurrentTime; - XSendEvent(dpy, c->win, False, NoEventMask, &ev); - } - return exists; -} - -void -setfocus(Client *c) -{ - if (!c->neverfocus) { - XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); - XChangeProperty(dpy, root, netatom[NetActiveWindow], - XA_WINDOW, 32, PropModeReplace, - (unsigned char *) &(c->win), 1); - } - sendevent(c, wmatom[WMTakeFocus]); -} - -void -setfullscreen(Client *c, int fullscreen) -{ - if (fullscreen && !c->isfullscreen) { - XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, - PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); - c->isfullscreen = 1; - c->oldstate = c->isfloating; - c->oldbw = c->bw; - c->bw = 0; - c->isfloating = 1; - resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); - XRaiseWindow(dpy, c->win); - } else if (!fullscreen && c->isfullscreen){ - XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, - PropModeReplace, (unsigned char*)0, 0); - c->isfullscreen = 0; - c->isfloating = c->oldstate; - c->bw = c->oldbw; - c->x = c->oldx; - c->y = c->oldy; - c->w = c->oldw; - c->h = c->oldh; - resizeclient(c, c->x, c->y, c->w, c->h); - arrange(c->mon); - } -} - -void -setgaps(const Arg *arg) -{ - if ((arg->i == 0) || (selmon->gappx + arg->i < 0)) - selmon->gappx = 0; - else - selmon->gappx += arg->i; - arrange(selmon); -} - -void -setlayout(const Arg *arg) -{ - if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) - selmon->sellt ^= 1; - if (arg && arg->v) - selmon->lt[selmon->sellt] = (Layout *)arg->v; - strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); - if (selmon->sel) - arrange(selmon); - else - drawbar(selmon); -} - -/* arg > 1.0 will set mfact absolutely */ -void -setmfact(const Arg *arg) -{ - float f; - - if (!arg || !selmon->lt[selmon->sellt]->arrange) - return; - f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; - if (f < 0.1 || f > 0.9) - return; - selmon->mfact = f; - arrange(selmon); -} - -void -setup(void) -{ - int i; - XSetWindowAttributes wa; - Atom utf8string; - - /* clean up any zombies immediately */ - sigchld(0); - - /* init screen */ - screen = DefaultScreen(dpy); - sw = DisplayWidth(dpy, screen); - sh = DisplayHeight(dpy, screen); - root = RootWindow(dpy, screen); - drw = drw_create(dpy, screen, root, sw, sh); - if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) - die("no fonts could be loaded."); - lrpad = drw->fonts->h; - bh = drw->fonts->h + 2; - updategeom(); - /* init atoms */ - utf8string = XInternAtom(dpy, "UTF8_STRING", False); - wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); - wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); - wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); - wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); - netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); - netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); - netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); - netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); - netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); - netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); - netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); - netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); - netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); - /* init cursors */ - cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); - cursor[CurResize] = drw_cur_create(drw, XC_sizing); - cursor[CurMove] = drw_cur_create(drw, XC_fleur); - /* init appearance */ - scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); - for (i = 0; i < LENGTH(colors); i++) - scheme[i] = drw_scm_create(drw, colors[i], 3); - /* init bars */ - updatebars(); - updatestatus(); - /* supporting window for NetWMCheck */ - wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); - XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32, - PropModeReplace, (unsigned char *) &wmcheckwin, 1); - XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8, - PropModeReplace, (unsigned char *) "dwm", 3); - XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32, - PropModeReplace, (unsigned char *) &wmcheckwin, 1); - /* EWMH support per view */ - XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, - PropModeReplace, (unsigned char *) netatom, NetLast); - XDeleteProperty(dpy, root, netatom[NetClientList]); - /* select events */ - wa.cursor = cursor[CurNormal]->cursor; - wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask - |ButtonPressMask|PointerMotionMask|EnterWindowMask - |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; - XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); - XSelectInput(dpy, root, wa.event_mask); - grabkeys(); - focus(NULL); -} - - -void -seturgent(Client *c, int urg) -{ - XWMHints *wmh; - - c->isurgent = urg; - if (!(wmh = XGetWMHints(dpy, c->win))) - return; - wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint); - XSetWMHints(dpy, c->win, wmh); - XFree(wmh); -} - -void -showhide(Client *c) -{ - if (!c) - return; - if (ISVISIBLE(c)) { - /* show clients top down */ - XMoveWindow(dpy, c->win, c->x, c->y); - if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen) - resize(c, c->x, c->y, c->w, c->h, 0); - showhide(c->snext); - } else { - /* hide clients bottom up */ - showhide(c->snext); - XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); - } -} - -void -sigchld(int unused) -{ - if (signal(SIGCHLD, sigchld) == SIG_ERR) - die("can't install SIGCHLD handler:"); - while (0 < waitpid(-1, NULL, WNOHANG)); -} - -void -spawn(const Arg *arg) -{ - if (arg->v == dmenucmd) - dmenumon[0] = '0' + selmon->num; - if (fork() == 0) { - if (dpy) - close(ConnectionNumber(dpy)); - setsid(); - execvp(((char **)arg->v)[0], (char **)arg->v); - fprintf(stderr, "dwm: execvp %s", ((char **)arg->v)[0]); - perror(" failed"); - exit(EXIT_SUCCESS); - } -} - -void -tag(const Arg *arg) -{ - if (selmon->sel && arg->ui & TAGMASK) { - selmon->sel->tags = arg->ui & TAGMASK; - focus(NULL); - arrange(selmon); - } -} - -void -tagmon(const Arg *arg) -{ - if (!selmon->sel || !mons->next) - return; - sendmon(selmon->sel, dirtomon(arg->i)); -} - -void -tile(Monitor *m) -{ - unsigned int i, n, h, mw, my, ty; - Client *c; - - for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); - if (n == 0) - return; - - if (n > m->nmaster) - mw = m->nmaster ? m->ww * m->mfact : 0; - else - mw = m->ww - m->gappx; - for (i = 0, my = ty = m->gappx, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) - if (i < m->nmaster) { - h = (m->wh - my) / (MIN(n, m->nmaster) - i) - m->gappx; - resize(c, m->wx + m->gappx, m->wy + my, mw - (2*c->bw) - m->gappx, h - (2*c->bw), 0); - my += HEIGHT(c) + m->gappx; - } else { - h = (m->wh - ty) / (n - i) - m->gappx; - resize(c, m->wx + mw + m->gappx, m->wy + ty, m->ww - mw - (2*c->bw) - 2*m->gappx, h - (2*c->bw), 0); - ty += HEIGHT(c) + m->gappx; - } -} - -void -togglebar(const Arg *arg) -{ - selmon->showbar = !selmon->showbar; - updatebarpos(selmon); - XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); - arrange(selmon); -} - -void -togglefloating(const Arg *arg) -{ - if (!selmon->sel) - return; - if (selmon->sel->isfullscreen) /* no support for fullscreen windows */ - return; - selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; - if (selmon->sel->isfloating) - resize(selmon->sel, selmon->sel->x, selmon->sel->y, - selmon->sel->w, selmon->sel->h, 0); - arrange(selmon); -} - -void -toggletag(const Arg *arg) -{ - unsigned int newtags; - - if (!selmon->sel) - return; - newtags = selmon->sel->tags ^ (arg->ui & TAGMASK); - if (newtags) { - selmon->sel->tags = newtags; - focus(NULL); - arrange(selmon); - } -} - -void -toggleview(const Arg *arg) -{ - unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); - - if (newtagset) { - selmon->tagset[selmon->seltags] = newtagset; - focus(NULL); - arrange(selmon); - } -} - -void -unfocus(Client *c, int setfocus) -{ - if (!c) - return; - grabbuttons(c, 0); - XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); - if (setfocus) { - XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); - XDeleteProperty(dpy, root, netatom[NetActiveWindow]); - } -} - -void -unmanage(Client *c, int destroyed) -{ - Monitor *m = c->mon; - XWindowChanges wc; - - detach(c); - detachstack(c); - if (!destroyed) { - wc.border_width = c->oldbw; - XGrabServer(dpy); /* avoid race conditions */ - XSetErrorHandler(xerrordummy); - XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ - XUngrabButton(dpy, AnyButton, AnyModifier, c->win); - setclientstate(c, WithdrawnState); - XSync(dpy, False); - XSetErrorHandler(xerror); - XUngrabServer(dpy); - } - free(c); - focus(NULL); - updateclientlist(); - arrange(m); -} - -void -unmapnotify(XEvent *e) -{ - Client *c; - XUnmapEvent *ev = &e->xunmap; - - if ((c = wintoclient(ev->window))) { - if (ev->send_event) - setclientstate(c, WithdrawnState); - else - unmanage(c, 0); - } -} - -void -updatebars(void) -{ - Monitor *m; - XSetWindowAttributes wa = { - .override_redirect = True, - .background_pixmap = ParentRelative, - .event_mask = ButtonPressMask|ExposureMask - }; - XClassHint ch = {"dwm", "dwm"}; - for (m = mons; m; m = m->next) { - if (m->barwin) - continue; - m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), - CopyFromParent, DefaultVisual(dpy, screen), - CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); - XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); - XMapRaised(dpy, m->barwin); - XSetClassHint(dpy, m->barwin, &ch); - } -} - -void -updatebarpos(Monitor *m) -{ - m->wy = m->my; - m->wh = m->mh; - if (m->showbar) { - m->wh -= bh; - m->by = m->topbar ? m->wy : m->wy + m->wh; - m->wy = m->topbar ? m->wy + bh : m->wy; - } else - m->by = -bh; -} - -void -updateclientlist() -{ - Client *c; - Monitor *m; - - XDeleteProperty(dpy, root, netatom[NetClientList]); - for (m = mons; m; m = m->next) - for (c = m->clients; c; c = c->next) - XChangeProperty(dpy, root, netatom[NetClientList], - XA_WINDOW, 32, PropModeAppend, - (unsigned char *) &(c->win), 1); -} - -int -updategeom(void) -{ - int dirty = 0; - -#ifdef XINERAMA - if (XineramaIsActive(dpy)) { - int i, j, n, nn; - Client *c; - Monitor *m; - XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn); - XineramaScreenInfo *unique = NULL; - - for (n = 0, m = mons; m; m = m->next, n++); - /* only consider unique geometries as separate screens */ - unique = ecalloc(nn, sizeof(XineramaScreenInfo)); - for (i = 0, j = 0; i < nn; i++) - if (isuniquegeom(unique, j, &info[i])) - memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo)); - XFree(info); - nn = j; - if (n <= nn) { /* new monitors available */ - for (i = 0; i < (nn - n); i++) { - for (m = mons; m && m->next; m = m->next); - if (m) - m->next = createmon(); - else - mons = createmon(); - } - for (i = 0, m = mons; i < nn && m; m = m->next, i++) - if (i >= n - || unique[i].x_org != m->mx || unique[i].y_org != m->my - || unique[i].width != m->mw || unique[i].height != m->mh) - { - dirty = 1; - m->num = i; - m->mx = m->wx = unique[i].x_org; - m->my = m->wy = unique[i].y_org; - m->mw = m->ww = unique[i].width; - m->mh = m->wh = unique[i].height; - updatebarpos(m); - } - } else { /* less monitors available nn < n */ - for (i = nn; i < n; i++) { - for (m = mons; m && m->next; m = m->next); - while ((c = m->clients)) { - dirty = 1; - m->clients = c->next; - detachstack(c); - c->mon = mons; - attach(c); - attachstack(c); - } - if (m == selmon) - selmon = mons; - cleanupmon(m); - } - } - free(unique); - } else -#endif /* XINERAMA */ - { /* default monitor setup */ - if (!mons) - mons = createmon(); - if (mons->mw != sw || mons->mh != sh) { - dirty = 1; - mons->mw = mons->ww = sw; - mons->mh = mons->wh = sh; - updatebarpos(mons); - } - } - if (dirty) { - selmon = mons; - selmon = wintomon(root); - } - return dirty; -} - -void -updatenumlockmask(void) -{ - unsigned int i, j; - XModifierKeymap *modmap; - - numlockmask = 0; - modmap = XGetModifierMapping(dpy); - for (i = 0; i < 8; i++) - for (j = 0; j < modmap->max_keypermod; j++) - if (modmap->modifiermap[i * modmap->max_keypermod + j] - == XKeysymToKeycode(dpy, XK_Num_Lock)) - numlockmask = (1 << i); - XFreeModifiermap(modmap); -} - -void -updatesizehints(Client *c) -{ - long msize; - XSizeHints size; - - if (!XGetWMNormalHints(dpy, c->win, &size, &msize)) - /* size is uninitialized, ensure that size.flags aren't used */ - size.flags = PSize; - if (size.flags & PBaseSize) { - c->basew = size.base_width; - c->baseh = size.base_height; - } else if (size.flags & PMinSize) { - c->basew = size.min_width; - c->baseh = size.min_height; - } else - c->basew = c->baseh = 0; - if (size.flags & PResizeInc) { - c->incw = size.width_inc; - c->inch = size.height_inc; - } else - c->incw = c->inch = 0; - if (size.flags & PMaxSize) { - c->maxw = size.max_width; - c->maxh = size.max_height; - } else - c->maxw = c->maxh = 0; - if (size.flags & PMinSize) { - c->minw = size.min_width; - c->minh = size.min_height; - } else if (size.flags & PBaseSize) { - c->minw = size.base_width; - c->minh = size.base_height; - } else - c->minw = c->minh = 0; - if (size.flags & PAspect) { - c->mina = (float)size.min_aspect.y / size.min_aspect.x; - c->maxa = (float)size.max_aspect.x / size.max_aspect.y; - } else - c->maxa = c->mina = 0.0; - c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); -} - -void -updatestatus(void) -{ - if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) - strcpy(stext, "dwm-"VERSION); - drawbar(selmon); -} - -void -updatetitle(Client *c) -{ - if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) - gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name); - if (c->name[0] == '\0') /* hack to mark broken clients */ - strcpy(c->name, broken); -} - -void -updatewindowtype(Client *c) -{ - Atom state = getatomprop(c, netatom[NetWMState]); - Atom wtype = getatomprop(c, netatom[NetWMWindowType]); - - if (state == netatom[NetWMFullscreen]) - setfullscreen(c, 1); - if (wtype == netatom[NetWMWindowTypeDialog]) - c->isfloating = 1; -} - -void -updatewmhints(Client *c) -{ - XWMHints *wmh; - - if ((wmh = XGetWMHints(dpy, c->win))) { - if (c == selmon->sel && wmh->flags & XUrgencyHint) { - wmh->flags &= ~XUrgencyHint; - XSetWMHints(dpy, c->win, wmh); - } else - c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0; - if (wmh->flags & InputHint) - c->neverfocus = !wmh->input; - else - c->neverfocus = 0; - XFree(wmh); - } -} - -void -view(const Arg *arg) -{ - if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) - return; - selmon->seltags ^= 1; /* toggle sel tagset */ - if (arg->ui & TAGMASK) - selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; - focus(NULL); - arrange(selmon); -} - -Client * -wintoclient(Window w) -{ - Client *c; - Monitor *m; - - for (m = mons; m; m = m->next) - for (c = m->clients; c; c = c->next) - if (c->win == w) - return c; - return NULL; -} - -Monitor * -wintomon(Window w) -{ - int x, y; - Client *c; - Monitor *m; - - if (w == root && getrootptr(&x, &y)) - return recttomon(x, y, 1, 1); - for (m = mons; m; m = m->next) - if (w == m->barwin) - return m; - if ((c = wintoclient(w))) - return c->mon; - return selmon; -} - -/* There's no way to check accesses to destroyed windows, thus those cases are - * ignored (especially on UnmapNotify's). Other types of errors call Xlibs - * default error handler, which may call exit. */ -int -xerror(Display *dpy, XErrorEvent *ee) -{ - if (ee->error_code == BadWindow - || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) - || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) - || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) - || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) - || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) - || (ee->request_code == X_GrabButton && ee->error_code == BadAccess) - || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) - || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) - return 0; - fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", - ee->request_code, ee->error_code); - return xerrorxlib(dpy, ee); /* may call exit */ -} - -int -xerrordummy(Display *dpy, XErrorEvent *ee) -{ - return 0; -} - -/* Startup Error handler to check if another window manager - * is already running. */ -int -xerrorstart(Display *dpy, XErrorEvent *ee) -{ - die("dwm: another window manager is already running"); - return -1; -} - -void -zoom(const Arg *arg) -{ - Client *c = selmon->sel; - - if (!selmon->lt[selmon->sellt]->arrange - || (selmon->sel && selmon->sel->isfloating)) - return; - if (c == nexttiled(selmon->clients)) - if (!c || !(c = nexttiled(c->next))) - return; - pop(c); -} - -int -main(int argc, char *argv[]) -{ - if (argc == 2 && !strcmp("-v", argv[1])) - die("dwm-"VERSION); - else if (argc != 1) - die("usage: dwm [-v]"); - if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) - fputs("warning: no locale support\n", stderr); - if (!(dpy = XOpenDisplay(NULL))) - die("dwm: cannot open display"); - checkotherwm(); - setup(); -#ifdef __OpenBSD__ - if (pledge("stdio rpath proc exec", NULL) == -1) - die("pledge"); -#endif /* __OpenBSD__ */ - scan(); - run(); - cleanup(); - XCloseDisplay(dpy); - return EXIT_SUCCESS; -} diff --git a/dwm-6.2/patches/dwm-systray-20210217-61bb8b2.diff b/dwm-6.2/patches/dwm-systray-20210217-61bb8b2.diff new file mode 100644 index 0000000..02604e5 --- /dev/null +++ b/dwm-6.2/patches/dwm-systray-20210217-61bb8b2.diff @@ -0,0 +1,754 @@ +From 54948f3b69824de6eba41d281ff72c08131451a8 Mon Sep 17 00:00:00 2001 +From: Hritik Vijay +Date: Wed, 17 Feb 2021 19:30:25 +0530 +Subject: [PATCH] [Patch] Systray for dwm + +This patch adds an option for systray placement via `systrayleft`. + +Original author: Jan Christoph Ebersbach , inspired by http://code.google.com/p/dwm-plus +URL: http://dwm.suckless.org/patches/systray +dwm 6.2 port by Igor Gevka +--- + config.def.h | 6 +- + dwm.c | 406 +++++++++++++++++++++++++++++++++++++++++++++++---- + 2 files changed, 385 insertions(+), 27 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 1c0b587..9e74de2 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -3,6 +3,11 @@ + /* appearance */ + static const unsigned int borderpx = 1; /* border pixel of windows */ + static const unsigned int snap = 32; /* snap pixel */ ++static const unsigned int systraypinning = 0; /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */ ++static const unsigned int systrayonleft = 0; /* 0: systray in the right corner, >0: systray on left of status text */ ++static const unsigned int systrayspacing = 2; /* systray spacing */ ++static const int systraypinningfailfirst = 1; /* 1: if pinning fails, display systray on the first monitor, False: display systray on the last monitor*/ ++static const int showsystray = 1; /* 0 means no systray */ + static const int showbar = 1; /* 0 means no bar */ + static const int topbar = 1; /* 0 means bottom bar */ + static const char *fonts[] = { "monospace:size=10" }; +@@ -112,4 +117,3 @@ static Button buttons[] = { + { ClkTagBar, MODKEY, Button1, tag, {0} }, + { ClkTagBar, MODKEY, Button3, toggletag, {0} }, + }; +- +diff --git a/dwm.c b/dwm.c +index 664c527..f0d50ee 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -57,12 +57,30 @@ + #define TAGMASK ((1 << LENGTH(tags)) - 1) + #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) + ++#define SYSTEM_TRAY_REQUEST_DOCK 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 { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ + enum { SchemeNorm, SchemeSel }; /* color schemes */ + enum { NetSupported, NetWMName, NetWMState, NetWMCheck, ++ NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetSystemTrayOrientationHorz, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, + NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ ++enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */ + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ + enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ +@@ -141,6 +159,12 @@ typedef struct { + int monitor; + } Rule; + ++typedef struct Systray Systray; ++struct Systray { ++ Window win; ++ Client *icons; ++}; ++ + /* function declarations */ + static void applyrules(Client *c); + static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); +@@ -172,6 +196,7 @@ static void focusstack(const Arg *arg); + static Atom getatomprop(Client *c, Atom prop); + static int getrootptr(int *x, int *y); + static long getstate(Window w); ++static unsigned int getsystraywidth(); + static int gettextprop(Window w, Atom atom, char *text, unsigned int size); + static void grabbuttons(Client *c, int focused); + static void grabkeys(void); +@@ -189,13 +214,16 @@ static void pop(Client *); + static void propertynotify(XEvent *e); + static void quit(const Arg *arg); + static Monitor *recttomon(int x, int y, int w, int h); ++static void removesystrayicon(Client *i); + static void resize(Client *c, int x, int y, int w, int h, int interact); ++static void resizebarwin(Monitor *m); + static void resizeclient(Client *c, int x, int y, int w, int h); + static void resizemouse(const Arg *arg); ++static void resizerequest(XEvent *e); + static void restack(Monitor *m); + static void run(void); + static void scan(void); +-static int sendevent(Client *c, Atom proto); ++static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4); + static void sendmon(Client *c, Monitor *m); + static void setclientstate(Client *c, long state); + static void setfocus(Client *c); +@@ -207,6 +235,7 @@ static void seturgent(Client *c, int urg); + static void showhide(Client *c); + static void sigchld(int unused); + static void spawn(const Arg *arg); ++static Monitor *systraytomon(Monitor *m); + static void tag(const Arg *arg); + static void tagmon(const Arg *arg); + static void tile(Monitor *); +@@ -224,18 +253,23 @@ static int updategeom(void); + static void updatenumlockmask(void); + static void updatesizehints(Client *c); + static void updatestatus(void); ++static void updatesystray(void); ++static void updatesystrayicongeom(Client *i, int w, int h); ++static void updatesystrayiconstate(Client *i, XPropertyEvent *ev); + static void updatetitle(Client *c); + static void updatewindowtype(Client *c); + static void updatewmhints(Client *c); + static void view(const Arg *arg); + static Client *wintoclient(Window w); + static Monitor *wintomon(Window w); ++static Client *wintosystrayicon(Window w); + static int xerror(Display *dpy, XErrorEvent *ee); + static int xerrordummy(Display *dpy, XErrorEvent *ee); + static int xerrorstart(Display *dpy, XErrorEvent *ee); + static void zoom(const Arg *arg); + + /* variables */ ++static Systray *systray = NULL; + static const char broken[] = "broken"; + static char stext[256]; + static int screen; +@@ -258,9 +292,10 @@ static void (*handler[LASTEvent]) (XEvent *) = { + [MapRequest] = maprequest, + [MotionNotify] = motionnotify, + [PropertyNotify] = propertynotify, ++ [ResizeRequest] = resizerequest, + [UnmapNotify] = unmapnotify + }; +-static Atom wmatom[WMLast], netatom[NetLast]; ++static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast]; + static int running = 1; + static Cur *cursor[CurLast]; + static Clr **scheme; +@@ -440,7 +475,7 @@ buttonpress(XEvent *e) + arg.ui = 1 << i; + } else if (ev->x < x + blw) + click = ClkLtSymbol; +- else if (ev->x > selmon->ww - (int)TEXTW(stext)) ++ else if (ev->x > selmon->ww - (int)TEXTW(stext) - getsystraywidth()) + click = ClkStatusText; + else + click = ClkWinTitle; +@@ -483,6 +518,11 @@ cleanup(void) + XUngrabKey(dpy, AnyKey, AnyModifier, root); + while (mons) + cleanupmon(mons); ++ if (showsystray) { ++ XUnmapWindow(dpy, systray->win); ++ XDestroyWindow(dpy, systray->win); ++ free(systray); ++ } + for (i = 0; i < CurLast; i++) + drw_cur_free(drw, cursor[i]); + for (i = 0; i < LENGTH(colors); i++) +@@ -513,9 +553,57 @@ cleanupmon(Monitor *mon) + void + clientmessage(XEvent *e) + { ++ XWindowAttributes wa; ++ XSetWindowAttributes swa; + XClientMessageEvent *cme = &e->xclient; + Client *c = wintoclient(cme->window); + ++ if (showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) { ++ /* add systray icons */ ++ if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) { ++ if (!(c = (Client *)calloc(1, sizeof(Client)))) ++ die("fatal: could not malloc() %u bytes\n", sizeof(Client)); ++ if (!(c->win = cme->data.l[2])) { ++ free(c); ++ return; ++ } ++ c->mon = selmon; ++ c->next = systray->icons; ++ systray->icons = c; ++ if (!XGetWindowAttributes(dpy, c->win, &wa)) { ++ /* use sane defaults */ ++ wa.width = bh; ++ wa.height = bh; ++ wa.border_width = 0; ++ } ++ c->x = c->oldx = c->y = c->oldy = 0; ++ c->w = c->oldw = wa.width; ++ c->h = c->oldh = wa.height; ++ c->oldbw = wa.border_width; ++ c->bw = 0; ++ c->isfloating = True; ++ /* reuse tags field as mapped status */ ++ c->tags = 1; ++ updatesizehints(c); ++ updatesystrayicongeom(c, wa.width, wa.height); ++ XAddToSaveSet(dpy, c->win); ++ XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask); ++ XReparentWindow(dpy, c->win, systray->win, 0, 0); ++ /* use parents background color */ ++ swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; ++ XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa); ++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION); ++ /* FIXME not sure if I have to send these events, too */ ++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_FOCUS_IN, 0 , systray->win, XEMBED_EMBEDDED_VERSION); ++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0 , systray->win, XEMBED_EMBEDDED_VERSION); ++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_MODALITY_ON, 0 , systray->win, XEMBED_EMBEDDED_VERSION); ++ XSync(dpy, False); ++ resizebarwin(selmon); ++ updatesystray(); ++ setclientstate(c, NormalState); ++ } ++ return; ++ } + if (!c) + return; + if (cme->message_type == netatom[NetWMState]) { +@@ -568,7 +656,7 @@ configurenotify(XEvent *e) + for (c = m->clients; c; c = c->next) + if (c->isfullscreen) + resizeclient(c, m->mx, m->my, m->mw, m->mh); +- XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); ++ resizebarwin(m); + } + focus(NULL); + arrange(NULL); +@@ -653,6 +741,11 @@ destroynotify(XEvent *e) + + if ((c = wintoclient(ev->window))) + unmanage(c, 1); ++ else if ((c = wintosystrayicon(ev->window))) { ++ removesystrayicon(c); ++ resizebarwin(selmon); ++ updatesystray(); ++ } + } + + void +@@ -696,19 +789,23 @@ dirtomon(int dir) + void + drawbar(Monitor *m) + { +- int x, w, tw = 0; ++ int x, w, tw = 0, stw = 0; + int boxs = drw->fonts->h / 9; + int boxw = drw->fonts->h / 6 + 2; + unsigned int i, occ = 0, urg = 0; + Client *c; + ++ if(showsystray && m == systraytomon(m) && !systrayonleft) ++ stw = getsystraywidth(); ++ + /* draw status first so it can be overdrawn by tags later */ + if (m == selmon) { /* status is only drawn on selected monitor */ + drw_setscheme(drw, scheme[SchemeNorm]); +- tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ +- drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); ++ tw = TEXTW(stext) - lrpad + 2; /* 2px padding */ ++ drw_text(drw, m->ww - tw - stw, 0, tw, bh, lrpad / 2 - 2, stext, 0); + } + ++ resizebarwin(m); + for (c = m->clients; c; c = c->next) { + occ |= c->tags; + if (c->isurgent) +@@ -729,7 +826,7 @@ drawbar(Monitor *m) + drw_setscheme(drw, scheme[SchemeNorm]); + x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); + +- if ((w = m->ww - tw - x) > bh) { ++ if ((w = m->ww - tw - stw - x) > bh) { + if (m->sel) { + drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); +@@ -740,7 +837,7 @@ drawbar(Monitor *m) + drw_rect(drw, x, 0, w, bh, 1, 1); + } + } +- drw_map(drw, m->barwin, 0, 0, m->ww, bh); ++ drw_map(drw, m->barwin, 0, 0, m->ww - stw, bh); + } + + void +@@ -777,8 +874,11 @@ expose(XEvent *e) + Monitor *m; + XExposeEvent *ev = &e->xexpose; + +- if (ev->count == 0 && (m = wintomon(ev->window))) ++ if (ev->count == 0 && (m = wintomon(ev->window))) { + drawbar(m); ++ if (m == selmon) ++ updatesystray(); ++ } + } + + void +@@ -863,10 +963,17 @@ getatomprop(Client *c, Atom prop) + unsigned long dl; + unsigned char *p = NULL; + Atom da, atom = None; ++ /* FIXME getatomprop should return the number of items and a pointer to ++ * the stored data instead of this workaround */ ++ Atom req = XA_ATOM; ++ if (prop == xatom[XembedInfo]) ++ req = xatom[XembedInfo]; + +- if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, ++ if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req, + &da, &di, &dl, &dl, &p) == Success && p) { + atom = *(Atom *)p; ++ if (da == xatom[XembedInfo] && dl == 2) ++ atom = ((Atom *)p)[1]; + XFree(p); + } + return atom; +@@ -900,6 +1007,16 @@ getstate(Window w) + return result; + } + ++unsigned int ++getsystraywidth() ++{ ++ unsigned int w = 0; ++ Client *i; ++ if(showsystray) ++ for(i = systray->icons; i; w += i->w + systrayspacing, i = i->next) ; ++ return w ? w + systrayspacing : 1; ++} ++ + int + gettextprop(Window w, Atom atom, char *text, unsigned int size) + { +@@ -1004,7 +1121,7 @@ killclient(const Arg *arg) + { + if (!selmon->sel) + return; +- if (!sendevent(selmon->sel, wmatom[WMDelete])) { ++ if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) { + XGrabServer(dpy); + XSetErrorHandler(xerrordummy); + XSetCloseDownMode(dpy, DestroyAll); +@@ -1092,6 +1209,12 @@ maprequest(XEvent *e) + { + static XWindowAttributes wa; + XMapRequestEvent *ev = &e->xmaprequest; ++ Client *i; ++ if ((i = wintosystrayicon(ev->window))) { ++ sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION); ++ resizebarwin(selmon); ++ updatesystray(); ++ } + + if (!XGetWindowAttributes(dpy, ev->window, &wa)) + return; +@@ -1216,6 +1339,16 @@ propertynotify(XEvent *e) + Window trans; + XPropertyEvent *ev = &e->xproperty; + ++ if ((c = wintosystrayicon(ev->window))) { ++ if (ev->atom == XA_WM_NORMAL_HINTS) { ++ updatesizehints(c); ++ updatesystrayicongeom(c, c->w, c->h); ++ } ++ else ++ updatesystrayiconstate(c, ev); ++ resizebarwin(selmon); ++ updatesystray(); ++ } + if ((ev->window == root) && (ev->atom == XA_WM_NAME)) + updatestatus(); + else if (ev->state == PropertyDelete) +@@ -1266,6 +1399,20 @@ recttomon(int x, int y, int w, int h) + return r; + } + ++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); ++} ++ ++ + void + resize(Client *c, int x, int y, int w, int h, int interact) + { +@@ -1273,6 +1420,14 @@ resize(Client *c, int x, int y, int w, int h, int interact) + resizeclient(c, x, y, w, h); + } + ++void ++resizebarwin(Monitor *m) { ++ unsigned int w = m->ww; ++ if (showsystray && m == systraytomon(m) && !systrayonleft) ++ w -= getsystraywidth(); ++ XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh); ++} ++ + void + resizeclient(Client *c, int x, int y, int w, int h) + { +@@ -1345,6 +1500,19 @@ resizemouse(const Arg *arg) + } + } + ++void ++resizerequest(XEvent *e) ++{ ++ XResizeRequestEvent *ev = &e->xresizerequest; ++ Client *i; ++ ++ if ((i = wintosystrayicon(ev->window))) { ++ updatesystrayicongeom(i, ev->width, ev->height); ++ resizebarwin(selmon); ++ updatesystray(); ++ } ++} ++ + void + restack(Monitor *m) + { +@@ -1434,26 +1602,36 @@ setclientstate(Client *c, long state) + } + + int +-sendevent(Client *c, Atom proto) ++sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4) + { + int n; +- Atom *protocols; ++ Atom *protocols, mt; + int exists = 0; + XEvent ev; + +- if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { +- while (!exists && n--) +- exists = protocols[n] == proto; +- XFree(protocols); ++ if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) { ++ mt = wmatom[WMProtocols]; ++ if (XGetWMProtocols(dpy, w, &protocols, &n)) { ++ while (!exists && n--) ++ exists = protocols[n] == proto; ++ XFree(protocols); ++ } ++ } ++ else { ++ exists = True; ++ mt = proto; + } + if (exists) { + ev.type = ClientMessage; +- ev.xclient.window = c->win; +- ev.xclient.message_type = wmatom[WMProtocols]; ++ ev.xclient.window = w; ++ ev.xclient.message_type = mt; + ev.xclient.format = 32; +- ev.xclient.data.l[0] = proto; +- ev.xclient.data.l[1] = CurrentTime; +- XSendEvent(dpy, c->win, False, NoEventMask, &ev); ++ ev.xclient.data.l[0] = d0; ++ ev.xclient.data.l[1] = d1; ++ ev.xclient.data.l[2] = d2; ++ ev.xclient.data.l[3] = d3; ++ ev.xclient.data.l[4] = d4; ++ XSendEvent(dpy, w, False, mask, &ev); + } + return exists; + } +@@ -1467,7 +1645,7 @@ setfocus(Client *c) + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &(c->win), 1); + } +- sendevent(c, wmatom[WMTakeFocus]); ++ sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0); + } + + void +@@ -1556,6 +1734,10 @@ setup(void) + wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); + netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); ++ netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False); ++ netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False); ++ netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False); ++ netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False); + netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); + netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); + netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); +@@ -1563,6 +1745,9 @@ setup(void) + netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); + netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); + netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); ++ xatom[Manager] = XInternAtom(dpy, "MANAGER", False); ++ xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False); ++ xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False); + /* init cursors */ + cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); + cursor[CurResize] = drw_cur_create(drw, XC_sizing); +@@ -1571,6 +1756,8 @@ setup(void) + scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); + for (i = 0; i < LENGTH(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], 3); ++ /* init system tray */ ++ updatesystray(); + /* init bars */ + updatebars(); + updatestatus(); +@@ -1704,7 +1891,18 @@ togglebar(const Arg *arg) + { + selmon->showbar = !selmon->showbar; + updatebarpos(selmon); +- XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); ++ resizebarwin(selmon); ++ if (showsystray) { ++ XWindowChanges wc; ++ if (!selmon->showbar) ++ wc.y = -bh; ++ else if (selmon->showbar) { ++ wc.y = 0; ++ if (!selmon->topbar) ++ wc.y = selmon->mh - bh; ++ } ++ XConfigureWindow(dpy, systray->win, CWY, &wc); ++ } + arrange(selmon); + } + +@@ -1799,11 +1997,18 @@ unmapnotify(XEvent *e) + else + unmanage(c, 0); + } ++ else if ((c = wintosystrayicon(ev->window))) { ++ /* KLUDGE! sometimes icons occasionally unmap their windows, but do ++ * _not_ destroy them. We map those windows back */ ++ XMapRaised(dpy, c->win); ++ updatesystray(); ++ } + } + + void + updatebars(void) + { ++ unsigned int w; + Monitor *m; + XSetWindowAttributes wa = { + .override_redirect = True, +@@ -1814,10 +2019,15 @@ updatebars(void) + for (m = mons; m; m = m->next) { + if (m->barwin) + continue; +- m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), ++ w = m->ww; ++ if (showsystray && m == systraytomon(m)) ++ w -= getsystraywidth(); ++ m->barwin = XCreateWindow(dpy, root, m->wx, m->by, w, bh, 0, DefaultDepth(dpy, screen), + CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); + XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); ++ if (showsystray && m == systraytomon(m)) ++ XMapRaised(dpy, systray->win); + XMapRaised(dpy, m->barwin); + XSetClassHint(dpy, m->barwin, &ch); + } +@@ -1993,6 +2203,124 @@ updatestatus(void) + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + strcpy(stext, "dwm-"VERSION); + drawbar(selmon); ++ updatesystray(); ++} ++ ++void ++updatesystrayicongeom(Client *i, int w, int h) ++{ ++ if (i) { ++ i->h = bh; ++ if (w == h) ++ i->w = bh; ++ else if (h == bh) ++ i->w = w; ++ else ++ i->w = (int) ((float)bh * ((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 > bh) { ++ if (i->w == i->h) ++ i->w = bh; ++ else ++ i->w = (int) ((float)bh * ((float)i->w / (float)i->h)); ++ i->h = bh; ++ } ++ } ++} ++ ++void ++updatesystrayiconstate(Client *i, XPropertyEvent *ev) ++{ ++ long flags; ++ int code = 0; ++ ++ if (!showsystray || !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); ++} ++ ++void ++updatesystray(void) ++{ ++ XSetWindowAttributes wa; ++ XWindowChanges wc; ++ Client *i; ++ Monitor *m = systraytomon(NULL); ++ unsigned int x = m->mx + m->mw; ++ unsigned int sw = TEXTW(stext) - lrpad + systrayspacing; ++ unsigned int w = 1; ++ ++ if (!showsystray) ++ return; ++ if (systrayonleft) ++ x -= sw; ++ if (!systray) { ++ /* init systray */ ++ if (!(systray = (Systray *)calloc(1, sizeof(Systray)))) ++ die("fatal: could not malloc() %u bytes\n", sizeof(Systray)); ++ systray->win = XCreateSimpleWindow(dpy, root, x, m->by, w, bh, 0, 0, scheme[SchemeSel][ColBg].pixel); ++ wa.event_mask = ButtonPressMask | ExposureMask; ++ wa.override_redirect = True; ++ wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; ++ XSelectInput(dpy, systray->win, SubstructureNotifyMask); ++ XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32, ++ PropModeReplace, (unsigned char *)&netatom[NetSystemTrayOrientationHorz], 1); ++ XChangeWindowAttributes(dpy, systray->win, CWEventMask|CWOverrideRedirect|CWBackPixel, &wa); ++ 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; ++ } ++ } ++ for (w = 0, i = systray->icons; i; i = i->next) { ++ /* make sure the background color stays the same */ ++ wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; ++ XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa); ++ XMapRaised(dpy, i->win); ++ w += systrayspacing; ++ i->x = w; ++ XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h); ++ w += i->w; ++ if (i->mon != m) ++ i->mon = m; ++ } ++ w = w ? w + systrayspacing : 1; ++ x -= w; ++ XMoveResizeWindow(dpy, systray->win, x, m->by, w, bh); ++ wc.x = x; wc.y = m->by; wc.width = w; wc.height = bh; ++ wc.stack_mode = Above; wc.sibling = m->barwin; ++ XConfigureWindow(dpy, systray->win, CWX|CWY|CWWidth|CWHeight|CWSibling|CWStackMode, &wc); ++ XMapWindow(dpy, systray->win); ++ XMapSubwindows(dpy, systray->win); ++ /* redraw background */ ++ XSetForeground(dpy, drw->gc, scheme[SchemeNorm][ColBg].pixel); ++ XFillRectangle(dpy, systray->win, drw->gc, 0, 0, w, bh); ++ XSync(dpy, False); + } + + void +@@ -2060,6 +2388,16 @@ wintoclient(Window w) + return NULL; + } + ++Client * ++wintosystrayicon(Window w) { ++ Client *i = NULL; ++ ++ if (!showsystray || !w) ++ return i; ++ for (i = systray->icons; i && i->win != w; i = i->next) ; ++ return i; ++} ++ + Monitor * + wintomon(Window w) + { +@@ -2113,6 +2451,22 @@ xerrorstart(Display *dpy, XErrorEvent *ee) + return -1; + } + ++Monitor * ++systraytomon(Monitor *m) { ++ Monitor *t; ++ int i, n; ++ if(!systraypinning) { ++ if(!m) ++ return selmon; ++ return m == selmon ? m : NULL; ++ } ++ for(n = 1, t = mons; t && t->next; n++, t = t->next) ; ++ for(i = 1, t = mons; t && t->next && i < systraypinning; i++, t = t->next) ; ++ if(systraypinningfailfirst && n < systraypinning) ++ return mons; ++ return t; ++} ++ + void + zoom(const Arg *arg) + { +-- +2.30.1 + diff --git a/st-0.8.4/Makefile b/st-0.8.4/Makefile index 470ac86..38240da 100644 --- a/st-0.8.4/Makefile +++ b/st-0.8.4/Makefile @@ -4,7 +4,7 @@ include config.mk -SRC = st.c x.c +SRC = st.c x.c hb.c OBJ = $(SRC:.c=.o) all: options st @@ -22,7 +22,8 @@ config.h: $(CC) $(STCFLAGS) -c $< st.o: config.h st.h win.h -x.o: arg.h config.h st.h win.h +x.o: arg.h config.h st.h win.h hb.h +hb.o: st.h $(OBJ): config.h config.mk diff --git a/st-0.8.4/config.def.h b/st-0.8.4/config.def.h index 1a4765e..09a02ab 100644 --- a/st-0.8.4/config.def.h +++ b/st-0.8.4/config.def.h @@ -5,7 +5,8 @@ * * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html */ -static char *font = "MesloLGS NF:pixelsize=15:antialias=true:autohint=true"; +static char *font = "Hack Nerd Font Mono:pixelsize=16:antialias=true:autohint=true"; +/*static char *font = "MesloLGS NF:pixelsize=15:antialias=true:autohint=true";*/ static int borderpx = 2; /* diff --git a/st-0.8.4/config.mk b/st-0.8.4/config.mk index aaa54ff..1741840 100644 --- a/st-0.8.4/config.mk +++ b/st-0.8.4/config.mk @@ -15,10 +15,12 @@ PKG_CONFIG = pkg-config # includes and libs INCS = -I$(X11INC) \ `$(PKG_CONFIG) --cflags fontconfig` \ - `$(PKG_CONFIG) --cflags freetype2` + `$(PKG_CONFIG) --cflags freetype2` \ + `$(PKG_CONFIG) --cflags harfbuzz` LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\ `$(PKG_CONFIG) --libs fontconfig` \ - `$(PKG_CONFIG) --libs freetype2` + `$(PKG_CONFIG) --libs freetype2` \ + `$(PKG_CONFIG) --libs harfbuzz` # flags STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 diff --git a/st-0.8.4/hb.c b/st-0.8.4/hb.c new file mode 100644 index 0000000..7df2828 --- /dev/null +++ b/st-0.8.4/hb.c @@ -0,0 +1,136 @@ +#include +#include +#include +#include +#include +#include + +#include "st.h" + +void hbtransformsegment(XftFont *xfont, const Glyph *string, hb_codepoint_t *codepoints, int start, int length); +hb_font_t *hbfindfont(XftFont *match); + +typedef struct { + XftFont *match; + hb_font_t *font; +} HbFontMatch; + +static int hbfontslen = 0; +static HbFontMatch *hbfontcache = NULL; + +void +hbunloadfonts() +{ + for (int i = 0; i < hbfontslen; i++) { + hb_font_destroy(hbfontcache[i].font); + XftUnlockFace(hbfontcache[i].match); + } + + if (hbfontcache != NULL) { + free(hbfontcache); + hbfontcache = NULL; + } + hbfontslen = 0; +} + +hb_font_t * +hbfindfont(XftFont *match) +{ + for (int i = 0; i < hbfontslen; i++) { + if (hbfontcache[i].match == match) + return hbfontcache[i].font; + } + + /* Font not found in cache, caching it now. */ + hbfontcache = realloc(hbfontcache, sizeof(HbFontMatch) * (hbfontslen + 1)); + FT_Face face = XftLockFace(match); + hb_font_t *font = hb_ft_font_create(face, NULL); + if (font == NULL) + die("Failed to load Harfbuzz font."); + + hbfontcache[hbfontslen].match = match; + hbfontcache[hbfontslen].font = font; + hbfontslen += 1; + + return font; +} + +void +hbtransform(XftGlyphFontSpec *specs, const Glyph *glyphs, size_t len, int x, int y) +{ + int start = 0, length = 1, gstart = 0; + hb_codepoint_t *codepoints = calloc(len, sizeof(hb_codepoint_t)); + + for (int idx = 1, specidx = 1; idx < len; idx++) { + if (glyphs[idx].mode & ATTR_WDUMMY) { + length += 1; + continue; + } + + if (specs[specidx].font != specs[start].font || ATTRCMP(glyphs[gstart], glyphs[idx]) || selected(x + idx, y) != selected(x + gstart, y)) { + hbtransformsegment(specs[start].font, glyphs, codepoints, gstart, length); + + /* Reset the sequence. */ + length = 1; + start = specidx; + gstart = idx; + } else { + length += 1; + } + + specidx++; + } + + /* EOL. */ + hbtransformsegment(specs[start].font, glyphs, codepoints, gstart, length); + + /* Apply the transformation to glyph specs. */ + for (int i = 0, specidx = 0; i < len; i++) { + if (glyphs[i].mode & ATTR_WDUMMY) + continue; + + if (codepoints[i] != specs[specidx].glyph) + ((Glyph *)glyphs)[i].mode |= ATTR_LIGA; + + specs[specidx++].glyph = codepoints[i]; + } + + free(codepoints); +} + +void +hbtransformsegment(XftFont *xfont, const Glyph *string, hb_codepoint_t *codepoints, int start, int length) +{ + hb_font_t *font = hbfindfont(xfont); + if (font == NULL) + return; + + Rune rune; + ushort mode = USHRT_MAX; + hb_buffer_t *buffer = hb_buffer_create(); + hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); + + /* Fill buffer with codepoints. */ + for (int i = start; i < (start+length); i++) { + rune = string[i].u; + mode = string[i].mode; + if (mode & ATTR_WDUMMY) + rune = 0x0020; + hb_buffer_add_codepoints(buffer, &rune, 1, 0, 1); + } + + /* Shape the segment. */ + hb_shape(font, buffer, NULL, 0); + + /* Get new glyph info. */ + hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, NULL); + + /* Write new codepoints. */ + for (int i = 0; i < length; i++) { + hb_codepoint_t gid = info[i].codepoint; + codepoints[start+i] = gid; + } + + /* Cleanup. */ + hb_buffer_destroy(buffer); +} diff --git a/st-0.8.4/hb.h b/st-0.8.4/hb.h new file mode 100644 index 0000000..b3e02d0 --- /dev/null +++ b/st-0.8.4/hb.h @@ -0,0 +1,7 @@ +#include +#include +#include + +void hbunloadfonts(); +void hbtransform(XftGlyphFontSpec *, const Glyph *, size_t, int, int); + diff --git a/st-0.8.4/patches/st-ligatures-alpha-scrollback-20200430-0.8.3.diff b/st-0.8.4/patches/st-ligatures-alpha-scrollback-20200430-0.8.3.diff new file mode 100644 index 0000000..cfd59ec --- /dev/null +++ b/st-0.8.4/patches/st-ligatures-alpha-scrollback-20200430-0.8.3.diff @@ -0,0 +1,307 @@ +diff --git a/Makefile b/Makefile +index 470ac86..38240da 100644 +--- a/Makefile ++++ b/Makefile +@@ -4,7 +4,7 @@ + + include config.mk + +-SRC = st.c x.c ++SRC = st.c x.c hb.c + OBJ = $(SRC:.c=.o) + + all: options st +@@ -22,7 +22,8 @@ config.h: + $(CC) $(STCFLAGS) -c $< + + st.o: config.h st.h win.h +-x.o: arg.h config.h st.h win.h ++x.o: arg.h config.h st.h win.h hb.h ++hb.o: st.h + + $(OBJ): config.h config.mk + +diff --git a/config.mk b/config.mk +index ddc65ae..07a3d14 100644 +--- a/config.mk ++++ b/config.mk +@@ -15,10 +15,12 @@ PKG_CONFIG = pkg-config + # includes and libs + INCS = -I$(X11INC) \ + `$(PKG_CONFIG) --cflags fontconfig` \ +- `$(PKG_CONFIG) --cflags freetype2` ++ `$(PKG_CONFIG) --cflags freetype2` \ ++ `$(PKG_CONFIG) --cflags harfbuzz` + LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\ + `$(PKG_CONFIG) --libs fontconfig` \ +- `$(PKG_CONFIG) --libs freetype2` ++ `$(PKG_CONFIG) --libs freetype2` \ ++ `$(PKG_CONFIG) --libs harfbuzz` + + # flags + STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 +diff --git a/hb.c b/hb.c +new file mode 100644 +index 0000000..7df2828 +--- /dev/null ++++ b/hb.c +@@ -0,0 +1,136 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "st.h" ++ ++void hbtransformsegment(XftFont *xfont, const Glyph *string, hb_codepoint_t *codepoints, int start, int length); ++hb_font_t *hbfindfont(XftFont *match); ++ ++typedef struct { ++ XftFont *match; ++ hb_font_t *font; ++} HbFontMatch; ++ ++static int hbfontslen = 0; ++static HbFontMatch *hbfontcache = NULL; ++ ++void ++hbunloadfonts() ++{ ++ for (int i = 0; i < hbfontslen; i++) { ++ hb_font_destroy(hbfontcache[i].font); ++ XftUnlockFace(hbfontcache[i].match); ++ } ++ ++ if (hbfontcache != NULL) { ++ free(hbfontcache); ++ hbfontcache = NULL; ++ } ++ hbfontslen = 0; ++} ++ ++hb_font_t * ++hbfindfont(XftFont *match) ++{ ++ for (int i = 0; i < hbfontslen; i++) { ++ if (hbfontcache[i].match == match) ++ return hbfontcache[i].font; ++ } ++ ++ /* Font not found in cache, caching it now. */ ++ hbfontcache = realloc(hbfontcache, sizeof(HbFontMatch) * (hbfontslen + 1)); ++ FT_Face face = XftLockFace(match); ++ hb_font_t *font = hb_ft_font_create(face, NULL); ++ if (font == NULL) ++ die("Failed to load Harfbuzz font."); ++ ++ hbfontcache[hbfontslen].match = match; ++ hbfontcache[hbfontslen].font = font; ++ hbfontslen += 1; ++ ++ return font; ++} ++ ++void ++hbtransform(XftGlyphFontSpec *specs, const Glyph *glyphs, size_t len, int x, int y) ++{ ++ int start = 0, length = 1, gstart = 0; ++ hb_codepoint_t *codepoints = calloc(len, sizeof(hb_codepoint_t)); ++ ++ for (int idx = 1, specidx = 1; idx < len; idx++) { ++ if (glyphs[idx].mode & ATTR_WDUMMY) { ++ length += 1; ++ continue; ++ } ++ ++ if (specs[specidx].font != specs[start].font || ATTRCMP(glyphs[gstart], glyphs[idx]) || selected(x + idx, y) != selected(x + gstart, y)) { ++ hbtransformsegment(specs[start].font, glyphs, codepoints, gstart, length); ++ ++ /* Reset the sequence. */ ++ length = 1; ++ start = specidx; ++ gstart = idx; ++ } else { ++ length += 1; ++ } ++ ++ specidx++; ++ } ++ ++ /* EOL. */ ++ hbtransformsegment(specs[start].font, glyphs, codepoints, gstart, length); ++ ++ /* Apply the transformation to glyph specs. */ ++ for (int i = 0, specidx = 0; i < len; i++) { ++ if (glyphs[i].mode & ATTR_WDUMMY) ++ continue; ++ ++ if (codepoints[i] != specs[specidx].glyph) ++ ((Glyph *)glyphs)[i].mode |= ATTR_LIGA; ++ ++ specs[specidx++].glyph = codepoints[i]; ++ } ++ ++ free(codepoints); ++} ++ ++void ++hbtransformsegment(XftFont *xfont, const Glyph *string, hb_codepoint_t *codepoints, int start, int length) ++{ ++ hb_font_t *font = hbfindfont(xfont); ++ if (font == NULL) ++ return; ++ ++ Rune rune; ++ ushort mode = USHRT_MAX; ++ hb_buffer_t *buffer = hb_buffer_create(); ++ hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); ++ ++ /* Fill buffer with codepoints. */ ++ for (int i = start; i < (start+length); i++) { ++ rune = string[i].u; ++ mode = string[i].mode; ++ if (mode & ATTR_WDUMMY) ++ rune = 0x0020; ++ hb_buffer_add_codepoints(buffer, &rune, 1, 0, 1); ++ } ++ ++ /* Shape the segment. */ ++ hb_shape(font, buffer, NULL, 0); ++ ++ /* Get new glyph info. */ ++ hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, NULL); ++ ++ /* Write new codepoints. */ ++ for (int i = 0; i < length; i++) { ++ hb_codepoint_t gid = info[i].codepoint; ++ codepoints[start+i] = gid; ++ } ++ ++ /* Cleanup. */ ++ hb_buffer_destroy(buffer); ++} +diff --git a/hb.h b/hb.h +new file mode 100644 +index 0000000..b3e02d0 +--- /dev/null ++++ b/hb.h +@@ -0,0 +1,7 @@ ++#include ++#include ++#include ++ ++void hbunloadfonts(); ++void hbtransform(XftGlyphFontSpec *, const Glyph *, size_t, int, int); ++ +diff --git a/st.c b/st.c +index 641edc0..14a0b66 100644 +--- a/st.c ++++ b/st.c +@@ -2670,7 +2670,8 @@ draw(void) + drawregion(0, 0, term.col, term.row); + if (term.scr == 0) + xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], +- term.ocx, term.ocy, term.line[term.ocy][term.ocx]); ++ term.ocx, term.ocy, term.line[term.ocy][term.ocx], ++ term.line[term.ocy], term.col); + term.ocx = cx; + term.ocy = term.c.y; + xfinishdraw(); +diff --git a/st.h b/st.h +index 2b2ba94..01bfb05 100644 +--- a/st.h ++++ b/st.h +@@ -11,7 +11,8 @@ + #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) + #define DEFAULT(a, b) (a) = (a) ? (a) : (b) + #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) +-#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \ ++#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP) & (~ATTR_LIGA)) != ((b).mode & (~ATTR_WRAP) & (~ATTR_LIGA)) || \ ++ (a).fg != (b).fg || \ + (a).bg != (b).bg) + #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ + (t1.tv_nsec-t2.tv_nsec)/1E6) +@@ -33,6 +34,7 @@ enum glyph_attribute { + ATTR_WRAP = 1 << 8, + ATTR_WIDE = 1 << 9, + ATTR_WDUMMY = 1 << 10, ++ ATTR_LIGA = 1 << 11, + ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, + }; + +diff --git a/win.h b/win.h +index a6ef1b9..bc0d180 100644 +--- a/win.h ++++ b/win.h +@@ -25,7 +25,7 @@ enum win_mode { + + void xbell(void); + void xclipcopy(void); +-void xdrawcursor(int, int, Glyph, int, int, Glyph); ++void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int); + void xdrawline(Line, int, int, int); + void xfinishdraw(void); + void xloadcols(void); +diff --git a/x.c b/x.c +index 50da23c..bec3e76 100644 +--- a/x.c ++++ b/x.c +@@ -19,6 +19,7 @@ char *argv0; + #include "arg.h" + #include "st.h" + #include "win.h" ++#include "hb.h" + + /* types used in config.h */ + typedef struct { +@@ -1040,6 +1041,9 @@ xunloadfont(Font *f) + void + xunloadfonts(void) + { ++ /* Clear Harfbuzz font cache. */ ++ hbunloadfonts(); ++ + /* Free the loaded fonts in the font cache. */ + while (frclen > 0) + XftFontClose(xw.dpy, frc[--frclen].font); +@@ -1246,7 +1250,7 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x + mode = glyphs[i].mode; + + /* Skip dummy wide-character spacing. */ +- if (mode == ATTR_WDUMMY) ++ if (mode & ATTR_WDUMMY) + continue; + + /* Determine font for glyph if different from previous glyph. */ +@@ -1353,6 +1357,9 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x + numspecs++; + } + ++ /* Harfbuzz transformation for ligatures. */ ++ hbtransform(specs, glyphs, len, x, y); ++ + return numspecs; + } + +@@ -1502,14 +1509,17 @@ xdrawglyph(Glyph g, int x, int y) + } + + void +-xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) ++xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int len) + { + Color drawcol; + + /* remove the old cursor */ + if (selected(ox, oy)) + og.mode ^= ATTR_REVERSE; +- xdrawglyph(og, ox, oy); ++ ++ /* Redraw the line where cursor was previously. ++ * It will restore the ligatures broken by the cursor. */ ++ xdrawline(line, 0, oy, len); + + if (IS_SET(MODE_HIDE)) + return; diff --git a/st-0.8.4/patches/st-w3m-0.8.3.diff b/st-0.8.4/patches/st-w3m-0.8.3.diff new file mode 100644 index 0000000..a4e104b --- /dev/null +++ b/st-0.8.4/patches/st-w3m-0.8.3.diff @@ -0,0 +1,42 @@ +From 69cffc587b54b0a9cd81adb87abad8e526d5b25b Mon Sep 17 00:00:00 2001 +From: "Avi Halachmi (:avih)" +Date: Thu, 4 Jun 2020 17:35:08 +0300 +Subject: [PATCH] support w3m images + +w3m images are a hack which renders on top of the terminal's drawable, +which didn't work in st because when using double buffering, the front +buffer (on which w3m draws its images) is ignored, and st draws only +on the back buffer, which is then copied to the front buffer. + +There's a patch to make it work at the FAQ already, but that patch +canceles double-buffering, which can have negative side effects on +some cases such as flickering. + +This patch achieves the same goal but instead of canceling the double +buffer it first copies the front buffer to the back buffer. + +This has the same issues as the FAQ patch in that the cursor line is +deleted at the image (because st renders always full lines), but +otherwise it's simpler and does keeps double buffering. +--- + x.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/x.c b/x.c +index e5f1737..b6ae162 100644 +--- a/x.c ++++ b/x.c +@@ -1594,6 +1594,8 @@ xsettitle(char *p) + int + xstartdraw(void) + { ++ if (IS_SET(MODE_VISIBLE)) ++ XCopyArea(xw.dpy, xw.win, xw.buf, dc.gc, 0, 0, win.w, win.h, 0, 0); + return IS_SET(MODE_VISIBLE); + } + + +base-commit: 43a395ae91f7d67ce694e65edeaa7bbc720dd027 +-- +2.17.1 + diff --git a/st-0.8.4/st.c b/st-0.8.4/st.c index 8418471..dffeba8 100644 --- a/st-0.8.4/st.c +++ b/st-0.8.4/st.c @@ -2692,7 +2692,8 @@ draw(void) drawregion(0, 0, term.col, term.row); if (term.scr == 0) xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], - term.ocx, term.ocy, term.line[term.ocy][term.ocx]); + term.ocx, term.ocy, term.line[term.ocy][term.ocx], + term.line[term.ocy], term.col); term.ocx = cx; term.ocy = term.c.y; xfinishdraw(); diff --git a/st-0.8.4/st.h b/st-0.8.4/st.h index 94af1fe..8641166 100644 --- a/st-0.8.4/st.h +++ b/st-0.8.4/st.h @@ -11,7 +11,8 @@ #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) #define DEFAULT(a, b) (a) = (a) ? (a) : (b) #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) -#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \ +#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP) & (~ATTR_LIGA)) != ((b).mode & (~ATTR_WRAP) & (~ATTR_LIGA)) || \ + (a).fg != (b).fg || \ (a).bg != (b).bg) #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ (t1.tv_nsec-t2.tv_nsec)/1E6) @@ -33,6 +34,7 @@ enum glyph_attribute { ATTR_WRAP = 1 << 8, ATTR_WIDE = 1 << 9, ATTR_WDUMMY = 1 << 10, + ATTR_LIGA = 1 << 11, ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, }; diff --git a/st-0.8.4/win.h b/st-0.8.4/win.h index a6ef1b9..bc0d180 100644 --- a/st-0.8.4/win.h +++ b/st-0.8.4/win.h @@ -25,7 +25,7 @@ enum win_mode { void xbell(void); void xclipcopy(void); -void xdrawcursor(int, int, Glyph, int, int, Glyph); +void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int); void xdrawline(Line, int, int, int); void xfinishdraw(void); void xloadcols(void); diff --git a/st-0.8.4/x.c b/st-0.8.4/x.c index 8f70da3..65cae94 100644 --- a/st-0.8.4/x.c +++ b/st-0.8.4/x.c @@ -20,6 +20,7 @@ char *argv0; #include "arg.h" #include "st.h" #include "win.h" +#include "hb.h" /* types used in config.h */ typedef struct { @@ -1071,6 +1072,9 @@ xunloadfont(Font *f) void xunloadfonts(void) { + /* Clear Harfbuzz font cache. */ + hbunloadfonts(); + /* Free the loaded fonts in the font cache. */ while (frclen > 0) XftFontClose(xw.dpy, frc[--frclen].font); @@ -1277,7 +1281,7 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x mode = glyphs[i].mode; /* Skip dummy wide-character spacing. */ - if (mode == ATTR_WDUMMY) + if (mode & ATTR_WDUMMY) continue; /* Determine font for glyph if different from previous glyph. */ @@ -1384,6 +1388,9 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x numspecs++; } + /* Harfbuzz transformation for ligatures. */ + hbtransform(specs, glyphs, len, x, y); + return numspecs; } @@ -1533,14 +1540,17 @@ xdrawglyph(Glyph g, int x, int y) } void -xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) +xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int len) { Color drawcol; /* remove the old cursor */ if (selected(ox, oy)) og.mode ^= ATTR_REVERSE; - xdrawglyph(og, ox, oy); + + /* Redraw the line where cursor was previously. + * It will restore the ligatures broken by the cursor. */ + xdrawline(line, 0, oy, len); if (IS_SET(MODE_HIDE)) return; @@ -1643,6 +1653,8 @@ xsettitle(char *p) int xstartdraw(void) { + if (IS_SET(MODE_VISIBLE)) + XCopyArea(xw.dpy, xw.win, xw.buf, dc.gc, 0, 0, win.w, win.h, 0, 0); return IS_SET(MODE_VISIBLE); }