commit 11498264d51331a6269aa306b55dda75ec60fba9 Author: exkc Date: Wed Jul 20 15:48:25 2022 +0800 djjd\# Please enter the commit message for your changes. Lines starting diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..aa0a3ab --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT/X Consortium License + +(C)opyright MMVI Anselm R. Garbe + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c75f6f3 --- /dev/null +++ b/Makefile @@ -0,0 +1,55 @@ +# dumbwm - dynamic window manager +# (C)opyright MMVI Anselm R. Garbe + +include config.mk + +SRC = bar.c client.c dev.c draw.c event.c main.c util.c +OBJ = ${SRC:.c=.o} +MAN1 = dumbwm.1 +BIN = dumbwm + +all: config dumbwm + @echo finished + +config: + @echo dumbwm build options: + @echo "LIBS = ${LIBS}" + @echo "CFLAGS = ${CFLAGS}" + @echo "LDFLAGS = ${LDFLAGS}" + @echo "CC = ${CC}" + +.c.o: + @echo CC $< + @${CC} -c ${CFLAGS} $< + +${OBJ}: dumbwm.h + +dumbwm: ${OBJ} + @echo LD $@ + @${CC} -o $@ ${OBJ} ${LDFLAGS} + +clean: + rm -f dumbwm *.o core + +dist: clean + mkdir -p dumbwm-${VERSION} + cp -R Makefile README LICENSE config.mk *.h *.c ${MAN1} dumbwm-${VERSION} + tar -cf dumbwm-${VERSION}.tar dumbwm-${VERSION} + gzip dumbwm-${VERSION}.tar + rm -rf dumbwm-${VERSION} + +install: all + @mkdir -p ${DESTDIR}${PREFIX}/bin + @cp -f ${BIN} ${DESTDIR}${PREFIX}/bin + @echo installed executable files to ${DESTDIR}${PREFIX}/bin + @mkdir -p ${DESTDIR}${MANPREFIX}/man1 + @cp -f ${MAN1} ${DESTDIR}${MANPREFIX}/man1 + @echo installed manual pages to ${DESTDIR}${MANPREFIX}/man1 + +uninstall: + for i in ${BIN}; do \ + rm -f ${DESTDIR}${PREFIX}/bin/`basename $$i`; \ + done + for i in ${MAN1}; do \ + rm -f ${DESTDIR}${MANPREFIX}/man1/`basename $$i`; \ + done diff --git a/bar.c b/bar.c new file mode 100644 index 0000000..03e0786 --- /dev/null +++ b/bar.c @@ -0,0 +1,46 @@ +/* + * (C)opyright MMVI Anselm R. Garbe + * See LICENSE file for license details. + */ + +#include "dumbwm.h" + +void +barclick(XButtonPressedEvent *e) +{ + int x = 0; + Arg a; + for(a.i = 0; a.i < TLast; a.i++) { + x += textw(tags[a.i]) + dc.font.height; + if(e->x < x) { + view(&a); + return; + } + } +} + +void +draw_bar() +{ + int i; + dc.x = dc.y = 0; + dc.w = bw; + drawtext(NULL, False, False); + + dc.w = 0; + for(i = 0; i < TLast; i++) { + dc.x += dc.w; + dc.w = textw(tags[i]) + dc.font.height; + drawtext(tags[i], i == tsel, True); + } + if(sel) { + dc.x += dc.w; + dc.w = textw(sel->name) + dc.font.height; + drawtext(sel->name, True, True); + } + dc.w = textw(stext) + dc.font.height; + dc.x = bx + bw - dc.w; + drawtext(stext, False, False); + XCopyArea(dpy, dc.drawable, barwin, dc.gc, 0, 0, bw, bh, 0, 0); + XFlush(dpy); +} diff --git a/bar.o b/bar.o new file mode 100644 index 0000000..5527d63 Binary files /dev/null and b/bar.o differ diff --git a/client.c b/client.c new file mode 100644 index 0000000..6dc2f70 --- /dev/null +++ b/client.c @@ -0,0 +1,616 @@ +/* + * (C)opyright MMVI Anselm R. Garbe + * See LICENSE file for license details. + */ + +#include +#include +#include +#include +#include + +#include "dumbwm.h" + +void (*arrange)(Arg *) = tiling; + +static Rule rule[] = { + /* class instance tags floating */ + { "Firefox-bin", "Gecko", { [Twww] = "www" }, False }, +}; + +static Client * +next(Client *c) +{ + for(; c && !c->tags[tsel]; c = c->next); + return c; +} + +void +zoom(Arg *arg) +{ + Client **l, *c; + + if(!sel) + return; + + if(sel == next(clients) && sel->next) { + if((c = next(sel->next))) + sel = c; + } + + for(l = &clients; *l && *l != sel; l = &(*l)->next); + *l = sel->next; + + sel->next = clients; /* pop */ + clients = sel; + arrange(NULL); + focus(sel); +} + +void +max(Arg *arg) +{ + if(!sel) + return; + sel->x = sx; + sel->y = sy + bh; + sel->w = sw - 2 * sel->border; + sel->h = sh - 2 * sel->border - bh; + craise(sel); + resize(sel, False); +} + +void +view(Arg *arg) +{ + Client *c; + + tsel = arg->i; + arrange(NULL); + + for(c = clients; c; c = next(c->next)) + draw_client(c); + draw_bar(); +} + +void +tappend(Arg *arg) +{ + if(!sel) + return; + + sel->tags[arg->i] = tags[arg->i]; + arrange(NULL); +} + +void +ttrunc(Arg *arg) +{ + int i; + if(!sel) + return; + + for(i = 0; i < TLast; i++) + sel->tags[i] = NULL; + tappend(arg); +} + +static void +ban_client(Client *c) +{ + XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); + XMoveWindow(dpy, c->title, c->tx + 2 * sw, c->ty); +} + +void +floating(Arg *arg) +{ + Client *c; + + arrange = floating; + for(c = clients; c; c = c->next) { + if(c->tags[tsel]) + resize(c, True); + else + ban_client(c); + } + if(sel && !sel->tags[tsel]) { + if((sel = next(clients))) { + craise(sel); + focus(sel); + } + } +} + +void +tiling(Arg *arg) +{ + Client *c; + int n, i, w, h; + + w = sw - mw; + arrange = tiling; + for(n = 0, c = clients; c; c = c->next) + if(c->tags[tsel] && !c->floating) + n++; + + if(n > 1) + h = (sh - bh) / (n - 1); + else + h = sh - bh; + + for(i = 0, c = clients; c; c = c->next) { + if(c->tags[tsel]) { + if(c->floating) { + craise(c); + resize(c, True); + continue; + } + if(n == 1) { + c->x = sx; + c->y = sy + bh; + c->w = sw - 2 * c->border; + c->h = sh - 2 * c->border - bh; + } + else if(i == 0) { + c->x = sx; + c->y = sy + bh; + c->w = mw - 2 * c->border; + c->h = sh - 2 * c->border - bh; + } + else { + c->x = sx + mw; + c->y = sy + (i - 1) * h + bh; + c->w = w - 2 * c->border; + c->h = h - 2 * c->border; + } + resize(c, False); + i++; + } + else + ban_client(c); + } + if(!sel || (sel && !sel->tags[tsel])) { + if((sel = next(clients))) { + craise(sel); + focus(sel); + } + } +} + +void +prevc(Arg *arg) +{ + Client *c; + + if(!sel) + return; + + if((c = sel->revert && sel->revert->tags[tsel] ? sel->revert : NULL)) { + craise(c); + focus(c); + } +} + +void +nextc(Arg *arg) +{ + Client *c; + + if(!sel) + return; + + if(!(c = next(sel->next))) + c = next(clients); + if(c) { + craise(c); + c->revert = sel; + focus(c); + } +} + +void +ckill(Arg *arg) +{ + if(!sel) + return; + if(sel->proto & WM_PROTOCOL_DELWIN) + send_message(sel->win, wm_atom[WMProtocols], wm_atom[WMDelete]); + else + XKillClient(dpy, sel->win); +} + +static void +resize_title(Client *c) +{ + int i; + + c->tw = 0; + for(i = 0; i < TLast; i++) + if(c->tags[i]) + c->tw += textw(c->tags[i]) + dc.font.height; + c->tw += textw(c->name) + dc.font.height; + if(c->tw > c->w) + c->tw = c->w + 2; + c->tx = c->x + c->w - c->tw + 2; + c->ty = c->y; + XMoveResizeWindow(dpy, c->title, c->tx, c->ty, c->tw, c->th); +} + +void +update_name(Client *c) +{ + XTextProperty name; + int n; + char **list = NULL; + + name.nitems = 0; + c->name[0] = 0; + XGetTextProperty(dpy, c->win, &name, net_atom[NetWMName]); + if(!name.nitems) + XGetWMName(dpy, c->win, &name); + if(!name.nitems) + return; + if(name.encoding == XA_STRING) + strncpy(c->name, (char *)name.value, sizeof(c->name)); + else { + if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success + && n > 0 && *list) + { + strncpy(c->name, *list, sizeof(c->name)); + XFreeStringList(list); + } + } + XFree(name.value); + resize_title(c); +} + +void +update_size(Client *c) +{ + XSizeHints size; + long msize; + if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags) + size.flags = PSize; + c->flags = size.flags; + if(c->flags & PBaseSize) { + c->basew = size.base_width; + c->baseh = size.base_height; + } + else + c->basew = c->baseh = 0; + if(c->flags & PResizeInc) { + c->incw = size.width_inc; + c->inch = size.height_inc; + } + else + c->incw = c->inch = 0; + if(c->flags & PMaxSize) { + c->maxw = size.max_width; + c->maxh = size.max_height; + } + else + c->maxw = c->maxh = 0; + if(c->flags & PMinSize) { + c->minw = size.min_width; + c->minh = size.min_height; + } + else + c->minw = c->minh = 0; + if(c->flags & PWinGravity) + c->grav = size.win_gravity; + else + c->grav = NorthWestGravity; +} + +void +craise(Client *c) +{ + XRaiseWindow(dpy, c->win); + XRaiseWindow(dpy, c->title); +} + +void +lower(Client *c) +{ + XLowerWindow(dpy, c->title); + XLowerWindow(dpy, c->win); +} + +void +focus(Client *c) +{ + Client *old = sel; + XEvent ev; + + XFlush(dpy); + sel = c; + if(old && old != c) + draw_client(old); + draw_client(c); + XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); + XFlush(dpy); + while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + +static void +init_tags(Client *c) +{ + XClassHint ch; + static unsigned int len = rule ? sizeof(rule) / sizeof(rule[0]) : 0; + unsigned int i, j; + Bool matched = False; + + if(!len) { + c->tags[tsel] = tags[tsel]; + return; + } + + if(XGetClassHint(dpy, c->win, &ch)) { + if(ch.res_class && ch.res_name) { + for(i = 0; i < len; i++) + if(!strncmp(rule[i].class, ch.res_class, sizeof(rule[i].class)) + && !strncmp(rule[i].instance, ch.res_name, sizeof(rule[i].instance))) + { + for(j = 0; j < TLast; j++) + c->tags[j] = rule[i].tags[j]; + c->floating = rule[i].floating; + matched = True; + break; + } + } + if(ch.res_class) + XFree(ch.res_class); + if(ch.res_name) + XFree(ch.res_name); + } + + if(!matched) + c->tags[tsel] = tags[tsel]; +} + +void +manage(Window w, XWindowAttributes *wa) +{ + Client *c, **l; + XSetWindowAttributes twa; + Window trans; + + c = emallocz(sizeof(Client)); + c->win = w; + c->tx = c->x = wa->x; + c->ty = c->y = wa->y; + if(c->y < bh) + c->ty = c->y += bh; + c->tw = c->w = wa->width; + c->h = wa->height; + c->th = bh; + c->border = 1; + c->proto = win_proto(c->win); + update_size(c); + XSelectInput(dpy, c->win, + StructureNotifyMask | PropertyChangeMask | EnterWindowMask); + XGetTransientForHint(dpy, c->win, &trans); + twa.override_redirect = 1; + twa.background_pixmap = ParentRelative; + twa.event_mask = ExposureMask; + + c->title = XCreateWindow(dpy, root, c->tx, c->ty, c->tw, c->th, + 0, DefaultDepth(dpy, screen), CopyFromParent, + DefaultVisual(dpy, screen), + CWOverrideRedirect | CWBackPixmap | CWEventMask, &twa); + + update_name(c); + init_tags(c); + + for(l = &clients; *l; l = &(*l)->next); + c->next = *l; /* *l == nil */ + *l = c; + + XGrabButton(dpy, Button1, Mod1Mask, c->win, False, ButtonPressMask, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button2, Mod1Mask, c->win, False, ButtonPressMask, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button3, Mod1Mask, c->win, False, ButtonPressMask, + GrabModeAsync, GrabModeSync, None, None); + + if(!c->floating) + c->floating = trans + || ((c->maxw == c->minw) && (c->maxh == c->minh)); + + arrange(NULL); + /* mapping the window now prevents flicker */ + if(c->tags[tsel]) { + XMapRaised(dpy, c->win); + XMapRaised(dpy, c->title); + focus(c); + } + else { + ban_client(c); + XMapRaised(dpy, c->win); + XMapRaised(dpy, c->title); + } +} + +void +gravitate(Client *c, Bool invert) +{ + int dx = 0, dy = 0; + + switch(c->grav) { + case StaticGravity: + case NorthWestGravity: + case NorthGravity: + case NorthEastGravity: + dy = c->border; + break; + case EastGravity: + case CenterGravity: + case WestGravity: + dy = -(c->h / 2) + c->border; + break; + case SouthEastGravity: + case SouthGravity: + case SouthWestGravity: + dy = -c->h; + break; + default: + break; + } + + switch (c->grav) { + case StaticGravity: + case NorthWestGravity: + case WestGravity: + case SouthWestGravity: + dx = c->border; + break; + case NorthGravity: + case CenterGravity: + case SouthGravity: + dx = -(c->w / 2) + c->border; + break; + case NorthEastGravity: + case EastGravity: + case SouthEastGravity: + dx = -(c->w + c->border); + break; + default: + break; + } + + if(invert) { + dx = -dx; + dy = -dy; + } + c->x += dx; + c->y += dy; +} + + +void +resize(Client *c, Bool inc) +{ + XConfigureEvent e; + + if(inc) { + if(c->incw) + c->w -= (c->w - c->basew) % c->incw; + if(c->inch) + c->h -= (c->h - c->baseh) % c->inch; + } + if(c->x > sw) /* might happen on restart */ + c->x = sw - c->w; + if(c->y > sh) + c->ty = c->y = sh - c->h; + if(c->minw && c->w < c->minw) + c->w = c->minw; + if(c->minh && c->h < c->minh) + c->h = c->minh; + if(c->maxw && c->w > c->maxw) + c->w = c->maxw; + if(c->maxh && c->h > c->maxh) + c->h = c->maxh; + resize_title(c); + XSetWindowBorderWidth(dpy, c->win, 1); + XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); + e.type = ConfigureNotify; + e.event = c->win; + e.window = c->win; + e.x = c->x; + e.y = c->y; + e.width = c->w; + e.height = c->h; + e.border_width = c->border; + e.above = None; + e.override_redirect = False; + XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&e); + XFlush(dpy); +} + +static int +dummy_error_handler(Display *dsply, XErrorEvent *err) +{ + return 0; +} + +void +unmanage(Client *c) +{ + Client **l; + + XGrabServer(dpy); + XSetErrorHandler(dummy_error_handler); + + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + XDestroyWindow(dpy, c->title); + + for(l = &clients; *l && *l != c; l = &(*l)->next); + *l = c->next; + for(l = &clients; *l; l = &(*l)->next) + if((*l)->revert == c) + (*l)->revert = NULL; + if(sel == c) + sel = sel->revert ? sel->revert : clients; + + free(c); + + XFlush(dpy); + XSetErrorHandler(error_handler); + XUngrabServer(dpy); + arrange(NULL); + if(sel) + focus(sel); +} + +Client * +gettitle(Window w) +{ + Client *c; + for(c = clients; c; c = c->next) + if(c->title == w) + return c; + return NULL; +} + +Client * +getclient(Window w) +{ + Client *c; + for(c = clients; c; c = c->next) + if(c->win == w) + return c; + return NULL; +} + +void +draw_client(Client *c) +{ + int i; + if(c == sel) { + draw_bar(); + XUnmapWindow(dpy, c->title); + XSetWindowBorder(dpy, c->win, dc.fg); + return; + } + + XSetWindowBorder(dpy, c->win, dc.bg); + XMapWindow(dpy, c->title); + + dc.x = dc.y = 0; + + dc.w = 0; + for(i = 0; i < TLast; i++) { + if(c->tags[i]) { + dc.x += dc.w; + dc.w = textw(c->tags[i]) + dc.font.height; + drawtext(c->tags[i], False, True); + } + } + dc.x += dc.w; + dc.w = textw(c->name) + dc.font.height; + drawtext(c->name, False, True); + XCopyArea(dpy, dc.drawable, c->title, dc.gc, + 0, 0, c->tw, c->th, 0, 0); + XFlush(dpy); +} diff --git a/client.o b/client.o new file mode 100644 index 0000000..75185d8 Binary files /dev/null and b/client.o differ diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..96ef58a --- /dev/null +++ b/config.mk @@ -0,0 +1,31 @@ +# Customize to fit your system + +# paths +PREFIX = /usr/local +CONFPREFIX = ${PREFIX}/etc +MANPREFIX = ${PREFIX}/share/man + +X11INC = /usr/X11R6/include +X11LIB = /usr/X11R6/lib + +VERSION = 0.1 + +# includes and libs +LIBS = -L${PREFIX}/lib -L/usr/lib -lc -L${X11LIB} -lX11 + +# Linux/BSD +CFLAGS = -Os -I. -I${PREFIX}/include -I/usr/include -I${X11INC} \ + -DVERSION=\"${VERSION}\" +LDFLAGS = ${LIBS} +#CFLAGS = -g -Wall -O2 -I. -I${PREFIX}/include -I/usr/include -I${X11INC} \ +# -DVERSION=\"${VERSION}\" +#LDFLAGS = -g ${LIBS} + + +# Solaris +#CFLAGS = -fast -xtarget=ultra ${INCLUDES} -DVERSION=\"${VERSION}\" +#LIBS += -lnsl -lsocket + +AR = ar cr +CC = cc +RANLIB = ranlib diff --git a/dev.c b/dev.c new file mode 100644 index 0000000..1f8c52a --- /dev/null +++ b/dev.c @@ -0,0 +1,151 @@ +/* + * (C)opyright MMVI Anselm R. Garbe + * See LICENSE file for license details. + */ + +#include "dumbwm.h" + +#include +#include +#include +#include + +/********** CUSTOMIZE **********/ + +const char *term[] = { + "urxvtc", "-tr", "+sb", "-bg", "black", "-fg", "white", "-fn", + "-*-terminus-medium-*-*-*-13-*-*-*-*-*-iso10646-*",NULL +}; +const char *browse[] = { "firefox", NULL }; +const char *xlock[] = { "xlock", NULL }; + +static Key key[] = { + /* modifier key function arguments */ + { Mod1Mask, XK_Return, zoom, { 0 } }, + { Mod1Mask, XK_k, prevc, { 0 } }, + { Mod1Mask, XK_j, nextc, { 0 } }, + { Mod1Mask, XK_m, max, { 0 } }, + { Mod1Mask, XK_0, view, { .i = Tscratch } }, + { Mod1Mask, XK_1, view, { .i = Tdev } }, + { Mod1Mask, XK_2, view, { .i = Twww } }, + { Mod1Mask, XK_3, view, { .i = Twork } }, + { Mod1Mask, XK_space, tiling, { 0 } }, + { Mod1Mask|ShiftMask, XK_space, floating, { 0 } }, + { Mod1Mask|ShiftMask, XK_0, ttrunc, { .i = Tscratch } }, + { Mod1Mask|ShiftMask, XK_1, ttrunc, { .i = Tdev } }, + { Mod1Mask|ShiftMask, XK_2, ttrunc, { .i = Twww } }, + { Mod1Mask|ShiftMask, XK_3, ttrunc, { .i = Twork } }, + { Mod1Mask|ShiftMask, XK_c, ckill, { 0 } }, + { Mod1Mask|ShiftMask, XK_q, quit, { 0 } }, + { Mod1Mask|ShiftMask, XK_Return, spawn, { .argv = term } }, + { Mod1Mask|ShiftMask, XK_w, spawn, { .argv = browse } }, + { Mod1Mask|ShiftMask, XK_l, spawn, { .argv = xlock } }, + { ControlMask, XK_0, tappend, { .i = Tscratch } }, + { ControlMask, XK_1, tappend, { .i = Tdev } }, + { ControlMask, XK_2, tappend, { .i = Twww } }, + { ControlMask, XK_3, tappend, { .i = Twork } }, +}; + +/********** CUSTOMIZE **********/ + +void +update_keys(void) +{ + static unsigned int len = key ? sizeof(key) / sizeof(key[0]) : 0; + unsigned int i; + KeyCode code; + + for(i = 0; i < len; i++) { + code = XKeysymToKeycode(dpy, key[i].keysym); + XUngrabKey(dpy, code, key[i].mod, root); + XGrabKey(dpy, code, key[i].mod, root, True, GrabModeAsync, GrabModeAsync); + } +} + +void +keypress(XEvent *e) +{ + XKeyEvent *ev = &e->xkey; + static unsigned int len = key ? sizeof(key) / sizeof(key[0]) : 0; + unsigned int i; + KeySym keysym; + + keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); + for(i = 0; i < len; i++) + if((keysym == key[i].keysym) && (key[i].mod == ev->state)) { + if(key[i].func) + key[i].func(&key[i].arg); + return; + } +} + +#define ButtonMask (ButtonPressMask | ButtonReleaseMask) +#define MouseMask (ButtonMask | PointerMotionMask) + +void +mresize(Client *c) +{ + XEvent ev; + int ocx, ocy; + + ocx = c->x; + ocy = c->y; + if(XGrabPointer(dpy, root, False, MouseMask, GrabModeAsync, GrabModeAsync, + None, cursor[CurResize], CurrentTime) != GrabSuccess) + return; + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w, c->h); + for(;;) { + XMaskEvent(dpy, MouseMask | ExposureMask, &ev); + switch(ev.type) { + default: break; + case Expose: + handler[Expose](&ev); + break; + case MotionNotify: + XFlush(dpy); + c->w = abs(ocx - ev.xmotion.x); + c->h = abs(ocy - ev.xmotion.y); + c->x = (ocx <= ev.xmotion.x) ? ocx : ocx - c->w; + c->y = (ocy <= ev.xmotion.y) ? ocy : ocy - c->h; + resize(c, True); + break; + case ButtonRelease: + XUngrabPointer(dpy, CurrentTime); + return; + } + } +} + +void +mmove(Client *c) +{ + XEvent ev; + int x1, y1, ocx, ocy, di; + unsigned int dui; + Window dummy; + + ocx = c->x; + ocy = c->y; + if(XGrabPointer(dpy, root, False, MouseMask, GrabModeAsync, GrabModeAsync, + None, cursor[CurMove], CurrentTime) != GrabSuccess) + return; + XQueryPointer(dpy, root, &dummy, &dummy, &x1, &y1, &di, &di, &dui); + for(;;) { + XMaskEvent(dpy, MouseMask | ExposureMask, &ev); + switch (ev.type) { + default: break; + case Expose: + handler[Expose](&ev); + break; + case MotionNotify: + XFlush(dpy); + c->x = ocx + (ev.xmotion.x - x1); + c->y = ocy + (ev.xmotion.y - y1); + resize(c, False); + break; + case ButtonRelease: + XUngrabPointer(dpy, CurrentTime); + return; + } + } +} diff --git a/dev.o b/dev.o new file mode 100644 index 0000000..348247c Binary files /dev/null and b/dev.o differ diff --git a/draw.c b/draw.c new file mode 100644 index 0000000..c237b93 --- /dev/null +++ b/draw.c @@ -0,0 +1,158 @@ +/* + * (C)opyright MMIV-MMVI Anselm R. Garbe + * See LICENSE file for license details. + */ + +#include +#include + +#include + +#include "dumbwm.h" + +static void +drawborder(void) +{ + XPoint points[5]; + XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter); + XSetForeground(dpy, dc.gc, dc.border); + points[0].x = dc.x; + points[0].y = dc.y; + points[1].x = dc.w - 1; + points[1].y = 0; + points[2].x = 0; + points[2].y = dc.h - 1; + points[3].x = -(dc.w - 1); + points[3].y = 0; + points[4].x = 0; + points[4].y = -(dc.h - 1); + XDrawLines(dpy, dc.drawable, dc.gc, points, 5, CoordModePrevious); +} + +void +drawtext(const char *text, Bool invert, Bool border) +{ + int x, y, w, h; + unsigned int len; + static char buf[256]; + XGCValues gcv; + XRectangle r = { dc.x, dc.y, dc.w, dc.h }; + + XSetForeground(dpy, dc.gc, invert ? dc.fg : dc.bg); + XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); + + w = 0; + if(border) + drawborder(); + + if(!text) + return; + + len = strlen(text); + if(len >= sizeof(buf)) + len = sizeof(buf) - 1; + memcpy(buf, text, len); + buf[len] = 0; + + h = dc.font.ascent + dc.font.descent; + y = dc.y + (dc.h / 2) - (h / 2) + dc.font.ascent; + x = dc.x + (h / 2); + + /* shorten text if necessary */ + while(len && (w = textnw(buf, len)) > dc.w - h) + buf[--len] = 0; + + if(w > dc.w) + return; /* too long */ + + gcv.foreground = invert ? dc.bg : dc.fg; + gcv.background = invert ? dc.fg : dc.bg; + if(dc.font.set) { + XChangeGC(dpy, dc.gc, GCForeground | GCBackground, &gcv); + XmbDrawImageString(dpy, dc.drawable, dc.font.set, dc.gc, + x, y, buf, len); + } + else { + gcv.font = dc.font.xfont->fid; + XChangeGC(dpy, dc.gc, GCForeground | GCBackground | GCFont, &gcv); + XDrawImageString(dpy, dc.drawable, dc.gc, x, y, buf, len); + } +} + +unsigned long +initcolor(const char *colstr) +{ + XColor color; + Colormap cmap = DefaultColormap(dpy, screen); + + XAllocNamedColor(dpy, cmap, colstr, &color, &color); + return color.pixel; +} + +unsigned int +textnw(char *text, unsigned int len) +{ + XRectangle r; + if(dc.font.set) { + XmbTextExtents(dc.font.set, text, len, NULL, &r); + return r.width; + } + return XTextWidth(dc.font.xfont, text, len); +} + +unsigned int +textw(char *text) +{ + return textnw(text, strlen(text)); +} + +void +initfont(const char *fontstr) +{ + char **missing, *def; + int i, n; + + missing = NULL; + setlocale(LC_ALL, ""); + if(dc.font.set) + XFreeFontSet(dpy, dc.font.set); + dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def); + if(missing) { + while(n--) + fprintf(stderr, "missing fontset: %s\n", missing[n]); + XFreeStringList(missing); + if(dc.font.set) { + XFreeFontSet(dpy, dc.font.set); + dc.font.set = NULL; + } + } + if(dc.font.set) { + XFontSetExtents *font_extents; + XFontStruct **xfonts; + char **font_names; + + dc.font.ascent = dc.font.descent = 0; + font_extents = XExtentsOfFontSet(dc.font.set); + n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names); + for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++) { + if(dc.font.ascent < (*xfonts)->ascent) + dc.font.ascent = (*xfonts)->ascent; + if(dc.font.descent < (*xfonts)->descent) + dc.font.descent = (*xfonts)->descent; + xfonts++; + } + } + else { + if(dc.font.xfont) + XFreeFont(dpy, dc.font.xfont); + dc.font.xfont = NULL; + dc.font.xfont = XLoadQueryFont(dpy, fontstr); + if (!dc.font.xfont) + dc.font.xfont = XLoadQueryFont(dpy, "fixed"); + if (!dc.font.xfont) + error("error, cannot init 'fixed' font\n"); + dc.font.ascent = dc.font.xfont->ascent; + dc.font.descent = dc.font.xfont->descent; + } + dc.font.height = dc.font.ascent + dc.font.descent; +} diff --git a/draw.o b/draw.o new file mode 100644 index 0000000..8ef32e7 Binary files /dev/null and b/draw.o differ diff --git a/dumbwm b/dumbwm new file mode 100755 index 0000000..759cdc9 Binary files /dev/null and b/dumbwm differ diff --git a/dumbwm.h b/dumbwm.h new file mode 100644 index 0000000..6843870 --- /dev/null +++ b/dumbwm.h @@ -0,0 +1,155 @@ +/* + * (C)opyright MMVI Anselm R. Garbe + * See LICENSE file for license details. + */ + +#include + +/********** CUSTOMIZE **********/ + +#define FONT "-*-terminus-medium-*-*-*-13-*-*-*-*-*-iso10646-*" +#define BGCOLOR "#666699" +#define FGCOLOR "#eeeeee" +#define BORDERCOLOR "#9999CC" +#define MASTERW 52 /* percent */ +#define WM_PROTOCOL_DELWIN 1 + +/* tags */ +enum { Tscratch, Tdev, Twww, Twork, TLast }; + +/********** CUSTOMIZE **********/ + +typedef union Arg Arg; +typedef struct DC DC; +typedef struct Client Client; +typedef struct Fnt Fnt; +typedef struct Key Key; +typedef struct Rule Rule; + +union Arg { + const char **argv; + int i; +}; + +/* atoms */ +enum { WMProtocols, WMDelete, WMLast }; +enum { NetSupported, NetWMName, NetLast }; + +/* cursor */ +enum { CurNormal, CurResize, CurMove, CurInput, CurLast }; + +struct Fnt { + XFontStruct *xfont; + XFontSet set; + int ascent; + int descent; + int height; +}; + +struct DC { /* draw context */ + GC gc; + Drawable drawable; + int x, y, w, h; + Fnt font; + unsigned long bg; + unsigned long fg; + unsigned long border; +}; + +struct Client { + char name[256]; + char *tags[TLast]; + int proto; + int x, y, w, h; + int tx, ty, tw, th; + int basew, baseh, incw, inch, maxw, maxh, minw, minh; + int grav; + unsigned int border; + long flags; + Bool floating; + Window win; + Window title; + Client *next; + Client *revert; +}; + +struct Rule { + const char *class; + const char *instance; + char *tags[TLast]; + Bool floating; +}; + +struct Key { + unsigned long mod; + KeySym keysym; + void (*func)(Arg *arg); + Arg arg; +}; + +extern Display *dpy; +extern Window root, barwin; +extern Atom wm_atom[WMLast], net_atom[NetLast]; +extern Cursor cursor[CurLast]; +extern Bool running, issel; +extern void (*handler[LASTEvent])(XEvent *); +extern void (*arrange)(Arg *); + +extern int tsel, screen, sx, sy, sw, sh, bx, by, bw, bh, mw; +extern char *tags[TLast], stext[1024]; + +extern DC dc; +extern Client *clients, *sel; + +/* bar.c */ +extern void draw_bar(); +extern void barclick(XButtonPressedEvent *e); + +/* client.c */ +extern void manage(Window w, XWindowAttributes *wa); +extern void unmanage(Client *c); +extern Client *getclient(Window w); +extern void focus(Client *c); +extern void update_name(Client *c); +extern void draw_client(Client *c); +extern void resize(Client *c, Bool inc); +extern void update_size(Client *c); +extern Client *gettitle(Window w); +extern void craise(Client *c); +extern void lower(Client *c); +extern void ckill(Arg *arg); +extern void nextc(Arg *arg); +extern void prevc(Arg *arg); +extern void max(Arg *arg); +extern void floating(Arg *arg); +extern void tiling(Arg *arg); +extern void ttrunc(Arg *arg); +extern void tappend(Arg *arg); +extern void view(Arg *arg); +extern void zoom(Arg *arg); +extern void gravitate(Client *c, Bool invert); + +/* draw.c */ +extern void drawtext(const char *text, Bool invert, Bool border); +extern unsigned long initcolor(const char *colstr); +extern void initfont(const char *fontstr); +extern unsigned int textnw(char *text, unsigned int len); +extern unsigned int textw(char *text); +extern unsigned int texth(void); + +/* dev.c */ +extern void update_keys(void); +extern void keypress(XEvent *e); +extern void mresize(Client *c); +extern void mmove(Client *c); + +/* main.c */ +extern int error_handler(Display *dsply, XErrorEvent *e); +extern void send_message(Window w, Atom a, long value); +extern int win_proto(Window w); +extern void quit(Arg *arg); + +/* util.c */ +extern void error(const char *errstr, ...); +extern void *emallocz(unsigned int size); +extern void spawn(Arg *arg); diff --git a/event.c b/event.c new file mode 100644 index 0000000..b4ca5c4 --- /dev/null +++ b/event.c @@ -0,0 +1,216 @@ +/* + * (C)opyright MMVI Anselm R. Garbe + * See LICENSE file for license details. + */ + +#include +#include +#include +#include +#include +#include + +#include "dumbwm.h" + +/* local functions */ +static void buttonpress(XEvent *e); +static void configurerequest(XEvent *e); +static void destroynotify(XEvent *e); +static void enternotify(XEvent *e); +static void leavenotify(XEvent *e); +static void expose(XEvent *e); +static void keymapnotify(XEvent *e); +static void maprequest(XEvent *e); +static void propertynotify(XEvent *e); +static void unmapnotify(XEvent *e); + +void (*handler[LASTEvent]) (XEvent *) = { + [ButtonPress] = buttonpress, + [ConfigureRequest] = configurerequest, + [DestroyNotify] = destroynotify, + [EnterNotify] = enternotify, + [LeaveNotify] = leavenotify, + [Expose] = expose, + [KeyPress] = keypress, + [KeymapNotify] = keymapnotify, + [MapRequest] = maprequest, + [PropertyNotify] = propertynotify, + [UnmapNotify] = unmapnotify +}; + +static void +buttonpress(XEvent *e) +{ + XButtonPressedEvent *ev = &e->xbutton; + Client *c; + + if(barwin == ev->window) + barclick(ev); + else if((c = getclient(ev->window))) { + craise(c); + switch(ev->button) { + default: + break; + case Button1: + mmove(c); + break; + case Button2: + lower(c); + break; + case Button3: + mresize(c); + break; + } + } +} + +static void +configurerequest(XEvent *e) +{ + XConfigureRequestEvent *ev = &e->xconfigurerequest; + XWindowChanges wc; + Client *c; + + ev->value_mask &= ~CWSibling; + if((c = getclient(ev->window))) { + gravitate(c, True); + if(ev->value_mask & CWX) + c->x = ev->x; + if(ev->value_mask & CWY) + c->y = ev->y; + if(ev->value_mask & CWWidth) + c->w = ev->width; + if(ev->value_mask & CWHeight) + c->h = ev->height; + if(ev->value_mask & CWBorderWidth) + c->border = 1; + gravitate(c, False); + resize(c, True); + } + + wc.x = ev->x; + wc.y = ev->y; + wc.width = ev->width; + wc.height = ev->height; + wc.border_width = 1; + wc.sibling = None; + wc.stack_mode = Above; + ev->value_mask &= ~CWStackMode; + ev->value_mask |= CWBorderWidth; + XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); + XFlush(dpy); +} + +static void +destroynotify(XEvent *e) +{ + Client *c; + XDestroyWindowEvent *ev = &e->xdestroywindow; + + if((c = getclient(ev->window))) + unmanage(c); +} + +static void +enternotify(XEvent *e) +{ + XCrossingEvent *ev = &e->xcrossing; + Client *c; + + if(ev->mode != NotifyNormal || ev->detail == NotifyInferior) + return; + + if((c = getclient(ev->window))) + focus(c); + else if(ev->window == root) + issel = True; +} + +static void +leavenotify(XEvent *e) +{ + XCrossingEvent *ev = &e->xcrossing; + + if((ev->window == root) && !ev->same_screen) + issel = True; +} + +static void +expose(XEvent *e) +{ + XExposeEvent *ev = &e->xexpose; + Client *c; + + if(ev->count == 0) { + if((c = gettitle(ev->window))) + draw_client(c); + } +} + +static void +keymapnotify(XEvent *e) +{ + update_keys(); +} + +static void +maprequest(XEvent *e) +{ + XMapRequestEvent *ev = &e->xmaprequest; + static XWindowAttributes wa; + + if(!XGetWindowAttributes(dpy, ev->window, &wa)) + return; + + if(wa.override_redirect) { + XSelectInput(dpy, ev->window, + (StructureNotifyMask | PropertyChangeMask)); + return; + } + + if(!getclient(ev->window)) + manage(ev->window, &wa); +} + +static void +propertynotify(XEvent *e) +{ + XPropertyEvent *ev = &e->xproperty; + Window trans; + Client *c; + + if(ev->state == PropertyDelete) + return; /* ignore */ + + if((c = getclient(ev->window))) { + if(ev->atom == wm_atom[WMProtocols]) { + c->proto = win_proto(c->win); + return; + } + switch (ev->atom) { + default: break; + case XA_WM_TRANSIENT_FOR: + XGetTransientForHint(dpy, c->win, &trans); + if(!c->floating && (c->floating = (trans != 0))) + arrange(NULL); + break; + case XA_WM_NORMAL_HINTS: + update_size(c); + break; + } + if(ev->atom == XA_WM_NAME || ev->atom == net_atom[NetWMName]) { + update_name(c); + draw_client(c); + } + } +} + +static void +unmapnotify(XEvent *e) +{ + Client *c; + XUnmapEvent *ev = &e->xunmap; + + if((c = getclient(ev->window))) + unmanage(c); +} diff --git a/event.o b/event.o new file mode 100644 index 0000000..cf0e3d1 Binary files /dev/null and b/event.o differ diff --git a/main.c b/main.c new file mode 100644 index 0000000..72c2eb0 --- /dev/null +++ b/main.c @@ -0,0 +1,327 @@ +/* + * (C)opyright MMVI Anselm R. Garbe + * See LICENSE file for license details. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "dumbwm.h" + +/********** CUSTOMIZE **********/ + +char *tags[TLast] = { + [Tscratch] = "scratch", + [Tdev] = "dev", + [Twww] = "www", + [Twork] = "work", +}; + +/********** CUSTOMIZE **********/ + +/* X structs */ +Display *dpy; +Window root, barwin; +Atom wm_atom[WMLast], net_atom[NetLast]; +Cursor cursor[CurLast]; +Bool running = True; +Bool issel; + +int tsel = Tdev; /* default tag */ +int screen, sx, sy, sw, sh, bx, by, bw, bh, mw; +char stext[1024]; + +DC dc = {0}; +Client *clients = NULL; +Client *sel = NULL; + +static Bool other_wm_running; +static const char version[] = + "dumbwm-" VERSION ", (C)opyright MMVI Anselm R. Garbe\n"; +static int (*x_error_handler) (Display *, XErrorEvent *); + +static void +usage() { error("usage: dumbwm [-v]\n"); } + +static void +scan_wins() +{ + unsigned int i, num; + Window *wins; + XWindowAttributes wa; + Window d1, d2; + + if(XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { + for(i = 0; i < num; i++) { + if(!XGetWindowAttributes(dpy, wins[i], &wa)) + continue; + if(wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) + continue; + if(wa.map_state == IsViewable) + manage(wins[i], &wa); + } + } + if(wins) + XFree(wins); +} + +static int +win_property(Window w, Atom a, Atom t, long l, unsigned char **prop) +{ + Atom real; + int format; + unsigned long res, extra; + int status; + + status = XGetWindowProperty(dpy, w, a, 0L, l, False, t, &real, &format, + &res, &extra, prop); + + if(status != Success || *prop == 0) { + return 0; + } + if(res == 0) { + free((void *) *prop); + } + return res; +} + +int +win_proto(Window w) +{ + unsigned char *protocols; + long res; + int protos = 0; + int i; + + res = win_property(w, wm_atom[WMProtocols], XA_ATOM, 20L, &protocols); + if(res <= 0) { + return protos; + } + for(i = 0; i < res; i++) { + if(protocols[i] == wm_atom[WMDelete]) + protos |= WM_PROTOCOL_DELWIN; + } + free((char *) protocols); + return protos; +} + +void +send_message(Window w, Atom a, long value) +{ + XEvent e; + + e.type = ClientMessage; + e.xclient.window = w; + e.xclient.message_type = a; + e.xclient.format = 32; + e.xclient.data.l[0] = value; + e.xclient.data.l[1] = CurrentTime; + XSendEvent(dpy, w, False, NoEventMask, &e); + XFlush(dpy); +} + +/* + * There's no way to check accesses to destroyed windows, thus + * those cases are ignored (especially on UnmapNotify's). + * Other types of errors call Xlib's default error handler, which + * calls exit(). + */ +int +error_handler(Display *dpy, XErrorEvent *error) +{ + if(error->error_code == BadWindow + || (error->request_code == X_SetInputFocus + && error->error_code == BadMatch) + || (error->request_code == X_PolyText8 + && error->error_code == BadDrawable) + || (error->request_code == X_PolyFillRectangle + && error->error_code == BadDrawable) + || (error->request_code == X_PolySegment + && error->error_code == BadDrawable) + || (error->request_code == X_ConfigureWindow + && error->error_code == BadMatch) + || (error->request_code == X_GrabKey + && error->error_code == BadAccess)) + return 0; + fprintf(stderr, "dumbwm: fatal error: request code=%d, error code=%d\n", + error->request_code, error->error_code); + return x_error_handler(dpy, error); /* may call exit() */ +} + +/* + * Startup Error handler to check if another window manager + * is already running. + */ +static int +startup_error_handler(Display *dpy, XErrorEvent *error) +{ + other_wm_running = True; + return -1; +} + +static void +cleanup() +{ + while(sel) { + resize(sel, True); + unmanage(sel); + } + XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); +} + +void +quit(Arg *arg) +{ + running = False; +} + +int +main(int argc, char *argv[]) +{ + int i, n; + fd_set rd; + XSetWindowAttributes wa; + unsigned int mask; + Bool readstdin = True; + Window w; + XEvent ev; + + for(i = 1; (i < argc) && (argv[i][0] == '-'); i++) { + switch (argv[i][1]) { + case 'v': + fprintf(stdout, "%s", version); + exit(0); + break; + default: + usage(); + break; + } + } + + dpy = XOpenDisplay(0); + if(!dpy) + error("dumbwm: cannot connect X server\n"); + + screen = DefaultScreen(dpy); + root = RootWindow(dpy, screen); + + /* check if another WM is already running */ + other_wm_running = False; + XSetErrorHandler(startup_error_handler); + /* this causes an error if some other WM is running */ + XSelectInput(dpy, root, SubstructureRedirectMask); + XFlush(dpy); + + if(other_wm_running) + error("dumbwm: another window manager is already running\n"); + + XSetErrorHandler(0); + x_error_handler = XSetErrorHandler(error_handler); + + /* init atoms */ + wm_atom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); + wm_atom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); + net_atom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); + net_atom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); + XChangeProperty(dpy, root, net_atom[NetSupported], XA_ATOM, 32, + PropModeReplace, (unsigned char *) net_atom, NetLast); + + /* init cursors */ + cursor[CurNormal] = XCreateFontCursor(dpy, XC_left_ptr); + cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing); + cursor[CurMove] = XCreateFontCursor(dpy, XC_fleur); + + update_keys(); + + /* style */ + dc.bg = initcolor(BGCOLOR); + dc.fg = initcolor(FGCOLOR); + dc.border = initcolor(BORDERCOLOR); + initfont(FONT); + + sx = sy = 0; + sw = DisplayWidth(dpy, screen); + sh = DisplayHeight(dpy, screen); + mw = (sw * MASTERW) / 100; + + wa.override_redirect = 1; + wa.background_pixmap = ParentRelative; + wa.event_mask = ButtonPressMask | ExposureMask; + + bx = by = 0; + bw = sw; + dc.h = bh = dc.font.height + 4; + barwin = XCreateWindow(dpy, root, bx, by, bw, bh, 0, DefaultDepth(dpy, screen), + CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); + XDefineCursor(dpy, barwin, cursor[CurNormal]); + XMapRaised(dpy, barwin); + + dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen)); + dc.gc = XCreateGC(dpy, root, 0, 0); + draw_bar(); + + issel = XQueryPointer(dpy, root, &w, &w, &i, &i, &i, &i, &mask); + + wa.event_mask = SubstructureRedirectMask | EnterWindowMask \ + | LeaveWindowMask; + wa.cursor = cursor[CurNormal]; + + XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa); + + strcpy(stext, "dumbwm-"VERSION); + scan_wins(); + + /* main event loop, reads status text from stdin as well */ +Mainloop: + while(running) { + FD_ZERO(&rd); + if(readstdin) + FD_SET(STDIN_FILENO, &rd); + FD_SET(ConnectionNumber(dpy), &rd); + + i = select(ConnectionNumber(dpy) + 1, &rd, 0, 0, 0); + if(i == -1 && errno == EINTR) + continue; + if(i < 0) + error("select failed\n"); + else if(i > 0) { + if(FD_ISSET(ConnectionNumber(dpy), &rd)) { + while(XPending(dpy)) { + XNextEvent(dpy, &ev); + if(handler[ev.type]) + (handler[ev.type])(&ev); /* call handler */ + } + } + if(readstdin && FD_ISSET(STDIN_FILENO, &rd)) { + i = n = 0; + for(;;) { + if((i = getchar()) == EOF) { + /* broken pipe/end of producer */ + readstdin = False; + strcpy(stext, "broken pipe"); + goto Mainloop; + } + if(i == '\n' || n >= sizeof(stext) - 1) + break; + stext[n++] = i; + } + stext[n] = 0; + draw_bar(); + } + } + } + + cleanup(); + XCloseDisplay(dpy); + + return 0; +} diff --git a/main.o b/main.o new file mode 100644 index 0000000..c26a278 Binary files /dev/null and b/main.o differ diff --git a/util.c b/util.c new file mode 100644 index 0000000..da90aa1 --- /dev/null +++ b/util.c @@ -0,0 +1,59 @@ +/* + * (C)opyright MMVI Anselm R. Garbe + * See LICENSE file for license details. + */ + +#include +#include +#include +#include +#include +#include + +#include "dumbwm.h" + +void +error(const char *errstr, ...) { + va_list ap; + va_start(ap, errstr); + vfprintf(stderr, errstr, ap); + va_end(ap); + exit(1); +} + +static void +bad_malloc(unsigned int size) +{ + fprintf(stderr, "fatal: could not malloc() %d bytes\n", + (int) size); + exit(1); +} + +void * +emallocz(unsigned int size) +{ + void *res = calloc(1, size); + if(!res) + bad_malloc(size); + return res; +} + +void +spawn(Arg *arg) +{ + char **argv = (char **)arg->argv; + if(!argv || !argv[0]) + return; + if(fork() == 0) { + if(fork() == 0) { + if(dpy) + close(ConnectionNumber(dpy)); + setsid(); + execvp(argv[0], argv); + fprintf(stderr, "dumbwm: execvp %s", argv[0]); + perror(" failed"); + } + exit (0); + } + wait(0); +} diff --git a/util.o b/util.o new file mode 100644 index 0000000..ec19c34 Binary files /dev/null and b/util.o differ