diff options
| author | Ivan Kanakarakis <ivan.kanak@gmail.com> | 2011-12-14 10:45:23 +0200 |
|---|---|---|
| committer | Ivan Kanakarakis <ivan.kanak@gmail.com> | 2011-12-14 11:22:41 +0200 |
| commit | 2cd9db89227308082b8bda949528277b40ebb9ed (patch) | |
| tree | c429b1cef32b2c21af8ef3298f4696426f3e3524 /monsterwm.c | |
| parent | 68335f2ecf2cb8427d8874e1f96fae0c87cfb870 (diff) | |
split rename to monsterwm
Diffstat (limited to 'monsterwm.c')
| -rw-r--r-- | monsterwm.c | 793 |
1 files changed, 793 insertions, 0 deletions
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 <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <unistd.h> +#include <string.h> +#include <signal.h> +#include <sys/wait.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/keysym.h> +#include <X11/Xproto.h> +#include <X11/Xatom.h> + +#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; i<LENGTH(keys); i++) { + code = XKeysymToKeycode(dis, keys[i].keysym); + XGrabKey(dis, code, keys[i].mod, root, True, GrabModeAsync, GrabModeAsync); + XGrabKey(dis, code, keys[i].mod | LockMask, root, True, GrabModeAsync, GrabModeAsync); + XGrabKey(dis, code, keys[i].mod | numlockmask, root, True, GrabModeAsync, GrabModeAsync); + XGrabKey(dis, code, keys[i].mod | numlockmask | LockMask, root, True, GrabModeAsync, GrabModeAsync); + } +} + +void keypress(XEvent *e) { + static unsigned int len = sizeof keys / sizeof keys[0]; + unsigned int i; + KeySym keysym; + XKeyEvent *ev = &e->xkey; + + 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; i<count; i++) + if(wintype[i] == atoms[_NET_WM_WINDOW_TYPE_UTILITY] || + wintype[i] == atoms[_NET_WM_WINDOW_TYPE_NOTIFICATION] || + wintype[i] == atoms[_NET_WM_WINDOW_TYPE_SPLASH] || + wintype[i] == atoms[_NET_WM_WINDOW_TYPE_DIALOG] || + wintype[i] == atoms[_NET_WM_WINDOW_TYPE_DOCK] ){ + add_window(ev->window); + 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; i<LENGTH(rules); i++) + if(strcmp(ch.res_class, rules[i].class) == 0) { + int cd = current_desktop; + save_desktop(cd); + select_desktop(rules[i].desktop); + add_window(ev->window); + 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; d<DESKTOPS && !found; select_desktop(d++)) + for(c=head; c; c=c->next) + 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; i<DESKTOPS; i++) + save_desktop(i); + change_desktop((Arg){.i = DEFAULT_DESKTOP}); + + win_focus = getcolor(FOCUS); + win_unfocus = getcolor(UNFOCUS); + + XModifierKeymap *modmap = XGetModifierMapping(dis); + for (int k=0; k<8; k++) + for (int j=0; j<modmap->max_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; +} |
