From 2cd9db89227308082b8bda949528277b40ebb9ed Mon Sep 17 00:00:00 2001 From: Ivan Kanakarakis Date: Wed, 14 Dec 2011 10:45:23 +0200 Subject: split rename to monsterwm --- .gitignore | 2 +- Makefile | 26 +- README.md | 24 +- config.def.h | 2 +- config.h | 2 +- dminiwm.c | 793 ----------------------------------------------------------- monsterwm.c | 793 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 818 insertions(+), 824 deletions(-) delete mode 100644 dminiwm.c create mode 100644 monsterwm.c diff --git a/.gitignore b/.gitignore index 244dcdc..56613e0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -dminiwm +monsterwm *.o *.swp *~ diff --git a/Makefile b/Makefile index 887817a..48f4979 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,5 @@ VERSION = cookies-git - -CFLAGS += -Wall -LDADD += -lX11 -LDFLAGS = -EXEC = dminiwm +WMNAME = monsterwm PREFIX ?= /usr/local BINDIR ?= ${PREFIX}/bin @@ -18,17 +14,17 @@ LIBS = -L/usr/lib -lc -L${X11LIB} -lX11 CPPFLAGS = -DVERSION=\"${VERSION}\" CFLAGS = -g -std=c99 -pedantic -Wall -Wextra -Os ${INCS} ${CPPFLAGS} LDFLAGS = -g ${LIBS} -#$(CC) $(LDFLAGS) -s -Os -o $@ $+ $(LDADD) -CC = cc +CC = cc +EXEC = ${WMNAME} -SRC = dminiwm.c +SRC = ${WMNAME}.c OBJ = ${SRC:.c=.o} -all: options dminiwm +all: options ${WMNAME} options: - @echo dminiwm build options: + @echo ${WMNAME} build options: @echo "CFLAGS = ${CFLAGS}" @echo "LDFLAGS = ${LDFLAGS}" @echo "CC = ${CC}" @@ -43,22 +39,22 @@ config.h: @echo creating $@ from config.def.h @cp config.def.h $@ -dminiwm: ${OBJ} +monsterwm: ${OBJ} @echo CC -o $@ @${CC} -o $@ ${OBJ} ${LDFLAGS} clean: @echo cleaning - @rm -fv dminiwm ${OBJ} dminiwm-${VERSION}.tar.gz + @rm -fv ${WMNAME} ${OBJ} ${WMNAME}-${VERSION}.tar.gz install: all @echo installing executable file to ${DESTDIR}${PREFIX}/bin @mkdir -p ${DESTDIR}${PREFIX}/bin - @cp -f dminiwm ${DESTDIR}${PREFIX}/bin - @chmod 755 ${DESTDIR}${PREFIX}/bin/dminiwm + @cp -f ${WMNAME} ${DESTDIR}${PREFIX}/bin + @chmod 755 ${DESTDIR}${PREFIX}/bin/${WMNAME} uninstall: @echo removing executable file from ${DESTDIR}${PREFIX}/bin - @rm -f ${DESTDIR}${PREFIX}/bin/dminiwm + @rm -f ${DESTDIR}${PREFIX}/bin/${WMNAME} .PHONY: all options clean install uninstall diff --git a/README.md b/README.md index aef6275..91f8a89 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,30 @@ -dminiwm -======= +monsterwm +========= ### ~ it's minimal and dynamic -[moetunes][] started this from [catwm][] 31/12/10 ([ArchLinux forums][catf]) -Credits and licensing included in `dminiwm.c` and/or `config.def.h`. +[moetunes][] started this based on [catwm][] 31/12/10 ([ArchLinux forums][catf]) creating [dminiwm][] +Credits and licensing included in `monsterwm.c` and/or `config.def.h`. For screenshots and ramblings/updates check [the topic on ArchLinux forums][dminif] [moetunes]: https://github.com/moetunes [catwm]: https://github.com/pyknite/catwm [catf]: https://bbs.archlinux.org/viewtopic.php?id=100215&p=1 + [dminiwm]: https://github.com/moetunes/dminiwm [dminif]: https://bbs.archlinux.org/viewtopic.php?id=126463 Summary ------- -**dminiwm** is a very minimal and lightweight dynamic tiling window manager. -It will try to stay under 1000 SLOC. -Currently under 800 lines with the config file included. - +**monsterwm** is a very minimal, lightweight, monsterous, dynamic tiling window manager. +It will try to stay as small as possible. Currently under 750 lines with the config file included. Modes ----- It allows the "normal" method of tiling window managers (with the new window as the master) -and with the new window opened at the bottom of the stack (like dwm's attach\_aside) +and with the new window opened at the bottom of the stack (like `dwm`'s attach\_aside) --- @@ -97,9 +96,8 @@ Build and install. Bugs ---- -For any issues report at [the topic on ArchLinux forums][dminif], -or [fill an issue][bug] on [GitHub][ghp] +For any bug or request [fill an issue][bug] on [GitHub][ghp] - [bug]: https://github.com/c00kiemon5ter/dminiwm/issues - [ghp]: https://github.com/c00kiemon5ter/dminiwm + [bug]: https://github.com/c00kiemon5ter/monsterwm/issues + [ghp]: https://github.com/c00kiemon5ter/monsterwm diff --git a/config.def.h b/config.def.h index 62ed754..7e50930 100644 --- a/config.def.h +++ b/config.def.h @@ -1,6 +1,6 @@ /* Based on: * - catwm at https://github.com/pyknite/catwm - * - 2wm at http://hg.suckless.org/2wm/ + * - dminiwm at https://github.com/moetunes/dminiwm * - dwm at http://dwm.suckless.org/ * * This program is free software: you can redistribute it and/or modify diff --git a/config.h b/config.h index e66e38f..2bc37a1 100644 --- a/config.h +++ b/config.h @@ -1,6 +1,6 @@ /* Based on: * - catwm at https://github.com/pyknite/catwm - * - 2wm at http://hg.suckless.org/2wm/ + * - dminiwm at https://github.com/moetunes/dminiwm * - dwm at http://dwm.suckless.org/ * * This program is free software: you can redistribute it and/or modify diff --git a/dminiwm.c b/dminiwm.c deleted file mode 100644 index a194d9a..0000000 --- a/dminiwm.c +++ /dev/null @@ -1,793 +0,0 @@ -/* Based on: - * - catwm at https://github.com/pyknite/catwm - * - 2wm at http://hg.suckless.org/2wm/ - * - dwm at http://dwm.suckless.org/ - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define LENGTH(x) (sizeof(x)/sizeof(*x)) - -enum { - WM_PROTOCOLS, - WM_DELETE_WINDOW, - _NET_WM_WINDOW_TYPE, - _NET_WM_WINDOW_TYPE_UTILITY, - _NET_WM_WINDOW_TYPE_DOCK, - _NET_WM_WINDOW_TYPE_SPLASH, - _NET_WM_WINDOW_TYPE_DIALOG, - _NET_WM_WINDOW_TYPE_NOTIFICATION, - ATOM_COUNT -}; - -/* layout modes */ -enum { - TILE, - MONOCLE, - BSTACK, - GRID, -}; - -/* structs */ -typedef union { - const char** com; - const int i; -} Arg; - -typedef struct { - unsigned int mod; - KeySym keysym; - void (*function)(const Arg arg); - const Arg arg; -} key; - -typedef struct client { - struct client *next; - struct client *prev; - Window win; -} client; - -typedef struct { - int master_size; - int mode; - int growth; - client *head; - client *current; - Bool showpanel; -} desktop; - -typedef struct { - const char *class; - const int desktop; - const Bool follow; -} AppRule; - -/* Functions */ -static void add_window(Window w); -static void buttonpressed(XEvent *e); -static void change_desktop(const Arg arg); -static void client_to_desktop(const Arg arg); -static void destroynotify(XEvent *e); -static void enternotify(XEvent *e); -static void die(const char* errstr, ...); -static int xerrorstart(); -static unsigned long getcolor(const char* color); -static void grabkeys(void); -static void keypress(XEvent *e); -static void killclient(); -static void maprequest(XEvent *e); -static void move_down(); -static void move_up(); -static void rotate_desktop(const Arg arg); -static void next_win(); -static void prev_win(); -static void cleanup(void); -static void quit(const Arg arg); -static void removeclient(client *c); -static void resize_master(const Arg arg); -static void resize_stack(const Arg arg); -static void save_desktop(int i); -static void select_desktop(int i); -static void togglepanel(); -static void deletewindow(Window w); -static void setup(void); -static void sigchld(); -static void spawn(const Arg arg); -static void run(void); -static void swap_master(); -static void last_desktop(); -static void switch_mode(const Arg arg); -static void tile(void); -static void update_current(void); - -#include "config.h" - -/* variables */ -static Bool running = True; -static Bool showpanel = SHOW_PANEL; -static int retval = 0; -static int current_desktop = 0; -static int previous_desktop = 0; -static int growth = 0; -static int mode = DEFAULT_MODE; -static int master_size; -static int sh; -static int sw; -static int screen; -static int xerror(Display *dis, XErrorEvent *ee); -static int (*xerrorxlib)(Display *, XErrorEvent *); -static unsigned int win_focus; -static unsigned int win_unfocus; -static unsigned int numlockmask = 0; /* dynamic key lock mask */ -static Display *dis; -static Window root; -static client *head = NULL; -static client *current = NULL; -static Atom atoms[ATOM_COUNT]; -static desktop desktops[DESKTOPS]; - -/* events array */ -static void (*events[LASTEvent])(XEvent *e) = { - [KeyPress] = keypress, - [MapRequest] = maprequest, - [EnterNotify] = enternotify, - [ButtonPress] = buttonpressed, - [DestroyNotify] = destroynotify, -}; - -/* ~~~ Window Management ~~~ */ -void add_window(Window w) { - client *c, *t; - - if(!(c = (client *)calloc(1, sizeof(client)))) - die("error: could not calloc() %u bytes\n", sizeof(client)); - - if(!head) { - c->win = w; - head = c; - } else if(ATTACH_ASIDE) { - for(t=head; t->next; t=t->next); - c->prev = t; - c->win = w; - t->next = c; - } else { - t = head; - c->next = t; - t->prev = c; - c->win = w; - head = c; - } - - current = c; - save_desktop(current_desktop); - - if(FOLLOW_MOUSE) XSelectInput(dis, c->win, EnterWindowMask); -} - -void removeclient(client *c) { - if(!c->prev) { /* w is head */ - if(!c->next) { /* head is only window on screen */ - free(head); - head = NULL; - } else { /* more windows on screen */ - head->next->prev = NULL; - head = head->next; - } - current = head; - } else { /* w is on stack */ - if(!c->next) { /* w is last window on screen */ - c->prev->next = NULL; - } else { /* w is somewhere in the middle */ - c->next->prev = c->prev; - c->prev->next = c->next; - } - current = c->prev; - } - - save_desktop(current_desktop); - tile(); - if(mode == MONOCLE && current) - XMapWindow(dis, current->win); - update_current(); -} - -void killclient() { - if(!current) return; - deletewindow(current->win); - removeclient(current); -} - -void next_win() { - if(!current || (!current->next && !current->prev)) return; - if(mode == MONOCLE) XUnmapWindow(dis, current->win); - current = (current->next) ? current->next : head; - if(mode == MONOCLE) XMapWindow(dis, current->win); - update_current(); -} - -void prev_win() { - if(!current || (!current->next && !current->prev)) return; - if(mode == MONOCLE) XUnmapWindow(dis, current->win); - if(current->prev) current = current->prev; - else while(current->next) current=current->next; - if(mode == MONOCLE) XMapWindow(dis, current->win); - update_current(); -} - -void move_down() { - if(!current || current == head || !current->next) return; - Window tmpwin = current->win; - current->win = current->next->win; - current->next->win = tmpwin; - current = current->next; - save_desktop(current_desktop); - tile(); - update_current(); -} - -void move_up() { - if(!current || current == head || !current->prev) return; - Window tmpwin = current->win; - current->win = current->prev->win; - current->prev->win = tmpwin; - current = current->prev; - save_desktop(current_desktop); - tile(); - update_current(); -} - -void swap_master() { - if(!current || !head->next || mode == MONOCLE) return; - Window tmpwin = head->win; - current = (current == head) ? head->next : current; - head->win = current->win; - current->win = tmpwin; - current = head; - save_desktop(current_desktop); - tile(); - update_current(); -} - -/* ~~~ Desktop Management ~~~ */ -void change_desktop(const Arg arg) { - if(arg.i == current_desktop) return; - previous_desktop = current_desktop; - - /* save current desktop settings and unmap windows */ - save_desktop(current_desktop); - for(client *c=head; c; c=c->next) - XUnmapWindow(dis, c->win); - - /* read new desktop properties, tile and map new windows */ - select_desktop(arg.i); - tile(); - if(mode == MONOCLE && current) XMapWindow(dis, current->win); - else for(client *c=head; c; c=c->next) XMapWindow(dis, c->win); - - update_current(); -} - -void last_desktop() { - change_desktop((Arg){.i = previous_desktop}); -} - -void rotate_desktop(const Arg arg) { - change_desktop((Arg){.i = (current_desktop + DESKTOPS + arg.i) % DESKTOPS}); -} - -void client_to_desktop(const Arg arg) { - if(arg.i == current_desktop || !current) return; - - client *c = current; - int cd = current_desktop; - - select_desktop(arg.i); - add_window(c->win); - save_desktop(arg.i); - - select_desktop(cd); - XUnmapWindow(dis, c->win); - removeclient(c); - save_desktop(cd); - - tile(); - update_current(); - - if(FOLLOW_WINDOW) change_desktop(arg); -} - -void save_desktop(int i) { - desktops[i].master_size = master_size; - desktops[i].mode = mode; - desktops[i].growth = growth; - desktops[i].head = head; - desktops[i].current = current; - desktops[i].showpanel = showpanel; -} - -void select_desktop(int i) { - if(current_desktop == i) return; - master_size = desktops[i].master_size; - mode = desktops[i].mode; - growth = desktops[i].growth; - head = desktops[i].head; - current = desktops[i].current; - showpanel = desktops[i].showpanel; - current_desktop = i; -} - -void togglepanel() { - showpanel = !showpanel; - save_desktop(current_desktop); - tile(); -} - -void tile(void) { - if(!head) return; /* no need to arange anything */ - - client *c; - int n = 0; - int x = 0; - int panel_height = (showpanel) ? PANEL_HEIGHT : 0; - int y = (TOP_PANEL) ? panel_height : 0; - sh = XDisplayHeight(dis, screen) - panel_height - BORDER_WIDTH; - - if(!head->next) - XMoveResizeWindow(dis, head->win, 0, y, sw + 2*BORDER_WIDTH, sh + 2*BORDER_WIDTH); - else switch(mode) { - case TILE: - /* master window */ - XMoveResizeWindow(dis, head->win, 0, y, master_size - BORDER_WIDTH, sh - BORDER_WIDTH); - /* stack */ - for(n=0, c=head->next; c; c=c->next, ++n); /* count windows */ - growth = (n == 1) ? 0 : growth; /* if only one window don't care about growth */ - XMoveResizeWindow(dis, head->next->win, master_size + BORDER_WIDTH, y, sw - master_size - 2*BORDER_WIDTH, sh/n + growth - BORDER_WIDTH); - y += sh/n + growth; - for(c=head->next->next; c; c=c->next) { - XMoveResizeWindow(dis, c->win, master_size + BORDER_WIDTH, y, sw - master_size - 2*BORDER_WIDTH, sh/n - growth/(n-1) - BORDER_WIDTH); - y += sh/n - growth / (n-1); - } - break; - case MONOCLE: - for(c=head; c; c=c->next) - XMoveResizeWindow(dis, c->win, 0, y, sw + 2*BORDER_WIDTH, sh + 2*BORDER_WIDTH); - break; - case BSTACK: - /* master window */ - XMoveResizeWindow(dis, head->win, 0, y, sw - BORDER_WIDTH, master_size - BORDER_WIDTH); - /* stack */ - for(n=0, c=head->next; c; c=c->next, ++n); /* count windows */ - growth = (n == 1) ? 0 : growth; /* if only one window don't care about growth */ - XMoveResizeWindow(dis, head->next->win, 0, y + master_size + BORDER_WIDTH, sw/n + growth - BORDER_WIDTH, sh - master_size - 2*BORDER_WIDTH); - x = sw/n + growth; - for(c=head->next->next; c; c=c->next) { - XMoveResizeWindow(dis, c->win, x, y + master_size + BORDER_WIDTH, sw/n - growth/(n-1) - BORDER_WIDTH, sh-master_size - 2*BORDER_WIDTH); - x += sw/n - growth/(n-1); - } - break; - case GRID: - { - int xpos = 0; - int wdt = 0; - int ht = 0; - - for(c=head; c; c=c->next) ++x; - - for(c=head; c; c=c->next) { - ++n; - if(x >= 7) { - wdt = sw/3 - BORDER_WIDTH; - ht = sh/3 - BORDER_WIDTH; - if(n == 1 || n == 4 || n == 7) - xpos = 0; - if(n == 2 || n == 5 || n == 8) - xpos = sw/3 + BORDER_WIDTH; - if(n == 3 || n == 6 || n == 9) - xpos = 2*(sw/3) + BORDER_WIDTH; - if(n == 4 || n == 7) - y += sh/3 + BORDER_WIDTH; - if(n == x && n == 7) - wdt = sw - BORDER_WIDTH; - if(n == x && n == 8) - wdt = 2*(sw/3) - BORDER_WIDTH; - } else - if(x >= 5) { - wdt = sw/3 - BORDER_WIDTH; - ht = sh/2 - BORDER_WIDTH; - if(n == 1 || n == 4) - xpos = 0; - if(n == 2 || n == 5) - xpos = sw/3 + BORDER_WIDTH; - if(n == 3 || n == 6) - xpos = 2*(sw/3) + BORDER_WIDTH; - if(n == 4) - y += sh/2; // + BORDER_WIDTH; - if(n == x && n == 5) - wdt = 2*(sw/3) - BORDER_WIDTH; - } else { - if(x > 2) { - if(n == 1 || n == 2) - ht = sh/2 + growth - BORDER_WIDTH; - if(n >= 3) - ht = sh/2 - growth - 2*BORDER_WIDTH; - } - else - ht = sh - BORDER_WIDTH; - if(n == 1 || n == 3) { - xpos = 0; - wdt = master_size - BORDER_WIDTH; - } - if(n == 2 || n == 4) { - xpos = master_size+BORDER_WIDTH; - wdt = sw - master_size - 2*BORDER_WIDTH; - } - if(n == 3) - y += sh/2 + growth + BORDER_WIDTH; - if(n == x && n == 3) - wdt = sw - BORDER_WIDTH; - } - XMoveResizeWindow(dis, c->win, xpos, y, wdt, ht); - } - } - break; - } - free(c); -} - -void update_current(void) { - client *c; - - for(c=head; c; c=c->next) { - if(!head->next || mode == MONOCLE) - XSetWindowBorderWidth(dis, c->win, 0); - else - XSetWindowBorderWidth(dis, c->win, BORDER_WIDTH); - - if(current == c) { /* highlight current window */ - XSetWindowBorder(dis, c->win, win_focus); - XSetInputFocus(dis, c->win, RevertToParent, CurrentTime); - XRaiseWindow(dis, c->win); - if(CLICK_TO_FOCUS) XUngrabButton(dis, AnyButton, AnyModifier, c->win); - } else { - XSetWindowBorder(dis, c->win, win_unfocus); - if(CLICK_TO_FOCUS) XGrabButton(dis, AnyButton, AnyModifier, c->win, True, - ButtonPressMask|ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None); - } - } - free(c); - XSync(dis, False); -} - -void switch_mode(const Arg arg) { - if(mode == arg.i) return; - if(mode == MONOCLE) for(client *c=head; c; c=c->next) XMapWindow(dis, c->win); - mode = arg.i; - if(mode == TILE || mode == GRID) master_size = sw * MASTER_SIZE; - else if(mode == BSTACK) master_size = sh * MASTER_SIZE; - tile(); - update_current(); -} - -void resize_master(const Arg arg) { - master_size += arg.i; - tile(); -} - -void resize_stack(const Arg arg) { - growth += arg.i; - tile(); -} - -/* ~~~ Keyboard Management ~~~ */ -void grabkeys(void) { - KeyCode code; - - XUngrabKey(dis, AnyKey, AnyModifier, root); - for(unsigned int i=0; ixkey; - - keysym = XKeycodeToKeysym(dis, (KeyCode)ev->keycode, 0); - for(i = 0; i < len; i++) - if(keysym == keys[i].keysym && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) && keys[i].function) - keys[i].function(keys[i].arg); -} - -/* ~~~ Signal Management ~~~ */ -void maprequest(XEvent *e) { - XMapRequestEvent *ev = &e->xmaprequest; - static XWindowAttributes wa; - - if(!XGetWindowAttributes(dis, ev->window, &wa)) return; - if(wa.override_redirect) return; - - /* window is transient */ - Window trans; - if(XGetTransientForHint(dis, ev->window, &trans) && trans) { - add_window(ev->window); - XMapWindow(dis, ev->window); - XSetInputFocus(dis, ev->window, RevertToParent, CurrentTime); - XRaiseWindow(dis, ev->window); - XFree(&trans); - return; - } - - /* window type is not normal (_NET_WM_WINDOW_TYPE_NORMAL) */ - Atom realType, *wintype; - int realFormat; - unsigned long count, extra; - unsigned char *data; - if(XGetWindowProperty(dis, ev->window, atoms[_NET_WM_WINDOW_TYPE], 0, 32, False, - XA_ATOM, &realType, &realFormat, &count, &extra, &data) == Success) - if(realType == XA_ATOM && count) { - wintype = (unsigned long *)data; - if(data) XFree(data); - for(unsigned int i=0; iwindow); - XMapWindow(dis, ev->window); - XSetInputFocus(dis, ev->window, RevertToParent, CurrentTime); - XRaiseWindow(dis, ev->window); - return; - } - } - - /* window is normal and has a rule set */ - XClassHint ch = {0, 0}; - if(XGetClassHint(dis, ev->window, &ch)) - for(unsigned int i=0; iwindow); - select_desktop(cd); - if(cd == rules[i].desktop) { - XMapWindow(dis, ev->window); - tile(); - update_current(); - } else if(rules[i].follow) - change_desktop((Arg){.i = rules[i].desktop}); - if(ch.res_class) XFree(ch.res_class); - if(ch.res_name) XFree(ch.res_name); - return; - } - - /* window is normal and has no rule set */ - add_window(ev->window); - XMapWindow(dis, ev->window); - tile(); - update_current(); -} - -void destroynotify(XEvent *e) { - client *c; - XDestroyWindowEvent *ev = &e->xdestroywindow; - int cd = current_desktop; - Bool found = False; - - save_desktop(cd); - for(int d=0; dnext) - if((found = ev->window == c->win)) { - removeclient(c); - break; - } - select_desktop(cd); -} - -void enternotify(XEvent *e) { - client *c; - XCrossingEvent *ev = &e->xcrossing; - - if(FOLLOW_MOUSE) { - if((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root) return; - for(c=head; c; c=c->next) - if(ev->window == c->win) { - current = c; - update_current(); - return; - } - } -} - -void buttonpressed(XEvent *e) { - client *c; - XButtonPressedEvent *ev = &e->xbutton; - - if(CLICK_TO_FOCUS && ev->window != current->win && ev->button == Button1) - for(c=head; c; c=c->next) - if(ev->window == c->win) { - current = c; - update_current(); - return; - } -} - -void deletewindow(Window w) { - XEvent ev; - ev.type = ClientMessage; - ev.xclient.window = w; - ev.xclient.message_type = atoms[WM_PROTOCOLS]; - ev.xclient.format = 32; - ev.xclient.data.l[0] = atoms[WM_DELETE_WINDOW]; - ev.xclient.data.l[1] = CurrentTime; - XSendEvent(dis, w, False, NoEventMask, &ev); -} - -unsigned long getcolor(const char* color) { - Colormap map = DefaultColormap(dis, screen); - XColor c; - - if(!XAllocNamedColor(dis, map, color, &c, &c)) - die("error: cannot allocate color '%s'\n", c); - return c.pixel; -} - -void quit(const Arg arg) { - retval = arg.i; - running = False; -} - -void cleanup(void) { - Window root_return; - Window parent_return; - Window *children; - unsigned int nchildren; - - XUngrabKey(dis, AnyKey, AnyModifier, root); - - XQueryTree(dis, root, &root_return, &parent_return, &children, &nchildren); - for(unsigned int i = 0; i < nchildren; i++) - deletewindow(children[i]); - if(children) XFree(children); - - XSync(dis, False); - XSetInputFocus(dis, PointerRoot, RevertToPointerRoot, CurrentTime); -} - -void setup(void) { - sigchld(); - - screen = DefaultScreen(dis); - root = RootWindow(dis, screen); - - sw = XDisplayWidth(dis, screen) - BORDER_WIDTH; - sh = XDisplayHeight(dis, screen) - ((showpanel) ? PANEL_HEIGHT : 0) - BORDER_WIDTH; - master_size = ((mode == BSTACK) ? sh : sw) * MASTER_SIZE; - for(int i=0; imax_keypermod; j++) - if(modmap->modifiermap[modmap->max_keypermod*k + j] == XKeysymToKeycode(dis, XK_Num_Lock)) - numlockmask = (1 << k); - XFreeModifiermap(modmap); - - /* set up atoms for dialog/notification windows */ - atoms[WM_PROTOCOLS] = XInternAtom(dis, "WM_PROTOCOLS", False); - atoms[WM_DELETE_WINDOW] = XInternAtom(dis, "WM_DELETE_WINDOW", False); - atoms[_NET_WM_WINDOW_TYPE] = XInternAtom(dis, "_NET_WM_WINDOW_TYPE", False); - atoms[_NET_WM_WINDOW_TYPE_UTILITY] = XInternAtom(dis, "_NET_WM_WINDOW_TYPE_UTILITY", False); - atoms[_NET_WM_WINDOW_TYPE_DOCK] = XInternAtom(dis, "_NET_WM_WINDOW_TYPE_DOCK", False); - atoms[_NET_WM_WINDOW_TYPE_SPLASH] = XInternAtom(dis, "_NET_WM_WINDOW_TYPE_SPLASH", False); - atoms[_NET_WM_WINDOW_TYPE_DIALOG] = XInternAtom(dis, "_NET_WM_WINDOW_TYPE_DIALOG", False); - atoms[_NET_WM_WINDOW_TYPE_NOTIFICATION] = XInternAtom(dis, "_NET_WM_WINDOW_TYPE_NOTIFICATION", False); - - /* check if another window manager is running */ - xerrorxlib = XSetErrorHandler(xerrorstart); - XSelectInput(dis, DefaultRootWindow(dis), SubstructureNotifyMask|SubstructureRedirectMask); - XSync(dis, False); - XSetErrorHandler(xerror); - XSync(dis, False); - - grabkeys(); -} - -int xerrorstart() { - die("error: another window manager is already running\n"); - return -1; -} - -/* 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 *dis, XErrorEvent *ee) { - if(ee->error_code == BadWindow - || (ee->error_code == BadMatch && (ee->request_code == X_SetInputFocus || ee->request_code == X_ConfigureWindow)) - || (ee->error_code == BadDrawable && (ee->request_code == X_PolyText8 || ee->request_code == X_PolyFillRectangle - || ee->request_code == X_PolySegment || ee->request_code == X_CopyArea)) - || (ee->error_code == BadAccess && ee->request_code == X_GrabKey)) - return 0; - fprintf(stderr, "error: xerror: request code: %d, error code: %d\n", ee->request_code, ee->error_code); - return xerrorxlib(dis, ee); /* may call exit */ -} - -void sigchld() { - if(signal(SIGCHLD, sigchld) == SIG_ERR) - die("error: can't install SIGCHLD handler\n"); - while(0 < waitpid(-1, NULL, WNOHANG)); -} - -void spawn(const Arg arg) { - if(fork() == 0) { - if(dis) close(ConnectionNumber(dis)); - setsid(); - execvp((char*)arg.com[0], (char**)arg.com); - fprintf(stderr, "error: execvp %s", (char *)arg.com[0]); - perror(" failed"); /* also prints the err msg */ - exit(EXIT_SUCCESS); - } -} - -void run(void) { - XEvent ev; - while(running && !XNextEvent(dis, &ev)) - if(events[ev.type]) - events[ev.type](&ev); -} - -void die(const char *errstr, ...) { - va_list ap; - va_start(ap, errstr); - vfprintf(stderr, errstr, ap); - va_end(ap); - exit(EXIT_FAILURE); -} - -int main(int argc, char *argv[]) { - if(argc == 2 && strcmp("-v", argv[1]) == 0) { - fprintf(stdout, "dminiwm-%s\n", VERSION); - return EXIT_SUCCESS; - } else if(argc != 1) - die("usage: dminiwm [-v]\n"); - if(!(dis = XOpenDisplay(NULL))) - die("error: cannot open display\n"); - setup(); - run(); - cleanup(); - XCloseDisplay(dis); - return retval; -} diff --git a/monsterwm.c b/monsterwm.c new file mode 100644 index 0000000..474bf22 --- /dev/null +++ b/monsterwm.c @@ -0,0 +1,793 @@ +/* Based on: + * - catwm at https://github.com/pyknite/catwm + * - dminiwm at https://github.com/moetunes/dminiwm + * - dwm at http://dwm.suckless.org/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LENGTH(x) (sizeof(x)/sizeof(*x)) + +enum { + WM_PROTOCOLS, + WM_DELETE_WINDOW, + _NET_WM_WINDOW_TYPE, + _NET_WM_WINDOW_TYPE_UTILITY, + _NET_WM_WINDOW_TYPE_DOCK, + _NET_WM_WINDOW_TYPE_SPLASH, + _NET_WM_WINDOW_TYPE_DIALOG, + _NET_WM_WINDOW_TYPE_NOTIFICATION, + ATOM_COUNT +}; + +/* layout modes */ +enum { + TILE, + MONOCLE, + BSTACK, + GRID, +}; + +/* structs */ +typedef union { + const char** com; + const int i; +} Arg; + +typedef struct { + unsigned int mod; + KeySym keysym; + void (*function)(const Arg arg); + const Arg arg; +} key; + +typedef struct client { + struct client *next; + struct client *prev; + Window win; +} client; + +typedef struct { + int master_size; + int mode; + int growth; + client *head; + client *current; + Bool showpanel; +} desktop; + +typedef struct { + const char *class; + const int desktop; + const Bool follow; +} AppRule; + +/* Functions */ +static void add_window(Window w); +static void buttonpressed(XEvent *e); +static void change_desktop(const Arg arg); +static void client_to_desktop(const Arg arg); +static void destroynotify(XEvent *e); +static void enternotify(XEvent *e); +static void die(const char* errstr, ...); +static int xerrorstart(); +static unsigned long getcolor(const char* color); +static void grabkeys(void); +static void keypress(XEvent *e); +static void killclient(); +static void maprequest(XEvent *e); +static void move_down(); +static void move_up(); +static void rotate_desktop(const Arg arg); +static void next_win(); +static void prev_win(); +static void cleanup(void); +static void quit(const Arg arg); +static void removeclient(client *c); +static void resize_master(const Arg arg); +static void resize_stack(const Arg arg); +static void save_desktop(int i); +static void select_desktop(int i); +static void togglepanel(); +static void deletewindow(Window w); +static void setup(void); +static void sigchld(); +static void spawn(const Arg arg); +static void run(void); +static void swap_master(); +static void last_desktop(); +static void switch_mode(const Arg arg); +static void tile(void); +static void update_current(void); + +#include "config.h" + +/* variables */ +static Bool running = True; +static Bool showpanel = SHOW_PANEL; +static int retval = 0; +static int current_desktop = 0; +static int previous_desktop = 0; +static int growth = 0; +static int mode = DEFAULT_MODE; +static int master_size; +static int sh; +static int sw; +static int screen; +static int xerror(Display *dis, XErrorEvent *ee); +static int (*xerrorxlib)(Display *, XErrorEvent *); +static unsigned int win_focus; +static unsigned int win_unfocus; +static unsigned int numlockmask = 0; /* dynamic key lock mask */ +static Display *dis; +static Window root; +static client *head = NULL; +static client *current = NULL; +static Atom atoms[ATOM_COUNT]; +static desktop desktops[DESKTOPS]; + +/* events array */ +static void (*events[LASTEvent])(XEvent *e) = { + [KeyPress] = keypress, + [MapRequest] = maprequest, + [EnterNotify] = enternotify, + [ButtonPress] = buttonpressed, + [DestroyNotify] = destroynotify, +}; + +/* ~~~ Window Management ~~~ */ +void add_window(Window w) { + client *c, *t; + + if(!(c = (client *)calloc(1, sizeof(client)))) + die("error: could not calloc() %u bytes\n", sizeof(client)); + + if(!head) { + c->win = w; + head = c; + } else if(ATTACH_ASIDE) { + for(t=head; t->next; t=t->next); + c->prev = t; + c->win = w; + t->next = c; + } else { + t = head; + c->next = t; + t->prev = c; + c->win = w; + head = c; + } + + current = c; + save_desktop(current_desktop); + + if(FOLLOW_MOUSE) XSelectInput(dis, c->win, EnterWindowMask); +} + +void removeclient(client *c) { + if(!c->prev) { /* w is head */ + if(!c->next) { /* head is only window on screen */ + free(head); + head = NULL; + } else { /* more windows on screen */ + head->next->prev = NULL; + head = head->next; + } + current = head; + } else { /* w is on stack */ + if(!c->next) { /* w is last window on screen */ + c->prev->next = NULL; + } else { /* w is somewhere in the middle */ + c->next->prev = c->prev; + c->prev->next = c->next; + } + current = c->prev; + } + + save_desktop(current_desktop); + tile(); + if(mode == MONOCLE && current) + XMapWindow(dis, current->win); + update_current(); +} + +void killclient() { + if(!current) return; + deletewindow(current->win); + removeclient(current); +} + +void next_win() { + if(!current || (!current->next && !current->prev)) return; + if(mode == MONOCLE) XUnmapWindow(dis, current->win); + current = (current->next) ? current->next : head; + if(mode == MONOCLE) XMapWindow(dis, current->win); + update_current(); +} + +void prev_win() { + if(!current || (!current->next && !current->prev)) return; + if(mode == MONOCLE) XUnmapWindow(dis, current->win); + if(current->prev) current = current->prev; + else while(current->next) current=current->next; + if(mode == MONOCLE) XMapWindow(dis, current->win); + update_current(); +} + +void move_down() { + if(!current || current == head || !current->next) return; + Window tmpwin = current->win; + current->win = current->next->win; + current->next->win = tmpwin; + current = current->next; + save_desktop(current_desktop); + tile(); + update_current(); +} + +void move_up() { + if(!current || current == head || !current->prev) return; + Window tmpwin = current->win; + current->win = current->prev->win; + current->prev->win = tmpwin; + current = current->prev; + save_desktop(current_desktop); + tile(); + update_current(); +} + +void swap_master() { + if(!current || !head->next || mode == MONOCLE) return; + Window tmpwin = head->win; + current = (current == head) ? head->next : current; + head->win = current->win; + current->win = tmpwin; + current = head; + save_desktop(current_desktop); + tile(); + update_current(); +} + +/* ~~~ Desktop Management ~~~ */ +void change_desktop(const Arg arg) { + if(arg.i == current_desktop) return; + previous_desktop = current_desktop; + + /* save current desktop settings and unmap windows */ + save_desktop(current_desktop); + for(client *c=head; c; c=c->next) + XUnmapWindow(dis, c->win); + + /* read new desktop properties, tile and map new windows */ + select_desktop(arg.i); + tile(); + if(mode == MONOCLE && current) XMapWindow(dis, current->win); + else for(client *c=head; c; c=c->next) XMapWindow(dis, c->win); + + update_current(); +} + +void last_desktop() { + change_desktop((Arg){.i = previous_desktop}); +} + +void rotate_desktop(const Arg arg) { + change_desktop((Arg){.i = (current_desktop + DESKTOPS + arg.i) % DESKTOPS}); +} + +void client_to_desktop(const Arg arg) { + if(arg.i == current_desktop || !current) return; + + client *c = current; + int cd = current_desktop; + + select_desktop(arg.i); + add_window(c->win); + save_desktop(arg.i); + + select_desktop(cd); + XUnmapWindow(dis, c->win); + removeclient(c); + save_desktop(cd); + + tile(); + update_current(); + + if(FOLLOW_WINDOW) change_desktop(arg); +} + +void save_desktop(int i) { + desktops[i].master_size = master_size; + desktops[i].mode = mode; + desktops[i].growth = growth; + desktops[i].head = head; + desktops[i].current = current; + desktops[i].showpanel = showpanel; +} + +void select_desktop(int i) { + if(current_desktop == i) return; + master_size = desktops[i].master_size; + mode = desktops[i].mode; + growth = desktops[i].growth; + head = desktops[i].head; + current = desktops[i].current; + showpanel = desktops[i].showpanel; + current_desktop = i; +} + +void togglepanel() { + showpanel = !showpanel; + save_desktop(current_desktop); + tile(); +} + +void tile(void) { + if(!head) return; /* no need to arange anything */ + + client *c; + int n = 0; + int x = 0; + int panel_height = (showpanel) ? PANEL_HEIGHT : 0; + int y = (TOP_PANEL) ? panel_height : 0; + sh = XDisplayHeight(dis, screen) - panel_height - BORDER_WIDTH; + + if(!head->next) + XMoveResizeWindow(dis, head->win, 0, y, sw + 2*BORDER_WIDTH, sh + 2*BORDER_WIDTH); + else switch(mode) { + case TILE: + /* master window */ + XMoveResizeWindow(dis, head->win, 0, y, master_size - BORDER_WIDTH, sh - BORDER_WIDTH); + /* stack */ + for(n=0, c=head->next; c; c=c->next, ++n); /* count windows */ + growth = (n == 1) ? 0 : growth; /* if only one window don't care about growth */ + XMoveResizeWindow(dis, head->next->win, master_size + BORDER_WIDTH, y, sw - master_size - 2*BORDER_WIDTH, sh/n + growth - BORDER_WIDTH); + y += sh/n + growth; + for(c=head->next->next; c; c=c->next) { + XMoveResizeWindow(dis, c->win, master_size + BORDER_WIDTH, y, sw - master_size - 2*BORDER_WIDTH, sh/n - growth/(n-1) - BORDER_WIDTH); + y += sh/n - growth / (n-1); + } + break; + case MONOCLE: + for(c=head; c; c=c->next) + XMoveResizeWindow(dis, c->win, 0, y, sw + 2*BORDER_WIDTH, sh + 2*BORDER_WIDTH); + break; + case BSTACK: + /* master window */ + XMoveResizeWindow(dis, head->win, 0, y, sw - BORDER_WIDTH, master_size - BORDER_WIDTH); + /* stack */ + for(n=0, c=head->next; c; c=c->next, ++n); /* count windows */ + growth = (n == 1) ? 0 : growth; /* if only one window don't care about growth */ + XMoveResizeWindow(dis, head->next->win, 0, y + master_size + BORDER_WIDTH, sw/n + growth - BORDER_WIDTH, sh - master_size - 2*BORDER_WIDTH); + x = sw/n + growth; + for(c=head->next->next; c; c=c->next) { + XMoveResizeWindow(dis, c->win, x, y + master_size + BORDER_WIDTH, sw/n - growth/(n-1) - BORDER_WIDTH, sh-master_size - 2*BORDER_WIDTH); + x += sw/n - growth/(n-1); + } + break; + case GRID: + { + int xpos = 0; + int wdt = 0; + int ht = 0; + + for(c=head; c; c=c->next) ++x; + + for(c=head; c; c=c->next) { + ++n; + if(x >= 7) { + wdt = sw/3 - BORDER_WIDTH; + ht = sh/3 - BORDER_WIDTH; + if(n == 1 || n == 4 || n == 7) + xpos = 0; + if(n == 2 || n == 5 || n == 8) + xpos = sw/3 + BORDER_WIDTH; + if(n == 3 || n == 6 || n == 9) + xpos = 2*(sw/3) + BORDER_WIDTH; + if(n == 4 || n == 7) + y += sh/3 + BORDER_WIDTH; + if(n == x && n == 7) + wdt = sw - BORDER_WIDTH; + if(n == x && n == 8) + wdt = 2*(sw/3) - BORDER_WIDTH; + } else + if(x >= 5) { + wdt = sw/3 - BORDER_WIDTH; + ht = sh/2 - BORDER_WIDTH; + if(n == 1 || n == 4) + xpos = 0; + if(n == 2 || n == 5) + xpos = sw/3 + BORDER_WIDTH; + if(n == 3 || n == 6) + xpos = 2*(sw/3) + BORDER_WIDTH; + if(n == 4) + y += sh/2; // + BORDER_WIDTH; + if(n == x && n == 5) + wdt = 2*(sw/3) - BORDER_WIDTH; + } else { + if(x > 2) { + if(n == 1 || n == 2) + ht = sh/2 + growth - BORDER_WIDTH; + if(n >= 3) + ht = sh/2 - growth - 2*BORDER_WIDTH; + } + else + ht = sh - BORDER_WIDTH; + if(n == 1 || n == 3) { + xpos = 0; + wdt = master_size - BORDER_WIDTH; + } + if(n == 2 || n == 4) { + xpos = master_size+BORDER_WIDTH; + wdt = sw - master_size - 2*BORDER_WIDTH; + } + if(n == 3) + y += sh/2 + growth + BORDER_WIDTH; + if(n == x && n == 3) + wdt = sw - BORDER_WIDTH; + } + XMoveResizeWindow(dis, c->win, xpos, y, wdt, ht); + } + } + break; + } + free(c); +} + +void update_current(void) { + client *c; + + for(c=head; c; c=c->next) { + if(!head->next || mode == MONOCLE) + XSetWindowBorderWidth(dis, c->win, 0); + else + XSetWindowBorderWidth(dis, c->win, BORDER_WIDTH); + + if(current == c) { /* highlight current window */ + XSetWindowBorder(dis, c->win, win_focus); + XSetInputFocus(dis, c->win, RevertToParent, CurrentTime); + XRaiseWindow(dis, c->win); + if(CLICK_TO_FOCUS) XUngrabButton(dis, AnyButton, AnyModifier, c->win); + } else { + XSetWindowBorder(dis, c->win, win_unfocus); + if(CLICK_TO_FOCUS) XGrabButton(dis, AnyButton, AnyModifier, c->win, True, + ButtonPressMask|ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None); + } + } + free(c); + XSync(dis, False); +} + +void switch_mode(const Arg arg) { + if(mode == arg.i) return; + if(mode == MONOCLE) for(client *c=head; c; c=c->next) XMapWindow(dis, c->win); + mode = arg.i; + if(mode == TILE || mode == GRID) master_size = sw * MASTER_SIZE; + else if(mode == BSTACK) master_size = sh * MASTER_SIZE; + tile(); + update_current(); +} + +void resize_master(const Arg arg) { + master_size += arg.i; + tile(); +} + +void resize_stack(const Arg arg) { + growth += arg.i; + tile(); +} + +/* ~~~ Keyboard Management ~~~ */ +void grabkeys(void) { + KeyCode code; + + XUngrabKey(dis, AnyKey, AnyModifier, root); + for(unsigned int i=0; ixkey; + + keysym = XKeycodeToKeysym(dis, (KeyCode)ev->keycode, 0); + for(i = 0; i < len; i++) + if(keysym == keys[i].keysym && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) && keys[i].function) + keys[i].function(keys[i].arg); +} + +/* ~~~ Signal Management ~~~ */ +void maprequest(XEvent *e) { + XMapRequestEvent *ev = &e->xmaprequest; + static XWindowAttributes wa; + + if(!XGetWindowAttributes(dis, ev->window, &wa)) return; + if(wa.override_redirect) return; + + /* window is transient */ + Window trans; + if(XGetTransientForHint(dis, ev->window, &trans) && trans) { + add_window(ev->window); + XMapWindow(dis, ev->window); + XSetInputFocus(dis, ev->window, RevertToParent, CurrentTime); + XRaiseWindow(dis, ev->window); + XFree(&trans); + return; + } + + /* window type is not normal (_NET_WM_WINDOW_TYPE_NORMAL) */ + Atom realType, *wintype; + int realFormat; + unsigned long count, extra; + unsigned char *data; + if(XGetWindowProperty(dis, ev->window, atoms[_NET_WM_WINDOW_TYPE], 0, 32, False, + XA_ATOM, &realType, &realFormat, &count, &extra, &data) == Success) + if(realType == XA_ATOM && count) { + wintype = (unsigned long *)data; + if(data) XFree(data); + for(unsigned int i=0; iwindow); + XMapWindow(dis, ev->window); + XSetInputFocus(dis, ev->window, RevertToParent, CurrentTime); + XRaiseWindow(dis, ev->window); + return; + } + } + + /* window is normal and has a rule set */ + XClassHint ch = {0, 0}; + if(XGetClassHint(dis, ev->window, &ch)) + for(unsigned int i=0; iwindow); + select_desktop(cd); + if(cd == rules[i].desktop) { + XMapWindow(dis, ev->window); + tile(); + update_current(); + } else if(rules[i].follow) + change_desktop((Arg){.i = rules[i].desktop}); + if(ch.res_class) XFree(ch.res_class); + if(ch.res_name) XFree(ch.res_name); + return; + } + + /* window is normal and has no rule set */ + add_window(ev->window); + XMapWindow(dis, ev->window); + tile(); + update_current(); +} + +void destroynotify(XEvent *e) { + client *c; + XDestroyWindowEvent *ev = &e->xdestroywindow; + int cd = current_desktop; + Bool found = False; + + save_desktop(cd); + for(int d=0; dnext) + if((found = ev->window == c->win)) { + removeclient(c); + break; + } + select_desktop(cd); +} + +void enternotify(XEvent *e) { + client *c; + XCrossingEvent *ev = &e->xcrossing; + + if(FOLLOW_MOUSE) { + if((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root) return; + for(c=head; c; c=c->next) + if(ev->window == c->win) { + current = c; + update_current(); + return; + } + } +} + +void buttonpressed(XEvent *e) { + client *c; + XButtonPressedEvent *ev = &e->xbutton; + + if(CLICK_TO_FOCUS && ev->window != current->win && ev->button == Button1) + for(c=head; c; c=c->next) + if(ev->window == c->win) { + current = c; + update_current(); + return; + } +} + +void deletewindow(Window w) { + XEvent ev; + ev.type = ClientMessage; + ev.xclient.window = w; + ev.xclient.message_type = atoms[WM_PROTOCOLS]; + ev.xclient.format = 32; + ev.xclient.data.l[0] = atoms[WM_DELETE_WINDOW]; + ev.xclient.data.l[1] = CurrentTime; + XSendEvent(dis, w, False, NoEventMask, &ev); +} + +unsigned long getcolor(const char* color) { + Colormap map = DefaultColormap(dis, screen); + XColor c; + + if(!XAllocNamedColor(dis, map, color, &c, &c)) + die("error: cannot allocate color '%s'\n", c); + return c.pixel; +} + +void quit(const Arg arg) { + retval = arg.i; + running = False; +} + +void cleanup(void) { + Window root_return; + Window parent_return; + Window *children; + unsigned int nchildren; + + XUngrabKey(dis, AnyKey, AnyModifier, root); + + XQueryTree(dis, root, &root_return, &parent_return, &children, &nchildren); + for(unsigned int i = 0; i < nchildren; i++) + deletewindow(children[i]); + if(children) XFree(children); + + XSync(dis, False); + XSetInputFocus(dis, PointerRoot, RevertToPointerRoot, CurrentTime); +} + +void setup(void) { + sigchld(); + + screen = DefaultScreen(dis); + root = RootWindow(dis, screen); + + sw = XDisplayWidth(dis, screen) - BORDER_WIDTH; + sh = XDisplayHeight(dis, screen) - ((showpanel) ? PANEL_HEIGHT : 0) - BORDER_WIDTH; + master_size = ((mode == BSTACK) ? sh : sw) * MASTER_SIZE; + for(int i=0; imax_keypermod; j++) + if(modmap->modifiermap[modmap->max_keypermod*k + j] == XKeysymToKeycode(dis, XK_Num_Lock)) + numlockmask = (1 << k); + XFreeModifiermap(modmap); + + /* set up atoms for dialog/notification windows */ + atoms[WM_PROTOCOLS] = XInternAtom(dis, "WM_PROTOCOLS", False); + atoms[WM_DELETE_WINDOW] = XInternAtom(dis, "WM_DELETE_WINDOW", False); + atoms[_NET_WM_WINDOW_TYPE] = XInternAtom(dis, "_NET_WM_WINDOW_TYPE", False); + atoms[_NET_WM_WINDOW_TYPE_UTILITY] = XInternAtom(dis, "_NET_WM_WINDOW_TYPE_UTILITY", False); + atoms[_NET_WM_WINDOW_TYPE_DOCK] = XInternAtom(dis, "_NET_WM_WINDOW_TYPE_DOCK", False); + atoms[_NET_WM_WINDOW_TYPE_SPLASH] = XInternAtom(dis, "_NET_WM_WINDOW_TYPE_SPLASH", False); + atoms[_NET_WM_WINDOW_TYPE_DIALOG] = XInternAtom(dis, "_NET_WM_WINDOW_TYPE_DIALOG", False); + atoms[_NET_WM_WINDOW_TYPE_NOTIFICATION] = XInternAtom(dis, "_NET_WM_WINDOW_TYPE_NOTIFICATION", False); + + /* check if another window manager is running */ + xerrorxlib = XSetErrorHandler(xerrorstart); + XSelectInput(dis, DefaultRootWindow(dis), SubstructureNotifyMask|SubstructureRedirectMask); + XSync(dis, False); + XSetErrorHandler(xerror); + XSync(dis, False); + + grabkeys(); +} + +int xerrorstart() { + die("error: another window manager is already running\n"); + return -1; +} + +/* 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 *dis, XErrorEvent *ee) { + if(ee->error_code == BadWindow + || (ee->error_code == BadMatch && (ee->request_code == X_SetInputFocus || ee->request_code == X_ConfigureWindow)) + || (ee->error_code == BadDrawable && (ee->request_code == X_PolyText8 || ee->request_code == X_PolyFillRectangle + || ee->request_code == X_PolySegment || ee->request_code == X_CopyArea)) + || (ee->error_code == BadAccess && ee->request_code == X_GrabKey)) + return 0; + fprintf(stderr, "error: xerror: request code: %d, error code: %d\n", ee->request_code, ee->error_code); + return xerrorxlib(dis, ee); /* may call exit */ +} + +void sigchld() { + if(signal(SIGCHLD, sigchld) == SIG_ERR) + die("error: can't install SIGCHLD handler\n"); + while(0 < waitpid(-1, NULL, WNOHANG)); +} + +void spawn(const Arg arg) { + if(fork() == 0) { + if(dis) close(ConnectionNumber(dis)); + setsid(); + execvp((char*)arg.com[0], (char**)arg.com); + fprintf(stderr, "error: execvp %s", (char *)arg.com[0]); + perror(" failed"); /* also prints the err msg */ + exit(EXIT_SUCCESS); + } +} + +void run(void) { + XEvent ev; + while(running && !XNextEvent(dis, &ev)) + if(events[ev.type]) + events[ev.type](&ev); +} + +void die(const char *errstr, ...) { + va_list ap; + va_start(ap, errstr); + vfprintf(stderr, errstr, ap); + va_end(ap); + exit(EXIT_FAILURE); +} + +int main(int argc, char *argv[]) { + if(argc == 2 && strcmp("-v", argv[1]) == 0) { + fprintf(stdout, "monsterwm-%s\n", VERSION); + return EXIT_SUCCESS; + } else if(argc != 1) + die("usage: monsterwm [-v]\n"); + if(!(dis = XOpenDisplay(NULL))) + die("error: cannot open display\n"); + setup(); + run(); + cleanup(); + XCloseDisplay(dis); + return retval; +} -- cgit v1.3.1