diff options
| -rw-r--r-- | README.md | 36 | ||||
| -rw-r--r-- | config.h | 108 | ||||
| -rw-r--r-- | monsterwm.c | 180 |
3 files changed, 227 insertions, 97 deletions
@@ -1,6 +1,14 @@ monsterwm ========= +Maintaining my version of monsterwm. Changes: +- remove unused functions +- track my config +- merge window titles into main +- merge rectangles into main +- add `ignore` to AppRules to allow, e.g., stalonetray without being managed +- print status for lemonbar periodically (and on every update event) + → tiny and monstrous! --------------------- @@ -110,30 +118,9 @@ The user can define an empty space (by default 18px) on the bottom or top(defaul screen, to be used by a panel. The panel is toggleable, but will be visible if no windows are on the screen. -Monsterwm does not provide a panel and/or statusbar itself. Instead it adheres -to the [UNIX philosophy][unix] and outputs information about the existent -desktop, the number of windows on each, the mode of each desktop, the current -desktop and urgent hints whenever needed. The user can use whatever tool or -panel suits him best (dzen2, conky, w/e), to process and display that information. - -To disable the panel completely set `PANEL_HEIGHT` to zero `0`. -The `SHOW_PANELL` setting controls whether the panel is visible on startup, -it does not control whether there is a panel or not. - - [unix]: http://en.wikipedia.org/wiki/Unix_philosophy - -Here is a list of minimal and lightweight panels: - - * [`bar`](https://github.com/LemonBoy/bar) - * [`some_sorta_bar`](https://github.com/moetunes/Some_sorta_bar) - * [`bipolarbar`](https://github.com/moetunes/bipolarbar) - * [`mopag`](https://github.com/c00kiemon5ter/mopag) (pager) - -You can find an examples configurations of panels [here](https://gist.github.com/1905427). -You can actually parse monsterwm's output with any language you want, -build anything you want, and display the information however you like. -Do not be limited by those examples. +This fork generates output intended to be piped to [lemonbar](https://github.com/LemonBoy/bar). +To change the status output, modify `write_status`. Installation ------------ @@ -163,11 +150,9 @@ Currently: * [initlayouts] : define initial layouts for every desktop * [monocleborders] : adds borders to the monocle layout * [nmaster] : adds nmaster layout - multiple master windows for BSTACK and TILE layouts - * [rectangle] : draws only a rectangle when moving/resizing windows to keep resources low (ie through an ssh forwarded session) * [showhide] : adds a function to show and hide all windows on all desktops * [uselessgaps] : adds gaps around every window on screen * [warpcursor] : cursors follows and is placed in the center of the current window - * [windowtitles] : along with the rest desktop info, output the title of the current window [centerwindow]: https://github.com/c00kiemon5ter/monsterwm/tree/centerwindow [fibonacci]: https://github.com/c00kiemon5ter/monsterwm/tree/fibonacci @@ -200,6 +185,7 @@ The is also **xinerama** support for multiple monitors. Bugs ---- +Only if it's certain to be a bug with upstream and not my modifications: For any bug or request [fill an issue][bug] on [GitHub][ghp] or report on the [ArchLinux topic][monsterwm] [bug]: https://github.com/c00kiemon5ter/monsterwm/issues diff --git a/config.h b/config.h new file mode 100644 index 0000000..aca1e5d --- /dev/null +++ b/config.h @@ -0,0 +1,108 @@ +/* see LICENSE for copyright and license */ + +#ifndef CONFIG_H +#define CONFIG_H + +/** modifiers **/ +#define MOD1 Mod1Mask /* ALT key */ +#define MOD4 Mod4Mask /* Super/Windows key */ +#define CONTROL ControlMask /* Control key */ +#define SHIFT ShiftMask /* Shift key */ + +/** generic settings **/ +#define MASTER_SIZE 0.6 +#define SHOW_PANEL True /* show panel by default on exec */ +#define TOP_PANEL False /* False means panel is on bottom */ +#define PANEL_HEIGHT 18 /* 0 for no space for panel, thus no panel */ +#define DEFAULT_MODE TILE /* initial layout/mode: TILE MONOCLE BSTACK GRID FLOAT */ +#define ATTACH_ASIDE True /* False means new window is master */ +#define FOLLOW_WINDOW False /* follow the window when moved to a different desktop */ +#define FOLLOW_MOUSE True /* focus the window the mouse just entered */ +#define CLICK_TO_FOCUS True /* focus an unfocused window when clicked */ +#define FOCUS_BUTTON Button3 /* mouse button to be used along with CLICK_TO_FOCUS */ +#define BORDER_WIDTH 2 /* window border width */ +#define FOCUS "#ff950e" /* focused window border color */ +#define UNFOCUS "#444444" /* unfocused window border color */ +#define MINWSZ 50 /* minimum window size in pixels */ +#define DEFAULT_DESKTOP 0 /* the desktop to focus initially */ +#define DESKTOPS 8 /* number of desktops - edit DESKTOPCHANGE keys to suit */ + +/** + * open applications to specified desktop with specified mode. + * if desktop is negative, then current is assumed + */ +static const AppRule rules[] = { \ + /* class desktop follow float ignore*/ + { "stalonetray", -1, True, True, True }, +}; + +/* helper for spawning shell commands */ +#define SHCMD(cmd) {.com = (const char*[]){"/bin/sh", "-c", cmd, NULL}} + +/** + * custom commands + * must always end with ', NULL };' + */ +static const char *termcmd[] = { "alacritty", NULL }; +static const char *menucmd[] = { "run", NULL }; + +#define DESKTOPCHANGE(K,N) \ + { MOD4, K, change_desktop, {.i = N} }, \ + { MOD4|ShiftMask, K, client_to_desktop, {.i = N} }, + +/** + * keyboard shortcuts + */ +static Key keys[] = { + /* modifier key function argument */ + + // Commands + { MOD4, XK_t, spawn, {.com = termcmd} }, + { MOD4, XK_d, spawn, {.com = menucmd} }, + { MOD4, XK_space, spawn, SHCMD("mpc toggle") }, + { MOD4|SHIFT, XK_space, spawn, SHCMD("albumbler.py") }, + { MOD4, XK_h, spawn, SHCMD("lock") }, + { MOD4|SHIFT|CONTROL, XK_space, spawn, SHCMD("notify-send --app-name=Playing $(mpc current)")}, + { MOD4|SHIFT|CONTROL, XK_q, quit, {.i = 0} }, /* quit with exit value 0 */ + + // Windows + { MOD4, XK_n, next_win, {NULL} }, + { MOD4, XK_o, prev_win, {NULL} }, + { MOD4|SHIFT, XK_n, move_down, {NULL} }, + { MOD4|SHIFT, XK_o, move_up, {NULL} }, + { MOD4|SHIFT, XK_k, killclient, {NULL} }, + { MOD4, XK_Return, swap_master, {NULL} }, + { MOD4, XK_BackSpace, focusurgent, {NULL} }, + { MOD4, XK_e, resize_master, {.i = +10} }, /* increase size in px */ + { MOD4, XK_i, resize_master, {.i = -10} }, /* decrease size in px */ + + // Desktops + { MOD4, XK_Tab, last_desktop, {NULL} }, + DESKTOPCHANGE( XK_l, 0) + DESKTOPCHANGE( XK_u, 1) + DESKTOPCHANGE( XK_y, 2) + DESKTOPCHANGE( XK_semicolon, 3) + DESKTOPCHANGE( XK_q, 4) + DESKTOPCHANGE( XK_w, 5) + DESKTOPCHANGE( XK_f, 6) + DESKTOPCHANGE( XK_p, 7) + + // Layouts + { MOD4, XK_a, switch_mode, {.i = TILE} }, + { MOD4, XK_r, switch_mode, {.i = MONOCLE} }, + { MOD4, XK_s, switch_mode, {.i = BSTACK} }, + { MOD4|SHIFT, XK_a, switch_mode, {.i = GRID} }, + { MOD4|SHIFT, XK_r, switch_mode, {.i = FLOAT} }, + { MOD4, XK_b, togglepanel, {NULL} }, +}; + +/** + * mouse shortcuts + */ +static Button buttons[] = { + { MOD4, Button1, mousemotion, {.i = MOVE} }, + { MOD4, Button3, mousemotion, {.i = RESIZE} }, +}; +#endif + +/* vim: set expandtab ts=4 sts=4 sw=4 : */ diff --git a/monsterwm.c b/monsterwm.c index f832223..fdb68eb 100644 --- a/monsterwm.c +++ b/monsterwm.c @@ -1,5 +1,5 @@ /* see license for copyright and license */ - +#define _XOPEN_SOURCE #include <stdlib.h> #include <stdio.h> #include <err.h> @@ -8,6 +8,8 @@ #include <string.h> #include <signal.h> #include <sys/wait.h> +#include <time.h> +#include <sys/time.h> #include <X11/Xutil.h> #include <X11/XKBlib.h> #include <X11/Xproto.h> @@ -74,7 +76,7 @@ typedef struct { typedef struct { const char *class; const int desktop; - const Bool follow, floating; + const Bool follow, floating, ignore; } AppRule; /* exposed function prototypes sorted alphabetically */ @@ -85,15 +87,11 @@ static void killclient(); static void last_desktop(); static void move_down(); static void move_up(); -static void moveresize(const Arg *arg); static void mousemotion(const Arg *arg); static void next_win(); static void prev_win(); static void quit(const Arg *arg); static void resize_master(const Arg *arg); -static void resize_stack(const Arg *arg); -static void rotate(const Arg *arg); -static void rotate_filled(const Arg *arg); static void spawn(const Arg *arg); static void swap_master(); static void switch_mode(const Arg *arg); @@ -147,7 +145,7 @@ static void cleanup(void); static void clientmessage(XEvent *e); static void configurerequest(XEvent *e); static void deletewindow(Window w); -static void desktopinfo(void); +static void write_status(void); static void destroynotify(XEvent *e); static void enternotify(XEvent *e); static void focus(Client *c, Desktop *d); @@ -280,7 +278,7 @@ void change_desktop(const Arg *arg) { if (d->curr) XUnmapWindow(dis, d->curr->win); XChangeWindowAttributes(dis, root, CWEventMask, &(XSetWindowAttributes){.event_mask = ROOTMASK}); if (n->head) { tile(n); focus(n->curr, n); } - desktopinfo(); + write_status(); } /** @@ -319,7 +317,7 @@ void client_to_desktop(const Arg *arg) { /* link client to new desktop and make it the current */ focus(l ? (l->next = c):n->head ? (n->head->next = c):(n->head = c), n); - if (FOLLOW_WINDOW) change_desktop(arg); else desktopinfo(); + if (FOLLOW_WINDOW) change_desktop(arg); else write_status(); } /** @@ -404,30 +402,52 @@ void deletewindow(Window w) { } /** - * output info about the desktops on standard output stream - * - * the information is formatted as a space separated line - * where each token contains information about a desktop. - * each token is a formatted as ':' separated string of values. - * the values are: - * - the desktop number/id - * - the desktop's client count - * - the desktop's tiling layout mode/id - * - whether the desktop is the current focused (1) or not (0) - * - whether any client in that desktop has received an urgent hint - * - * once the info is collected, immediately flush the stream + * output status bar */ -void desktopinfo(void) { +void write_status(void) { Desktop *d = NULL; Client *c = NULL; Bool urgent = False; + time_t now; + struct tm *ptm; + char buff[32]; + // Desktops + printf("%%{l}"); for (int w = 0, i = 0; i < DESKTOPS; i++, w = 0, urgent = False) { for (d = &desktops[i], c = d->head; c; urgent |= c->isurgn, ++w, c = c->next); - printf("%d:%d:%d:%d:%d%c", i, w, d->mode, i == currdeskidx, urgent, (i < DESKTOPS - 1) ? ' ':'|'); + if (w>0) { + printf("[%c%d%c] ", urgent ? '!' : ' ', i, i == currdeskidx ? '*' : ' '); + } } - printf("%s\n", desktops[currdeskidx].curr ? desktops[currdeskidx].curr->title : ""); + + // Currently focused window + printf("%%{c}%s\n", desktops[currdeskidx].curr ? desktops[currdeskidx].curr->title : ""); + + // Times + printf("%%{r}"); + now = time(NULL); + + putenv("TZ=PST8PDT"); + ptm = localtime(&now); + strftime(buff, sizeof(buff), "%Z %H:%M", ptm); + printf("%s | ", buff); + + putenv("TZ=America/Chicago"); + ptm = localtime(&now); + strftime(buff, sizeof(buff), "%Z %H:%M", ptm); + printf("%s | ", buff); + + putenv("TZ=UTC"); + ptm = localtime(&now); + strftime(buff, sizeof(buff), "%Z %H:%M", ptm); + printf("%s | ", buff); + + putenv("TZ=Europe/Amsterdam"); + ptm = localtime(&now); + strftime(buff, sizeof(buff), "%Z %H:%M:%S %D", ptm); + printf("%s", buff); + fflush(stdout); } @@ -556,7 +576,7 @@ void focus(Client *c, Desktop *d) { PropModeReplace, (unsigned char *)&d->curr->win, 1); XSync(dis, False); - desktopinfo(); + write_status(); } /** @@ -709,12 +729,21 @@ void maprequest(XEvent *e) { Bool follow = False, floating = False; int newdsk = currdeskidx; - if (XGetClassHint(dis, w, &ch)) for (unsigned int i = 0; i < LENGTH(rules); i++) - if (strstr(ch.res_class, rules[i].class) || strstr(ch.res_name, rules[i].class)) { - if (rules[i].desktop >= 0 && rules[i].desktop < DESKTOPS) newdsk = rules[i].desktop; - follow = rules[i].follow, floating = rules[i].floating; - break; + if (XGetClassHint(dis, w, &ch)) { + for (unsigned int i = 0; i < LENGTH(rules); i++) { + if (strstr(ch.res_class, rules[i].class) || strstr(ch.res_name, rules[i].class)) { + if (rules[i].desktop >= 0 && rules[i].desktop < DESKTOPS) newdsk = rules[i].desktop; + follow = rules[i].follow; + floating = rules[i].floating; + if (rules[i].ignore) { + if (ch.res_class) XFree(ch.res_class); + if (ch.res_name) XFree(ch.res_name); + return; + } + break; + } } + } if (ch.res_class) XFree(ch.res_class); if (ch.res_name) XFree(ch.res_name); @@ -740,7 +769,7 @@ void maprequest(XEvent *e) { else if (follow) change_desktop(&(Arg){.i = newdsk}); focus(c, d); - if (!follow) desktopinfo(); + if (!follow) write_status(); } /** @@ -775,17 +804,39 @@ void mousemotion(const Arg *arg) { if (!d->curr->isfloat && !d->curr->istrans) { d->curr->isfloat = True; tile(d); focus(d->curr, d); } + /* init rectangle properties */ + XGCValues gv = { .function = GXinvert, .subwindow_mode = IncludeInferiors, .line_width = BORDER_WIDTH }; + GC gc = XCreateGC(dis, root, GCFunction|GCSubwindowMode|GCLineWidth, &gv); + + /* draw rectangle */ + if (arg->i == MOVE) XDrawRectangle(dis, root, gc, xw = wa.x, yh = wa.y, wa.width, wa.height); + else XDrawRectangle(dis, root, gc, wa.x, wa.y, xw = wa.width, yh = wa.height); + do { XMaskEvent(dis, BUTTONMASK|PointerMotionMask|SubstructureRedirectMask, &ev); if (ev.type == MotionNotify) { + /* clear rectangle from prev position */ + if (arg->i == MOVE) XDrawRectangle(dis, root, gc, xw, yh, wa.width, wa.height); + else if (arg->i == RESIZE) XDrawRectangle(dis, root, gc, wa.x, wa.y, xw, yh); + xw = (arg->i == MOVE ? wa.x:wa.width) + ev.xmotion.x - rx; yh = (arg->i == MOVE ? wa.y:wa.height) + ev.xmotion.y - ry; - if (arg->i == RESIZE) XResizeWindow(dis, d->curr->win, - xw > MINWSZ ? xw:wa.width, yh > MINWSZ ? yh:wa.height); - else if (arg->i == MOVE) XMoveWindow(dis, d->curr->win, xw, yh); + + /* draw rectangle in new position */ + if (arg->i == MOVE) XDrawRectangle(dis, root, gc, xw, yh, wa.width, wa.height); + else if (arg->i == RESIZE) XDrawRectangle(dis, root, gc, wa.x, wa.y, xw, yh); } else if (ev.type == ConfigureRequest || ev.type == MapRequest) events[ev.type](&ev); } while (ev.type != ButtonRelease); + /* clear rectangle from last position */ + if (arg->i == MOVE) XDrawRectangle(dis, root, gc, xw, yh, wa.width, wa.height); + else if (arg->i == RESIZE) XDrawRectangle(dis, root, gc, wa.x, wa.y, xw, yh); + + /* actually move/resize the window to the new position/size */ + if (arg->i == RESIZE) XResizeWindow(dis, d->curr->win, + xw > MINWSZ ? xw:wa.width, yh > MINWSZ ? yh:wa.height); + else if (arg->i == MOVE) XMoveWindow(dis, d->curr->win, xw, yh); + XUngrabPointer(dis, CurrentTime); } @@ -877,17 +928,6 @@ void move_up(void) { if (!d->curr->isfloat && !d->curr->istrans) tile(d); } -/** - * move and resize a window with the keyboard - */ -void moveresize(const Arg *arg) { - Desktop *d = &desktops[currdeskidx]; - XWindowAttributes wa; - if (!d->curr || !XGetWindowAttributes(dis, d->curr->win, &wa)) return; - if (!d->curr->isfloat && !d->curr->istrans) { d->curr->isfloat = True; tile(d); focus(d->curr, d); } - XMoveResizeWindow(dis, d->curr->win, wa.x + ((int *)arg->v)[0], wa.y + ((int *)arg->v)[1], - wa.width + ((int *)arg->v)[2], wa.height + ((int *)arg->v)[3]); -} /** * cyclic focus the next window @@ -936,7 +976,7 @@ void propertynotify(XEvent *e) { c->title[sizeof(c->title) - 1] = '\0'; if (name.value) XFree(name.value); } - desktopinfo(); + write_status(); } /** @@ -962,7 +1002,7 @@ void removeclient(Client *c, Desktop *d) { if (c == d->curr || (d->head && !d->head->next)) focus(d->prev, d); if (!(c->isfloat || c->istrans) || (d->head && !d->head->next)) tile(d); free(c); - desktopinfo(); + write_status(); } /** @@ -978,36 +1018,33 @@ void resize_master(const Arg *arg) { } /** - * resize the first stack window + * main event loop + * on receival of an event call the appropriate handler */ -void resize_stack(const Arg *arg) { - desktops[currdeskidx].sasz += arg->i; - tile(&desktops[currdeskidx]); -} -/** - * jump and focus the next or previous desktop - */ -void rotate(const Arg *arg) { - change_desktop(&(Arg){.i = (DESKTOPS + currdeskidx + arg->i) % DESKTOPS}); + +// check if FD has been updated within last second +static int wait_fd(int fd) { + struct timeval tv = { .tv_sec = 1, .tv_usec = 0}; + fd_set in_fds; FD_ZERO(&in_fds); FD_SET(fd, &in_fds); + return select(fd+1, &in_fds, 0, 0, &tv); } -/** - * jump and focus the next non-empty desktop - */ -void rotate_filled(const Arg *arg) { - int n = arg->i; - while (n < DESKTOPS && !desktops[(DESKTOPS + currdeskidx + n) % DESKTOPS].head) (n += arg->i); - change_desktop(&(Arg){.i = (DESKTOPS + currdeskidx + n) % DESKTOPS}); +// Like XNextEvent, except that it waits at most a second +int XNextEventTimeout(XEvent *event) { + if (XPending(dis) || wait_fd(ConnectionNumber(dis))) { + XNextEvent(dis, event); + return 0; + } + return 1; } -/** - * main event loop - * on receival of an event call the appropriate handler - */ void run(void) { XEvent ev; - while(running && !XNextEvent(dis, &ev)) if (events[ev.type]) events[ev.type](&ev); + while(running) { + if (!XNextEventTimeout(&ev) && events[ev.type]) events[ev.type](&ev); + write_status(); + } } /** @@ -1188,7 +1225,7 @@ void switch_mode(const Arg *arg) { if (d->mode != arg->i) d->mode = arg->i; else if (d->mode != FLOAT) for (Client *c = d->head; c; c = c->next) c->isfloat = False; if (d->head) { tile(d); focus(d->curr, d); } - desktopinfo(); + write_status(); } /** @@ -1257,7 +1294,6 @@ int main(int argc, char *argv[]) { else if (argc != 1) errx(EXIT_FAILURE, "usage: man monsterwm"); if (!(dis = XOpenDisplay(NULL))) errx(EXIT_FAILURE, "cannot open display"); setup(); - desktopinfo(); /* zero out every desktop on (re)start */ run(); cleanup(); XCloseDisplay(dis); |
