aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md36
-rw-r--r--config.h108
-rw-r--r--monsterwm.c180
3 files changed, 227 insertions, 97 deletions
diff --git a/README.md b/README.md
index ecfd416..80ad59a 100644
--- a/README.md
+++ b/README.md
@@ -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);