commit 4a7273b827d4179c66c1353623215348df6f4131
parent b71f8a147ed082864f523b86a7de799b6363608f
Author: Anselm R. Garbe <arg@10kloc.org>
Date: Thu, 12 Oct 2006 15:11:25 +0200
reorganized, still liblitz there
Diffstat:
| Makefile | | | 2 | +- |
| area.c | | | 348 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| bar.c | | | 147 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| client.c | | | 823 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| cmd/Makefile | | | 28 | ---------------------------- |
| cmd/wm/area.c | | | 348 | ------------------------------------------------------------------------------- |
| cmd/wm/bar.c | | | 147 | ------------------------------------------------------------------------------- |
| cmd/wm/client.c | | | 823 | ------------------------------------------------------------------------------- |
| cmd/wm/column.c | | | 429 | ------------------------------------------------------------------------------- |
| cmd/wm/event.c | | | 314 | ------------------------------------------------------------------------------- |
| cmd/wm/frame.c | | | 117 | ------------------------------------------------------------------------------- |
| cmd/wm/fs.c | | | 905 | ------------------------------------------------------------------------------- |
| cmd/wm/geom.c | | | 49 | ------------------------------------------------- |
| cmd/wm/key.c | | | 265 | ------------------------------------------------------------------------------- |
| cmd/wm/mouse.c | | | 287 | ------------------------------------------------------------------------------- |
| cmd/wm/rule.c | | | 76 | ---------------------------------------------------------------------------- |
| cmd/wm/view.c | | | 444 | ------------------------------------------------------------------------------- |
| cmd/wm/wm.c | | | 410 | ------------------------------------------------------------------------------- |
| cmd/wm/wm.h | | | 309 | ------------------------------------------------------------------------------- |
| cmd/wm/wmii | | | 10 | ---------- |
| cmd/wm/wmii.1 | | | 209 | ------------------------------------------------------------------------------- |
| cmd/wm/wmiiwm.1 | | | 162 | ------------------------------------------------------------------------------- |
| cmd/wmiimenu.1 | | | 84 | ------------------------------------------------------------------------------- |
| cmd/wmiir.1 | | | 86 | ------------------------------------------------------------------------------- |
| cmd/wmiir.c | | | 324 | ------------------------------------------------------------------------------- |
| column.c | | | 429 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| doc/Makefile | | | 25 | ------------------------- |
| doc/guide_en.tex | | | 1005 | ------------------------------------------------------------------------------- |
| doc/wmii.svg | | | 61 | ------------------------------------------------------------- |
| doc/wmii.tex | | | 83 | ------------------------------------------------------------------------------- |
| event.c | | | 314 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| frame.c | | | 117 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| fs.c | | | 905 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| geom.c | | | 49 | +++++++++++++++++++++++++++++++++++++++++++++++++ |
| key.c | | | 265 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| libcext/Makefile | | | 28 | ---------------------------- |
| libcext/assert.c | | | 10 | ---------- |
| libcext/cext.h | | | 38 | -------------------------------------- |
| libcext/malloc.c | | | 54 | ------------------------------------------------------ |
| libcext/strlcat.c | | | 55 | ------------------------------------------------------- |
| libcext/strlcpy.c | | | 47 | ----------------------------------------------- |
| libcext/tokenize.c | | | 34 | ---------------------------------- |
| libcext/trim.c | | | 35 | ----------------------------------- |
| libixp/Makefile | | | 25 | ------------------------- |
| libixp/client.c | | | 220 | ------------------------------------------------------------------------------- |
| libixp/convert.c | | | 236 | ------------------------------------------------------------------------------- |
| libixp/intmap.c | | | 144 | ------------------------------------------------------------------------------- |
| libixp/ixp.h | | | 369 | ------------------------------------------------------------------------------- |
| libixp/message.c | | | 243 | ------------------------------------------------------------------------------- |
| libixp/request.c | | | 394 | ------------------------------------------------------------------------------- |
| libixp/server.c | | | 130 | ------------------------------------------------------------------------------- |
| libixp/socket.c | | | 200 | ------------------------------------------------------------------------------- |
| libixp/transport.c | | | 77 | ----------------------------------------------------------------------------- |
| liblitz/brush.c | | | 3 | +-- |
| liblitz/color.c | | | 3 | +-- |
| liblitz/font.c | | | 10 | ++++------ |
| mouse.c | | | 287 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| rule.c | | | 76 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| test/test_fs.sh | | | 19 | ------------------- |
| view.c | | | 444 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| wm.c | | | 410 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| wm.h | | | 309 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| wmii | | | 10 | ++++++++++ |
| wmii.1 | | | 209 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| wmii.svg | | | 61 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| wmiiwm.1 | | | 162 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
66 files changed, 5372 insertions(+), 9369 deletions(-)
diff --git a/Makefile b/Makefile
@@ -3,7 +3,7 @@
include config.mk
-SUBDIRS = libcext liblitz libixp cmd
+SUBDIRS = liblitz
BIN = cmd/wm/wmii cmd/wm/wmiiwm cmd/wmiir
diff --git a/area.c b/area.c
@@ -0,0 +1,348 @@
+/*
+ * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
+ * See LICENSE file for license details.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "wm.h"
+
+Client *
+sel_client_of_area(Area *a)
+{
+ return a && a->sel ? a->sel->client : nil;
+}
+
+Area *
+create_area(View *v, Area *pos, unsigned int w)
+{
+ static unsigned short id = 1;
+ unsigned int area_size, col_size;
+ unsigned int min_width = screen->rect.width/NCOL;
+ Area *a, **p = pos ? &pos->next : &v->area;
+
+ for(area_size = 0, a=v->area; a; a=a->next, area_size++);
+
+ col_size = area_size ? area_size - 1 : 0;
+
+ if(!w) {
+ if(col_size)
+ w = screen->rect.width / (col_size + 1);
+ else
+ w = screen->rect.width;
+ }
+ if(w < min_width)
+ w = min_width;
+
+ if(col_size && col_size * min_width + w > screen->rect.width)
+ return nil;
+
+ if(area_size > 1)
+ scale_view(v, screen->rect.width - w);
+ a = cext_emallocz(sizeof(Area));
+ a->view = v;
+ a->id = id++;
+ a->rect = screen->rect;
+ a->rect.height = screen->rect.height - screen->brect.height;
+ a->mode = def.colmode;
+ a->rect.width = w;
+ a->frame = nil;
+ a->sel = nil;
+
+ a->next = *p;
+ *p = a;
+
+ v->sel = a;
+ return a;
+}
+
+void
+destroy_area(Area *a)
+{
+ Client *c;
+ Area *ta;
+ View *v = a->view;
+
+ cext_assert(!a->frame && "wmiiwm: fatal, destroying non-empty area");
+
+ if(v->revert == a)
+ v->revert = nil;
+
+ for(c=client; c; c=c->next)
+ if(c->revert == a)
+ c->revert = nil;
+
+ for(ta=v->area; ta && ta->next != a; ta=ta->next);
+ if(ta) {
+ ta->next = a->next;
+ if(v->sel == a)
+ v->sel = ta->floating ? ta->next : ta;
+ }
+ free(a);
+}
+
+static void
+place_client(Area *a, Client *c)
+{
+ static unsigned int mx, my;
+ static Bool *field = nil;
+ Frame *fr;
+ Bool fit = False;
+ BlitzAlign align = CENTER;
+ unsigned int i, j, x, y, maxx, maxy, dx, dy, cx, cy, diff, num = 0;
+ XPoint p1 = {0, 0}, p2 = {0, 0};
+ Frame *f = c->sel;
+ int snap = screen->rect.height / 66;
+ XRectangle *rects;
+
+ if(c->trans)
+ return;
+ if(c->rect.width >= a->rect.width
+ || c->rect.height >= a->rect.height
+ || c->size.flags & USPosition
+ || c->size.flags & PPosition)
+ return;
+
+ rects = rects_of_view(a->view, &num);
+ if(!field) {
+ mx = screen->rect.width / 8;
+ my = screen->rect.height / 8;
+ field = cext_emallocz(my * mx * sizeof(Bool));
+ }
+
+ for(y = 0; y < my; y++)
+ for(x = 0; x < mx; x++)
+ field[y*mx + x] = True;
+
+ dx = screen->rect.width / mx;
+ dy = screen->rect.height / my;
+ for(fr=a->frame; fr; fr=fr->anext) {
+ if(fr == f) {
+ cx = f->rect.width / dx;
+ cy = f->rect.height / dy;
+ continue;
+ }
+ if(fr->rect.x < 0)
+ x = 0;
+ else
+ x = fr->rect.x / dx;
+ if(fr->rect.y < 0)
+ y = 0;
+ else
+ y = fr->rect.y / dy;
+ maxx = (fr->rect.x + fr->rect.width) / dx;
+ maxy = (fr->rect.y + fr->rect.height) / dy;
+ for(j = y; j < my && j < maxy; j++)
+ for(i = x; i < mx && i < maxx; i++)
+ field[j*mx + i] = False;
+ }
+
+ for(y = 0; y < my; y++)
+ for(x = 0; x < mx; x++) {
+ if(field[y*mx + x]) {
+ for(i = x; (i < mx) && field[y*mx + i]; i++);
+ for(j = y; (j < my) && field[j*mx + x]; j++);
+ if(((i - x) * (j - y) > (p2.x - p1.x) * (p2.y - p1.y))
+ && (i - x > cx) && (j - y > cy))
+ {
+ fit = True;
+ p1.x = x;
+ p1.y = y;
+ p2.x = i;
+ p2.y = j;
+ }
+ }
+ }
+
+ if(fit) {
+ p1.x *= dx;
+ p1.y *= dy;
+ }
+
+ if(fit && (p1.x + f->rect.width < a->rect.x + a->rect.width))
+ f->rect.x = p1.x;
+ else {
+ diff = a->rect.width - f->rect.width;
+ f->rect.x = a->rect.x + (random() % (diff ? diff : 1));
+ }
+
+ if(fit && (p1.y + f->rect.height < a->rect.y + a->rect.height))
+ f->rect.y = p1.y;
+ else {
+ diff = a->rect.height - f->rect.height;
+ f->rect.y = a->rect.y + (random() % (diff ? diff : 1));
+ }
+
+ snap_rect(rects, num, &f->rect, &align, snap);
+ if(rects)
+ free(rects);
+}
+
+void
+send_to_area(Area *to, Area *from, Frame *f)
+{
+ cext_assert(to->view == f->view);
+
+ if(to->floating != from->floating) {
+ XRectangle temp = f->revert;
+ f->revert = f->rect;
+ f->rect = temp;
+ }
+ f->client->revert = from;
+ detach_from_area(from, f);
+ attach_to_area(to, f, True);
+ focus_client(f->client, True);
+}
+
+void
+attach_to_area(Area *a, Frame *f, Bool send)
+{
+ unsigned int h, n_frame;
+ Frame **fa, *ft;
+ View *v = a->view;
+ Client *c = f->client;
+
+ for(ft=a->frame, n_frame=1; ft; ft=ft->anext, n_frame++);
+
+ h = 0;
+ c->floating = a->floating;
+ if(!c->floating) {
+ h = a->rect.height / n_frame;
+ if(a->frame)
+ scale_column(a, a->rect.height - h);
+ }
+
+ if(!send && !c->floating) { /* column */
+ unsigned int w = newcolw_of_view(v);
+ if(v->area->next->frame && w) {
+ a = new_column(v, a, w);
+ arrange_view(v);
+ }
+ }
+
+ fa = a->sel ? &a->sel->anext : &a->frame;
+ f->anext = *fa;
+ *fa = f;
+
+ f->area = a;
+ a->sel = f;
+
+ if(!c->floating) { /* column */
+ f->rect.height = h;
+ arrange_column(a, False);
+ }else /* floating */
+ place_client(a, c);
+}
+
+void
+detach_from_area(Area *a, Frame *f)
+{
+ Frame **ft, *pr = nil;
+ Client *c = f->client;
+ View *v = a->view;
+
+ for(ft=&a->frame; *ft; ft=&(*ft)->anext) {
+ if(*ft == f) break;
+ pr = *ft;
+ }
+ cext_assert(*ft == f);
+ *ft = f->anext;
+
+ if(a->sel == f)
+ a->sel = pr ? pr : *ft;
+
+ if(!a->floating) {
+ if(a->frame)
+ arrange_column(a, False);
+ else {
+ if(v->area->next->next)
+ destroy_area(a);
+ else if(!a->frame && v->area->frame)
+ v->sel = v->area; /* focus floating area if it contains something */
+ arrange_view(v);
+ }
+ }
+ else if(!a->frame) {
+ if(c->trans) {
+ /* focus area of transient, if possible */
+ Client *cl = client_of_win(c->trans);
+ if(cl && cl->frame) {
+ a = cl->sel->area;
+ if(a->view == v)
+ v->sel = a;
+ }
+ }
+ else if(v->area->next->frame)
+ v->sel = v->area->next; /* focus first col as fallback */
+ }
+}
+
+char *
+select_area(Area *a, char *arg)
+{
+ Area *new;
+ unsigned int i;
+ Frame *p, *f;
+ View *v;
+ static char Ebadvalue[] = "bad value";
+
+ v = a->view;
+ f = a->sel;
+
+ if(!strncmp(arg, "toggle", 7)) {
+ if(a != v->area)
+ new = v->area;
+ else if(v->revert)
+ new = v->revert;
+ else
+ new = v->area->next;
+ } else if(!strncmp(arg, "left", 5)) {
+ if(a->floating)
+ return Ebadvalue;
+ for(new=v->area->next;
+ new && new->next != a;
+ new=new->next);
+ if(!new)
+ new=v->area->next;
+ } else if(!strncmp(arg, "right", 5)) {
+ if(a->floating)
+ return Ebadvalue;
+ new = a->next ? a->next : a;
+ }
+ else if(!strncmp(arg, "up", 3)) {
+ if(!f)
+ return Ebadvalue;
+ for(p=a->frame; p->anext; p=p->anext)
+ if(p->anext == f) break;
+ a->sel = p;
+ arrange_column(a, False);
+ if(v == screen->sel)
+ focus_view(screen, v);
+ flush_masked_events(EnterWindowMask);
+ return nil;
+ }
+ else if(!strncmp(arg, "down", 5)) {
+ if(!f)
+ return Ebadvalue;
+ p = f->anext ? f->anext : a->frame;
+ a->sel = p;
+ arrange_column(a, False);
+ if(v == screen->sel)
+ focus_view(screen, v);
+ flush_masked_events(EnterWindowMask);
+ return nil;
+ }
+ else {
+ if(sscanf(arg, "%d", &i) != 1)
+ return Ebadvalue;
+ for(new=view->area; i && new->next; new=new->next, i--);
+ }
+ if(new->sel)
+ focus_client(new->sel->client, True);
+ v->sel = new;
+ if(a->floating != new->floating)
+ v->revert = a;
+ return nil;
+}
diff --git a/bar.c b/bar.c
@@ -0,0 +1,147 @@
+/*
+ * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
+ * See LICENSE file for license details.
+ */
+
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "wm.h"
+
+Bar *free_bars = nil;
+
+Bar *
+create_bar(Bar **b_link, char *name)
+{
+ static unsigned int id = 1;
+ Bar **i, *b = bar_of_name(*b_link, name);;
+ if(b)
+ return b;
+
+ if(free_bars) {
+ b = free_bars;
+ free_bars = b->next;
+ memset(b, 0, sizeof(*b));
+ }
+ else
+ b = cext_emallocz(sizeof(Bar));
+
+ b->id = id++;
+ cext_strlcpy(b->name, name, sizeof(b->name));
+ b->brush = screen->bbrush;
+ b->brush.color = def.normcolor;
+
+ for(i=b_link; *i; i=&(*i)->next)
+ if(strcmp((*i)->name, name) >= 0)
+ break;
+ b->next = *i;
+ *i = b;
+
+ return b;
+}
+
+void
+destroy_bar(Bar **b_link, Bar *b)
+{
+ Bar **p;
+ for(p=b_link; *p && *p != b; p=&(*p)->next);
+ *p = b->next;
+
+ b->next = free_bars;
+ free_bars = b;
+}
+
+void
+resize_bar(WMScreen *s)
+{
+ View *v;
+
+ s->brect = s->rect;
+ s->brect.height = blitz_labelh(&def.font);
+ s->brect.y = s->rect.height - s->brect.height;
+ XMoveResizeWindow(blz.dpy, s->barwin, s->brect.x, s->brect.y, s->brect.width, s->brect.height);
+ XSync(blz.dpy, False);
+ draw_bar(s);
+
+ for(v=view; v; v=v->next)
+ arrange_view(v);
+}
+
+void
+draw_bar(WMScreen *s)
+{
+ unsigned int width, tw, nb, size;
+ float shrink;
+ Bar *b, *tb, *largest, **pb;
+
+ blitz_draw_tile(&s->bbrush);
+
+ if(!s->lbar && !s->rbar)
+ goto MapBar;
+
+ largest = b = tb = nil;
+ tw = width = nb = size = 0;
+
+ for(b=s->lbar, nb=2 ;nb; --nb && (b = s->rbar))
+ for(; b; b=b->next) {
+ b->brush.rect.x = b->brush.rect.y = 0;
+ b->brush.rect.width = def.font.height;
+ if(b->text && strlen(b->text))
+ b->brush.rect.width += blitz_textwidth(b->brush.font, b->text);
+ b->brush.rect.height = s->brect.height;
+ width += b->brush.rect.width;
+ }
+
+ /* Not enough room. Shrink bars until they all fit */
+ if(width > s->brect.width) {
+ for(b=s->lbar, nb=2 ;nb; --nb && (b = s->rbar))
+ for(; b; b=b->next) {
+ for(pb=&largest; *pb; pb=&(*pb)->smaller)
+ if((*pb)->brush.rect.width < b->brush.rect.width) break;
+ b->smaller = *pb;
+ *pb = b;
+ }
+ for(tb=largest; tb; tb=tb->smaller) {
+ width -= tb->brush.rect.width;
+ tw += tb->brush.rect.width;
+ shrink = (s->brect.width - width) / (float)tw;
+ if(tb->smaller)
+ if(tb->brush.rect.width * shrink >= tb->smaller->brush.rect.width)
+ break;
+ }
+ if(tb)
+ for(b=largest; b != tb->smaller; b=b->smaller)
+ b->brush.rect.width = floor(b->brush.rect.width * shrink);
+ width += tw * shrink;
+ tb=nil;
+ }
+
+ for(b=s->lbar, nb=2 ;nb; b=s->rbar, nb--)
+ for(; b; tb = b, b=b->next) {
+ if(b == s->rbar) {
+ b->brush.align = EAST;
+ s->rbar->brush.rect.width += (s->brect.width - width);
+ }else
+ b->brush.align = CENTER;
+ if(tb)
+ b->brush.rect.x = tb->brush.rect.x + tb->brush.rect.width;
+ blitz_draw_label(&b->brush, b->text);
+ }
+MapBar:
+ XCopyArea(blz.dpy, s->bbrush.drawable, s->barwin, s->bbrush.gc, 0, 0,
+ s->brect.width, s->brect.height, 0, 0);
+ XSync(blz.dpy, False);
+}
+
+Bar *
+bar_of_name(Bar *b_link, const char *name)
+{
+ static char buf[256];
+ Bar *b;
+
+ cext_strlcpy(buf, name, sizeof(buf));
+ for(b=b_link; b; b=b->next)
+ if(!strncmp(b->name, name, sizeof(b->name))) break;
+ return b;
+}
diff --git a/client.c b/client.c
@@ -0,0 +1,823 @@
+/*
+ * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
+ * See LICENSE file for license details.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <X11/Xatom.h>
+
+#include "wm.h"
+
+static char *Ebadcmd = "bad command",
+ *Ebadvalue = "bad value";
+
+#define CLIENT_MASK (StructureNotifyMask | PropertyChangeMask | EnterWindowMask)
+
+Client *
+sel_client()
+{
+ return screen->sel && screen->sel->sel->sel ? screen->sel->sel->sel->client : nil;
+}
+
+int
+idx_of_client(Client *c)
+{
+ Client *cl;
+ int i = 0;
+ for(cl=client; cl && cl != c; cl=cl->next, i++);
+ return cl ? i : -1;
+}
+
+Client *
+client_of_win(Window w)
+{
+ Client *c;
+ for(c=client; c && c->win != w; c=c->next);
+ return c;
+}
+
+Frame *
+frame_of_win(Window w)
+{
+ Client *c;
+ for(c=client; c && c->framewin != w; c=c->next);
+ return c ? c->frame : nil;
+}
+
+static void
+update_client_name(Client *c)
+{
+ XTextProperty name;
+ XClassHint ch;
+ int n;
+ char **list = nil;
+
+ name.nitems = 0;
+ c->name[0] = 0;
+ XGetTextProperty(blz.dpy, c->win, &name, net_atom[NetWMName]);
+ if(!name.nitems)
+ XGetWMName(blz.dpy, c->win, &name);
+ if(!name.nitems)
+ return;
+ if(name.encoding == XA_STRING)
+ cext_strlcpy(c->name, (char *)name.value, sizeof(c->name));
+ else {
+ if(XmbTextPropertyToTextList(blz.dpy, &name, &list, &n) >= Success
+ && n > 0 && *list)
+ {
+ cext_strlcpy(c->name, *list, sizeof(c->name));
+ XFreeStringList(list);
+ }
+ }
+ XFree(name.value);
+ if(XGetClassHint(blz.dpy, c->win, &ch)) {
+ snprintf(c->props, sizeof(c->props), "%s:%s:%s",
+ ch.res_class ? ch.res_class : "",
+ ch.res_name ? ch.res_name : "",
+ c->name);
+ if(ch.res_class)
+ XFree(ch.res_class);
+ if(ch.res_name)
+ XFree(ch.res_name);
+ }
+}
+
+Client *
+create_client(Window w, XWindowAttributes *wa)
+{
+ Client **t, *c = (Client *) cext_emallocz(sizeof(Client));
+ XSetWindowAttributes fwa;
+ long msize;
+ unsigned int i;
+ static unsigned int id = 1;
+
+ c->id = id++;
+ c->win = w;
+ c->rect.x = wa->x;
+ c->rect.y = wa->y;
+ c->border = wa->border_width;
+ c->rect.width = wa->width;
+ c->rect.height = wa->height;
+ XSetWindowBorderWidth(blz.dpy, c->win, 0);
+ c->proto = win_proto(c->win);
+ XGetTransientForHint(blz.dpy, c->win, &c->trans);
+ if(!XGetWMNormalHints(blz.dpy, c->win, &c->size, &msize) || !c->size.flags)
+ c->size.flags = PSize;
+ if(c->size.flags & PMinSize && c->size.flags & PMaxSize
+ && c->size.min_width == c->size.max_width
+ && c->size.min_height == c->size.max_height)
+ c->fixedsize = True;
+ else
+ c->fixedsize = False;
+ XAddToSaveSet(blz.dpy, c->win);
+ update_client_name(c);
+ fwa.override_redirect = 1;
+ fwa.background_pixmap = ParentRelative;
+ fwa.event_mask =
+ SubstructureRedirectMask | SubstructureNotifyMask | ExposureMask
+ | ButtonPressMask | PointerMotionMask | ButtonReleaseMask | KeyPressMask;
+
+ c->framewin = XCreateWindow(blz.dpy, blz.root, c->rect.x, c->rect.y,
+ c->rect.width + 2 * def.border,
+ c->rect.height + def.border + blitz_labelh(&def.font), 0,
+ DefaultDepth(blz.dpy, blz.screen), CopyFromParent,
+ DefaultVisual(blz.dpy, blz.screen),
+ CWOverrideRedirect | CWBackPixmap | CWEventMask, &fwa);
+ c->gc = XCreateGC(blz.dpy, c->framewin, 0, 0);
+ XSync(blz.dpy, False);
+
+ for(t=&client, i=0; *t; t=&(*t)->next, i++);
+ c->next = *t; /* *t == nil */
+ *t = c;
+
+ write_event("CreateClient %d\n", i);
+ return c;
+}
+
+void
+update_client_grab(Client *c, Bool is_sel)
+{
+ if(is_sel) {
+ ungrab_mouse(c->framewin, AnyModifier, AnyButton);
+ grab_mouse(c->framewin, def.mod, Button1);
+ grab_mouse(c->framewin, def.mod, Button3);
+ }
+ else
+ grab_mouse(c->framewin, AnyModifier, Button1);
+}
+
+void
+focus_client(Client *c, Bool restack)
+{
+ Client *old_in_area;
+ Client *old;
+ Frame *f;
+ View *v;
+
+ if(!sel_screen)
+ return;
+
+ f = c->sel;
+ v = f->area->view;
+ old = sel_client();
+ old_in_area = sel_client_of_area(f->area);
+
+ v->sel = f->area;
+ f->area->sel = f;
+ c->floating = f->area->floating;
+
+ if(restack)
+ restack_view(v);
+ else {
+ if(old)
+ update_client_grab(old, False);
+ update_client_grab(c, True);
+ }
+
+ if(!c->floating && f->area->mode == Colstack)
+ arrange_column(f->area, False);
+ XSetInputFocus(blz.dpy, c->win, RevertToPointerRoot, CurrentTime);
+ if(old && old != old_in_area && old != c) {
+ update_frame_widget_colors(old->sel);
+ draw_frame(old->sel);
+ }
+ if(old_in_area && old_in_area != c) {
+ update_frame_widget_colors(old_in_area->sel);
+ draw_frame(old_in_area->sel);
+ }
+ update_frame_widget_colors(c->sel);
+ draw_frame(c->sel);
+ XSync(blz.dpy, False);
+ write_event("ClientFocus %d\n", idx_of_client(c));
+}
+
+void
+map_client(Client *c)
+{
+ XSelectInput(blz.dpy, c->win, CLIENT_MASK & ~StructureNotifyMask);
+ XMapWindow(blz.dpy, c->win);
+ XSelectInput(blz.dpy, c->win, CLIENT_MASK);
+}
+
+void
+unmap_client(Client *c)
+{
+ XSelectInput(blz.dpy, c->win, CLIENT_MASK & ~StructureNotifyMask);
+ XUnmapWindow(blz.dpy, c->win);
+ XSelectInput(blz.dpy, c->win, CLIENT_MASK);
+}
+
+void
+reparent_client(Client *c, Window w, int x, int y)
+{
+ XSelectInput(blz.dpy, c->win, CLIENT_MASK & ~StructureNotifyMask);
+ XReparentWindow(blz.dpy, c->win, w, x, y);
+ XSelectInput(blz.dpy, c->win, CLIENT_MASK);
+}
+
+void
+configure_client(Client *c)
+{
+ XConfigureEvent e;
+ Frame *f = c->sel;
+ e.type = ConfigureNotify;
+ e.event = c->win;
+ e.window = c->win;
+ e.x = c->rect.x;
+ e.y = c->rect.y;
+ if(f) {
+ e.x += f->rect.x;
+ e.y += f->rect.y;
+ }
+ e.width = c->rect.width;
+ e.height = c->rect.height;
+ e.border_width = c->border;
+ e.above = None;
+ e.override_redirect = False;
+ XSendEvent(blz.dpy, c->win, False,
+ StructureNotifyMask, (XEvent *) & e);
+ XSync(blz.dpy, False);
+}
+
+static void
+send_client_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(blz.dpy, w, False, NoEventMask, &e);
+ XSync(blz.dpy, False);
+}
+
+void
+kill_client(Client * c)
+{
+ if(c->proto & WM_PROTOCOL_DELWIN)
+ send_client_message(c->win, wm_atom[WMProtocols], wm_atom[WMDelete]);
+ else
+ XKillClient(blz.dpy, c->win);
+}
+
+void
+prop_client(Client *c, XPropertyEvent *e)
+{
+ long msize;
+
+ if(e->atom == wm_atom[WMProtocols]) {
+ c->proto = win_proto(c->win);
+ return;
+ }
+ switch (e->atom) {
+ case XA_WM_TRANSIENT_FOR:
+ XGetTransientForHint(blz.dpy, c->win, &c->trans);
+ break;
+ case XA_WM_NORMAL_HINTS:
+ if(!XGetWMNormalHints(blz.dpy, c->win, &c->size, &msize) || !c->size.flags) {
+ c->size.flags = PSize;
+ }
+ if(c->size.flags & PMinSize && c->size.flags & PMaxSize
+ && c->size.min_width == c->size.max_width
+ && c->size.min_height == c->size.max_height)
+ c->fixedsize = True;
+ else
+ c->fixedsize = False;
+ break;
+ }
+ if(e->atom == XA_WM_NAME || e->atom == net_atom[NetWMName]) {
+ update_client_name(c);
+ if(c->frame)
+ draw_frame(c->sel);
+ }
+}
+
+void
+gravitate_client(Client *c, Bool invert)
+{
+ int dx = 0, dy = 0;
+ int gravity = NorthWestGravity;
+
+ if(c->size.flags & PWinGravity) {
+ gravity = c->size.win_gravity;
+ }
+
+ /* y */
+ switch (gravity) {
+ case StaticGravity:
+ case NorthWestGravity:
+ case NorthGravity:
+ case NorthEastGravity:
+ dy = blitz_labelh(&def.font);
+ break;
+ case EastGravity:
+ case CenterGravity:
+ case WestGravity:
+ dy = -(c->rect.height / 2) + blitz_labelh(&def.font);
+ break;
+ case SouthEastGravity:
+ case SouthGravity:
+ case SouthWestGravity:
+ dy = -c->rect.height;
+ break;
+ default:
+ break;
+ }
+
+ /* x */
+ switch (gravity) {
+ case StaticGravity:
+ case NorthWestGravity:
+ case WestGravity:
+ case SouthWestGravity:
+ dx = def.border;
+ break;
+ case NorthGravity:
+ case CenterGravity:
+ case SouthGravity:
+ dx = -(c->rect.width / 2) + def.border;
+ break;
+ case NorthEastGravity:
+ case EastGravity:
+ case SouthEastGravity:
+ dx = -(c->rect.width + def.border);
+ break;
+ default:
+ break;
+ }
+
+ if(invert) {
+ dx = -dx;
+ dy = -dy;
+ }
+ c->rect.x += dx;
+ c->rect.y += dy;
+}
+
+void
+manage_client(Client *c)
+{
+ XTextProperty tags;
+ Client *trans;
+
+ tags.nitems = 0;
+ XGetTextProperty(blz.dpy, c->win, &tags, tags_atom);
+
+ if(c->trans && (trans = client_of_win(c->trans)))
+ cext_strlcpy(c->tags, trans->tags, sizeof(c->tags));
+ else if(tags.nitems)
+ cext_strlcpy(c->tags, (char *)tags.value, sizeof(c->tags));
+ XFree(tags.value);
+
+ if(!strlen(c->tags))
+ apply_rules(c);
+
+ apply_tags(c, c->tags);
+
+ reparent_client(c, c->framewin, c->rect.x, c->rect.y);
+
+ if(!starting)
+ update_views();
+ map_client(c);
+ XMapWindow(blz.dpy, c->framewin);
+ XSync(blz.dpy, False);
+ if(c->sel->area->view == screen->sel)
+ focus_client(c, False);
+ flush_masked_events(EnterWindowMask);
+}
+
+static int
+dummy_error_handler(Display *dpy, XErrorEvent *error)
+{
+ return 0;
+}
+
+void
+destroy_client(Client *c)
+{
+ char *dummy = nil;
+ Client **tc;
+
+ XGrabServer(blz.dpy);
+ XSetErrorHandler(dummy_error_handler);
+
+ if(c->frame) {
+ c->rect.x = c->sel->rect.x;
+ c->rect.y = c->sel->rect.y;
+ }
+
+ update_client_views(c, &dummy);
+
+ unmap_client(c);
+
+ reparent_client(c, blz.root, c->rect.x, c->rect.y);
+ XFreeGC(blz.dpy, c->gc);
+ XDestroyWindow(blz.dpy, c->framewin);
+
+ for(tc=&client; *tc && *tc != c; tc=&(*tc)->next);
+ cext_assert(*tc == c);
+ *tc = c->next;
+
+ update_views();
+ free(c);
+
+ XSync(blz.dpy, False);
+ XSetErrorHandler(wmii_error_handler);
+ XUngrabServer(blz.dpy);
+ flush_masked_events(EnterWindowMask);
+}
+
+void
+match_sizehints(Client *c, XRectangle *r, Bool floating, BlitzAlign sticky)
+{
+ XSizeHints *s = &c->size;
+ unsigned int dx = 2 * def.border;
+ unsigned int dy = def.border + blitz_labelh(&def.font);
+ unsigned int hdiff, wdiff;
+
+ if(floating && (s->flags & PMinSize)) {
+ if(r->width < s->min_width + dx) {
+ wdiff = (s->min_width + dx) - r->width;
+ r->width += wdiff;
+ if((sticky & EAST) && !(sticky & WEST))
+ r->x -= wdiff;
+ }
+ if(r->height < s->min_height + dy) {
+ hdiff = (s->min_height + dy) - r->height;
+ r->height += hdiff;
+ if((sticky & SOUTH) && !(sticky & NORTH))
+ r->y -= hdiff;
+ }
+ }
+ if(floating && (s->flags & PMaxSize)) {
+ if(r->width > s->max_width + dx) {
+ wdiff = r->width - (s->max_width + dx);
+ r->width -= wdiff;
+ if((sticky & EAST) && !(sticky & WEST))
+ r->x += wdiff;
+ }
+ if(r->height > s->max_height + dy) {
+ hdiff = r->height - (s->max_height + dy);
+ r->height -= hdiff;
+ if((sticky & SOUTH) && !(sticky & NORTH))
+ r->y += hdiff;
+ }
+ }
+
+ if(s->flags & PResizeInc) {
+ int w = 0, h = 0;
+
+ if(s->flags & PBaseSize) {
+ w = s->base_width;
+ h = s->base_height;
+ } else if(s->flags & PMinSize) {
+ /* base_{width,height} default to min_{width,height} */
+ w = s->min_width;
+ h = s->min_height;
+ }
+ /* client_width = base_width + i * s->width_inc for an integer i */
+ w = r->width - dx - w;
+ if(s->width_inc > 0) {
+ wdiff = w % s->width_inc;
+ r->width -= wdiff;
+ if((sticky & EAST) && !(sticky & WEST))
+ r->x += wdiff;
+ }
+
+ h = r->height - dy - h;
+ if(s->height_inc > 0) {
+ hdiff = h % s->height_inc;
+ r->height -= hdiff;
+ if((sticky & SOUTH) && !(sticky & NORTH))
+ r->y += hdiff;
+ }
+ }
+}
+
+void
+resize_client(Client *c, XRectangle *r, Bool ignore_xcall)
+{
+ Frame *f = c->sel;
+ Bool floating = f->area->floating;
+ unsigned int max_height;
+
+ BlitzAlign stickycorner = 0;
+ if(f->rect.x != r->x && f->rect.x + f->rect.width == r->x + r->width)
+ stickycorner |= EAST;
+ else
+ stickycorner |= WEST;
+ if(f->rect.y != r->y && f->rect.y + f->rect.height == r->y + r->height)
+ stickycorner |= SOUTH;
+ else
+ stickycorner |= NORTH;
+
+ f->rect = *r;
+
+ if((f->area->mode != Colstack) || (f->area->sel == f))
+ match_sizehints(c, &c->sel->rect, floating, stickycorner);
+
+ max_height = screen->rect.height - blitz_labelh(&def.font);
+ if(!ignore_xcall) {
+ if(floating) {
+ if((c->rect.width == screen->rect.width) &&
+ (c->rect.height == screen->rect.height)) {
+ f->rect.x = -def.border;
+ f->rect.y = -blitz_labelh(&def.font);
+ }else{
+ if(f->rect.height > max_height)
+ f->rect.height = max_height;
+ if(f->rect.width > screen->rect.width)
+ f->rect.width = screen->rect.width;
+ if(f->rect.x + f->rect.width > screen->rect.width)
+ f->rect.x = screen->rect.width - f->rect.width;
+ if(f->rect.y + f->rect.height > max_height)
+ f->rect.y = max_height - f->rect.height;
+ if(f->rect.x < 0)
+ f->rect.x = 0;
+ if(f->rect.y < 0)
+ f->rect.y = 0;
+ }
+ }
+ if(f->area->view == screen->sel)
+ XMoveResizeWindow(blz.dpy, c->framewin, f->rect.x,
+ f->rect.y, f->rect.width, f->rect.height);
+ else
+ XMoveResizeWindow(blz.dpy, c->framewin, 2 * screen->rect.width + f->rect.x,
+ f->rect.y, f->rect.width, f->rect.height);
+ }
+
+ c->rect.x = def.border;
+ c->rect.y = blitz_labelh(&def.font);
+ if((f->area->sel == f) || (f->area->mode != Colstack)) {
+ c->rect.width = f->rect.width - 2 * def.border;
+ c->rect.height = f->rect.height - def.border - blitz_labelh(&def.font);
+ }
+ if(!ignore_xcall) {
+ XMoveResizeWindow(blz.dpy, c->win, c->rect.x, c->rect.y,
+ c->rect.width, c->rect.height);
+ configure_client(c);
+ }
+}
+
+void
+newcol_client(Client *c, char *arg)
+{
+ Frame *f = c->sel;
+ Area *to, *a = f->area;
+ View *v = a->view;
+
+ if(a->floating)
+ return;
+ if(!f->anext && f == a->frame)
+ return;
+
+ if(!strncmp(arg, "prev", 5)) {
+ for(to=v->area; to && to->next != a; to=to->next);
+ to = new_column(v, to, 0);
+ send_to_area(to, a, f);
+ }
+ else if(!strncmp(arg, "next", 5)) {
+ to = new_column(v, a, 0);
+ send_to_area(to, a, f);
+ }
+ else
+ return;
+ flush_masked_events(EnterWindowMask);
+}
+
+void
+move_client(Client *c, char *arg)
+{
+ Frame *f = c->sel;
+ XRectangle new = f->rect;
+ int x, y;
+
+ if(sscanf(arg, "%d %d", &x, &y) != 2)
+ return;
+ new.x += x;
+ new.y += y;
+ if(!f->area->floating)
+ resize_column(f->client, &new, nil);
+ else
+ resize_client(f->client, &new, False);
+}
+
+void
+size_client(Client *c, char *arg)
+{
+ Frame *f = c->sel;
+ XRectangle new = f->rect;
+ int w, h;
+
+ if(sscanf(arg, "%d %d", &w, &h) != 2)
+ return;
+ new.width += w;
+ new.height += h;
+ if(!f->area->floating)
+ resize_column(f->client, &new, nil);
+ else
+ resize_client(f->client, &new, False);
+}
+
+char *
+send_client(Frame *f, char *arg)
+{
+ Area *to, *a;
+ Client *c;
+ Frame *tf;
+ View *v;
+ int j;
+
+ a = f->area;
+ v = a->view;
+ c = f->client;
+
+ if(!strncmp(arg, "toggle", 7)) {
+ if(!a->floating)
+ to = v->area;
+ else if(c->revert && !c->revert->floating)
+ to = c->revert;
+ else
+ to = v->area->next;
+ send_to_area(to, a, f);
+ }else if(!a->floating) {
+ if(!strncmp(arg, "left", 5)) {
+ if(a->floating)
+ return Ebadvalue;
+ for(to=v->area->next; to && a != to->next; to=to->next);
+ if(!to && (f->anext || f != a->frame))
+ to=new_column(v, v->area, 0);
+ if(!to)
+ return Ebadvalue;
+ send_to_area(to, a, f);
+ }
+ else if(!strncmp(arg, "right", 5)) {
+ if(a->floating)
+ return Ebadvalue;
+ if(!(to = a->next) && (f->anext || f!= a->frame))
+ to = new_column(v, a, 0);
+ if(!to)
+ return Ebadvalue;
+ send_to_area(to, a, f);
+ }
+ else if(!strncmp(arg, "up", 3)) {
+ for(tf=a->frame; tf && tf->anext != f; tf=tf->anext);
+ if(!tf)
+ return Ebadvalue;
+ remove_frame(f);
+ insert_frame(tf, f, True);
+ arrange_column(a, False);
+ focus_client(c, True);
+ }
+ else if(!strncmp(arg, "down", 5)) {
+ if(!f->anext)
+ return Ebadvalue;
+ remove_frame(f);
+ insert_frame(f->anext, f, False);
+ arrange_column(a, False);
+ focus_client(c, True);
+ }
+ else {
+ if(sscanf(arg, "%d", &j) != 1)
+ return Ebadvalue;
+ for(to=v->area; to && j; to=to->next, j--);
+ send_to_area(to, a, f);
+ }
+ }else
+ return Ebadvalue;
+ flush_masked_events(EnterWindowMask);
+ update_views();
+ if(f->view == screen->sel) {
+ focus_client(f->client, False);
+ focus_view(screen, f->view);
+ }
+ return nil;
+}
+
+/* convenience function */
+void
+focus(Client *c, Bool restack)
+{
+ View *v;
+ Frame *f;
+
+ if(!(f = c->sel)) return;
+ v = f->area->view;
+
+ arrange_column(f->area, False);
+ focus_client(c, restack);
+ focus_view(screen, v);
+}
+
+void
+update_client_views(Client *c, char **tags)
+{
+ int cmp;
+ Frame *f;
+ Frame **fp = &c->frame;
+ while(*fp || *tags) {
+ while(*fp && (!*tags || (cmp=strcmp((*fp)->view->name, *tags)) < 0)) {
+ f = *fp;
+ detach_from_area(f->area, f);
+ *fp = f->cnext;
+ free(f);
+ if(c->sel == f)
+ c->sel = *fp;
+ }
+
+ if(*tags) {
+ if(!*fp || cmp > 0) {
+ f = create_frame(c, get_view(*tags));
+ if(f->view == screen->sel || !c->sel)
+ c->sel = f;
+ attach_to_view(f->view, f);
+ f->cnext = *fp;
+ *fp = f;
+ }
+ if(*fp) fp=&(*fp)->cnext;
+ tags++;
+ }
+ }
+ focus_view(screen, screen->sel);
+}
+
+static int
+compare_tags(const void *a, const void *b) {
+ return strcmp(*(char **)a, *(char **)b);
+}
+
+void
+apply_tags(Client *c, const char *tags)
+{
+ unsigned int i, j, n;
+ int len;
+ char buf[256];
+ char *toks[32];
+
+ cext_strlcpy(buf, tags, sizeof(buf));
+ if(!(n = cext_tokenize(toks, 31, buf, '+')))
+ return;
+
+ for(i=0, j=0; i < n; i++) {
+ if(!strncmp(toks[i], "~", 2))
+ c->floating = True;
+ else if(!strncmp(toks[i], "!", 2))
+ toks[j++] = view ? screen->sel->name : "nil";
+ else if(strncmp(toks[i], "sel", 4))
+ toks[j++] = toks[i];
+ }
+
+ c->tags[0] = '\0';
+ qsort(toks, j, sizeof(char *), compare_tags);
+
+ len = sizeof(c->tags);
+ if(!j) toks[j++] = "nil";
+ for(i=0, n=0; i < j && len > 1; i++)
+ if(!n || strcmp(toks[i], toks[n-1])) {
+ if(n)
+ len -= cext_strlcat(c->tags, "+", len);
+ len -= cext_strlcat(c->tags, toks[i], len);
+ toks[n++] = toks[i];
+ }
+ toks[n] = nil;
+ update_client_views(c, toks);
+
+ XChangeProperty(blz.dpy, c->win, tags_atom, XA_STRING, 8,
+ PropModeReplace, (unsigned char *)c->tags, strlen(c->tags));
+}
+
+static void
+match_tags(Client *c, const char *prop)
+{
+ Rule *r;
+ regmatch_t tmpregm;
+
+ for(r=def.tagrules.rule; r; r=r->next)
+ if(!regexec(&r->regex, prop, 1, &tmpregm, 0))
+ if(!strlen(c->tags) || !strncmp(c->tags, "nil", 4))
+ apply_tags(c, r->value);
+}
+
+void
+apply_rules(Client *c)
+{
+ if(def.tagrules.string)
+ match_tags(c, c->props);
+
+ if(!strlen(c->tags))
+ apply_tags(c, "nil");
+}
+
+char *
+message_client(Client *c, char *message)
+{
+ if(!strncmp(message, "kill", 5)) {
+ kill_client(c);
+ return nil;
+ }
+ return Ebadcmd;
+}
diff --git a/cmd/Makefile b/cmd/Makefile
@@ -1,28 +0,0 @@
-# window manager improved 2 utilities
-# (C)opyright MMIV-MMVI Anselm R. Garbe
-
-include ../config.mk
-
-CFLAGS += -I../liblitz -I../libixp -I../libcext
-LDFLAGS += -L../libixp -lixp -L../libcext -lcext
-X11LDFLAGS += -L../liblitz -llitz -L../libcext -lcext
-
-SRC = wmiir.c
-ALLSRC = ${SRC}
-
-all: ${ALLSRC:.c=}
- @echo built wmii commands
-
-.c.o:
- @echo CC $<
- @${CC} -c ${CFLAGS} $<
-
-${SRC:.c=}: ${SRC:.c=.o}
- @echo LD $@
- @${CC} -o $@ $@.o ${LDFLAGS}
-
-# Solaris
-# @${CC} -o $* $*.o ${LDFLAGS} -lsocket
-
-clean:
- rm -f ${ALLSRC:.c=} *.o
diff --git a/cmd/wm/area.c b/cmd/wm/area.c
@@ -1,348 +0,0 @@
-/*
- * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
- * See LICENSE file for license details.
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "wm.h"
-
-Client *
-sel_client_of_area(Area *a)
-{
- return a && a->sel ? a->sel->client : nil;
-}
-
-Area *
-create_area(View *v, Area *pos, unsigned int w)
-{
- static unsigned short id = 1;
- unsigned int area_size, col_size;
- unsigned int min_width = screen->rect.width/NCOL;
- Area *a, **p = pos ? &pos->next : &v->area;
-
- for(area_size = 0, a=v->area; a; a=a->next, area_size++);
-
- col_size = area_size ? area_size - 1 : 0;
-
- if(!w) {
- if(col_size)
- w = screen->rect.width / (col_size + 1);
- else
- w = screen->rect.width;
- }
- if(w < min_width)
- w = min_width;
-
- if(col_size && col_size * min_width + w > screen->rect.width)
- return nil;
-
- if(area_size > 1)
- scale_view(v, screen->rect.width - w);
- a = cext_emallocz(sizeof(Area));
- a->view = v;
- a->id = id++;
- a->rect = screen->rect;
- a->rect.height = screen->rect.height - screen->brect.height;
- a->mode = def.colmode;
- a->rect.width = w;
- a->frame = nil;
- a->sel = nil;
-
- a->next = *p;
- *p = a;
-
- v->sel = a;
- return a;
-}
-
-void
-destroy_area(Area *a)
-{
- Client *c;
- Area *ta;
- View *v = a->view;
-
- cext_assert(!a->frame && "wmiiwm: fatal, destroying non-empty area");
-
- if(v->revert == a)
- v->revert = nil;
-
- for(c=client; c; c=c->next)
- if(c->revert == a)
- c->revert = nil;
-
- for(ta=v->area; ta && ta->next != a; ta=ta->next);
- if(ta) {
- ta->next = a->next;
- if(v->sel == a)
- v->sel = ta->floating ? ta->next : ta;
- }
- free(a);
-}
-
-static void
-place_client(Area *a, Client *c)
-{
- static unsigned int mx, my;
- static Bool *field = nil;
- Frame *fr;
- Bool fit = False;
- BlitzAlign align = CENTER;
- unsigned int i, j, x, y, maxx, maxy, dx, dy, cx, cy, diff, num = 0;
- XPoint p1 = {0, 0}, p2 = {0, 0};
- Frame *f = c->sel;
- int snap = screen->rect.height / 66;
- XRectangle *rects;
-
- if(c->trans)
- return;
- if(c->rect.width >= a->rect.width
- || c->rect.height >= a->rect.height
- || c->size.flags & USPosition
- || c->size.flags & PPosition)
- return;
-
- rects = rects_of_view(a->view, &num);
- if(!field) {
- mx = screen->rect.width / 8;
- my = screen->rect.height / 8;
- field = cext_emallocz(my * mx * sizeof(Bool));
- }
-
- for(y = 0; y < my; y++)
- for(x = 0; x < mx; x++)
- field[y*mx + x] = True;
-
- dx = screen->rect.width / mx;
- dy = screen->rect.height / my;
- for(fr=a->frame; fr; fr=fr->anext) {
- if(fr == f) {
- cx = f->rect.width / dx;
- cy = f->rect.height / dy;
- continue;
- }
- if(fr->rect.x < 0)
- x = 0;
- else
- x = fr->rect.x / dx;
- if(fr->rect.y < 0)
- y = 0;
- else
- y = fr->rect.y / dy;
- maxx = (fr->rect.x + fr->rect.width) / dx;
- maxy = (fr->rect.y + fr->rect.height) / dy;
- for(j = y; j < my && j < maxy; j++)
- for(i = x; i < mx && i < maxx; i++)
- field[j*mx + i] = False;
- }
-
- for(y = 0; y < my; y++)
- for(x = 0; x < mx; x++) {
- if(field[y*mx + x]) {
- for(i = x; (i < mx) && field[y*mx + i]; i++);
- for(j = y; (j < my) && field[j*mx + x]; j++);
- if(((i - x) * (j - y) > (p2.x - p1.x) * (p2.y - p1.y))
- && (i - x > cx) && (j - y > cy))
- {
- fit = True;
- p1.x = x;
- p1.y = y;
- p2.x = i;
- p2.y = j;
- }
- }
- }
-
- if(fit) {
- p1.x *= dx;
- p1.y *= dy;
- }
-
- if(fit && (p1.x + f->rect.width < a->rect.x + a->rect.width))
- f->rect.x = p1.x;
- else {
- diff = a->rect.width - f->rect.width;
- f->rect.x = a->rect.x + (random() % (diff ? diff : 1));
- }
-
- if(fit && (p1.y + f->rect.height < a->rect.y + a->rect.height))
- f->rect.y = p1.y;
- else {
- diff = a->rect.height - f->rect.height;
- f->rect.y = a->rect.y + (random() % (diff ? diff : 1));
- }
-
- snap_rect(rects, num, &f->rect, &align, snap);
- if(rects)
- free(rects);
-}
-
-void
-send_to_area(Area *to, Area *from, Frame *f)
-{
- cext_assert(to->view == f->view);
-
- if(to->floating != from->floating) {
- XRectangle temp = f->revert;
- f->revert = f->rect;
- f->rect = temp;
- }
- f->client->revert = from;
- detach_from_area(from, f);
- attach_to_area(to, f, True);
- focus_client(f->client, True);
-}
-
-void
-attach_to_area(Area *a, Frame *f, Bool send)
-{
- unsigned int h, n_frame;
- Frame **fa, *ft;
- View *v = a->view;
- Client *c = f->client;
-
- for(ft=a->frame, n_frame=1; ft; ft=ft->anext, n_frame++);
-
- h = 0;
- c->floating = a->floating;
- if(!c->floating) {
- h = a->rect.height / n_frame;
- if(a->frame)
- scale_column(a, a->rect.height - h);
- }
-
- if(!send && !c->floating) { /* column */
- unsigned int w = newcolw_of_view(v);
- if(v->area->next->frame && w) {
- a = new_column(v, a, w);
- arrange_view(v);
- }
- }
-
- fa = a->sel ? &a->sel->anext : &a->frame;
- f->anext = *fa;
- *fa = f;
-
- f->area = a;
- a->sel = f;
-
- if(!c->floating) { /* column */
- f->rect.height = h;
- arrange_column(a, False);
- }else /* floating */
- place_client(a, c);
-}
-
-void
-detach_from_area(Area *a, Frame *f)
-{
- Frame **ft, *pr = nil;
- Client *c = f->client;
- View *v = a->view;
-
- for(ft=&a->frame; *ft; ft=&(*ft)->anext) {
- if(*ft == f) break;
- pr = *ft;
- }
- cext_assert(*ft == f);
- *ft = f->anext;
-
- if(a->sel == f)
- a->sel = pr ? pr : *ft;
-
- if(!a->floating) {
- if(a->frame)
- arrange_column(a, False);
- else {
- if(v->area->next->next)
- destroy_area(a);
- else if(!a->frame && v->area->frame)
- v->sel = v->area; /* focus floating area if it contains something */
- arrange_view(v);
- }
- }
- else if(!a->frame) {
- if(c->trans) {
- /* focus area of transient, if possible */
- Client *cl = client_of_win(c->trans);
- if(cl && cl->frame) {
- a = cl->sel->area;
- if(a->view == v)
- v->sel = a;
- }
- }
- else if(v->area->next->frame)
- v->sel = v->area->next; /* focus first col as fallback */
- }
-}
-
-char *
-select_area(Area *a, char *arg)
-{
- Area *new;
- unsigned int i;
- Frame *p, *f;
- View *v;
- static char Ebadvalue[] = "bad value";
-
- v = a->view;
- f = a->sel;
-
- if(!strncmp(arg, "toggle", 7)) {
- if(a != v->area)
- new = v->area;
- else if(v->revert)
- new = v->revert;
- else
- new = v->area->next;
- } else if(!strncmp(arg, "left", 5)) {
- if(a->floating)
- return Ebadvalue;
- for(new=v->area->next;
- new && new->next != a;
- new=new->next);
- if(!new)
- new=v->area->next;
- } else if(!strncmp(arg, "right", 5)) {
- if(a->floating)
- return Ebadvalue;
- new = a->next ? a->next : a;
- }
- else if(!strncmp(arg, "up", 3)) {
- if(!f)
- return Ebadvalue;
- for(p=a->frame; p->anext; p=p->anext)
- if(p->anext == f) break;
- a->sel = p;
- arrange_column(a, False);
- if(v == screen->sel)
- focus_view(screen, v);
- flush_masked_events(EnterWindowMask);
- return nil;
- }
- else if(!strncmp(arg, "down", 5)) {
- if(!f)
- return Ebadvalue;
- p = f->anext ? f->anext : a->frame;
- a->sel = p;
- arrange_column(a, False);
- if(v == screen->sel)
- focus_view(screen, v);
- flush_masked_events(EnterWindowMask);
- return nil;
- }
- else {
- if(sscanf(arg, "%d", &i) != 1)
- return Ebadvalue;
- for(new=view->area; i && new->next; new=new->next, i--);
- }
- if(new->sel)
- focus_client(new->sel->client, True);
- v->sel = new;
- if(a->floating != new->floating)
- v->revert = a;
- return nil;
-}
diff --git a/cmd/wm/bar.c b/cmd/wm/bar.c
@@ -1,147 +0,0 @@
-/*
- * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
- * See LICENSE file for license details.
- */
-
-#include <math.h>
-#include <string.h>
-#include <stdlib.h>
-
-#include "wm.h"
-
-Bar *free_bars = nil;
-
-Bar *
-create_bar(Bar **b_link, char *name)
-{
- static unsigned int id = 1;
- Bar **i, *b = bar_of_name(*b_link, name);;
- if(b)
- return b;
-
- if(free_bars) {
- b = free_bars;
- free_bars = b->next;
- memset(b, 0, sizeof(*b));
- }
- else
- b = cext_emallocz(sizeof(Bar));
-
- b->id = id++;
- cext_strlcpy(b->name, name, sizeof(b->name));
- b->brush = screen->bbrush;
- b->brush.color = def.normcolor;
-
- for(i=b_link; *i; i=&(*i)->next)
- if(strcmp((*i)->name, name) >= 0)
- break;
- b->next = *i;
- *i = b;
-
- return b;
-}
-
-void
-destroy_bar(Bar **b_link, Bar *b)
-{
- Bar **p;
- for(p=b_link; *p && *p != b; p=&(*p)->next);
- *p = b->next;
-
- b->next = free_bars;
- free_bars = b;
-}
-
-void
-resize_bar(WMScreen *s)
-{
- View *v;
-
- s->brect = s->rect;
- s->brect.height = blitz_labelh(&def.font);
- s->brect.y = s->rect.height - s->brect.height;
- XMoveResizeWindow(blz.dpy, s->barwin, s->brect.x, s->brect.y, s->brect.width, s->brect.height);
- XSync(blz.dpy, False);
- draw_bar(s);
-
- for(v=view; v; v=v->next)
- arrange_view(v);
-}
-
-void
-draw_bar(WMScreen *s)
-{
- unsigned int width, tw, nb, size;
- float shrink;
- Bar *b, *tb, *largest, **pb;
-
- blitz_draw_tile(&s->bbrush);
-
- if(!s->lbar && !s->rbar)
- goto MapBar;
-
- largest = b = tb = nil;
- tw = width = nb = size = 0;
-
- for(b=s->lbar, nb=2 ;nb; --nb && (b = s->rbar))
- for(; b; b=b->next) {
- b->brush.rect.x = b->brush.rect.y = 0;
- b->brush.rect.width = def.font.height;
- if(b->text && strlen(b->text))
- b->brush.rect.width += blitz_textwidth(b->brush.font, b->text);
- b->brush.rect.height = s->brect.height;
- width += b->brush.rect.width;
- }
-
- /* Not enough room. Shrink bars until they all fit */
- if(width > s->brect.width) {
- for(b=s->lbar, nb=2 ;nb; --nb && (b = s->rbar))
- for(; b; b=b->next) {
- for(pb=&largest; *pb; pb=&(*pb)->smaller)
- if((*pb)->brush.rect.width < b->brush.rect.width) break;
- b->smaller = *pb;
- *pb = b;
- }
- for(tb=largest; tb; tb=tb->smaller) {
- width -= tb->brush.rect.width;
- tw += tb->brush.rect.width;
- shrink = (s->brect.width - width) / (float)tw;
- if(tb->smaller)
- if(tb->brush.rect.width * shrink >= tb->smaller->brush.rect.width)
- break;
- }
- if(tb)
- for(b=largest; b != tb->smaller; b=b->smaller)
- b->brush.rect.width = floor(b->brush.rect.width * shrink);
- width += tw * shrink;
- tb=nil;
- }
-
- for(b=s->lbar, nb=2 ;nb; b=s->rbar, nb--)
- for(; b; tb = b, b=b->next) {
- if(b == s->rbar) {
- b->brush.align = EAST;
- s->rbar->brush.rect.width += (s->brect.width - width);
- }else
- b->brush.align = CENTER;
- if(tb)
- b->brush.rect.x = tb->brush.rect.x + tb->brush.rect.width;
- blitz_draw_label(&b->brush, b->text);
- }
-MapBar:
- XCopyArea(blz.dpy, s->bbrush.drawable, s->barwin, s->bbrush.gc, 0, 0,
- s->brect.width, s->brect.height, 0, 0);
- XSync(blz.dpy, False);
-}
-
-Bar *
-bar_of_name(Bar *b_link, const char *name)
-{
- static char buf[256];
- Bar *b;
-
- cext_strlcpy(buf, name, sizeof(buf));
- for(b=b_link; b; b=b->next)
- if(!strncmp(b->name, name, sizeof(b->name))) break;
- return b;
-}
diff --git a/cmd/wm/client.c b/cmd/wm/client.c
@@ -1,823 +0,0 @@
-/*
- * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
- * See LICENSE file for license details.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <X11/Xatom.h>
-
-#include "wm.h"
-
-static char *Ebadcmd = "bad command",
- *Ebadvalue = "bad value";
-
-#define CLIENT_MASK (StructureNotifyMask | PropertyChangeMask | EnterWindowMask)
-
-Client *
-sel_client()
-{
- return screen->sel && screen->sel->sel->sel ? screen->sel->sel->sel->client : nil;
-}
-
-int
-idx_of_client(Client *c)
-{
- Client *cl;
- int i = 0;
- for(cl=client; cl && cl != c; cl=cl->next, i++);
- return cl ? i : -1;
-}
-
-Client *
-client_of_win(Window w)
-{
- Client *c;
- for(c=client; c && c->win != w; c=c->next);
- return c;
-}
-
-Frame *
-frame_of_win(Window w)
-{
- Client *c;
- for(c=client; c && c->framewin != w; c=c->next);
- return c ? c->frame : nil;
-}
-
-static void
-update_client_name(Client *c)
-{
- XTextProperty name;
- XClassHint ch;
- int n;
- char **list = nil;
-
- name.nitems = 0;
- c->name[0] = 0;
- XGetTextProperty(blz.dpy, c->win, &name, net_atom[NetWMName]);
- if(!name.nitems)
- XGetWMName(blz.dpy, c->win, &name);
- if(!name.nitems)
- return;
- if(name.encoding == XA_STRING)
- cext_strlcpy(c->name, (char *)name.value, sizeof(c->name));
- else {
- if(XmbTextPropertyToTextList(blz.dpy, &name, &list, &n) >= Success
- && n > 0 && *list)
- {
- cext_strlcpy(c->name, *list, sizeof(c->name));
- XFreeStringList(list);
- }
- }
- XFree(name.value);
- if(XGetClassHint(blz.dpy, c->win, &ch)) {
- snprintf(c->props, sizeof(c->props), "%s:%s:%s",
- ch.res_class ? ch.res_class : "",
- ch.res_name ? ch.res_name : "",
- c->name);
- if(ch.res_class)
- XFree(ch.res_class);
- if(ch.res_name)
- XFree(ch.res_name);
- }
-}
-
-Client *
-create_client(Window w, XWindowAttributes *wa)
-{
- Client **t, *c = (Client *) cext_emallocz(sizeof(Client));
- XSetWindowAttributes fwa;
- long msize;
- unsigned int i;
- static unsigned int id = 1;
-
- c->id = id++;
- c->win = w;
- c->rect.x = wa->x;
- c->rect.y = wa->y;
- c->border = wa->border_width;
- c->rect.width = wa->width;
- c->rect.height = wa->height;
- XSetWindowBorderWidth(blz.dpy, c->win, 0);
- c->proto = win_proto(c->win);
- XGetTransientForHint(blz.dpy, c->win, &c->trans);
- if(!XGetWMNormalHints(blz.dpy, c->win, &c->size, &msize) || !c->size.flags)
- c->size.flags = PSize;
- if(c->size.flags & PMinSize && c->size.flags & PMaxSize
- && c->size.min_width == c->size.max_width
- && c->size.min_height == c->size.max_height)
- c->fixedsize = True;
- else
- c->fixedsize = False;
- XAddToSaveSet(blz.dpy, c->win);
- update_client_name(c);
- fwa.override_redirect = 1;
- fwa.background_pixmap = ParentRelative;
- fwa.event_mask =
- SubstructureRedirectMask | SubstructureNotifyMask | ExposureMask
- | ButtonPressMask | PointerMotionMask | ButtonReleaseMask | KeyPressMask;
-
- c->framewin = XCreateWindow(blz.dpy, blz.root, c->rect.x, c->rect.y,
- c->rect.width + 2 * def.border,
- c->rect.height + def.border + blitz_labelh(&def.font), 0,
- DefaultDepth(blz.dpy, blz.screen), CopyFromParent,
- DefaultVisual(blz.dpy, blz.screen),
- CWOverrideRedirect | CWBackPixmap | CWEventMask, &fwa);
- c->gc = XCreateGC(blz.dpy, c->framewin, 0, 0);
- XSync(blz.dpy, False);
-
- for(t=&client, i=0; *t; t=&(*t)->next, i++);
- c->next = *t; /* *t == nil */
- *t = c;
-
- write_event("CreateClient %d\n", i);
- return c;
-}
-
-void
-update_client_grab(Client *c, Bool is_sel)
-{
- if(is_sel) {
- ungrab_mouse(c->framewin, AnyModifier, AnyButton);
- grab_mouse(c->framewin, def.mod, Button1);
- grab_mouse(c->framewin, def.mod, Button3);
- }
- else
- grab_mouse(c->framewin, AnyModifier, Button1);
-}
-
-void
-focus_client(Client *c, Bool restack)
-{
- Client *old_in_area;
- Client *old;
- Frame *f;
- View *v;
-
- if(!sel_screen)
- return;
-
- f = c->sel;
- v = f->area->view;
- old = sel_client();
- old_in_area = sel_client_of_area(f->area);
-
- v->sel = f->area;
- f->area->sel = f;
- c->floating = f->area->floating;
-
- if(restack)
- restack_view(v);
- else {
- if(old)
- update_client_grab(old, False);
- update_client_grab(c, True);
- }
-
- if(!c->floating && f->area->mode == Colstack)
- arrange_column(f->area, False);
- XSetInputFocus(blz.dpy, c->win, RevertToPointerRoot, CurrentTime);
- if(old && old != old_in_area && old != c) {
- update_frame_widget_colors(old->sel);
- draw_frame(old->sel);
- }
- if(old_in_area && old_in_area != c) {
- update_frame_widget_colors(old_in_area->sel);
- draw_frame(old_in_area->sel);
- }
- update_frame_widget_colors(c->sel);
- draw_frame(c->sel);
- XSync(blz.dpy, False);
- write_event("ClientFocus %d\n", idx_of_client(c));
-}
-
-void
-map_client(Client *c)
-{
- XSelectInput(blz.dpy, c->win, CLIENT_MASK & ~StructureNotifyMask);
- XMapWindow(blz.dpy, c->win);
- XSelectInput(blz.dpy, c->win, CLIENT_MASK);
-}
-
-void
-unmap_client(Client *c)
-{
- XSelectInput(blz.dpy, c->win, CLIENT_MASK & ~StructureNotifyMask);
- XUnmapWindow(blz.dpy, c->win);
- XSelectInput(blz.dpy, c->win, CLIENT_MASK);
-}
-
-void
-reparent_client(Client *c, Window w, int x, int y)
-{
- XSelectInput(blz.dpy, c->win, CLIENT_MASK & ~StructureNotifyMask);
- XReparentWindow(blz.dpy, c->win, w, x, y);
- XSelectInput(blz.dpy, c->win, CLIENT_MASK);
-}
-
-void
-configure_client(Client *c)
-{
- XConfigureEvent e;
- Frame *f = c->sel;
- e.type = ConfigureNotify;
- e.event = c->win;
- e.window = c->win;
- e.x = c->rect.x;
- e.y = c->rect.y;
- if(f) {
- e.x += f->rect.x;
- e.y += f->rect.y;
- }
- e.width = c->rect.width;
- e.height = c->rect.height;
- e.border_width = c->border;
- e.above = None;
- e.override_redirect = False;
- XSendEvent(blz.dpy, c->win, False,
- StructureNotifyMask, (XEvent *) & e);
- XSync(blz.dpy, False);
-}
-
-static void
-send_client_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(blz.dpy, w, False, NoEventMask, &e);
- XSync(blz.dpy, False);
-}
-
-void
-kill_client(Client * c)
-{
- if(c->proto & WM_PROTOCOL_DELWIN)
- send_client_message(c->win, wm_atom[WMProtocols], wm_atom[WMDelete]);
- else
- XKillClient(blz.dpy, c->win);
-}
-
-void
-prop_client(Client *c, XPropertyEvent *e)
-{
- long msize;
-
- if(e->atom == wm_atom[WMProtocols]) {
- c->proto = win_proto(c->win);
- return;
- }
- switch (e->atom) {
- case XA_WM_TRANSIENT_FOR:
- XGetTransientForHint(blz.dpy, c->win, &c->trans);
- break;
- case XA_WM_NORMAL_HINTS:
- if(!XGetWMNormalHints(blz.dpy, c->win, &c->size, &msize) || !c->size.flags) {
- c->size.flags = PSize;
- }
- if(c->size.flags & PMinSize && c->size.flags & PMaxSize
- && c->size.min_width == c->size.max_width
- && c->size.min_height == c->size.max_height)
- c->fixedsize = True;
- else
- c->fixedsize = False;
- break;
- }
- if(e->atom == XA_WM_NAME || e->atom == net_atom[NetWMName]) {
- update_client_name(c);
- if(c->frame)
- draw_frame(c->sel);
- }
-}
-
-void
-gravitate_client(Client *c, Bool invert)
-{
- int dx = 0, dy = 0;
- int gravity = NorthWestGravity;
-
- if(c->size.flags & PWinGravity) {
- gravity = c->size.win_gravity;
- }
-
- /* y */
- switch (gravity) {
- case StaticGravity:
- case NorthWestGravity:
- case NorthGravity:
- case NorthEastGravity:
- dy = blitz_labelh(&def.font);
- break;
- case EastGravity:
- case CenterGravity:
- case WestGravity:
- dy = -(c->rect.height / 2) + blitz_labelh(&def.font);
- break;
- case SouthEastGravity:
- case SouthGravity:
- case SouthWestGravity:
- dy = -c->rect.height;
- break;
- default:
- break;
- }
-
- /* x */
- switch (gravity) {
- case StaticGravity:
- case NorthWestGravity:
- case WestGravity:
- case SouthWestGravity:
- dx = def.border;
- break;
- case NorthGravity:
- case CenterGravity:
- case SouthGravity:
- dx = -(c->rect.width / 2) + def.border;
- break;
- case NorthEastGravity:
- case EastGravity:
- case SouthEastGravity:
- dx = -(c->rect.width + def.border);
- break;
- default:
- break;
- }
-
- if(invert) {
- dx = -dx;
- dy = -dy;
- }
- c->rect.x += dx;
- c->rect.y += dy;
-}
-
-void
-manage_client(Client *c)
-{
- XTextProperty tags;
- Client *trans;
-
- tags.nitems = 0;
- XGetTextProperty(blz.dpy, c->win, &tags, tags_atom);
-
- if(c->trans && (trans = client_of_win(c->trans)))
- cext_strlcpy(c->tags, trans->tags, sizeof(c->tags));
- else if(tags.nitems)
- cext_strlcpy(c->tags, (char *)tags.value, sizeof(c->tags));
- XFree(tags.value);
-
- if(!strlen(c->tags))
- apply_rules(c);
-
- apply_tags(c, c->tags);
-
- reparent_client(c, c->framewin, c->rect.x, c->rect.y);
-
- if(!starting)
- update_views();
- map_client(c);
- XMapWindow(blz.dpy, c->framewin);
- XSync(blz.dpy, False);
- if(c->sel->area->view == screen->sel)
- focus_client(c, False);
- flush_masked_events(EnterWindowMask);
-}
-
-static int
-dummy_error_handler(Display *dpy, XErrorEvent *error)
-{
- return 0;
-}
-
-void
-destroy_client(Client *c)
-{
- char *dummy = nil;
- Client **tc;
-
- XGrabServer(blz.dpy);
- XSetErrorHandler(dummy_error_handler);
-
- if(c->frame) {
- c->rect.x = c->sel->rect.x;
- c->rect.y = c->sel->rect.y;
- }
-
- update_client_views(c, &dummy);
-
- unmap_client(c);
-
- reparent_client(c, blz.root, c->rect.x, c->rect.y);
- XFreeGC(blz.dpy, c->gc);
- XDestroyWindow(blz.dpy, c->framewin);
-
- for(tc=&client; *tc && *tc != c; tc=&(*tc)->next);
- cext_assert(*tc == c);
- *tc = c->next;
-
- update_views();
- free(c);
-
- XSync(blz.dpy, False);
- XSetErrorHandler(wmii_error_handler);
- XUngrabServer(blz.dpy);
- flush_masked_events(EnterWindowMask);
-}
-
-void
-match_sizehints(Client *c, XRectangle *r, Bool floating, BlitzAlign sticky)
-{
- XSizeHints *s = &c->size;
- unsigned int dx = 2 * def.border;
- unsigned int dy = def.border + blitz_labelh(&def.font);
- unsigned int hdiff, wdiff;
-
- if(floating && (s->flags & PMinSize)) {
- if(r->width < s->min_width + dx) {
- wdiff = (s->min_width + dx) - r->width;
- r->width += wdiff;
- if((sticky & EAST) && !(sticky & WEST))
- r->x -= wdiff;
- }
- if(r->height < s->min_height + dy) {
- hdiff = (s->min_height + dy) - r->height;
- r->height += hdiff;
- if((sticky & SOUTH) && !(sticky & NORTH))
- r->y -= hdiff;
- }
- }
- if(floating && (s->flags & PMaxSize)) {
- if(r->width > s->max_width + dx) {
- wdiff = r->width - (s->max_width + dx);
- r->width -= wdiff;
- if((sticky & EAST) && !(sticky & WEST))
- r->x += wdiff;
- }
- if(r->height > s->max_height + dy) {
- hdiff = r->height - (s->max_height + dy);
- r->height -= hdiff;
- if((sticky & SOUTH) && !(sticky & NORTH))
- r->y += hdiff;
- }
- }
-
- if(s->flags & PResizeInc) {
- int w = 0, h = 0;
-
- if(s->flags & PBaseSize) {
- w = s->base_width;
- h = s->base_height;
- } else if(s->flags & PMinSize) {
- /* base_{width,height} default to min_{width,height} */
- w = s->min_width;
- h = s->min_height;
- }
- /* client_width = base_width + i * s->width_inc for an integer i */
- w = r->width - dx - w;
- if(s->width_inc > 0) {
- wdiff = w % s->width_inc;
- r->width -= wdiff;
- if((sticky & EAST) && !(sticky & WEST))
- r->x += wdiff;
- }
-
- h = r->height - dy - h;
- if(s->height_inc > 0) {
- hdiff = h % s->height_inc;
- r->height -= hdiff;
- if((sticky & SOUTH) && !(sticky & NORTH))
- r->y += hdiff;
- }
- }
-}
-
-void
-resize_client(Client *c, XRectangle *r, Bool ignore_xcall)
-{
- Frame *f = c->sel;
- Bool floating = f->area->floating;
- unsigned int max_height;
-
- BlitzAlign stickycorner = 0;
- if(f->rect.x != r->x && f->rect.x + f->rect.width == r->x + r->width)
- stickycorner |= EAST;
- else
- stickycorner |= WEST;
- if(f->rect.y != r->y && f->rect.y + f->rect.height == r->y + r->height)
- stickycorner |= SOUTH;
- else
- stickycorner |= NORTH;
-
- f->rect = *r;
-
- if((f->area->mode != Colstack) || (f->area->sel == f))
- match_sizehints(c, &c->sel->rect, floating, stickycorner);
-
- max_height = screen->rect.height - blitz_labelh(&def.font);
- if(!ignore_xcall) {
- if(floating) {
- if((c->rect.width == screen->rect.width) &&
- (c->rect.height == screen->rect.height)) {
- f->rect.x = -def.border;
- f->rect.y = -blitz_labelh(&def.font);
- }else{
- if(f->rect.height > max_height)
- f->rect.height = max_height;
- if(f->rect.width > screen->rect.width)
- f->rect.width = screen->rect.width;
- if(f->rect.x + f->rect.width > screen->rect.width)
- f->rect.x = screen->rect.width - f->rect.width;
- if(f->rect.y + f->rect.height > max_height)
- f->rect.y = max_height - f->rect.height;
- if(f->rect.x < 0)
- f->rect.x = 0;
- if(f->rect.y < 0)
- f->rect.y = 0;
- }
- }
- if(f->area->view == screen->sel)
- XMoveResizeWindow(blz.dpy, c->framewin, f->rect.x,
- f->rect.y, f->rect.width, f->rect.height);
- else
- XMoveResizeWindow(blz.dpy, c->framewin, 2 * screen->rect.width + f->rect.x,
- f->rect.y, f->rect.width, f->rect.height);
- }
-
- c->rect.x = def.border;
- c->rect.y = blitz_labelh(&def.font);
- if((f->area->sel == f) || (f->area->mode != Colstack)) {
- c->rect.width = f->rect.width - 2 * def.border;
- c->rect.height = f->rect.height - def.border - blitz_labelh(&def.font);
- }
- if(!ignore_xcall) {
- XMoveResizeWindow(blz.dpy, c->win, c->rect.x, c->rect.y,
- c->rect.width, c->rect.height);
- configure_client(c);
- }
-}
-
-void
-newcol_client(Client *c, char *arg)
-{
- Frame *f = c->sel;
- Area *to, *a = f->area;
- View *v = a->view;
-
- if(a->floating)
- return;
- if(!f->anext && f == a->frame)
- return;
-
- if(!strncmp(arg, "prev", 5)) {
- for(to=v->area; to && to->next != a; to=to->next);
- to = new_column(v, to, 0);
- send_to_area(to, a, f);
- }
- else if(!strncmp(arg, "next", 5)) {
- to = new_column(v, a, 0);
- send_to_area(to, a, f);
- }
- else
- return;
- flush_masked_events(EnterWindowMask);
-}
-
-void
-move_client(Client *c, char *arg)
-{
- Frame *f = c->sel;
- XRectangle new = f->rect;
- int x, y;
-
- if(sscanf(arg, "%d %d", &x, &y) != 2)
- return;
- new.x += x;
- new.y += y;
- if(!f->area->floating)
- resize_column(f->client, &new, nil);
- else
- resize_client(f->client, &new, False);
-}
-
-void
-size_client(Client *c, char *arg)
-{
- Frame *f = c->sel;
- XRectangle new = f->rect;
- int w, h;
-
- if(sscanf(arg, "%d %d", &w, &h) != 2)
- return;
- new.width += w;
- new.height += h;
- if(!f->area->floating)
- resize_column(f->client, &new, nil);
- else
- resize_client(f->client, &new, False);
-}
-
-char *
-send_client(Frame *f, char *arg)
-{
- Area *to, *a;
- Client *c;
- Frame *tf;
- View *v;
- int j;
-
- a = f->area;
- v = a->view;
- c = f->client;
-
- if(!strncmp(arg, "toggle", 7)) {
- if(!a->floating)
- to = v->area;
- else if(c->revert && !c->revert->floating)
- to = c->revert;
- else
- to = v->area->next;
- send_to_area(to, a, f);
- }else if(!a->floating) {
- if(!strncmp(arg, "left", 5)) {
- if(a->floating)
- return Ebadvalue;
- for(to=v->area->next; to && a != to->next; to=to->next);
- if(!to && (f->anext || f != a->frame))
- to=new_column(v, v->area, 0);
- if(!to)
- return Ebadvalue;
- send_to_area(to, a, f);
- }
- else if(!strncmp(arg, "right", 5)) {
- if(a->floating)
- return Ebadvalue;
- if(!(to = a->next) && (f->anext || f!= a->frame))
- to = new_column(v, a, 0);
- if(!to)
- return Ebadvalue;
- send_to_area(to, a, f);
- }
- else if(!strncmp(arg, "up", 3)) {
- for(tf=a->frame; tf && tf->anext != f; tf=tf->anext);
- if(!tf)
- return Ebadvalue;
- remove_frame(f);
- insert_frame(tf, f, True);
- arrange_column(a, False);
- focus_client(c, True);
- }
- else if(!strncmp(arg, "down", 5)) {
- if(!f->anext)
- return Ebadvalue;
- remove_frame(f);
- insert_frame(f->anext, f, False);
- arrange_column(a, False);
- focus_client(c, True);
- }
- else {
- if(sscanf(arg, "%d", &j) != 1)
- return Ebadvalue;
- for(to=v->area; to && j; to=to->next, j--);
- send_to_area(to, a, f);
- }
- }else
- return Ebadvalue;
- flush_masked_events(EnterWindowMask);
- update_views();
- if(f->view == screen->sel) {
- focus_client(f->client, False);
- focus_view(screen, f->view);
- }
- return nil;
-}
-
-/* convenience function */
-void
-focus(Client *c, Bool restack)
-{
- View *v;
- Frame *f;
-
- if(!(f = c->sel)) return;
- v = f->area->view;
-
- arrange_column(f->area, False);
- focus_client(c, restack);
- focus_view(screen, v);
-}
-
-void
-update_client_views(Client *c, char **tags)
-{
- int cmp;
- Frame *f;
- Frame **fp = &c->frame;
- while(*fp || *tags) {
- while(*fp && (!*tags || (cmp=strcmp((*fp)->view->name, *tags)) < 0)) {
- f = *fp;
- detach_from_area(f->area, f);
- *fp = f->cnext;
- free(f);
- if(c->sel == f)
- c->sel = *fp;
- }
-
- if(*tags) {
- if(!*fp || cmp > 0) {
- f = create_frame(c, get_view(*tags));
- if(f->view == screen->sel || !c->sel)
- c->sel = f;
- attach_to_view(f->view, f);
- f->cnext = *fp;
- *fp = f;
- }
- if(*fp) fp=&(*fp)->cnext;
- tags++;
- }
- }
- focus_view(screen, screen->sel);
-}
-
-static int
-compare_tags(const void *a, const void *b) {
- return strcmp(*(char **)a, *(char **)b);
-}
-
-void
-apply_tags(Client *c, const char *tags)
-{
- unsigned int i, j, n;
- int len;
- char buf[256];
- char *toks[32];
-
- cext_strlcpy(buf, tags, sizeof(buf));
- if(!(n = cext_tokenize(toks, 31, buf, '+')))
- return;
-
- for(i=0, j=0; i < n; i++) {
- if(!strncmp(toks[i], "~", 2))
- c->floating = True;
- else if(!strncmp(toks[i], "!", 2))
- toks[j++] = view ? screen->sel->name : "nil";
- else if(strncmp(toks[i], "sel", 4))
- toks[j++] = toks[i];
- }
-
- c->tags[0] = '\0';
- qsort(toks, j, sizeof(char *), compare_tags);
-
- len = sizeof(c->tags);
- if(!j) toks[j++] = "nil";
- for(i=0, n=0; i < j && len > 1; i++)
- if(!n || strcmp(toks[i], toks[n-1])) {
- if(n)
- len -= cext_strlcat(c->tags, "+", len);
- len -= cext_strlcat(c->tags, toks[i], len);
- toks[n++] = toks[i];
- }
- toks[n] = nil;
- update_client_views(c, toks);
-
- XChangeProperty(blz.dpy, c->win, tags_atom, XA_STRING, 8,
- PropModeReplace, (unsigned char *)c->tags, strlen(c->tags));
-}
-
-static void
-match_tags(Client *c, const char *prop)
-{
- Rule *r;
- regmatch_t tmpregm;
-
- for(r=def.tagrules.rule; r; r=r->next)
- if(!regexec(&r->regex, prop, 1, &tmpregm, 0))
- if(!strlen(c->tags) || !strncmp(c->tags, "nil", 4))
- apply_tags(c, r->value);
-}
-
-void
-apply_rules(Client *c)
-{
- if(def.tagrules.string)
- match_tags(c, c->props);
-
- if(!strlen(c->tags))
- apply_tags(c, "nil");
-}
-
-char *
-message_client(Client *c, char *message)
-{
- if(!strncmp(message, "kill", 5)) {
- kill_client(c);
- return nil;
- }
- return Ebadcmd;
-}
diff --git a/cmd/wm/column.c b/cmd/wm/column.c
@@ -1,429 +0,0 @@
-/*
- * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
- * See LICENSE file for license details.
- */
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "wm.h"
-
-char *
-str_of_column_mode(int mode)
-{
- switch(mode) {
- case Coldefault: return "default"; break;
- case Colstack: return "stack"; break;
- case Colmax: return "max"; break;
- default: break;
- }
- return nil;
-}
-
-int
-column_mode_of_str(char *arg)
-{
- if(!strncmp("default", arg, 8))
- return Coldefault;
- if(!strncmp("stack", arg, 6))
- return Colstack;
- if(!strncmp("max", arg, 4))
- return Colmax;
- return -1;
-}
-
-static void
-relax_column(Area *a)
-{
- unsigned int frame_size, yoff, h;
- Frame *f;
- int hdiff;
- Bool fallthrough = False;
-
- if(!a->frame)
- return;
-
- frame_size = 0;
- for(f=a->frame; f; f=f->anext)
- frame_size++;
-
- switch(a->mode) {
- case Coldefault:
- h = a->rect.height / frame_size;
- if(h < 2 * blitz_labelh(&def.font))
- fallthrough = True;
- break;
- case Colstack:
- h = a->rect.height - (frame_size - 1) * blitz_labelh(&def.font);
- if(h < 3 * blitz_labelh(&def.font))
- fallthrough = True;
- default:
- yoff = a->rect.y;
- break;
- }
-
- if(fallthrough) {
- for(f=a->frame; f; f=f->anext) {
- f->rect.x = a->rect.x + (a->rect.width - f->rect.width) / 2;
- f->rect.y = a->rect.y + (a->rect.height - f->rect.height) / 2;
- //resize_client(f->client, &f->rect, True);
- }
- return;
- }
-
- /* some relaxing from potential increment gaps */
- h = 0;
- for(f=a->frame; f; f=f->anext) {
- if(a->mode == Colmax) {
- if(h < f->rect.height)
- h = f->rect.height;
- }
- else
- h += f->rect.height;
- }
-
- hdiff = a->rect.height - h;
- if((a->mode == Coldefault) && (hdiff > 0)) {
- int hx;
- for(hx = 1; hx < hdiff; hx++)
- for(f=a->frame; f && (hx < hdiff); f=f->anext) {
- unsigned int tmp = f->rect.height;
- f->rect.height += hx;
- //resize_client(f->client, &f->rect, True);
- hdiff -= (f->rect.height - tmp);
- }
- }
-
- if(hdiff < 0)
- hdiff = 0;
- hdiff /= frame_size;
- yoff = a->rect.y + hdiff / 2;
- for(f=a->frame; f; f=f->anext) {
- f->rect.y = yoff;
- if(a->mode != Colmax || f == a->sel) {
- f->rect.x = a->rect.x + (a->rect.width - f->rect.width) / 2;
- yoff = f->rect.y + f->rect.height + hdiff;
- }
- //resize_client(f->client, &f->rect, True);
- }
-}
-
-void
-scale_column(Area *a, float h)
-{
- unsigned int yoff, frame_size = 0;
- Frame *f;
- unsigned int min_height = 2 * blitz_labelh(&def.font);
- float scale, dy = 0;
- int hdiff;
-
- if(!a->frame)
- return;
-
- for(f=a->frame; f; f=f->anext, frame_size++)
- dy += f->rect.height;
-
- scale = h / dy;
- yoff = 0;
- for(f=a->frame; f; f=f->anext) {
- f->rect.height *= scale;
- if(!f->anext)
- f->rect.height = h - yoff;
- yoff += f->rect.height;
- }
-
- /* min_height can only be respected when there is enough space; the caller should guarantee this */
- if(frame_size * min_height > h)
- return;
- yoff = 0;
- for(f=a->frame, frame_size--; f; f=f->anext, frame_size--) {
- if(f->rect.height < min_height)
- f->rect.height = min_height;
- else if((hdiff = yoff + f->rect.height - h + frame_size * min_height) > 0)
- f->rect.height -= hdiff;
- if(!f->anext)
- f->rect.height = h - yoff;
- yoff += f->rect.height;
- }
-}
-
-void
-arrange_column(Area *a, Bool dirty)
-{
- Frame *f;
- unsigned int num_frames = 0, yoff = a->rect.y, h;
- unsigned int min_height = 2 * blitz_labelh(&def.font);
-
- if(a->floating || !a->frame)
- return;
-
- for(f=a->frame; f; f=f->anext)
- num_frames++;
-
- switch(a->mode) {
- case Coldefault:
- h = a->rect.height / num_frames;
- if(h < min_height)
- goto Fallthrough;
- if(dirty) {
- for(f=a->frame; f; f=f->anext)
- f->rect.height = h;
- }
- scale_column(a, a->rect.height);
- for(f=a->frame; f; f=f->anext) {
- f->rect.x = a->rect.x;
- f->rect.y = yoff;
- f->rect.width = a->rect.width;
- yoff += f->rect.height;
- //resize_client(f->client, &f->rect, True);
- }
- break;
- case Colstack:
- h = a->rect.height - (num_frames - 1) * blitz_labelh(&def.font);
- if(h < 3 * blitz_labelh(&def.font))
- goto Fallthrough;
- for(f=a->frame; f; f=f->anext) {
- f->rect = a->rect;
- f->rect.y = yoff;
- if(f == a->sel)
- f->rect.height = h;
- else
- f->rect.height = blitz_labelh(&def.font);
- yoff += f->rect.height;
- //resize_client(f->client, &f->rect, True);
- }
- break;
-Fallthrough:
- case Colmax:
- for(f=a->frame; f; f=f->anext) {
- f->rect = a->rect;
- if(f != a->sel) f->rect.x = screen->rect.width * 2;
- //resize_client(f->client, &f->rect, True);
- }
- break;
- default:
- break;
- }
-
- relax_column(a);
- flush_masked_events(EnterWindowMask);
-}
-
-static void
-match_horiz(Area *a, XRectangle *r)
-{
- Frame *f;
-
- for(f=a->frame; f; f=f->anext) {
- f->rect.x = r->x;
- f->rect.width = r->width;
- //resize_client(f->client, &f->rect, True);
- }
-}
-
-static void
-drop_resize(Frame *f, XRectangle *new)
-{
- Area *west = nil, *east = nil, *a = f->area;
- View *v = a->view;
- Frame *north = nil, *south = nil;
- unsigned int min_height = 2 * blitz_labelh(&def.font);
- unsigned int min_width = screen->rect.width/NCOL;
-
- for(west=v->area->next; west && west->next != a; west=west->next);
- /* first managed area is indexed 1, thus (i > 1) ? ... */
- east = a->next;
-
- for(north=a->frame; north && north->anext != f; north=north->anext);
- south = f->anext;
-
- /* validate (and trim if necessary) horizontal resize */
- if(new->width < min_width) {
- if(new->x + new->width == f->rect.x + f->rect.width)
- new->x = a->rect.x + a->rect.width - min_width;
- new->width = min_width;
- }
- if(west && (new->x != f->rect.x)) {
- if(new->x < 0 || new->x < (west->rect.x + min_width)) {
- new->width -= (west->rect.x + min_width) - new->x;
- new->x = west->rect.x + min_width;
- }
- } else {
- new->width += new->x - a->rect.x;
- new->x = a->rect.x;
- }
- if(east && (new->x + new->width != f->rect.x + f->rect.width)) {
- if((new->x + new->width) > (east->rect.x + east->rect.width - min_width))
- new->width = (east->rect.x + east->rect.width - min_width) - new->x;
- } else
- new->width = (a->rect.x + a->rect.width) - new->x;
- if(new->width < min_width)
- goto AfterHorizontal;
-
- /* horizontal resize */
- if(west && (new->x != a->rect.x)) {
- west->rect.width = new->x - west->rect.x;
- a->rect.width += a->rect.x - new->x;
- a->rect.x = new->x;
- match_horiz(a, &a->rect);
- match_horiz(west, &west->rect);
- relax_column(west);
- }
- if(east && (new->x + new->width != a->rect.x + a->rect.width)) {
- east->rect.width -= new->x + new->width - east->rect.x;
- east->rect.x = new->x + new->width;
- a->rect.width = (new->x + new->width) - a->rect.x;
- match_horiz(a, &a->rect);
- match_horiz(east, &east->rect);
- relax_column(east);
- }
-AfterHorizontal:
-
- /* skip vertical resize unless the column is in equal mode */
- if(a->mode != Coldefault)
- goto AfterVertical;
-
- /* validate (and trim if necessary) vertical resize */
- if(new->height < min_height) {
- if(f->rect.height < min_height
- && (new->y == f->rect.y || new->y + new->height == f->rect.y + f->rect.height))
- goto AfterVertical;
- if(new->y + new->height == f->rect.y + f->rect.height)
- new->y = f->rect.y + f->rect.height - min_height;
- new->height = min_height;
- }
- if(north && (new->y != f->rect.y))
- if(new->y < 0 || new->y < (north->rect.y + min_height)) {
- new->height -= (north->rect.y + min_height) - new->y;
- new->y = north->rect.y + min_height;
- }
- if(south && (new->y + new->height != f->rect.y + f->rect.height)) {
- if((new->y + new->height) > (south->rect.y + south->rect.height - min_height))
- new->height = (south->rect.y + south->rect.height - min_height) - new->y;
- }
- if(new->height < min_height)
- goto AfterVertical;
-
- /* vertical resize */
- if(north && (new->y != f->rect.y)) {
- north->rect.height = new->y - north->rect.y;
- f->rect.height += f->rect.y - new->y;
- f->rect.y = new->y;
- //resize_client(north->client, &north->rect, True);
- //resize_client(f->client, &f->rect, True);
- }
- if(south && (new->y + new->height != f->rect.y + f->rect.height)) {
- south->rect.height -= new->y + new->height - south->rect.y;
- south->rect.y = new->y + new->height;
- f->rect.y = new->y;
- f->rect.height = new->height;
- //resize_client(f->client, &f->rect, False);
- //resize_client(south->client, &south->rect, True);
- }
-AfterVertical:
-
- relax_column(a);
- focus_view(screen, v);
-}
-
-static Frame *
-frame_of_point(XPoint *pt)
-{
- Area *a;
- View *v = screen->sel;
- Frame *f = nil;
-
- if(!v)
- return nil;
-
- for(a=v->area->next; a && !ispointinrect(pt->x, pt->y, &a->rect);
- a=a->next);
- if(a)
- for(f=a->frame; f && !ispointinrect(pt->x, pt->y, &f->rect);
- f=f->anext);
- return f;
-}
-
-static void
-drop_move(Frame *f, XRectangle *new, XPoint *pt)
-{
- Area *tgt, *src;
- Frame *ft;
- View *v;
-
- tgt = nil;
- src = f->area;
- v = src->view;
-
- if(!pt)
- return;
-
- for(tgt=v->area->next; tgt && !ispointinrect(pt->x, pt->y, &tgt->rect);
- tgt=tgt->next);
- if(tgt) {
- if(pt->x < 16) {
- if((src->frame && src->frame->anext) || (src != v->area->next)) {
- tgt = new_column(v, v->area->next, 0);
- send_to_area(tgt, src, f);
- }
- }
- else if(pt->x >= screen->rect.width - 16) {
- if((src->frame && src->frame->anext) || src->next) {
- for(tgt=src; tgt->next; tgt=tgt->next);
- tgt = new_column(v, tgt, 0);
- send_to_area(tgt, src, f);
- }
- }
- else if(src != tgt) {
- Client *c = f->client;
- Bool before;
- if(!(ft = frame_of_point(pt)) || (f == ft))
- return;
- before = pt->y < (ft->rect.y + ft->rect.height / 2);
- send_to_area(tgt, src, f);
-
- f = c->sel;
- remove_frame(f);
-
- if(before)
- insert_frame(ft, f, True);
- else
- insert_frame(ft, f, False);
-
- tgt->sel = f;
- arrange_column(tgt, False);
- }
- else { /* !tgt */
- if(!(ft = frame_of_point(pt)) || (f == ft))
- return;
- remove_frame(f);
-
- if(pt->y < (ft->rect.y + ft->rect.height / 2))
- insert_frame(ft, f, True);
- else
- insert_frame(ft, f, False);
-
- tgt->sel = f;
- arrange_column(tgt, False);
- }
- }
-}
-
-void
-resize_column(Client *c, XRectangle *r, XPoint *pt)
-{
- Frame *f = c->sel;
- if((f->rect.width == r->width) && (f->rect.height == r->height))
- drop_move(f, r, pt);
- else
- drop_resize(f, r);
-}
-
-Area *
-new_column(View *v, Area *pos, unsigned int w) {
- Area *a = create_area(v, pos, w);
- if(!a)
- return nil;
- arrange_view(v);
- return a;
-}
diff --git a/cmd/wm/event.c b/cmd/wm/event.c
@@ -1,314 +0,0 @@
-/*
- * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
- * See LICENSE file for license details.
- */
-
-#include <fcntl.h>
-#include <stdlib.h>
-#include <string.h>
-#include <X11/keysym.h>
-
-#include "wm.h"
-
-/* local functions */
-static void buttonpress(XEvent *e);
-static void buttonrelease(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 keypress(XEvent *e);
-static void mappingnotify(XEvent *e);
-static void maprequest(XEvent *e);
-static void propertynotify(XEvent *e);
-static void unmapnotify(XEvent *e);
-
-void (*handler[LASTEvent]) (XEvent *) = {
- [ButtonPress] = buttonpress,
- [ButtonRelease] = buttonrelease,
- [ConfigureRequest]= configurerequest,
- [DestroyNotify] = destroynotify,
- [EnterNotify] = enternotify,
- [LeaveNotify] = leavenotify,
- [Expose] = expose,
- [KeyPress] = keypress,
- [MappingNotify] = mappingnotify,
- [MapRequest] = maprequest,
- [PropertyNotify]= propertynotify,
- [UnmapNotify] = unmapnotify
-};
-
-void
-check_x_event(IXPConn *c)
-{
- XEvent ev;
- while(XPending(blz.dpy)) { /* main event loop */
- XNextEvent(blz.dpy, &ev);
- if(handler[ev.type])
- (handler[ev.type]) (&ev); /* call handler */
- }
-}
-
-unsigned int
-flush_masked_events(long even_mask)
-{
- XEvent ev;
- unsigned int n = 0;
- while(XCheckMaskEvent(blz.dpy, even_mask, &ev)) n++;
- return n;
-}
-
-static void
-buttonrelease(XEvent *e)
-{
- Frame *f;
- Bar *b;
- XButtonPressedEvent *ev = &e->xbutton;
- if(ev->window == screen->barwin) {
- for(b=screen->lbar; b; b=b->next)
- if(ispointinrect(ev->x, ev->y, &b->brush.rect))
- return write_event("LeftBarClick %d %s\n",
- ev->button, b->name);
- for(b=screen->rbar; b; b=b->next)
- if(ispointinrect(ev->x, ev->y, &b->brush.rect))
- return write_event("RightBarClick %d %s\n",
- ev->button, b->name);
- }
- else if((f = frame_of_win(ev->window)))
- write_event("ClientClick %d %d\n", idx_of_client(f->client), ev->button);
-}
-
-static void
-buttonpress(XEvent *e)
-{
- Frame *f;
- XButtonPressedEvent *ev = &e->xbutton;
-
- if((f = frame_of_win(ev->window))) {
- ev->state &= valid_mask;
- if((ev->state & def.mod) == def.mod) {
- focus(f->client, True);
- switch(ev->button) {
- case Button1:
- do_mouse_resize(f->client, CENTER);
- break;
- case Button3:
- do_mouse_resize(f->client, quadofcoord(&f->client->rect, ev->x, ev->y));
- default:
- break;
- }
- }
- else if(ev->button == Button1)
- focus(f->client, True);
- }
-}
-
-static void
-configurerequest(XEvent *e)
-{
- XConfigureRequestEvent *ev = &e->xconfigurerequest;
- XWindowChanges wc;
- XRectangle *frect;
- Client *c;
-
- c = client_of_win(ev->window);
- ev->value_mask &= ~CWSibling;
- if(c) {
- gravitate_client(c, True);
-
- if(ev->value_mask & CWX)
- c->rect.x = ev->x;
- if(ev->value_mask & CWY)
- c->rect.y = ev->y;
- if(ev->value_mask & CWWidth)
- c->rect.width = ev->width;
- if(ev->value_mask & CWHeight)
- c->rect.height = ev->height;
- if(ev->value_mask & CWBorderWidth)
- c->border = ev->border_width;
-
- gravitate_client(c, False);
-
- if(c->frame) {
- if(c->sel->area->floating)
- frect=&c->sel->rect;
- else
- frect=&c->sel->revert;
-
- if(c->rect.width >= screen->rect.width && c->rect.height >= screen->rect.height) {
- frect->y = wc.y = -blitz_labelh(&def.font);
- frect->x = wc.x = -def.border;
- }
- else {
- frect->y = wc.y = c->rect.y - blitz_labelh(&def.font);
- frect->x = wc.x = c->rect.x - def.border;
- }
- frect->width = wc.width = c->rect.width + 2 * def.border;
- frect->height = wc.height = c->rect.height + def.border
- + blitz_labelh(&def.font);
- wc.border_width = 1;
- wc.sibling = None;
- wc.stack_mode = ev->detail;
- if(c->sel->area->view != screen->sel)
- wc.x += 2 * screen->rect.width;
- if(c->sel->area->floating) {
- XConfigureWindow(blz.dpy, c->framewin, ev->value_mask, &wc);
- configure_client(c);
- }
- }
- }
-
- wc.x = ev->x;
- wc.y = ev->y;
- wc.width = ev->width;
- wc.height = ev->height;
-
- if(c && c->frame) {
- wc.x = def.border;
- wc.y = blitz_labelh(&def.font);
- wc.width = c->sel->rect.width - 2 * def.border;
- wc.height = c->sel->rect.height - def.border - blitz_labelh(&def.font);
- }
-
- wc.border_width = 0;
- wc.sibling = None;
- wc.stack_mode = Above;
- ev->value_mask &= ~CWStackMode;
- ev->value_mask |= CWBorderWidth;
- XConfigureWindow(blz.dpy, ev->window, ev->value_mask, &wc);
-
- XSync(blz.dpy, False);
-}
-
-static void
-destroynotify(XEvent *e)
-{
- Client *c;
- XDestroyWindowEvent *ev = &e->xdestroywindow;
-
- if((c = client_of_win(ev->window)))
- destroy_client(c);
-}
-
-static void
-enternotify(XEvent *e)
-{
- XCrossingEvent *ev = &e->xcrossing;
- Client *c;
-
- if(ev->mode != NotifyNormal || ev->detail == NotifyInferior)
- return;
-
- if((c = client_of_win(ev->window))) {
- Frame *f = c->sel;
- Area *a = f->area;
- if(a->mode == Colmax)
- c = a->sel->client;
- focus(c, False);
- }
- else if(ev->window == blz.root) {
- sel_screen = True;
- draw_frames();
- }
-}
-
-static void
-leavenotify(XEvent *e)
-{
- XCrossingEvent *ev = &e->xcrossing;
-
- if((ev->window == blz.root) && !ev->same_screen) {
- sel_screen = True;
- draw_frames();
- }
-}
-
-static void
-expose(XEvent *e)
-{
- XExposeEvent *ev = &e->xexpose;
- static Frame *f;
-
- if(ev->count == 0) {
- if(ev->window == screen->barwin)
- draw_bar(screen);
- else if((f = frame_of_win(ev->window)) && f->view == screen->sel)
- draw_frame(f);
- }
-}
-
-static void
-keypress(XEvent *e)
-{
- XKeyEvent *ev = &e->xkey;
- KeySym k = 0;
- char buf[32];
- int n;
- static Frame *f;
-
-
- ev->state &= valid_mask;
- if((f = frame_of_win(ev->window))) {
- buf[0] = 0;
- n = XLookupString(ev, buf, sizeof(buf), &k, 0);
- if(IsFunctionKey(k) || IsKeypadKey(k) || IsMiscFunctionKey(k)
- || IsPFKey(k) || IsPrivateKeypadKey(k))
- return;
- buf[n] = 0;
- }
- else
- kpress(blz.root, ev->state, (KeyCode) ev->keycode);
-}
-
-static void
-mappingnotify(XEvent *e)
-{
- XMappingEvent *ev = &e->xmapping;
-
- XRefreshKeyboardMapping(ev);
- if(ev->request == MappingKeyboard)
- update_keys();
-}
-
-static void
-maprequest(XEvent *e)
-{
- XMapRequestEvent *ev = &e->xmaprequest;
- static XWindowAttributes wa;
-
- if(!XGetWindowAttributes(blz.dpy, ev->window, &wa))
- return;
-
- if(wa.override_redirect) {
- XSelectInput(blz.dpy, ev->window,
- (StructureNotifyMask | PropertyChangeMask));
- return;
- }
-
- if(!client_of_win(ev->window))
- manage_client(create_client(ev->window, &wa));
-}
-
-static void
-propertynotify(XEvent *e)
-{
- XPropertyEvent *ev = &e->xproperty;
- Client *c;
-
- if(ev->state == PropertyDelete)
- return; /* ignore */
-
- if((c = client_of_win(ev->window)))
- prop_client(c, ev);
-}
-
-static void
-unmapnotify(XEvent *e)
-{
- Client *c;
- XUnmapEvent *ev = &e->xunmap;
-
- if((c = client_of_win(ev->window)))
- destroy_client(c);
-}
diff --git a/cmd/wm/frame.c b/cmd/wm/frame.c
@@ -1,117 +0,0 @@
-/*
- * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
- * See LICENSE file for license details.
- */
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "wm.h"
-
-Frame *
-create_frame(Client *c, View *v)
-{
- static unsigned short id = 1;
- Frame *f = cext_emallocz(sizeof(Frame));
-
- f->id = id++;
- f->client = c;
- f->view = v;
-
- if(c->frame) {
- f->revert = c->sel->revert;
- f->rect = c->sel->rect;
- }
- else{
- f->revert = f->rect = c->rect;
- f->revert.width = f->rect.width += 2 * def.border;
- f->revert.height = f->rect.height += def.border + blitz_labelh(&def.font);
- }
- f->collapsed = False;
-
- f->tile.blitz = &blz;
- f->tile.drawable = pmap;
- f->tile.gc = c->gc;
- f->tile.font = &def.font;
- f->tile.color = def.normcolor;
- f->tile.border = True;
- f->grabbox = f->titlebar = f->tile;
- f->titlebar.align = WEST;
-
- return f;
-}
-
-void
-remove_frame(Frame *f)
-{
- Area *a = f->area;
- Frame **ft = &a->frame;
-
- for(; *ft && *ft != f; ft=&(*ft)->anext);
- cext_assert(*ft == f);
- *ft = f->anext;
-}
-
-void
-insert_frame(Frame *pos, Frame *f, Bool before)
-{
- Area *a = f->area;
- if(before) {
- Frame *ft;
- for(ft=a->frame; ft && ft->anext != pos; ft=ft->anext);
- pos=ft;
- }
- Frame **p = pos ? &pos->anext : &a->frame;
- f->anext = *p;
- *p = f;
-}
-
-void
-update_frame_widget_colors(Frame *f)
-{
- if(sel_screen && (f->client == sel_client()))
- f->tile.color = f->titlebar.color = def.selcolor;
- else
- f->tile.color = f->titlebar.color = def.normcolor;
-
- if(f->area->sel == f)
- f->grabbox.color = def.selcolor;
- else
- f->grabbox.color = def.normcolor;
-}
-
-void
-draw_frame(Frame *f)
-{
- if(def.border) {
- f->tile.rect = f->rect;
- f->tile.rect.x = f->tile.rect.y = 0;
- }
-
- f->grabbox.rect = f->tile.rect;
- f->grabbox.rect.height = blitz_labelh(&def.font);
- f->grabbox.rect.width = def.font.height;
-
- f->titlebar.rect = f->grabbox.rect;
- f->titlebar.rect.x = f->grabbox.rect.x + f->grabbox.rect.width;
- f->titlebar.rect.width = f->rect.width - f->titlebar.rect.x;
-
- blitz_draw_tile(&f->tile);
- blitz_draw_tile(&f->grabbox);
- blitz_draw_label(&f->titlebar, f->client->name);
- XCopyArea(blz.dpy, pmap, f->client->framewin, f->client->gc,
- 0, 0, f->rect.width, f->rect.height, 0, 0);
- XSync(blz.dpy, False);
-}
-
-void
-draw_frames()
-{
- Client *c;
- for(c=client; c; c=c->next)
- if(c->sel && c->sel->view == screen->sel) {
- update_frame_widget_colors(c->sel);
- draw_frame(c->sel);
- }
-}
-
diff --git a/cmd/wm/fs.c b/cmd/wm/fs.c
@@ -1,905 +0,0 @@
-/*
- * (C)opyright MMVI Kris Maglione <fbsdaemon at gmail dot com>
- * See LICENSE file for license details.
- */
-
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-
-#include "wm.h"
-
-/* Datatypes: */
-/**************/
-typedef struct Dirtab Dirtab;
-struct Dirtab {
- char *name;
- unsigned char qtype;
- unsigned int type;
- unsigned int perm;
-};
-
-typedef struct FidLink FidLink;
-struct FidLink {
- FidLink *next;
- Fid *fid;
-};
-
-typedef struct FileId FileId;
-struct FileId {
- FileId *next;
- union {
- void *ref;
- char *buf;
- Bar *bar;
- Bar **bar_p;
- View *view;
- Client *client;
- Ruleset *rule;
- BlitzColor *col;
- };
- unsigned int id;
- unsigned int index;
- Dirtab tab;
- unsigned short nref;
-};
-
-/* Constants */
-/*************/
-enum { /* Dirs */
- FsRoot, FsDClient, FsDClients, FsDBars,
- FsDTag, FsDTags,
- /* Files */
- FsFBar, FsFCctl, FsFColRules,
- FsFCtags, FsFEvent, FsFKeys, FsFRctl,
- FsFTagRules, FsFTctl, FsFTindex,
- FsFprops
-};
-
-/* Error messages */
-static char
- *Enoperm = "permission denied",
- *Enofile = "file not found",
- *Ebadvalue = "bad value",
- *Einterrupted = "interrupted",
- *Ebadcmd = "bad command";
-
-/* Macros */
-#define QID(t, i) (((long long)((t)&0xFF)<<32)|((i)&0xFFFFFFFF))
-
-/* Global Vars */
-/***************/
-FileId *free_fileid = nil;
-P9Req *pending_event_reads = nil;
-FidLink *pending_event_fids;
-P9Srv p9srv = {
- .open= fs_open,
- .walk= fs_walk,
- .read= fs_read,
- .stat= fs_stat,
- .write= fs_write,
- .clunk= fs_clunk,
- .flush= fs_flush,
- .attach=fs_attach,
- .create=fs_create,
- .remove=fs_remove,
- .freefid=fs_freefid
-};
-
-/* ad-hoc file tree. Empty names ("") indicate dynamic entries to be filled
- * in by lookup_file */
-static Dirtab
-dirtab_root[]= {{".", P9QTDIR, FsRoot, 0500|P9DMDIR },
- {"rbar", P9QTDIR, FsDBars, 0700|P9DMDIR },
- {"lbar", P9QTDIR, FsDBars, 0700|P9DMDIR },
- {"client", P9QTDIR, FsDClients, 0500|P9DMDIR },
- {"tag", P9QTDIR, FsDTags, 0500|P9DMDIR },
- {"ctl", P9QTAPPEND, FsFRctl, 0600|P9DMAPPEND },
- {"colrules", P9QTFILE, FsFColRules, 0600 },
- {"event", P9QTFILE, FsFEvent, 0600 },
- {"keys", P9QTFILE, FsFKeys, 0600 },
- {"tagrules", P9QTFILE, FsFTagRules, 0600 },
- {nil}},
-dirtab_clients[]={{".", P9QTDIR, FsDClients, 0500|P9DMDIR },
- {"", P9QTDIR, FsDClient, 0500|P9DMDIR },
- {nil}},
-dirtab_client[]= {{".", P9QTDIR, FsDClient, 0500|P9DMDIR },
- {"ctl", P9QTAPPEND, FsFCctl, 0600|P9DMAPPEND },
- {"tags", P9QTFILE, FsFCtags, 0600 },
- {"props", P9QTFILE, FsFprops, 0400 },
- {nil}},
-dirtab_bars[]= {{".", P9QTDIR, FsDBars, 0700|P9DMDIR },
- {"", P9QTFILE, FsFBar, 0600 },
- {nil}},
-dirtab_tags[]= {{".", P9QTDIR, FsDTags, 0500|P9DMDIR },
- {"", P9QTDIR, FsDTag, 0500|P9DMDIR },
- {nil}},
-dirtab_tag[]= {{".", P9QTDIR, FsDTag, 0500|P9DMDIR },
- {"ctl", P9QTAPPEND, FsFTctl, 0600|P9DMAPPEND },
- {"index", P9QTFILE, FsFTindex, 0400 },
- {nil}};
-/* Writing the lists separately and using an array of their references
- * removes the need for casting and allows for C90 conformance,
- * since otherwise we would need to use compound literals */
-static Dirtab *dirtab[] = {
- [FsRoot] dirtab_root,
- [FsDBars] dirtab_bars,
- [FsDClients] dirtab_clients,
- [FsDClient] dirtab_client,
- [FsDTags] dirtab_tags,
- [FsDTag] dirtab_tag,
-};
-
-/* Utility Functions */
-/*********************/
-
-/* get_file/free_file save and reuse old FileId structs
- * since so many of them are needed for so many
- * purposes */
-static FileId *
-get_file() {
- FileId *temp;
- if(!free_fileid) {
- unsigned int i = 15;
- temp = cext_emallocz(sizeof(FileId) * i);
- for(; i; i--) {
- temp->next = free_fileid;
- free_fileid = temp++;
- }
- }
- temp = free_fileid;
- free_fileid = temp->next;
- temp->nref = 1;
- temp->next = nil;
- return temp;
-}
-
-static void
-free_file(FileId *f) {
- if(--f->nref)
- return;
- free(f->tab.name);
- f->next = free_fileid;
- free_fileid = f;
-}
-
-/* This function's name belies it's true purpose. It increases
- * the reference counts of the FileId list */
-static void
-clone_files(FileId *f) {
- for(; f; f=f->next)
- cext_assert(f->nref++);
-}
-
-/* This should be moved to libixp */
-static void
-write_buf(P9Req *r, void *buf, unsigned int len) {
- if(r->ifcall.offset >= len)
- return;
-
- len -= r->ifcall.offset;
- if(len > r->ifcall.count)
- len = r->ifcall.count;
- r->ofcall.data = cext_emalloc(len);
- memcpy(r->ofcall.data, buf + r->ifcall.offset, len);
- r->ofcall.count = len;
-}
-
-/* This should be moved to libixp */
-void
-write_to_buf(P9Req *r, void *buf, unsigned int *len, unsigned int max) {
- unsigned int offset, count;
-
- offset = (r->fid->omode&P9OAPPEND) ? *len : r->ifcall.offset;
- if(offset > *len || r->ifcall.count == 0) {
- r->ofcall.count = 0;
- return;
- }
-
- count = r->ifcall.count;
- if(max && (count > max - offset))
- count = max - offset;
-
- *len = offset + count;
-
- if(max == 0) {
- *(void **)buf = cext_erealloc(*(void **)buf, *len + 1);
- buf = *(void **)buf;
- }
-
- memcpy(buf + offset, r->ifcall.data, count);
- r->ofcall.count = count;
- ((char *)buf)[offset+count] = '\0';
-}
-
-/* This should be moved to libixp */
-void
-data_to_cstring(P9Req *r) {
- unsigned int i;
- i = r->ifcall.count;
- if(!i || r->ifcall.data[i - 1] != '\n')
- r->ifcall.data = cext_erealloc(r->ifcall.data, ++i);
- cext_assert(r->ifcall.data);
- r->ifcall.data[i - 1] = '\0';
-}
-
-/* This should be moved to liblitz */
-char *
-parse_colors(char **buf, int *buflen, BlitzColor *col) {
- unsigned int i;
- if(*buflen < 23 || 3 != sscanf(*buf, "#%06x #%06x #%06x", &i,&i,&i))
- return Ebadvalue;
- (*buflen) -= 23;
- bcopy(*buf, col->colstr, 23);
- blitz_loadcolor(&blz, col);
-
- (*buf) += 23;
- if(**buf == '\n' || **buf == ' ') {
- (*buf)++;
- (*buflen)--;
- }
- return nil;
-}
-
-char *
-message_root(char *message)
-{
- unsigned int n;
-
- if(!strchr(message, ' ')) {
- snprintf(buffer, BUFFER_SIZE, "%s ", message);
- message = buffer;
- }
-
- if(!strncmp(message, "quit ", 5))
- srv.running = 0;
- else if(!strncmp(message, "view ", 5))
- select_view(&message[5]);
- else if(!strncmp(message, "selcolors ", 10)) {
- message += 10;
- n = strlen(message);
- return parse_colors(&message, (int *)&n, &def.selcolor);
- }else if(!strncmp(message, "normcolors ", 11)) {
- message += 11;
- n = strlen(message);
- return parse_colors(&message, (int *)&n, &def.normcolor);
- }else if(!strncmp(message, "b1colors ", 9)) {
- message += 9;
- n = strlen(message);
- return parse_colors(&message, (int *)&n, &def.bcolor[0]);
- }else if(!strncmp(message, "b2colors ", 9)) {
- message += 9;
- n = strlen(message);
- return parse_colors(&message, (int *)&n, &def.bcolor[1]);
- }else if(!strncmp(message, "b3colors ", 9)) {
- message += 9;
- n = strlen(message);
- return parse_colors(&message, (int *)&n, &def.bcolor[2]);
- }else if(!strncmp(message, "font ", 5)) {
- message += 5;
- free(def.font.fontstr);
- def.font.fontstr = cext_estrdup(message);
- blitz_loadfont(&blz, &def.font);
- }else if(!strncmp(message, "border ", 7)) {
- message += 7;
- n = (unsigned int)strtol(message, &message, 10);
- if(*message)
- return Ebadvalue;
- def.border = n;
- }else if(!strncmp(message, "grabmod ", 8)) {
- message += 8;
- unsigned long mod;
- mod = mod_key_of_str(message);
- if(!(mod & (Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)))
- return Ebadvalue;
- cext_strlcpy(def.grabmod, message, sizeof(def.grabmod));
- def.mod = mod;
- if(view)
- restack_view(screen->sel);
- }else
- return Ebadcmd;
-
- return nil;
-}
-
-char *
-read_root_ctl()
-{
- unsigned int i = 0;
- if(screen->sel)
- i += snprintf(&buffer[i], (BUFFER_SIZE - i), "view %s\n", screen->sel->name);
- i += snprintf(&buffer[i], (BUFFER_SIZE - i), "selcolors %s\n", def.selcolor.colstr);
- i += snprintf(&buffer[i], (BUFFER_SIZE - i), "normcolors %s\n", def.normcolor.colstr);
- i += snprintf(&buffer[i], (BUFFER_SIZE - i), "font %s\n", def.font.fontstr);
- i += snprintf(&buffer[i], (BUFFER_SIZE - i), "grabmod %s\n", def.grabmod);
- i += snprintf(&buffer[i], (BUFFER_SIZE - i), "border %d\n", def.border);
- return buffer;
-}
-
-
-void
-respond_event(P9Req *r) {
- FileId *f = r->fid->aux;
- if(f->buf) {
- r->ofcall.data = (void *)f->buf;
- r->ofcall.count = strlen(f->buf);
- respond(r, nil);
- f->buf = nil;
- }else{
- r->aux = pending_event_reads;
- pending_event_reads = r;
- }
-}
-
-void
-write_event(char *format, ...) {
- unsigned int len, slen;
- va_list ap;
- FidLink *f;
- FileId *fi;
- P9Req *aux;
-
- va_start(ap, format);
- vsnprintf(buffer, BUFFER_SIZE, format, ap);
- va_end(ap);
-
- if(!(len = strlen(buffer)))
- return;
- for(f=pending_event_fids; f; f=f->next) {
- fi = f->fid->aux;
- slen = fi->buf ? strlen(fi->buf) : 0;
- fi->buf = cext_erealloc(fi->buf, slen + len + 1);
- fi->buf[slen] = '\0';
- strcat(fi->buf, buffer);
- }
- while((aux = pending_event_reads)) {
- pending_event_reads = pending_event_reads->aux;
- respond_event(aux);
- }
-}
-
-static void
-dostat(Stat *s, unsigned int len, FileId *f) {
- s->type = 0;
- s->dev = 0;
- s->qid.path = QID(f->tab.type, f->id);
- s->qid.version = 0;
- s->qid.type = f->tab.qtype;
- s->mode = f->tab.perm;
- s->atime = time(nil);
- s->mtime = time(nil);
- s->length = len;
- s->name = f->tab.name;
- s->uid = user;
- s->gid = user;
- s->muid = user;
-}
-
-/* lookup_file */
-/***************/
-/* All lookups and directory organization should be performed through
- * lookup_file, mostly through the dirtabs[] tree. */
-static FileId *
-lookup_file(FileId *parent, char *name)
-{
- FileId *ret, *file, **last;
- Dirtab *dir;
- Client *c;
- View *v;
- Bar *b;
- unsigned int i, id;
-
- if(!(parent->tab.perm & P9DMDIR))
- return nil;
-
- dir = dirtab[parent->tab.type];
- last = &ret;
- ret = nil;
-
- for(; dir->name; dir++) {
- /* Dynamic dirs */
- if(!*dir->name) { /* strlen(dir->name) == 0 */
- switch(parent->tab.type) {
- case FsDClients:
- if(!name || !strncmp(name, "sel", 4)) {
- if((c = sel_client())) {
- file = get_file();
- *last = file;
- last = &file->next;
- file->ref = c;
- file->id = c->id;
- file->index = idx_of_client(c);
- file->tab = *dir;
- file->tab.name = cext_estrdup("sel");
- }if(name) goto LastItem;
- }
- if(name) {
- id = (unsigned int)strtol(name, &name, 10);
- if(*name) goto NextItem;
- }
-
- i=0;
- for(c=client; c; c=c->next, i++) {
- if(!name || i == id) {
- file = get_file();
- *last = file;
- last = &file->next;
- file->ref = c;
- file->id = c->id;
- file->tab = *dir;
- file->tab.name = cext_emallocz(16);
- snprintf(file->tab.name, 16, "%d", i);
- if(name) goto LastItem;
- }
- }
- break;
- case FsDTags:
- if(!name || !strncmp(name, "sel", 4)) {
- if(screen->sel) {
- file = get_file();
- *last = file;
- last = &file->next;
- file->ref = screen->sel;
- file->id = screen->sel->id;
- file->tab = *dir;
- file->tab.name = cext_estrdup("sel");
- }if(name) goto LastItem;
- }
- for(v=view; v; v=v->next) {
- if(!name || !strcmp(name, v->name)) {
- file = get_file();
- *last = file;
- last = &file->next;
- file->ref = v;
- file->id = v->id;
- file->tab = *dir;
- file->tab.name = cext_estrdup(v->name);
- if(name) goto LastItem;
- }
- }
- break;
- case FsDBars:
- for(b=*parent->bar_p; b; b=b->next) {
- if(!name || !strcmp(name, b->name)) {
- file = get_file();
- *last = file;
- last = &file->next;
- file->ref = b;
- file->id = b->id;
- file->tab = *dir;
- file->tab.name = cext_estrdup(b->name);
- if(name) goto LastItem;
- }
- }
- break;
- }
- }else /* Static dirs */
- if(!name || !strcmp(name, dir->name)) {
- file = get_file();
- *last = file;
- last = &file->next;
- file->id = 0;
- file->ref = parent->ref;
- file->index = parent->index;
- file->tab = *dir;
- file->tab.name = cext_estrdup(file->tab.name);
-
- /* Special considerations: */
- switch(file->tab.type) {
- case FsDBars:
- if(!strncmp(file->tab.name, "lbar", 5))
- file->ref = &screen[0].lbar;
- else
- file->ref = &screen[0].rbar;
- break;
- case FsFColRules:
- file->ref = &def.colrules;
- break;
- case FsFTagRules:
- file->ref = &def.tagrules;
- break;
- }
- if(name) goto LastItem;
- }
- NextItem:
- continue;
- }
-LastItem:
- *last = nil;
- return ret;
-}
-
-/* Service Functions */
-/*********************/
-void
-fs_attach(P9Req *r) {
- FileId *f = get_file();
- f->tab = dirtab[FsRoot][0];
- f->tab.name = cext_estrdup("/");
- f->ref = nil; /* shut up valgrind */
- r->fid->aux = f;
- r->fid->qid.type = f->tab.qtype;
- r->fid->qid.path = QID(f->tab.type, 0);
- r->ofcall.qid = r->fid->qid;
- respond(r, nil);
-}
-
-void
-fs_walk(P9Req *r) {
- FileId *f, *nf;
- int i;
-
- f = r->fid->aux;
-
- clone_files(f);
- for(i=0; i < r->ifcall.nwname; i++) {
- if(!strncmp(r->ifcall.wname[i], "..", 3)) {
- if(f->next) {
- nf=f;
- f=f->next;
- free_file(nf);
- }
- }else{
- nf = lookup_file(f, r->ifcall.wname[i]);
- if(!nf)
- break;
- cext_assert(!nf->next);
- if(strncmp(r->ifcall.wname[i], ".", 2)) {
- nf->next = f;
- f = nf;
- }
- }
- r->ofcall.wqid[i].type = f->tab.qtype;
- r->ofcall.wqid[i].path = QID(f->tab.type, f->id);
- }
- /* There should be a way to do this on freefid() */
- if(i < r->ifcall.nwname) {
- while((nf = f)) {
- f=f->next;
- free_file(nf);
- }
- return respond(r, Enofile);
- }
-
- /* Remove refs for r->fid if no new fid */
- /* If Fids were ref counted, this could be
- * done in their decref function */
- if(r->ifcall.fid == r->ifcall.newfid) {
- nf=r->fid->aux;
- r->fid->aux = f;
- while((nf = f)) {
- f=f->next;
- free_file(nf);
- }
- }
-
- r->newfid->aux = f;
- r->ofcall.nwqid = i;
- respond(r, nil);
-}
-
-unsigned int
-fs_size(FileId *f) {
- switch(f->tab.type) {
- default:
- return 0;
- case FsFColRules:
- case FsFTagRules:
- return f->rule->size;
- case FsFKeys:
- return def.keyssz;
- case FsFCtags:
- return strlen(f->client->tags);
- case FsFprops:
- return strlen(f->client->props);
- }
-}
-
-void
-fs_stat(P9Req *r) {
- Stat s;
- int size;
- unsigned char *buf;
-
- dostat(&s, fs_size(r->fid->aux), r->fid->aux);
- r->ofcall.nstat = size = ixp_sizeof_stat(&s);
- buf = cext_emallocz(size);
- r->ofcall.stat = buf;
-
- ixp_pack_stat(&buf, &size, &s);
- respond(r, nil);
-}
-
-void
-fs_read(P9Req *r) {
- char *buf;
- FileId *f, *tf;
- int n, offset;
- int size;
-
- offset = 0;
- f = r->fid->aux;
-
- if(f->tab.perm & P9DMDIR && f->tab.perm & 0400) {
- Stat s;
- offset = 0;
- size = r->ifcall.count;
- buf = cext_emallocz(size);
- r->ofcall.data = buf;
-
- tf = f = lookup_file(f, nil);
- /* Note: f->tab.name == "." so we skip it */
- for(f=f->next; f; f=f->next) {
- dostat(&s, fs_size(f), f);
- n = ixp_sizeof_stat(&s);
- if(offset >= r->ifcall.offset) {
- if(size < n)
- break;
- ixp_pack_stat((unsigned char **)&buf, &size, &s);
- }
- offset += n;
- }
-
- while((f = tf)) {
- tf=tf->next;
- free_file(f);
- }
-
- r->ofcall.count = r->ifcall.count - size;
- return respond(r, nil);
- }else{
- switch(f->tab.type) {
- case FsFprops:
- write_buf(r, (void *)f->client->props, strlen(f->client->props));
- return respond(r, nil);
- case FsFColRules:
- case FsFTagRules:
- write_buf(r, (void *)f->rule->string, f->rule->size);
- return respond(r, nil);
- case FsFKeys:
- write_buf(r, (void *)def.keys, def.keyssz);
- return respond(r, nil);
- case FsFCtags:
- write_buf(r, (void *)f->client->tags, strlen(f->client->tags));
- return respond(r, nil);
- case FsFTctl:
- write_buf(r, (void *)f->view->name, strlen(f->view->name));
- return respond(r, nil);
- case FsFBar:
- write_buf(r, (void *)f->bar->buf, strlen(f->bar->buf));
- return respond(r, nil);
- case FsFRctl:
- buf = read_root_ctl();
- write_buf(r, buf, strlen(buf));
- return respond(r, nil);
- case FsFCctl:
- if(r->ifcall.offset)
- return respond(r, nil);
- r->ofcall.data = cext_emallocz(16);
- n = snprintf(r->ofcall.data, 16, "%d", f->index);
- cext_assert(n >= 0);
- r->ofcall.count = n;
- return respond(r, nil);
- case FsFTindex:
- buf = (char *)view_index(f->view);
- n = strlen(buf);
- write_buf(r, (void *)buf, n);
- return respond(r, nil);
- case FsFEvent:
- respond_event(r);
- return;
- }
- }
- /* This is an assert because it should this should not be called if
- * the file is not open for reading. */
- cext_assert(!"Read called on an unreadable file");
-}
-
-/* This function needs to be seriously cleaned up */
-void
-fs_write(P9Req *r) {
- FileId *f;
- char *errstr = nil;
- unsigned int i;
-
- if(r->ifcall.count == 0)
- return respond(r, nil);
-
- f = r->fid->aux;
- switch(f->tab.type) {
- case FsFColRules:
- case FsFTagRules:
- write_to_buf(r, &f->rule->string, &f->rule->size, 0);
- return respond(r, nil);
- case FsFKeys:
- write_to_buf(r, &def.keys, &def.keyssz, 0);
- return respond(r, nil);
- case FsFCtags:
- data_to_cstring(r);
- i=strlen(f->client->tags);
- write_to_buf(r, &f->client->tags, &i, 255);
- r->ofcall.count = i- r->ifcall.offset;
- return respond(r, nil);
- case FsFBar:
- /* XXX: This should validate after each write */
- i = strlen(f->bar->buf);
- write_to_buf(r, &f->bar->buf, &i, 279);
- r->ofcall.count = i - r->ifcall.offset;
- return respond(r, nil);
- case FsFCctl:
- data_to_cstring(r);
- if((errstr = message_client(f->client, r->ifcall.data)))
- return respond(r, errstr);
- r->ofcall.count = r->ifcall.count;
- return respond(r, nil);
- case FsFTctl:
- data_to_cstring(r);
- if((errstr = message_view(f->view, r->ifcall.data)))
- return respond(r, errstr);
- r->ofcall.count = r->ifcall.count;
- return respond(r, nil);
- case FsFRctl:
- data_to_cstring(r);
- { unsigned int n;
- char *toks[32];
- n = cext_tokenize(toks, 32, r->ifcall.data, '\n');
- for(i = 0; i < n; i++) {
- if(errstr)
- message_root(toks[i]);
- else
- errstr = message_root(toks[i]);
- }
- }
- if(errstr)
- return respond(r, errstr);
- r->ofcall.count = r->ifcall.count;
- return respond(r, nil);
- case FsFEvent:
- if(r->ifcall.data[r->ifcall.count-1] == '\n')
- write_event("%.*s", r->ifcall.count, r->ifcall.data);
- else
- write_event("%.*s\n", r->ifcall.count, r->ifcall.data);
- r->ofcall.count = r->ifcall.count;
- return respond(r, nil);
- }
- /* This is an assert because it should this should not be called if
- * the file is not open for writing. */
- cext_assert(!"Write called on an unwritable file");
-}
-
-void
-fs_open(P9Req *r) {
- FidLink *fl;
- FileId *f = r->fid->aux;
- switch(f->tab.type) {
- case FsFEvent:
- fl = cext_emallocz(sizeof(FidLink));
- fl->fid = r->fid;
- fl->next = pending_event_fids;
- pending_event_fids = fl;
- break;
- }
- if((r->ifcall.mode&3) == P9OEXEC)
- return respond(r, Enoperm);
- if((r->ifcall.mode&3) != P9OREAD && !(f->tab.perm & 0200))
- return respond(r, Enoperm);
- if((r->ifcall.mode&3) != P9OWRITE && !(f->tab.perm & 0400))
- return respond(r, Enoperm);
- if((r->ifcall.mode&~(3|P9OAPPEND|P9OTRUNC)))
- return respond(r, Enoperm);
-
- respond(r, nil);
-}
-
-void
-fs_create(P9Req *r) {
- FileId *f = r->fid->aux;
- switch(f->tab.type) {
- default:
- /* XXX: This should be taken care of by the library */
- return respond(r, Enoperm);
- case FsDBars:
- if(!strlen(r->ifcall.name))
- return respond(r, Ebadvalue);
- create_bar(f->bar_p, r->ifcall.name);
- f = lookup_file(f, r->ifcall.name);
- if(!f)
- return respond(r, Enofile);
-
- r->ofcall.qid.type = f->tab.qtype;
- r->ofcall.qid.path = QID(f->tab.type, f->id);
- f->next = r->fid->aux;
- r->fid->aux = f;
- respond(r, nil);
- break;
- }
-}
-
-void
-fs_remove(P9Req *r) {
- FileId *f = r->fid->aux;
- switch(f->tab.type) {
- default:
- /* XXX: This should be taken care of by the library */
- return respond(r, Enoperm);
- case FsFBar:
- destroy_bar(f->next->bar_p, f->bar);
- draw_bar(screen);
- respond(r, nil);
- break;
- }
-}
-
-void
-fs_clunk(P9Req *r) {
- Client *c;
- FidLink **fl, *ft;
- char *buf;
- int i;
- FileId *f = r->fid->aux;
-
- switch(f->tab.type) {
- case FsFColRules:
- update_rules(&f->rule->rule, f->rule->string);
- break;
- case FsFTagRules:
- update_rules(&f->rule->rule, f->rule->string);
- for(c=client; c; c=c->next)
- apply_rules(c);
- update_views();
- break;
- case FsFKeys:
- update_keys();
- break;
- case FsFCtags:
- apply_tags(f->client, f->client->tags);
- update_views();
- draw_frame(f->client->sel);
- break;
- case FsFBar:
- buf = f->bar->buf;
- i = strlen(f->bar->buf);
- parse_colors(&buf, &i, &f->bar->brush.color);
- while(i > 0 && buf[i - 1] == '\n')
- buf[--i] = '\0';
- cext_strlcpy(f->bar->text, buf, sizeof(f->bar->text));
- draw_bar(screen);
- break;
- case FsFEvent:
- for(fl=&pending_event_fids; *fl; fl=&(*fl)->next)
- if((*fl)->fid == r->fid) {
- ft = *fl;
- *fl = (*fl)->next;
- f = ft->fid->aux;
- free(f->buf);
- free(ft);
- break;
- }
- break;
- }
- respond(r, nil);
-}
-
-void
-fs_flush(P9Req *r) {
- P9Req **t;
- for(t=&pending_event_reads; *t; t=(P9Req **)&(*t)->aux)
- if(*t == r->oldreq) {
- *t = (*t)->aux;
- respond(r->oldreq, Einterrupted);
- break;
- }
- respond(r, nil);
-}
-
-void
-fs_freefid(Fid *f) {
- FileId *id, *tid;
-
- for(id=f->aux; id; id = tid) {
- tid = id->next;
- free_file(id);
- }
-}
diff --git a/cmd/wm/geom.c b/cmd/wm/geom.c
@@ -1,49 +0,0 @@
-/*
- * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
- * See LICENSE file for license details.
- */
-
-#include <math.h>
-
-#include "wm.h"
-
-Bool
-ispointinrect(int x, int y, XRectangle * r)
-{
- return (x >= r->x) && (x <= r->x + r->width)
- && (y >= r->y) && (y <= r->y + r->height);
-}
-
-BlitzAlign
-quadofcoord(XRectangle *rect, int x, int y)
-{
- BlitzAlign ret = 0;
- x -= rect->x;
- y -= rect->y;
-
- if(x >= rect->width * .5)
- ret |= EAST;
- if(x <= rect->width * .5)
- ret |= WEST;
- if(y <= rect->height * .5)
- ret |= NORTH;
- if(y >= rect->height * .5)
- ret |= SOUTH;
-
- return ret;
-}
-
-/* Syntax: <x> <y> <width> <height> */
-int
-strtorect(XRectangle *r, const char *val)
-{
- XRectangle new;
- if (!val)
- return -1;
-
- if(sscanf(val, "%hd %hd %hu %hu", &new.x, &new.y, &new.width, &new.height) != 4)
- return -1;
-
- *r = new;
- return 0;
-}
diff --git a/cmd/wm/key.c b/cmd/wm/key.c
@@ -1,265 +0,0 @@
-/*
- * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
- * See LICENSE file for license details.
- */
-
-#include <string.h>
-#include <stdlib.h>
-#include <X11/Xlib.h>
-#include <X11/keysym.h>
-
-#include "wm.h"
-
-void
-init_lock_keys()
-{
- XModifierKeymap *modmap;
- KeyCode num_lock;
- static int masks[] = {
- ShiftMask, LockMask, ControlMask, Mod1Mask,
- Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
- };
- int i;
-
- num_lock_mask = 0;
- modmap = XGetModifierMapping(blz.dpy);
- num_lock = XKeysymToKeycode(blz.dpy, XStringToKeysym("Num_Lock"));
-
- if(modmap && modmap->max_keypermod > 0) {
- int max = (sizeof(masks) / sizeof(int)) * modmap->max_keypermod;
- for(i = 0; i < max; i++)
- if(num_lock && (modmap->modifiermap[i] == num_lock))
- num_lock_mask = masks[i / modmap->max_keypermod];
- }
- XFreeModifiermap(modmap);
-
- valid_mask = 255 & ~(num_lock_mask | LockMask);
-}
-
-unsigned long
-mod_key_of_str(char *val)
-{
- unsigned long mod = 0;
- if (strstr(val, "Shift"))
- mod |= ShiftMask;
- if (strstr(val, "Control"))
- mod |= ControlMask;
- if (strstr(val, "Mod1"))
- mod |= Mod1Mask;
- if (strstr(val, "Mod2"))
- mod |= Mod2Mask;
- if (strstr(val, "Mod3"))
- mod |= Mod3Mask;
- if (strstr(val, "Mod4"))
- mod |= Mod4Mask;
- if (strstr(val, "Mod5"))
- mod |= Mod5Mask;
- return mod;
-}
-
-static void
-grab_key(Key *k)
-{
- XGrabKey(blz.dpy, k->key, k->mod, blz.root,
- True, GrabModeAsync, GrabModeAsync);
- if(num_lock_mask) {
- XGrabKey(blz.dpy, k->key, k->mod | num_lock_mask, blz.root,
- True, GrabModeAsync, GrabModeAsync);
- XGrabKey(blz.dpy, k->key, k->mod | num_lock_mask | LockMask, blz.root,
- True, GrabModeAsync, GrabModeAsync);
- }
- XSync(blz.dpy, False);
-}
-
-static void
-ungrab_key(Key *k)
-{
- XUngrabKey(blz.dpy, k->key, k->mod, blz.root);
- if(num_lock_mask) {
- XUngrabKey(blz.dpy, k->key, k->mod | num_lock_mask, blz.root);
- XUngrabKey(blz.dpy, k->key, k->mod | num_lock_mask | LockMask, blz.root);
- }
- XSync(blz.dpy, False);
-}
-
-static Key *
-name2key(const char *name)
-{
- Key *k;
- for(k=key; k && strncmp(k->name, name, sizeof(k->name)); k=k->lnext);
- return k;
-}
-
-static Key *
-get_key(const char *name)
-{
- char buf[128];
- char *seq[8];
- char *kstr;
- unsigned int i, toks;
- static unsigned short id = 1;
- Key *k = 0, *r = 0;
-
- if((k = name2key(name))) {
- ungrab_key(k);
- return k;
- }
-
- cext_strlcpy(buf, name, sizeof(buf));
- toks = cext_tokenize(seq, 8, buf, ',');
-
- for(i = 0; i < toks; i++) {
- if(!k)
- r = k = cext_emallocz(sizeof(Key));
- else {
- k->next = cext_emallocz(sizeof(Key));
- k = k->next;
- }
- cext_strlcpy(k->name, name, sizeof(k->name));
- kstr = strrchr(seq[i], '-');
- if(kstr)
- kstr++;
- else
- kstr = seq[i];
- k->key = XKeysymToKeycode(blz.dpy, XStringToKeysym(kstr));
- k->mod = mod_key_of_str(seq[i]);
- }
- if(r) {
- r->id = id++;
- r->lnext = key;
- key = r;
- }
-
- return r;
-}
-
-static void
-next_keystroke(unsigned long *mod, KeyCode *code)
-{
- XEvent e;
- KeySym sym;
- *mod = 0;
- do {
- XMaskEvent(blz.dpy, KeyPressMask, &e);
- *mod |= e.xkey.state & valid_mask;
- *code = (KeyCode) e.xkey.keycode;
- sym = XKeycodeToKeysym(blz.dpy, e.xkey.keycode, 0);
- } while(IsModifierKey(sym));
-}
-
-static void
-emulate_key_press(unsigned long mod, KeyCode key)
-{
- XEvent e;
- Window client_win;
- int revert;
-
- XGetInputFocus(blz.dpy, &client_win, &revert);
-
- e.xkey.type = KeyPress;
- e.xkey.time = CurrentTime;
- e.xkey.window = client_win;
- e.xkey.display = blz.dpy;
- e.xkey.state = mod;
- e.xkey.keycode = key;
- XSendEvent(blz.dpy, client_win, True, KeyPressMask, &e);
- e.xkey.type = KeyRelease;
- XSendEvent(blz.dpy, client_win, True, KeyReleaseMask, &e);
- XSync(blz.dpy, False);
-}
-
-static Key *
-match_keys(Key *k, unsigned long mod, KeyCode keycode, Bool seq)
-{
- Key *ret = nil, *next;
- for(next = k->tnext; k; (k=next) && (next=k->tnext)) {
- if(seq)
- k = k->next;
- if(k && (k->mod == mod) && (k->key == keycode)) {
- k->tnext = ret;
- ret = k;
- }
- }
- return ret;
-}
-
-static void
-kpress_seq(Window w, Key *done)
-{
- unsigned long mod;
- KeyCode key;
- Key *found;
-
- next_keystroke(&mod, &key);
-
- found = match_keys(done, mod, key, True);
- if((done->mod == mod) && (done->key == key))
- emulate_key_press(mod, key); /* double key */
- else {
- if(!found) {
- XBell(blz.dpy, 0);
- } /* grabbed but not found */
- else if(!found->tnext && !found->next)
- write_event("Key %s\n", found->name);
- else
- kpress_seq(w, found);
- }
-}
-
-void
-kpress(Window w, unsigned long mod, KeyCode keycode)
-{
- Key *k;
-
- for(k=key; k; k->tnext=k->lnext, k=k->lnext);
- Key *found = match_keys(key, mod, keycode, False);
-
- if(!found) {
- XBell(blz.dpy, 0);
- } /* grabbed but not found */
- else if(!found->tnext && !found->next)
- write_event("Key %s\n", found->name);
- else {
- XGrabKeyboard(blz.dpy, w, True, GrabModeAsync, GrabModeAsync, CurrentTime);
- kpress_seq(w, found);
- XUngrabKeyboard(blz.dpy, CurrentTime);
- XSync(blz.dpy, False);
- }
-}
-
-void
-update_keys()
-{
- Key *k, *n;
- char *l, *p;
-
- init_lock_keys();
-
- while((k = key)) {
- key = key->lnext;
- ungrab_key(k);
-
- while((n = k)) {
- k = k->next;
- free(n);
- }
- }
-
- for(l = p = def.keys; p && *p;) {
- if(*p == '\n') {
- *p = 0;
- if((k = get_key(l)))
- grab_key(k);
- *p = '\n';
- l = ++p;
- }
- else
- p++;
- }
- if(l < p && strlen(l)) {
- if((k = get_key(l)))
- grab_key(k);
- }
-
- XSync(blz.dpy, False);
-}
diff --git a/cmd/wm/mouse.c b/cmd/wm/mouse.c
@@ -1,287 +0,0 @@
-/*
- * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
- * (C)opyright MMVI Kris Maglione <fbsdaemon@gmail.com>
- * See LICENSE file for license details.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "wm.h"
-
-#define ButtonMask (ButtonPressMask | ButtonReleaseMask)
-#define MouseMask (ButtonMask | PointerMotionMask)
-
-static void
-rect_morph_xy(XRectangle *rect, int dx, int dy, BlitzAlign *mask)
-{
- BlitzAlign new_mask = 0;
- if(*mask & NORTH) {
- if(rect->height - dy >= 0 || *mask & SOUTH) {
- rect->y += dy;
- rect->height -= dy;
- }
- else {
- rect->y += rect->height;
- rect->height = dy - rect->height;
- new_mask ^= NORTH|SOUTH;
- }
- }
- if(*mask & SOUTH) {
- if(rect->height + dy >= 0 || *mask & NORTH)
- rect->height += dy;
- else {
- rect->height = -dy - rect->height;
- rect->y -= rect->height;
- new_mask ^= NORTH|SOUTH;
- }
- }
- if(*mask & EAST) {
- if(rect->width + dx >= 0 || *mask & WEST)
- rect->width += dx;
- else {
- rect->width = -dx - rect->width;
- rect->x -= rect->width;
- new_mask ^= EAST|WEST;
- }
- }
- if(*mask & WEST) {
- if(rect->width - dx >= 0 || *mask & EAST) {
- rect->x += dx;
- rect->width -= dx;
- }
- else {
- rect->x += rect->width;
- rect->width = dx - rect->width;
- new_mask ^= EAST|WEST;
- }
- }
- *mask ^= new_mask;
-}
-
-typedef struct {
- XRectangle *rects;
- int num;
- int x1, y1, x2, y2;
- BlitzAlign mask;
- int *delta;
-} SnapArgs;
-
-static void
-snap_line(SnapArgs *a)
-{
- int i, t_xy;
-
- /* horizontal */
- if(a->y1 == a->y2 && (a->mask & (NORTH|SOUTH))) {
- for(i=0; i < a->num; i++) {
- if(!((a->rects[i].x + a->rects[i].width < a->x1) ||
- (a->rects[i].x > a->x2))) {
-
- if(abs(a->rects[i].y - a->y1) <= abs(*a->delta))
- *a->delta = a->rects[i].y - a->y1;
-
- t_xy = a->rects[i].y + a->rects[i].height;
- if(abs(t_xy - a->y1) < abs(*a->delta))
- *a->delta = t_xy - a->y1;
- }
- }
- }
- else if (a->mask & (EAST|WEST)) {
- /* This is the same as above, tr/xy/yx/,
- * s/width/height/, s/height/width/ */
- for(i=0; i < a->num; i++) {
- if(!((a->rects[i].y + a->rects[i].height < a->y1) ||
- (a->rects[i].y > a->y2))) {
-
- if(abs(a->rects[i].x - a->x1) <= abs(*a->delta))
- *a->delta = a->rects[i].x - a->x1;
-
- t_xy = a->rects[i].x + a->rects[i].width;
- if(abs(t_xy - a->x1) < abs(*a->delta))
- *a->delta = t_xy - a->x1;
- }
- }
- }
-}
-
-BlitzAlign
-snap_rect(XRectangle *rects, int num, XRectangle *current,
- BlitzAlign *mask, int snap)
-{
- SnapArgs a = { rects, num, 0, 0, 0, 0, *mask, nil };
- int dx = snap + 1, dy = snap + 1;
- BlitzAlign ret;
-
- a.x1 = current->x;
- a.x2 = current->x + current->width;
- a.delta = &dy;
- if(*mask & NORTH) {
- a.y2 = a.y1 = current->y;
- snap_line(&a);
- }
- if(*mask & SOUTH) {
- a.y2 = a.y1 = current->y + current->height;
- snap_line(&a);
- }
-
- a.y1 = current->y;
- a.y2 = current->y + current->height;
- a.delta = &dx;
- if(*mask & EAST) {
- a.x1 = a.x2 = current->x + current->width;
- snap_line(&a);
- }
- if(*mask & WEST) {
- a.x1 = a.x2 = current->x;
- snap_line(&a);
- }
-
- rect_morph_xy(current, abs(dx) <= snap ? dx : 0,
- abs(dy) <= snap ? dy : 0, mask);
-
- ret = *mask;
- if(abs(dx) <= snap)
- ret ^= EAST|WEST;
- if(abs(dy) <= snap)
- ret ^= NORTH|SOUTH;
-
- return ret ^ CENTER;
-}
-
-static void
-draw_xor_border(XRectangle *r)
-{
- XRectangle xor = *r;
-
- xor.x += 2;
- xor.y += 2;
- xor.width = xor.width > 4 ? xor.width - 4 : 0;
- xor.height = xor.height > 4 ? xor.height - 4 : 0;
- XSetLineAttributes(blz.dpy, xorgc, 1, LineSolid, CapNotLast, JoinMiter);
- XDrawLine(blz.dpy, blz.root, xorgc, xor.x + 2, xor.y + xor.height / 2,
- xor.x + xor.width - 2, xor.y + xor.height / 2);
- XDrawLine(blz.dpy, blz.root, xorgc, xor.x + xor.width / 2, xor.y + 2,
- xor.x + xor.width / 2, xor.y + xor.height - 2);
- XSetLineAttributes(blz.dpy, xorgc, 4, LineSolid, CapNotLast, JoinMiter);
- XDrawRectangles(blz.dpy, blz.root, xorgc, &xor, 1);
- XSync(blz.dpy, False);
-}
-
-void
-do_mouse_resize(Client *c, BlitzAlign align)
-{
- BlitzAlign grav;
- int px, py, ox, oy, i;
- float rx, ry;
- Window dummy;
- XEvent ev;
- unsigned int num = 0, di;
- Frame *f = c->sel;
- Bool floating = f->area->floating;
- int snap = floating ? screen->rect.height / 66 : 0;
- XRectangle *rects = floating ? rects_of_view(f->area->view, &num) : nil;
- XRectangle frect = f->rect, ofrect;
- XRectangle origin = frect;
- XPoint pt;
-
- XQueryPointer(blz.dpy, c->framewin, &dummy, &dummy, &i, &i, &ox, &oy, &di);
- rx = (float)ox / frect.width;
- ry = (float)oy / frect.height;
-
- if (floating || align != CENTER) {
- px = ox = frect.width / 2;
- py = oy = frect.height / 2;
- if(align&NORTH)
- oy -= py;
- if(align&SOUTH)
- oy += py;
- if(align&EAST)
- ox += px;
- if(align&WEST)
- ox -= px;
-
- XWarpPointer(blz.dpy, None, c->framewin, 0, 0, 0, 0, ox, oy);
- }
-
- XTranslateCoordinates(blz.dpy, c->framewin, blz.root, ox, oy, &ox, &oy, &dummy);
- pt.x = ox; pt.y = oy;
-
- XSync(blz.dpy, False);
- if(XGrabPointer(blz.dpy, c->framewin, False, MouseMask, GrabModeAsync, GrabModeAsync,
- None, cursor[CurResize], CurrentTime) != GrabSuccess)
- return;
-
- XGrabServer(blz.dpy);
- draw_xor_border(&frect);
- for(;;) {
- XMaskEvent(blz.dpy, MouseMask | ExposureMask, &ev);
- switch (ev.type) {
- case ButtonRelease:
- draw_xor_border(&frect);
- if(!floating)
- resize_column(c, &frect, (align == CENTER) ? &pt : nil);
- else
- resize_client(c, &frect, False);
- if(rects)
- free(rects);
- XUngrabServer(blz.dpy);
- XUngrabPointer(blz.dpy, CurrentTime);
- XSync(blz.dpy, False);
-
- XWarpPointer(blz.dpy, None, c->framewin, 0, 0, 0, 0,
- frect.width * rx, frect.height * ry);
- return;
- break;
- case MotionNotify:
- ofrect = frect;
-
- pt.x = ev.xmotion.x;
- pt.y = ev.xmotion.y;
- XTranslateCoordinates(blz.dpy, c->framewin, blz.root, ev.xmotion.x,
- ev.xmotion.y, &px, &py, &dummy);
-
- rect_morph_xy(&origin, px-ox, py-oy, &align);
- frect=origin;
- ox=px; oy=py;
-
- if(floating)
- grav = snap_rect(rects, num, &frect, &align, snap);
- else
- grav = align ^ CENTER;
- match_sizehints(c, &frect, floating, grav);
-
- draw_xor_border(&ofrect);
- draw_xor_border(&frect);
- break;
- case Expose:
- (handler[Expose])(&ev);
- break;
- default: break;
- }
- }
-}
-
-void
-grab_mouse(Window w, unsigned long mod, unsigned int button)
-{
- XGrabButton(blz.dpy, button, mod, w, False, ButtonMask,
- GrabModeAsync, GrabModeSync, None, None);
- if((mod != AnyModifier) && num_lock_mask) {
- XGrabButton(blz.dpy, button, mod | num_lock_mask, w, False, ButtonMask,
- GrabModeAsync, GrabModeSync, None, None);
- XGrabButton(blz.dpy, button, mod | num_lock_mask | LockMask, w, False,
- ButtonMask, GrabModeAsync, GrabModeSync, None, None);
- }
-}
-
-void
-ungrab_mouse(Window w, unsigned long mod, unsigned int button)
-{
- XUngrabButton(blz.dpy, button, mod, w);
- if(mod != AnyModifier && num_lock_mask) {
- XUngrabButton(blz.dpy, button, mod | num_lock_mask, w);
- XUngrabButton(blz.dpy, button, mod | num_lock_mask | LockMask, w);
- }
-}
diff --git a/cmd/wm/rule.c b/cmd/wm/rule.c
@@ -1,76 +0,0 @@
-/*
- * (C)opyright MMVI Anselm R. Garbe <garbeam at gmail dot com>
- * See LICENSE file for license details.
- */
-
-#include <string.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include "wm.h"
-
-/* basic rule matching language /regex/ -> value
- * regex might contain POSIX regex syntax defined in regex(3) */
-enum {
- IGNORE,
- REGEX,
- VALUE
-};
-
-void
-update_rules(Rule **rule, const char *data)
-{
- int mode = IGNORE;
- Rule *rul;
- char *p, *r = nil, *v = nil, regex[256], value[256];
-
- if(!data || !strlen(data))
- return;
-
- while((rul = *rule)) {
- *rule = rul->next;
- regfree(&rul->regex);
- free(rul);
- }
-
- for(p = (char *)data; *p; p++)
- switch(mode) {
- case IGNORE:
- if(*p == '/') {
- mode = REGEX;
- r = regex;
- }
- else if(*p == '>') {
- mode = VALUE;
- value[0] = 0;
- v = value;
- }
- break;
- case REGEX:
- if(*p == '/') {
- mode = IGNORE;
- *r = 0;
- }
- else {
- *r = *p;
- r++;
- }
- break;
- case VALUE:
- if(*p == '\n' || *p == 0) {
- *rule = cext_emallocz(sizeof(Rule));
- *v = 0;
- cext_trim(value, " \t/");
- if(!regcomp(&(*rule)->regex, regex, 0)) {
- cext_strlcpy((*rule)->value, value, sizeof(rul->value));
- rule = &(*rule)->next;
- }
- else free(*rule);
- mode = IGNORE;
- }
- else {
- *v = *p;
- v++;
- }
- break;
- }
-}
diff --git a/cmd/wm/view.c b/cmd/wm/view.c
@@ -1,444 +0,0 @@
-/*
- * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
- * See LICENSE file for license details.
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "wm.h"
-
-static Bool
-is_empty(View *v)
-{
- Area *a;
- for(a=v->area; a; a=a->next)
- if(a->frame)
- return False;
- return True;
-}
-
-Frame *
-clientframe_of_view(View *v, Client *c)
-{
- Frame *f;
- for(f=c->frame; f; f=f->cnext)
- if(f->area->view == v)
- break;
- return f;
-}
-
-static void
-assign_sel_view(View *v)
-{
- if(screen->sel != v) {
- if(screen->sel)
- write_event("UnfocusTag %s\n",screen->sel->name);
- screen->sel = v;
- write_event("FocusTag %s\n", screen->sel->name);
- }
-}
-
-Client *
-sel_client_of_view(View *v) {
- return v->sel && v->sel->sel ? v->sel->sel->client : nil;
-}
-
-View *
-get_view(const char *name)
-{
- View *v;
- int cmp;
- for(v = view; v; v=v->next)
- if((cmp=strcmp(name, v->name)) >= 0)
- break;
- if(!v || cmp != 0)
- v = create_view(name);
- return v;
-}
-
-View *
-create_view(const char *name)
-{
- static unsigned short id = 1;
- View **i, *v = cext_emallocz(sizeof(View));
-
- v->id = id++;
- cext_strlcpy(v->name, name, sizeof(v->name));
- create_area(v, nil, 0);
- create_area(v, v->area, 0);
- v->area->floating = True;
-
- for(i=&view; *i; i=&(*i)->next)
- if(strcmp((*i)->name, name) < 0) break;
- v->next = *i;
- *i = v;
-
- write_event("CreateTag %s\n", v->name);
- if(!screen->sel)
- assign_sel_view(v);
-
- return v;
-}
-
-void
-destroy_view(View *v)
-{
- Area *a;
- View **i;
-
- while((a = v->area)) {
- v->area = a->next;
- destroy_area(a);
- };
-
- for(i=&view; *i; i=&(*i)->next)
- if(*i == v) break;
- *i = v->next;
-
- if(screen->sel == v)
- for(screen->sel=view; screen->sel && screen->sel->next; screen->sel=screen->sel->next)
- if(screen->sel->next == *i) break;
-
- write_event("DestroyTag %s\n", v->name);
- free(v);
-}
-
-static void
-update_frame_selectors(View *v)
-{
- Area *a;
- Frame *f;
- for(a=v->area; a; a=a->next)
- for(f=a->frame; f; f=f->anext)
- f->client->sel = f;
-}
-
-void
-focus_view(WMScreen *s, View *v)
-{
- Frame *f;
- Client *c;
-
- XGrabServer(blz.dpy);
- assign_sel_view(v);
-
- update_frame_selectors(v);
-
- /* gives all(!) clients proper geometry (for use of different tags) */
- for(c=client; c; c=c->next)
- if((f = c->sel)) {
- if(f->view == v) {
- resize_client(c, &f->rect, False);
- //XMoveWindow(blz.dpy, c->framewin, f->rect.x, f->rect.y);
- }else
- XMoveWindow(blz.dpy, c->framewin, 2 * s->rect.width + f->rect.x,
- f->rect.y);
- }
-
- if((c = sel_client()))
- focus_client(c, True);
-
- draw_frames();
- XSync(blz.dpy, False);
- XUngrabServer(blz.dpy);
- flush_masked_events(EnterWindowMask);
-}
-
-void
-select_view(const char *arg)
-{
- char buf[256];
- cext_strlcpy(buf, arg, sizeof(buf));
- cext_trim(buf, " \t+");
- if(!strlen(buf))
- return;
- assign_sel_view(get_view(arg));
- update_views(); /* performs focus_view */
-}
-
-void
-attach_to_view(View *v, Frame *f)
-{
- Area *a;
- Client *c = f->client;
-
- c->revert = nil;
-
- if(c->trans || c->floating || c->fixedsize
- || (c->rect.width == screen->rect.width && c->rect.height == screen->rect.height))
- a = v->area;
- else if(starting && v->sel->floating)
- a = v->area->next;
- else
- a = v->sel;
- attach_to_area(a, f, False);
- v->sel = a;
-}
-
-void
-restack_view(View *v)
-{
- Area *a;
- Frame *f;
- Client *c;
- unsigned int n=0, i=0;
- static Window *wins = nil;
- static unsigned int winssz = 0;
-
- for(c=client; c; c=c->next, i++);
- if(i > winssz) {
- winssz = 2 * i;
- wins = cext_erealloc(wins, sizeof(Window) * winssz);
- }
-
- for(a=v->area; a; a=a->next) {
- if(a->frame) {
- wins[n++] = a->sel->client->framewin;
- for(f=a->frame; f; f=f->anext)
- if(f != a->sel) n++;
- i=n;
- for(f=a->frame; f; f=f->anext) {
- Client *c = f->client;
- update_client_grab(c, (v->sel == a) && (a->sel == f));
- if(f != a->sel)
- wins[--i] = c->framewin;
- }
- }
- }
-
- if(n)
- XRestackWindows(blz.dpy, wins, n);
-}
-
-void
-scale_view(View *v, float w)
-{
- unsigned int xoff, col_size = 0;
- unsigned int min_width = screen->rect.width/NCOL;
- Area *a;
- float scale, dx = 0;
- int wdiff = 0;
-
- if(!v->area->next)
- return;
-
- for(a=v->area->next; a; a=a->next, col_size++)
- dx += a->rect.width;
- scale = w / dx;
- xoff = 0;
- for(a=v->area->next; a; a=a->next) {
- a->rect.width *= scale;
- if(!a->next)
- a->rect.width = w - xoff;
- xoff += a->rect.width;
- }
-
- /* min_width can only be respected when there is enough space; the caller should guarantee this */
- if(col_size * min_width > w)
- return;
- xoff = 0;
- for(a=v->area->next, col_size--; a; a=a->next, col_size--) {
- if(a->rect.width < min_width)
- a->rect.width = min_width;
- else if((wdiff = xoff + a->rect.width - w + col_size * min_width) > 0)
- a->rect.width -= wdiff;
- if(!a->next)
- a->rect.width = w - xoff;
- xoff += a->rect.width;
- }
-}
-
-void
-arrange_view(View *v)
-{
- unsigned int xoff = 0;
- Area *a;
-
- if(!v->area->next)
- return;
-
- scale_view(v, screen->rect.width);
- for(a=v->area->next; a; a=a->next) {
- a->rect.x = xoff;
- a->rect.y = 0;
- a->rect.height = screen->rect.height - screen->brect.height;
- xoff += a->rect.width;
- arrange_column(a, False);
- }
-}
-
-XRectangle *
-rects_of_view(View *v, unsigned int *num)
-{
- XRectangle *result;
- Frame *f;
-
- *num = 2;
- for(f=v->area->frame; f; f=f->anext, (*num)++);
-
- result = cext_emallocz(*num * sizeof(XRectangle));
- for(f=v->area->frame; f; f=f->anext)
- *result++ = f->rect;
- *result++ = screen->rect;
- *result++ = screen->brect;
-
- return result - *num;
-}
-
-/* XXX: This will need cleanup */
-unsigned char *
-view_index(View *v) {
- unsigned int a_i, buf_i, n;
- int len;
- Frame *f;
- Area *a;
-
- len = BUFFER_SIZE;
- buf_i = 0;
- for((a = v->area), (a_i = 0); a && len > 0; (a=a->next), (a_i++)) {
- if(a->floating)
- n = snprintf(&buffer[buf_i], len, "# ~ %d %d\n",
- a->rect.width, a->rect.height);
- else
- n = snprintf(&buffer[buf_i], len, "# %d %d %d\n",
- a_i, a->rect.x, a->rect.width);
- buf_i += n;
- len -= n;
- for(f=a->frame; f && len > 0; f=f->anext) {
- XRectangle *r = &f->rect;
- if(a->floating)
- n = snprintf(&buffer[buf_i], len, "~ %d %d %d %d %d %s\n",
- idx_of_client(f->client),
- r->x, r->y, r->width, r->height,
- f->client->props);
- else
- n = snprintf(&buffer[buf_i], len, "%d %d %d %d %s\n",
- a_i, idx_of_client(f->client), r->y,
- r->height, f->client->props);
- if(len - n < 0)
- return (unsigned char *)buffer;
- buf_i += n;
- len -= n;
- }
- }
- return (unsigned char *)buffer;
-}
-
-Client *
-client_of_message(View *v, char *message, unsigned int *next)
-{
- unsigned int i;
- Client *c;
-
- if(!strncmp(message, "sel ", 4)) {
- *next = 4;
- return sel_client_of_view(v);
- }
- if((1 != sscanf(message, "%d %n", &i, next)))
- return nil;
- for(c=client; i && c; c=c->next, i--);
- return c;
-}
-
-Area *
-area_of_message(View *v, char *message, unsigned int *next) {
- unsigned int i;
- Area *a;
-
- if(!strncmp(message, "sel ", 4)) {
- *next = 4;
- return v->sel;
- }
- if(!strncmp(message, "~ ", 2))
- return v->area;
- if(1 != sscanf(message, "%d %n", &i, next) || i == 0)
- return nil;
- for(a=v->area; i && a; a=a->next, i--);
- return a;
-}
-
-char *
-message_view(View *v, char *message) {
- unsigned int n, i;
- Client *c;
- Frame *f;
- Area *a;
- static char Ebadvalue[] = "bad value";
-
- if(!strncmp(message, "send ", 5)) {
- message += 5;
- if(!(c = client_of_message(v, message, &n)))
- return Ebadvalue;
- if(!(f = clientframe_of_view(v, c)))
- return Ebadvalue;
- return send_client(f, &message[n]);
- }
- if(!strncmp(message, "select ", 7)) {
- message += 7;
- return select_area(v->sel, message);
- }
- if(!strncmp(message, "colmode ", 8)) {
- message += 8;
- if(!(a = area_of_message(v, message, &n)) || a == v->area)
- return Ebadvalue;
- if((i = column_mode_of_str(&message[n])) == -1)
- return Ebadvalue;
- a->mode = i;
- arrange_column(a, True);
- restack_view(v);
- if(v == screen->sel)
- focus_view(screen, v);
- draw_frames();
- return nil;
- }
- return Ebadvalue;
-}
-
-void
-update_views()
-{
- View *n, *v;
- View *old = screen->sel;
-
- for(v=view; v; v=v->next)
- update_frame_selectors(v);
-
- if(old && !strncmp(old->name, "nil", 4))
- old = nil;
-
- for((v=view) && (n=v->next); v; (v=n) && (n=v->next))
- if((v != old) && is_empty(v))
- destroy_view(v);
-
- if(old)
- focus_view(screen, old);
- else if(screen->sel)
- focus_view(screen, screen->sel);
-}
-
-unsigned int
-newcolw_of_view(View *v)
-{
- Rule *r;
- Area *a;
- unsigned int i, n;
- regmatch_t tmpregm;
-
- for(r=def.colrules.rule; r; r=r->next) {
- if(!regexec(&r->regex, v->name, 1, &tmpregm, 0)) {
- char buf[256];
- char *toks[16];
- cext_strlcpy(buf, r->value, sizeof(buf));
- n = cext_tokenize(toks, 16, buf, '+');
- for(a=v->area, i=0; a; a=a->next, i++);
- if(n && n >= i) {
- if(sscanf(toks[i - 1], "%u", &n) == 1)
- return (screen->rect.width * n) / 100;
- }
- break;
- }
- }
- return 0;
-}
diff --git a/cmd/wm/wm.c b/cmd/wm/wm.c
@@ -1,410 +0,0 @@
-/*
- * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
- * See LICENSE file for license details.
- */
-
-#include <errno.h>
-#include <fcntl.h>
-#include <pwd.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <unistd.h>
-#include <X11/cursorfont.h>
-#include <X11/Xproto.h>
-#include <X11/keysym.h>
-#include <X11/Xatom.h>
-#include <X11/Xproto.h>
-
-#include "wm.h"
-
-static int other_wm_running;
-static int (*x_error_handler) (Display *, XErrorEvent *);
-static char version[] = "wmiiwm - " VERSION ", (C)opyright MMIV-MMVI Anselm R. Garbe\n";
-
-static void
-usage()
-{
- fputs("usage: wmiiwm -a <address> [-r <wmiirc>] [-v]\n", stderr);
- exit(1);
-}
-
-static void
-error(char *errstr, ...) {
- va_list ap;
- va_start(ap, errstr);
- vfprintf(stderr, errstr, ap);
- va_end(ap);
- exit(1);
-}
-
-static void
-scan_wins()
-{
- int i;
- unsigned int num;
- Window *wins;
- XWindowAttributes wa;
- Window d1, d2;
-
- if(XQueryTree(blz.dpy, blz.root, &d1, &d2, &wins, &num)) {
- for(i = 0; i < num; i++) {
- if(!XGetWindowAttributes(blz.dpy, wins[i], &wa))
- continue;
- if(wa.override_redirect || XGetTransientForHint(blz.dpy, wins[i], &d1))
- continue;
- if(wa.map_state == IsViewable)
- manage_client(create_client(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(blz.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)
-{
- Atom *protocols;
- long res;
- int protos = 0;
- int i;
-
- res = win_property(w, wm_atom[WMProtocols], XA_ATOM, 20L,
- ((unsigned char **) &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;
-}
-
-
-static void
-init_atoms()
-{
- wm_atom[WMProtocols] = XInternAtom(blz.dpy, "WM_PROTOCOLS", False);
- wm_atom[WMDelete] = XInternAtom(blz.dpy, "WM_DELETE_WINDOW", False);
- net_atom[NetSupported] = XInternAtom(blz.dpy, "_NET_SUPPORTED", False);
- net_atom[NetWMName] = XInternAtom(blz.dpy, "_NET_WM_NAME", False);
- tags_atom = XInternAtom(blz.dpy, "_WIN_TAGS", False);
-
- XChangeProperty(blz.dpy, blz.root, net_atom[NetSupported], XA_ATOM, 32,
- PropModeReplace, (unsigned char *) net_atom, NetLast);
-}
-
-static void
-init_cursors()
-{
- cursor[CurNormal] = XCreateFontCursor(blz.dpy, XC_left_ptr);
- cursor[CurResize] = XCreateFontCursor(blz.dpy, XC_sizing);
- cursor[CurMove] = XCreateFontCursor(blz.dpy, XC_fleur);
- cursor[CurInput] = XCreateFontCursor(blz.dpy, XC_xterm);
-}
-
-static void
-init_screen(WMScreen *screen)
-{
- Window w;
- int ret;
- unsigned mask;
- XGCValues gcv;
-
- gcv.subwindow_mode = IncludeInferiors;
- gcv.function = GXxor;
- gcv.foreground = def.selcolor.bg;
- gcv.plane_mask = AllPlanes;
- gcv.graphics_exposures = False;
- xorgc = XCreateGC(blz.dpy, blz.root, GCForeground | GCGraphicsExposures |
- GCFunction | GCSubwindowMode | GCPlaneMask, &gcv);
-
- screen->rect.x = screen->rect.y = 0;
- screen->rect.width = DisplayWidth(blz.dpy, blz.screen);
- screen->rect.height = DisplayHeight(blz.dpy, blz.screen);
- def.snap = screen->rect.height / 63;
-
- sel_screen = XQueryPointer(blz.dpy, blz.root, &w, &w, &ret, &ret, &ret, &ret, &mask);
-}
-
-/*
- * 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
-wmii_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, "wmiiwm: fatal error: Xrequest code=%d, Xerror code=%d\n",
- error->request_code, error->error_code);
- return x_error_handler(blz.dpy, error); /* calls 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 = 1;
- return -1;
-}
-
-static void
-cleanup()
-{
- Client *c;
- for(c=client; c; c=c->next)
- reparent_client(c, blz.root, c->sel->rect.x, c->sel->rect.y);
- XSetInputFocus(blz.dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
- XSync(blz.dpy, False);
-}
-
-int
-main(int argc, char *argv[])
-{
- int i;
- char *address = nil, *wmiirc = nil, *namespace, *errstr;
- WMScreen *s;
- struct passwd *passwd;
- XSetWindowAttributes wa;
-
- /* command line args */
- for(i = 1; (i < argc) && (argv[i][0] == '-'); i++) {
- switch (argv[i][1]) {
- case 'v':
- fprintf(stdout, "%s", version);
- exit(0);
- break;
- case 'a':
- if(i + 1 < argc)
- address = argv[++i];
- else
- usage();
- break;
- case 'r':
- if(i + 1 < argc)
- wmiirc = argv[++i];
- else
- usage();
- break;
- default:
- usage();
- break;
- }
- }
-
- starting = True;
-
- blz.dpy = XOpenDisplay(0);
- if(!blz.dpy)
- error("wmiiwm: cannot open dpy\n");
- blz.screen = DefaultScreen(blz.dpy);
- blz.root = RootWindow(blz.dpy, blz.screen);
-
- /* check if another WM is already running */
- other_wm_running = 0;
- XSetErrorHandler(startup_error_handler);
- /* this causes an error if some other WM is running */
- XSelectInput(blz.dpy, blz.root, SubstructureRedirectMask | EnterWindowMask);
- XSync(blz.dpy, False);
-
- if(other_wm_running)
- error("wmiiwm: another window manager is already running\n");
-
- if(!address)
- usage();
-
- /* Check namespace permissions */
- if(!strncmp(address, "unix!", 5)) {
- struct stat st;
- namespace = cext_estrdup(&address[5]);
-
- for(i = strlen(namespace) - 1; i >= 0; i--)
- if(namespace[i] == '/') break;
- namespace[i+1] = '\0';
- if(stat(namespace, &st))
- error("wmiiwm: can't stat namespace directory \"%s\": %s\n",
- namespace, strerror(errno));
- if(getuid() != st.st_uid)
- error("wmiiwm: namespace directory \"%s\" exists, but is not owned by you",
- namespace);
- if(st.st_mode & 077)
- error("wmiiwm: namespace directory \"%s\" exists, "
- "but has group or world permissions",
- namespace);
- free(namespace);
- }
-
- XSetErrorHandler(0);
- x_error_handler = XSetErrorHandler(wmii_error_handler);
- errstr = nil;
- i = ixp_create_sock(address, &errstr);
- if(i < 0)
- error("wmiiwm: fatal: %s\n", errstr);
-
- /* start wmiirc */
- if(wmiirc) {
- int name_len = strlen(wmiirc) + 6;
- char execstr[name_len];
- switch(fork()) {
- case 0:
- if(setsid() == -1)
- error("wmiim: can't setsid: %s\n", strerror(errno));
- close(i);
- close(ConnectionNumber(blz.dpy));
- snprintf(execstr, name_len, "exec %s", wmiirc);
- execl("/bin/sh", "sh", "-c", execstr, nil);
- error("wmiiwm: can't exec \"%s\": %s\n", wmiirc, strerror(errno));
- case -1:
- perror("wmiiwm: cannot fork wmiirc");
- default:
- break;
- }
- }
-
- /* IXP server */
- ixp_server_open_conn(&srv, i, &p9srv, serve_9pcon, nil);
-
- /* X server */
- ixp_server_open_conn(&srv, ConnectionNumber(blz.dpy), nil, check_x_event, nil);
-
- view = nil;
- client = nil;
- key = nil;
-
- passwd = getpwuid(getuid());
- user = cext_estrdup(passwd->pw_name);
-
- def.colrules.string = nil;
- def.colrules.size = 0;
- def.tagrules.string = nil;
- def.tagrules.size = 0;
- def.keys = nil;
- def.keyssz = 0;
- def.font.fontstr = cext_estrdup(BLITZ_FONT);
- def.border = 2;
- def.colmode = Coldefault;
- cext_strlcpy(def.selcolor.colstr, BLITZ_SELCOLORS, sizeof(def.selcolor.colstr));
- blitz_loadcolor(&blz, &def.selcolor);
- cext_strlcpy(def.normcolor.colstr, BLITZ_NORMCOLORS, sizeof(def.normcolor.colstr));
- blitz_loadcolor(&blz, &def.normcolor);
- cext_strlcpy(def.bcolor[0].colstr, BLITZ_B1COLORS, sizeof(def.bcolor[0].colstr));
- cext_strlcpy(def.bcolor[1].colstr, BLITZ_B2COLORS, sizeof(def.bcolor[1].colstr));
- cext_strlcpy(def.bcolor[2].colstr, BLITZ_B3COLORS, sizeof(def.bcolor[2].colstr));
- blitz_loadcolor(&blz, &def.bcolor[0]);
- blitz_loadcolor(&blz, &def.bcolor[1]);
- blitz_loadcolor(&blz, &def.bcolor[2]);
- cext_strlcpy(def.grabmod, "Mod1", sizeof(def.grabmod));
- def.mod = Mod1Mask;
-
- init_atoms();
- init_cursors();
- blitz_loadfont(&blz, &def.font);
- init_lock_keys();
-
- num_screens = 1;
- screens = cext_emallocz(num_screens * sizeof(*screens));
-
- for(i = 0; i < num_screens; i++) {
- s = &screens[i];
- s->lbar = nil;
- s->rbar = nil;
- s->sel = nil;
-
- init_screen(s);
-
- pmap = XCreatePixmap(blz.dpy, blz.root, s->rect.width, s->rect.height,
- DefaultDepth(blz.dpy, blz.screen));
-
- wa.event_mask = SubstructureRedirectMask | EnterWindowMask | LeaveWindowMask;
- wa.cursor = cursor[CurNormal];
- XChangeWindowAttributes(blz.dpy, blz.root, CWEventMask | CWCursor, &wa);
-
- wa.override_redirect = 1;
- wa.background_pixmap = ParentRelative;
- wa.event_mask = ExposureMask | ButtonReleaseMask
- | SubstructureRedirectMask | SubstructureNotifyMask;
-
- s->brect = s->rect;
- s->brect.height = blitz_labelh(&def.font);
- s->brect.y = s->rect.height - s->brect.height;
- s->barwin = XCreateWindow(blz.dpy, RootWindow(blz.dpy, blz.screen),
- s->brect.x, s->brect.y,
- s->brect.width, s->brect.height, 0,
- DefaultDepth(blz.dpy, blz.screen),
- CopyFromParent, DefaultVisual(blz.dpy, blz.screen),
- CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
- XSync(blz.dpy, False);
-
- s->bbrush.blitz = &blz;
- s->bbrush.gc = XCreateGC(blz.dpy, s->barwin, 0, 0);
- s->bbrush.drawable = pmap;
- s->bbrush.rect = s->brect;
- s->bbrush.rect.x = 0;
- s->bbrush.rect.y = 0;
- s->bbrush.color = def.normcolor;
- s->bbrush.font = &def.font;
- s->bbrush.border = True;
-
- draw_bar(s);
- XMapRaised(blz.dpy, s->barwin);
- }
-
- screen = &screens[0];
- scan_wins();
- update_views();
-
- starting = False;
-
- /* main event loop */
- errstr = ixp_server_loop(&srv);
- if(errstr)
- fprintf(stderr, "wmii: fatal: %s\n", errstr);
-
- cleanup();
- XCloseDisplay(blz.dpy);
- ixp_server_close(&srv);
-
- return errstr ? 1 : 0;
-}
diff --git a/cmd/wm/wm.h b/cmd/wm/wm.h
@@ -1,309 +0,0 @@
-/*
- * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
- * See LICENSE file for license details.
- */
-
-#include <stdio.h>
-#include <regex.h>
-#include <X11/Xlib.h>
-#include <X11/Xutil.h>
-
-#include <ixp.h>
-#include <blitz.h>
-
-/* WM atoms */
-enum { WMProtocols, WMDelete, WMLast };
-
-/* NET atoms */
-enum { NetSupported, NetWMName, NetLast };
-
-/* Column modes */
-enum { Coldefault, Colstack, Colmax };
-
-/* Cursor */
-enum { CurNormal, CurResize, CurMove, CurInput, CurLast };
-
-enum { NCOL = 16 };
-enum { WM_PROTOCOL_DELWIN = 1 };
-
-/* Data Structures */
-typedef struct View View;
-typedef struct Area Area;
-typedef struct Frame Frame;
-typedef struct Client Client;
-typedef struct Key Key;
-typedef struct Bar Bar;
-typedef struct Rule Rule;
-typedef struct Ruleset Ruleset;
-typedef struct WMScreen WMScreen;
-
-struct View {
- View *next;
- char name[256];
- unsigned short id;
- Area *area;
- Area *sel;
- Area *revert;
-};
-
-struct Area {
- Area *next;
- Frame *frame;
- Frame *sel;
- View *view;
- Bool floating;
- unsigned short id;
- int mode;
- XRectangle rect;
-};
-
-struct Frame {
- Frame *cnext;
- Frame *anext;
- View *view;
- Area *area;
- unsigned short id;
- XRectangle rect;
- XRectangle revert;
- Client *client;
- Bool collapsed;
- BlitzBrush tile;
- BlitzBrush grabbox;
- BlitzBrush titlebar;
-};
-
-struct Client {
- Client *next;
- Area *revert;
- Frame *frame;
- Frame *sel;
- char name[256];
- char tags[256];
- char props[512];
- unsigned short id;
- unsigned int border;
- int proto;
- Bool floating;
- Bool fixedsize;
- Window win;
- Window trans;
- Window framewin;
- XRectangle rect;
- XSizeHints size;
- GC gc;
-};
-
-struct Key {
- Key *next;
- Key *lnext;
- Key *tnext;
- unsigned short id;
- char name[128];
- unsigned long mod;
- KeyCode key;
-};
-
-struct Bar {
- Bar *next;
- Bar *smaller;
- char buf[280];
- char text[256];
- char name[256];
- unsigned short id;
- BlitzBrush brush;
-};
-
-struct Rule {
- Rule *next;
- regex_t regex;
- char value[256];
-};
-
-struct Ruleset {
- Rule *rule;
- char *string;
- unsigned int size;
-};
-
-/* global variables */
-struct {
- BlitzColor selcolor;
- BlitzColor normcolor;
- BlitzColor bcolor[3];
- BlitzFont font;
- unsigned int border;
- unsigned int snap;
- char *keys;
- unsigned int keyssz;
- Ruleset tagrules;
- Ruleset colrules;
- char grabmod[5];
- unsigned long mod;
- int colmode;
-} def;
-
-struct WMScreen {
- Bar *lbar;
- Bar *rbar;
- View *sel;
- Window barwin;
-
- XRectangle rect;
- XRectangle brect;
- BlitzBrush bbrush;
-} *screens, *screen;
-
-Client *client;
-View *view;
-Key *key;
-
-enum { BUFFER_SIZE = 8092 };
-char buffer[BUFFER_SIZE];
-
-/* IXP */
-IXPServer srv;
-P9Srv p9srv;
-
-/* X11 */
-unsigned int num_screens;
-Blitz blz;
-GC xorgc;
-char *user;
-Atom wm_atom[WMLast];
-Atom net_atom[NetLast];
-Atom tags_atom;
-Cursor cursor[CurLast];
-unsigned int valid_mask;
-unsigned int num_lock_mask;
-Bool sel_screen;
-Pixmap pmap;
-void (*handler[LASTEvent]) (XEvent *);
-
-/* Misc */
-Bool starting;
-
-/* wm.c */
-extern char *message_root(char *message);
-
-/* area.c */
-extern Area *create_area(View *v, Area *pos, unsigned int w);
-extern void destroy_area(Area *a);
-extern Area *area_of_id(View *t, unsigned short id);
-extern char *select_area(Area *a, char *arg);
-extern void send_to_area(Area *to, Area *from, Frame *f);
-extern void attach_to_area(Area *a, Frame *f, Bool send);
-extern void detach_from_area(Area *a, Frame *f);
-extern Client *sel_client_of_area(Area *a);
-
-/* bar.c */
-extern Bar *create_bar(Bar **b_link, char *name);
-extern void destroy_bar(Bar **b_link, Bar *b);
-extern void draw_bar(WMScreen *s);
-extern void resize_bar();
-extern Bar *bar_of_name(Bar *b_link, const char *name);
-
-/* client.c */
-extern Client *create_client(Window w, XWindowAttributes *wa);
-extern void destroy_client(Client *c);
-extern void configure_client(Client *c);
-extern void prop_client(Client *c, XPropertyEvent *e);
-extern void kill_client(Client *c);
-extern void gravitate_client(Client *c, Bool invert);
-extern void unmap_client(Client *c);
-extern void map_client(Client *c);
-extern void reparent_client(Client *c, Window w, int x, int y);
-extern void manage_client(Client *c);
-extern void focus_client(Client *c, Bool restack);
-extern void focus(Client *c, Bool restack);
-extern void resize_client(Client *c, XRectangle *r, Bool ignore_xcall);
-extern void match_sizehints(Client *c, XRectangle *r, Bool floating, BlitzAlign sticky);
-extern char *send_client(Frame *f, char *arg);
-extern char * message_client(Client *c, char *message);
-extern void move_client(Client *c, char *arg);
-extern void size_client(Client *c, char *arg);
-extern void newcol_client(Client *c, char *arg);
-extern Client *sel_client();
-extern Frame *frame_of_win(Window w);
-extern Client *client_of_win(Window w);
-extern int idx_of_client(Client *c);
-extern void update_client_grab(Client *c, Bool is_sel);
-extern void apply_rules(Client *c);
-extern void apply_tags(Client *c, const char *tags);
-
-/* column.c */
-extern void arrange_column(Area *a, Bool dirty);
-extern void scale_column(Area *a, float h);
-extern void resize_column(Client *c, XRectangle *r, XPoint *pt);
-extern int column_mode_of_str(char *arg);
-extern char *str_of_column_mode(int mode);
-extern Area *new_column(View *v, Area *pos, unsigned int w);
-
-/* event.c */
-extern void check_x_event(IXPConn *c);
-extern unsigned int flush_masked_events(long even_mask);
-
-/* frame.c */
-extern Frame *create_frame(Client *c, View *v);
-extern void remove_frame(Frame *f);
-extern void insert_frame(Frame *pos, Frame *f, Bool before);
-extern void draw_frame(Frame *f);
-extern void draw_frames();
-extern void update_frame_widget_colors(Frame *f);
-
-/* fs.c */
-extern void fs_attach(P9Req *r);
-extern void fs_clunk(P9Req *r);
-extern void fs_create(P9Req *r);
-extern void fs_flush(P9Req *r);
-extern void fs_freefid(Fid *f);
-extern void fs_open(P9Req *r);
-extern void fs_read(P9Req *r);
-extern void fs_remove(P9Req *r);
-extern void fs_stat(P9Req *r);
-extern void fs_walk(P9Req *r);
-extern void fs_write(P9Req *r);
-extern void write_event(char *format, ...);
-
-/* geom.c */
-extern Bool ispointinrect(int x, int y, XRectangle * r);
-extern BlitzAlign quadofcoord(XRectangle *rect, int x, int y);
-extern int strtorect(XRectangle *r, const char *val);
-
-/* key.c */
-extern void kpress(Window w, unsigned long mod, KeyCode keycode);
-extern void update_keys();
-extern void init_lock_keys();
-extern unsigned long mod_key_of_str(char *val);
-
-/* mouse.c */
-extern void do_mouse_resize(Client *c,BlitzAlign align);
-extern void grab_mouse(Window w, unsigned long mod, unsigned int button);
-extern void ungrab_mouse(Window w, unsigned long mod, unsigned int button);
-extern BlitzAlign snap_rect(XRectangle *rects, int num, XRectangle *current,
- BlitzAlign *mask, int snap);
-
-/* rule.c */
-extern void update_rules(Rule **rule, const char *data);
-
-/* view.c */
-extern void arrange_view(View *v);
-extern void scale_view(View *v, float w);
-extern View *get_view(const char *name);
-extern View *create_view(const char *name);
-extern void focus_view(WMScreen *s, View *v);
-extern void update_client_views(Client *c, char **tags);
-extern XRectangle *rects_of_view(View *v, unsigned int *num);
-extern View *view_of_id(unsigned short id);
-extern void select_view(const char *arg);
-extern void attach_to_view(View *v, Frame *f);
-extern Client *sel_client_of_view(View *v);
-extern char *message_view(View *v, char *message);
-extern void restack_view(View *v);
-extern unsigned char *view_index(View *v);
-extern void destroy_view(View *v);
-extern void update_views();
-extern unsigned int newcolw_of_view(View *v);
-
-/* wm.c */
-extern int wmii_error_handler(Display *dpy, XErrorEvent *error);
-extern int win_proto(Window w);
diff --git a/cmd/wm/wmii b/cmd/wm/wmii
@@ -1,10 +0,0 @@
-#!/bin/sh
-# start wmiiwm and wait for its termination
-
-WMII_CONFPATH="$HOME/.wmii-4:CONFPREFIX/wmii-4" export WMII_CONFPATH
-WMII_NS_DIR="/tmp/ns.$USER.${DISPLAY%.0}" export WMII_NS_DIR
-WMII_ADDRESS="unix!$WMII_NS_DIR/wmii" export WMII_ADDRESS
-mkdir -m 700 "$WMII_NS_DIR" 2>/dev/null
-
-mkdir $HOME/.wmii-4 2>/dev/null && CONFPREFIX/wmii-4/welcome &
-exec wmiiwm -a $WMII_ADDRESS -r `PATH="$WMII_CONFPATH:$PATH" which wmiirc`
diff --git a/cmd/wm/wmii.1 b/cmd/wm/wmii.1
@@ -1,209 +0,0 @@
-.TH WMII 1 wmii-4
-.SH NAME
-wmii \- window manager improved 2
-.SH SYNOPSIS
-.B wmii
-.SH DESCRIPTION
-.SS Overview
-.B wmii
-is a script that launches the wmii window manager and its various utilities and
-makes sure that they are configured for use.
-.SS Actions
-An action is a shell script in the default setup, but it can actually be
-any executable file. It is executed usually by selecting it from the
-actions menu.
-You can customize an action by copying it from the global action
-directory CONFPREFIX/wmii-4 to $HOME/.wmii-4 and then editing the copy to
-fit your needs. Of course you can also create your own actions there; make
-sure that they are executable.
-.P
-Here is a list of the default actions:
-.TP 2
-quit
-leave the window manager nicely
-.TP 2
-status
-periodically print date and load average to the bar
-.TP 2
-welcome
-display a welcome message that contains the wmii tutorial
-.TP 2
-wmiirc
-configure wmii
-.SS Default Key Bindings
-.PD 0
-.B Moving Around
-.RS 2
-.TP 16
-.I Key
-.I Action
-.TP
-.B Mod-h
-Move to a
-.B window
-to the
-.B left
-of the one currently focused
-.TP
-.B Mod-l
-Move to a
-.B window
-to the
-.B right
-of the one currently focused
-.TP
-.B Mod-j
-Move to a
-.B window below
-the one currently focused
-.TP
-.B Mod-k
-Move to a
-.B window above
-the one currently focused
-.TP
-.B Mod-space
-.B Toggle
-between the managed and floating
-.B layer
-.TP
-.BI Mod-t \ tag
-Move to the
-.B view
-of the given
-.I tag
-.TP
-.B Mod-[0-9]
-Move to the
-.B view
-with the given number
-.PD 1
-.P
-.RE
-.B Moving Things Around
-.RS 2
-.PD 0
-.TP 16
-.I Key
-.I Action
-.TP
-.B Mod-Shift-h
-Move the current window
-.B window
-to a column on the
-.B left
-.TP
-.B Mod-Shift-l
-Move the current window
-.B window
-to a column on the
-.B right
-.TP
-.B Mod-Shift-j
-Move the current
-.B window below
-the one beneath it
-.TP
-.B Mod-Shift-k
-Move the current
-.B window above
-the one above it
-.TP
-.B Mod-Shift-space
-.B Toggle
-the current
-.B window
-between the managed and floating
-.B layer
-.TP
-.BI Mod-Shift-t \ tag
-Move the current window to the
-.B view
-of the given
-.I tag
-.TP
-.B Mod-Shift-[0-9]
-Move to the current window to the
-.B view
-with the given number
-.PD 1
-.P
-.RE
-.B Miscellaneous
-.RS 2
-.PD 0
-.TP 16
-.I Key
-.I Action
-.TP
-.B Mod-m
-Switch the current column to
-.B max mode
-.TP
-.B Mod-s
-Switch the current column to
-.B stack mode
-.TP
-.B Mod-d
-Switch the current column to
-.B default mode
-.TP
-.B Mod-Shift-c
-.B Kill
-the selected client
-.TP
-.BI Mod-p \ program
-.B Execute
-.I program
-.TP
-.BI Mod-a \ action
-Execute the
-.B named action
-.TP
-.B Mod-Enter
-Start an
-.B xterm
-
-.SS Configuration
-If you feel the need to change the default configuration, then customize (as
-described above) the
-.B wmiirc
-action. This action is executed at the end of the
-.B wmii
-script and does all the work of setting up the window manager, the key
-bindings, the bar labels, etc.
-.SH FILES
-.TP
-/tmp/ns.$USER.${DISPLAY%.0}/wmii
-The wmii socket file which provides a 9P service.
-.TP
-CONFPREFIX/wmii-4
-Global action directory.
-.TP
-$HOME/.wmii-4
-User-specific action directory. Actions are first searched here.
-.SH ENVIRONMENT
-.TP
-HOME, DISPLAY
-See the section
-.B FILES
-above.
-.P
-The following variables are set and exported within
-.B wmii
-and thus can be used in actions:
-.TP
-PATH, OLD_PATH
-.B wmii
-adds the local and global actions directory to the front of the existing PATH.
-The original value is saved in OLD_PATH.
-.TP
-WMII_ADDRESS
-Socket file of
-.BR wmiiwm (1).
-Used by
-.BR wmiir (1).
-.SH SEE ALSO
-.BR wmiiwm (1),
-.BR dmenu (1),
-.BR wmiir (1)
diff --git a/cmd/wm/wmiiwm.1 b/cmd/wm/wmiiwm.1
@@ -1,162 +0,0 @@
-.TH WMIIWM 1 wmii-4
-.SH NAME
-wmiiwm \- window manager improved 2 (core)
-.SH SYNOPSIS
-.B wmiiwm
-.B \-a
-.I <address>
-.RB [ \-c ]
-.RB [ \-v ]
-.SH DESCRIPTION
-
-.PD 0
-.I The information in this manual page is
-.IR obselete .
-.P
-An update is not likely util
-.I wmii\-4
-nears release.
-
-.SS Overview
-.BR wmiiwm (1)
-is the core of window manager improved 2.
-.P
-.B wmii
-is a dynamic window manager for X11. In contrast to static window management
-the user rarely has to think about how to organize windows, no matter what he
-is doing or how many applications are used at the same time. The window manager
-adapts to the current environment and fits to the needs of the user, rather
-than forcing him to use a preset, fixed layout and trying to shoehorn all
-windows and applications into it.
-.P
-.B wmii
-supports classic and tiled window management with extended keyboard and mouse
-control. The classic window management arranges windows in a floating layer
-in which windows can be moved and resized freely. The tiled window management
-is based on columns which split up the screen horizontally. Each column handles
-arbitrary windows and arranges them vertically in a non\-overlapping way. They
-can then be moved and resized between and within columns at will.
-.P
-.B wmii
-provides a virtual filesystem which represents the internal state similar to
-the procfs of Unix operating systems. Modifying this virtual filesystem results
-in changing the state of the window manager. The virtual filesystem service can
-be accessed through 9P\-capable client programs, like
-.BR wmiir (1) .
-This allows simple and powerful remote control of the core window manager.
-.P
-.B wmii
-basically consists of clients, columns, views, and the bar, which are described
-in detail in the
-.B Terminology
-section.
-.SS Options
-.TP
-.BI \-a " address"
-Lets you specify the address which
-.B wmiiwm
-uses to listen for connections. The syntax for
-.I address
-is taken (along with many other profound ideas) from the Plan 9 operating
-system and has the form
-.B unix!/path/to/socket
-for unix socket files, and
-.B tcp!hostname!port
-for tcp sockets.
-.TP
-.B \-c
-Checks if another window manager is running. If not it exits with termination code
-0.
-.TP
-.B \-v
-Prints version information to stdout, then exits.
-.SS Terminology
-.TP 2
-Display
-A running X server instance consisting of input devices and screens.
-.TP 2
-Screen
-A physical or virtual (Xinerama or
-.BR Xnest (1))
-screen of an X display. A screen displays a bar window and a view at a time.
-.TP 2
-Window
-A (rectangular) drawable X object which is displayed on a screen, usually an
-application window.
-.TP 2
-Client
-An application window surrounded by a frame window containing a border and a
-title\-bar. The title\-bar contains three labels. The first one displays the tags
-of a client, the second one displays the client's title, and the third one
-displays the client's index and the total number of clients within the column.
-If the client is focused within the column, the third label is highlighted.
-If the client is attached to the floating layer, a ~ character is prepended in
-the third label as well.
-.TP 2
-Floating layer
-A screen layer of
-.B wmii
-on top of all other layers, where clients are arranged in a classic (floating)
-way. They can be resized or moved freely.
-.TP 2
-Managed layer
-A screen layer of
-.B wmii
-behind the floating layer, where clients are arranged in a non\-overlapping
-(managed) way. Here, the window manager dynamically assigns each client a
-size and position. The managed layer consists of columns.
-.TP 2
-Tag
-Alphanumeric strings which can be assigned to a client. This provides a
-mechanism to group clients with similar properties. Clients can have one
-tag, e.g.
-.IR work ,
-or several tags, e.g.
-.IR work+mail .
-Tags are separated with the
-.I +
-character.
-.TP 2
-View
-A set of clients containing a specific tag, quite similiar to a workspace in
-other window managers. It consists of the floating and managed layers.
-.TP 2
-Column
-A column is a screen area which arranges clients vertically in a
-non\-overlapping way. Columns provide three different modes, which arrange
-clients with equal size, stacked, or maximized respectively. Clients can be
-moved and resized between and within columns freely.
-.TP 2
-Bar
-The bar at the bottom of the screen displays a label for each view and
-allows the creation of arbitrary user\-defined labels.
-.TP 2
-Event
-An event is a message which can be read from a special file in the filesystem
-of
-.BR wmiiwm ,
-such as a mouse button press, a key press, or a message written by a different
-9P\-client.
-.SS Basic window management
-Running a raw
-.B wmiiwm
-process without the
-.BR wmii (1)
-script provides basic window management capabilities already. However to use
-it effectively, remote control through its filesystem interface is necessary.
-By default it is only usable with the mouse in conjunction with the
-.I Mod1 (Alt)
-modifier key. Other interactions like customizing the style, killing or
-retagging clients, or grabbing keys cannot be achieved without accessing the
-filesystem.
-.P
-The filesystem can be accessed by connecting to the
-.I address
-of
-.B wmiiwm
-with any 9P\-capable client, like
-.BR wmiir (1).
-.SH SEE ALSO
-.BR wmii (1),
-.BR dmenu (1),
-.BR wmiir (1)
diff --git a/cmd/wmiimenu.1 b/cmd/wmiimenu.1
@@ -1,84 +0,0 @@
-.TH WMIIMENU 1 wmii-4
-.SH NAME
-wmiimenu \- window manager improved 2 menu
-.SH SYNOPSIS
-.B wmiimenu
-.RB [ \-v ]
-.RB [ \-t
-.IR title ]
-.SH DESCRIPTION
-.SS Overview
-.B wmiimenu
-is a generic, highly customizable, and efficient menu for the X Window System,
-originally designed for
-.BR wmii (1).
-It supports arbitrary, user defined menu contents.
-.SS Options
-.TP
-.B \-v
-prints version information to stdout, then exits.
-.TP
-.BI \-t " title"
-displays
-.I title
-above the menu.
-.SS Usage
-.B wmiimenu
-reads a list of newline-separated items from stdin and creates a menu.
-When the user selects an item or enters any text and presses Enter, his choice
-is printed to stdout and
-.B wmiimenu
-terminates.
-.SS Keyboard Control
-.B wmiimenu
-is completely controlled by the keyboard. The following keys are recognized:
-.TP 2
-Any printable character
-appends the character to the text in the input field. This works as a filter:
-only items containing this text will be displayed.
-.TP 2
-Left/Right (Control-p/Control-n)
-select the previous/next item.
-.TP 2
-Tab (Control-i)
-copy the selected item to the input field.
-.TP 2
-Enter (Control-j)
-confirm selection and quit (print the selected item to stdout).
-.TP 2
-Shift-Enter (Shift-Control-j)
-confirm selection and quit (print the text in the input field to stdout).
-.TP 2
-Escape (Control-[)
-quit without selecting an item.
-.TP 2
-Backspace (Control-h)
-remove enough characters from the input field to change its filtering effect.
-.TP 2
-Control-u
-remove all characters from the input field.
-.SS Exit codes
-.B wmiimenu
-returns
-.B 0
-if Enter is pressed on termination,
-.B 1
-if Escape is pressed.
-.SH ENVIRONMENT
-.TP
-WMII_FONT
-The X11 font used to display each item in the menu.
-.br
-Default: fixed
-.TP
-WMII_NORMCOLORS
-The foreground, background, and border colors of a label. Syntactically, three blank-separated color values of the form #RRGGBB are expected.
-.br
-Default: #222222 #eeeeee #666666
-.TP
-WMII_SELCOLORS
-Like WMII_NORMCOLORS, but for the selected label.
-.br
-Default: #ffffff #335577 #447799
-.SH SEE ALSO
-.BR wmii (1)
diff --git a/cmd/wmiir.1 b/cmd/wmiir.1
@@ -1,86 +0,0 @@
-.TH WMIIR 1 wmii-4
-.SH NAME
-wmiir \- window manager improved 2 remote
-.SH SYNOPSIS
-.B wmiir
-.RB [ \-a
-.IR address ]
-.I action
-.I file
-.br
-.B wmiir
-.B \-v
-.SH DESCRIPTION
-.SS Overview
-.B wmiir
-is a client to access the filesystem of
-.BR wmiiwm (1)
-from the command line or from shell
-scripts. It can be used to configure
-.BR wmii (1).
-.SS Options
-.TP
-.BI \-a " address"
-Lets you specify the address to which
-.B wmiir
-will establish a connection. If this option is not supplied, and the
-environment variable WMII_ADDRESS is set,
-.B wmiir
-will use this value as its address. Currently, the address can only be a
-unix socket file or a tcp socket. The syntax for
-.I address
-is taken (along with many other profound ideas) from the Plan 9 operating
-system and has the form
-.BR unix!/path/to/socket
-for unix socket files, and
-.BR tcp!hostname!port
-for tcp sockets.
-.TP
-.B \-v
-Prints version information to stdout, then exits.
-.TP
-The syntax of the actions is as follows:
-.TP
-.B write
-Writes the supplied data from stdin to
-.IR file,
-overwriting any previous data. The data to be written is arbitrary
-and only gains meaning (and restrictions) when it is interpreted by
-.BR wmiiwm (1).
-See
-.B EXAMPLES
-below.
-.TP
-.B create
-Creates file or directory but does not write any data. If the file exists,
-nothing is done.
-.TP
-.B read
-Reads file or directory contents
-.TP
-.B remove
-Removes file or directory tree
-.SH ENVIRONMENT
-.TP
-WMII_ADDRESS
-See above.
-.SH EXAMPLES
-.TP
-.B wmiir read /
-This prints the root directory of the wmii filesystem. For more information
-about the contents of this filesystem, see
-.BR wmiiwm (1).
-.TP
-.B echo -n quit | wmiir write /ctl
-Write 'quit' to the main control file of the wmii filesystem, effectively
-leaving wmii.
-.TP
-.B echo -n view 2 | wmiir write /ctl
-Bring into view all clients tagged '2'. To learn about clients and
-tags, see
-.BR wmiiwm (1).
-.SH SEE ALSO
-.BR wmii (1),
-.BR wmiiwm (1)
-
-http://www.cs.bell-labs.com/sys/man/5/INDEX.html
diff --git a/cmd/wmiir.c b/cmd/wmiir.c
@@ -1,324 +0,0 @@
-/*
- * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
- * See LICENSE file for license details.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <dirent.h>
-#include <time.h>
-
-#include <ixp.h>
-
-static IXPClient c = { 0 };
-
-static char version[] = "wmiir - " VERSION ", (C)opyright MMIV-MMVI Anselm R. Garbe\n";
-
-static void
-usage()
-{
- fprintf(stderr, "%s",
- "usage: wmiir [-a <address>] [-v] create | read | ls [-l] | remove | write <file>\n");
- exit(1);
-}
-
-static void
-write_data(unsigned int fid)
-{
- void *data = cext_emallocz(c.ofcall.iounit);
- unsigned long long offset = 0;
- unsigned int len = 0;
-
- while((len = read(0, data, c.ofcall.iounit)) > 0) {
- if(ixp_client_write(&c, fid, offset, len, data) != len) {
- fprintf(stderr, "wmiir: cannot write file: %s\n", c.errstr);
- break;
- }
- offset += len;
- }
- if(offset == 0) /* do an explicit empty write when no writing has been done yet */
- if(ixp_client_write(&c, fid, offset, 0, 0) != 0)
- fprintf(stderr, "wmiir: cannot write file: %s\n", c.errstr);
- free(data);
-}
-
-static int
-xcreate(char *file)
-{
- unsigned int fid;
- char *p = strrchr(file, '/');
-
- if(!p)
- p = file;
-
- fid = c.root_fid << 2;
- /* walk to bottom-most directory */
- *p = 0;
- if(ixp_client_walk(&c, fid, file) == -1) {
- fprintf(stderr, "wmiir: cannot walk to '%s': %s\n", file, c.errstr);
- return -1;
- }
- p++;
- if(ixp_client_create(&c, fid, p, IXP_DMWRITE, IXP_OWRITE) == -1) {
- fprintf(stderr, "wmiir: cannot create file '%s': %s\n", p, c.errstr);
- return -1;
- }
- if(!(c.ofcall.qid.type&P9DMDIR))
- write_data(fid);
- return ixp_client_close(&c, fid);
-}
-
-static int
-xwrite(char *file, unsigned char mode)
-{
- /* open */
- unsigned int fid = c.root_fid << 2;
- if(ixp_client_walkopen(&c, fid, file, mode) == -1) {
- fprintf(stderr, "wmiir: cannot open file '%s': %s\n", file, c.errstr);
- return -1;
- }
- write_data(fid);
- return ixp_client_close(&c, fid);
-}
-
-static int
-comp_stat(const void *s1, const void *s2)
-{
- Stat *st1 = (Stat *)s1;
- Stat *st2 = (Stat *)s2;
- return strcmp(st1->name, st2->name);
-}
-
-static void
-setrwx(long m, char *s)
-{
- static char *modes[] =
- {
- "---",
- "--x",
- "-w-",
- "-wx",
- "r--",
- "r-x",
- "rw-",
- "rwx",
- };
- strncpy(s, modes[m], 3);
-}
-
-static char *
-str_of_mode(unsigned int mode)
-{
- static char buf[16];
-
- if(mode & IXP_DMDIR)
- buf[0]='d';
- else
- buf[0]='-';
-
- buf[1]='-';
- setrwx((mode >> 6) & 7, &buf[2]);
- setrwx((mode >> 3) & 7, &buf[5]);
- setrwx((mode >> 0) & 7, &buf[8]);
- buf[11] = 0;
- return buf;
-}
-
-static char *
-str_of_time(unsigned int val)
-{
- static char buf[32];
- time_t t = (time_t)(int)val;
- char *tstr = ctime(&t);
- cext_strlcpy(buf, tstr ? tstr : "in v a l id ", sizeof(buf));
- buf[strlen(buf) - 1] = 0;
- return buf;
-}
-
-static void
-print_stat(Stat *s, int details)
-{
- if(details)
- fprintf(stdout, "%s %s %s %5llu %s %s\n", str_of_mode(s->mode),
- s->uid, s->gid, s->length, str_of_time(s->mtime), s->name);
- else {
- if(s->mode & IXP_DMDIR)
- fprintf(stdout, "%s/\n", s->name);
- else
- fprintf(stdout, "%s\n", s->name);
- }
-}
-
-static void
-xls(void *result, unsigned int msize, int details)
-{
- unsigned int n = 0, i = 0;
- unsigned char *p = result;
- Stat *dir;
- static Stat stat;
-
- do {
- ixp_unpack_stat(&p, nil, &stat);
- n++;
- }
- while(p - (unsigned char*)result < msize);
- dir = (Stat *)cext_emallocz(sizeof(Stat) * n);
- p = result;
- do {
- ixp_unpack_stat(&p, nil, &dir[i++]);
- }
- while(p - (unsigned char*)result < msize);
- qsort(dir, n, sizeof(Stat), comp_stat);
- for(i = 0; i < n; i++)
- print_stat(&dir[i], details);
- free(dir);
- fflush(stdout);
-}
-
-static int
-xdir(char *file, int details)
-{
- unsigned int fid = c.root_fid << 2;
- /* XXX: buffer overflow */
- Stat *s = cext_emallocz(sizeof(Stat));
- unsigned char *buf;
- int count;
- static unsigned char result[IXP_MAX_MSG];
- void *data = nil;
- unsigned long long offset = 0;
-
- if(ixp_client_stat(&c, fid, file) == -1) {
- fprintf(stderr, "wmiir: cannot stat file '%s': %s\n", file, c.errstr);
- return -1;
- }
- buf = c.ofcall.stat;
- ixp_unpack_stat(&buf, nil, s);
- if(!(s->mode & IXP_DMDIR)) {
- print_stat(s, details);
- fflush(stdout);
- return 0;
- }
-
- /* directory */
- if(ixp_client_open(&c, fid, IXP_OREAD) == -1) {
- fprintf(stderr, "wmiir: cannot open directory '%s': %s\n", file, c.errstr);
- return -1;
- }
- while((count = ixp_client_read(&c, fid, offset, result, IXP_MAX_MSG)) > 0) {
- data = cext_erealloc(data, offset + count);
- memcpy(data + offset, result, count);
- offset += count;
- }
- if(count == -1) {
- fprintf(stderr, "wmiir: cannot read directory '%s': %s\n", file, c.errstr);
- return -1;
- }
- if(data)
- xls(data, offset + count, details);
- return ixp_client_close(&c, fid);
-}
-
-static int
-xread(char *file)
-{
- unsigned int fid = c.root_fid << 2;
- int count;
- static unsigned char result[IXP_MAX_MSG];
- unsigned long long offset = 0;
-
- if(ixp_client_walkopen(&c, fid, file, IXP_OREAD) == -1) {
- fprintf(stderr, "wmiir: cannot open file '%s': %s\n", file, c.errstr);
- return -1;
- }
-
- while((count = ixp_client_read(&c, fid, offset, result, IXP_MAX_MSG)) > 0) {
- write(1, result, count);
- offset += count;
- }
- if(count == -1) {
- fprintf(stderr, "wmiir: cannot read file/directory '%s': %s\n", file, c.errstr);
- return -1;
- }
- return ixp_client_close(&c, fid);
-}
-
-static int
-xremove(char *file)
-{
- unsigned int fid;
-
- fid = c.root_fid << 2;
- if(ixp_client_remove(&c, fid, file) == -1) {
- fprintf(stderr, "wmiir: cannot remove file '%s': %s\n", file, c.errstr);
- return -1;
- }
- return 0;
-}
-
-int
-main(int argc, char *argv[])
-{
- int ret = 0, i = 0, details = 0;
- char *cmd, *file, *address = getenv("WMII_ADDRESS");
-
- /* command line args */
- if(argc < 2)
- usage();
-
- for(i = 1; (i < argc) && (argv[i][0] == '-'); i++) {
- switch (argv[i][1]) {
- case 'v':
- fprintf(stdout, "%s", version);
- exit(0);
- break;
- case 'a':
- if(i + 1 < argc)
- address = argv[++i];
- else
- usage();
- break;
- default:
- usage();
- break;
- }
- }
- cmd = argv[argc - 2];
- file = argv[argc - 1];
- if((details = !strncmp(cmd, "-l", 3))) {
- if(argc < 3)
- usage();
- if(strncmp(argv[argc - 3], "ls", 3))
- usage();
- cmd = argv[argc - 3];
- }
-
- if(!address) {
- fprintf(stderr, "%s", "wmiir: error: $WMII_ADDRESS not set\n");
- usage();
- }
-
- if(ixp_client_dial(&c, address, getpid()) == -1) {
- fprintf(stderr, "wmiir: %s\n", c.errstr);
- exit(1);
- }
-
- if(!strncmp(cmd, "create", 7))
- ret = xcreate(file);
- else if(!strncmp(cmd, "ls", 3))
- ret = xdir(file, details);
- else if(!strncmp(cmd, "read", 5))
- ret = xread(file);
- else if(!strncmp(cmd, "remove", 7))
- ret = xremove(file);
- else if(!strncmp(cmd, "write", 6))
- ret = xwrite(file, IXP_OWRITE);
- else
- usage();
-
- /* close socket */
- ixp_client_hangup(&c);
-
- return ret;
-}
diff --git a/column.c b/column.c
@@ -0,0 +1,429 @@
+/*
+ * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
+ * See LICENSE file for license details.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "wm.h"
+
+char *
+str_of_column_mode(int mode)
+{
+ switch(mode) {
+ case Coldefault: return "default"; break;
+ case Colstack: return "stack"; break;
+ case Colmax: return "max"; break;
+ default: break;
+ }
+ return nil;
+}
+
+int
+column_mode_of_str(char *arg)
+{
+ if(!strncmp("default", arg, 8))
+ return Coldefault;
+ if(!strncmp("stack", arg, 6))
+ return Colstack;
+ if(!strncmp("max", arg, 4))
+ return Colmax;
+ return -1;
+}
+
+static void
+relax_column(Area *a)
+{
+ unsigned int frame_size, yoff, h;
+ Frame *f;
+ int hdiff;
+ Bool fallthrough = False;
+
+ if(!a->frame)
+ return;
+
+ frame_size = 0;
+ for(f=a->frame; f; f=f->anext)
+ frame_size++;
+
+ switch(a->mode) {
+ case Coldefault:
+ h = a->rect.height / frame_size;
+ if(h < 2 * blitz_labelh(&def.font))
+ fallthrough = True;
+ break;
+ case Colstack:
+ h = a->rect.height - (frame_size - 1) * blitz_labelh(&def.font);
+ if(h < 3 * blitz_labelh(&def.font))
+ fallthrough = True;
+ default:
+ yoff = a->rect.y;
+ break;
+ }
+
+ if(fallthrough) {
+ for(f=a->frame; f; f=f->anext) {
+ f->rect.x = a->rect.x + (a->rect.width - f->rect.width) / 2;
+ f->rect.y = a->rect.y + (a->rect.height - f->rect.height) / 2;
+ //resize_client(f->client, &f->rect, True);
+ }
+ return;
+ }
+
+ /* some relaxing from potential increment gaps */
+ h = 0;
+ for(f=a->frame; f; f=f->anext) {
+ if(a->mode == Colmax) {
+ if(h < f->rect.height)
+ h = f->rect.height;
+ }
+ else
+ h += f->rect.height;
+ }
+
+ hdiff = a->rect.height - h;
+ if((a->mode == Coldefault) && (hdiff > 0)) {
+ int hx;
+ for(hx = 1; hx < hdiff; hx++)
+ for(f=a->frame; f && (hx < hdiff); f=f->anext) {
+ unsigned int tmp = f->rect.height;
+ f->rect.height += hx;
+ //resize_client(f->client, &f->rect, True);
+ hdiff -= (f->rect.height - tmp);
+ }
+ }
+
+ if(hdiff < 0)
+ hdiff = 0;
+ hdiff /= frame_size;
+ yoff = a->rect.y + hdiff / 2;
+ for(f=a->frame; f; f=f->anext) {
+ f->rect.y = yoff;
+ if(a->mode != Colmax || f == a->sel) {
+ f->rect.x = a->rect.x + (a->rect.width - f->rect.width) / 2;
+ yoff = f->rect.y + f->rect.height + hdiff;
+ }
+ //resize_client(f->client, &f->rect, True);
+ }
+}
+
+void
+scale_column(Area *a, float h)
+{
+ unsigned int yoff, frame_size = 0;
+ Frame *f;
+ unsigned int min_height = 2 * blitz_labelh(&def.font);
+ float scale, dy = 0;
+ int hdiff;
+
+ if(!a->frame)
+ return;
+
+ for(f=a->frame; f; f=f->anext, frame_size++)
+ dy += f->rect.height;
+
+ scale = h / dy;
+ yoff = 0;
+ for(f=a->frame; f; f=f->anext) {
+ f->rect.height *= scale;
+ if(!f->anext)
+ f->rect.height = h - yoff;
+ yoff += f->rect.height;
+ }
+
+ /* min_height can only be respected when there is enough space; the caller should guarantee this */
+ if(frame_size * min_height > h)
+ return;
+ yoff = 0;
+ for(f=a->frame, frame_size--; f; f=f->anext, frame_size--) {
+ if(f->rect.height < min_height)
+ f->rect.height = min_height;
+ else if((hdiff = yoff + f->rect.height - h + frame_size * min_height) > 0)
+ f->rect.height -= hdiff;
+ if(!f->anext)
+ f->rect.height = h - yoff;
+ yoff += f->rect.height;
+ }
+}
+
+void
+arrange_column(Area *a, Bool dirty)
+{
+ Frame *f;
+ unsigned int num_frames = 0, yoff = a->rect.y, h;
+ unsigned int min_height = 2 * blitz_labelh(&def.font);
+
+ if(a->floating || !a->frame)
+ return;
+
+ for(f=a->frame; f; f=f->anext)
+ num_frames++;
+
+ switch(a->mode) {
+ case Coldefault:
+ h = a->rect.height / num_frames;
+ if(h < min_height)
+ goto Fallthrough;
+ if(dirty) {
+ for(f=a->frame; f; f=f->anext)
+ f->rect.height = h;
+ }
+ scale_column(a, a->rect.height);
+ for(f=a->frame; f; f=f->anext) {
+ f->rect.x = a->rect.x;
+ f->rect.y = yoff;
+ f->rect.width = a->rect.width;
+ yoff += f->rect.height;
+ //resize_client(f->client, &f->rect, True);
+ }
+ break;
+ case Colstack:
+ h = a->rect.height - (num_frames - 1) * blitz_labelh(&def.font);
+ if(h < 3 * blitz_labelh(&def.font))
+ goto Fallthrough;
+ for(f=a->frame; f; f=f->anext) {
+ f->rect = a->rect;
+ f->rect.y = yoff;
+ if(f == a->sel)
+ f->rect.height = h;
+ else
+ f->rect.height = blitz_labelh(&def.font);
+ yoff += f->rect.height;
+ //resize_client(f->client, &f->rect, True);
+ }
+ break;
+Fallthrough:
+ case Colmax:
+ for(f=a->frame; f; f=f->anext) {
+ f->rect = a->rect;
+ if(f != a->sel) f->rect.x = screen->rect.width * 2;
+ //resize_client(f->client, &f->rect, True);
+ }
+ break;
+ default:
+ break;
+ }
+
+ relax_column(a);
+ flush_masked_events(EnterWindowMask);
+}
+
+static void
+match_horiz(Area *a, XRectangle *r)
+{
+ Frame *f;
+
+ for(f=a->frame; f; f=f->anext) {
+ f->rect.x = r->x;
+ f->rect.width = r->width;
+ //resize_client(f->client, &f->rect, True);
+ }
+}
+
+static void
+drop_resize(Frame *f, XRectangle *new)
+{
+ Area *west = nil, *east = nil, *a = f->area;
+ View *v = a->view;
+ Frame *north = nil, *south = nil;
+ unsigned int min_height = 2 * blitz_labelh(&def.font);
+ unsigned int min_width = screen->rect.width/NCOL;
+
+ for(west=v->area->next; west && west->next != a; west=west->next);
+ /* first managed area is indexed 1, thus (i > 1) ? ... */
+ east = a->next;
+
+ for(north=a->frame; north && north->anext != f; north=north->anext);
+ south = f->anext;
+
+ /* validate (and trim if necessary) horizontal resize */
+ if(new->width < min_width) {
+ if(new->x + new->width == f->rect.x + f->rect.width)
+ new->x = a->rect.x + a->rect.width - min_width;
+ new->width = min_width;
+ }
+ if(west && (new->x != f->rect.x)) {
+ if(new->x < 0 || new->x < (west->rect.x + min_width)) {
+ new->width -= (west->rect.x + min_width) - new->x;
+ new->x = west->rect.x + min_width;
+ }
+ } else {
+ new->width += new->x - a->rect.x;
+ new->x = a->rect.x;
+ }
+ if(east && (new->x + new->width != f->rect.x + f->rect.width)) {
+ if((new->x + new->width) > (east->rect.x + east->rect.width - min_width))
+ new->width = (east->rect.x + east->rect.width - min_width) - new->x;
+ } else
+ new->width = (a->rect.x + a->rect.width) - new->x;
+ if(new->width < min_width)
+ goto AfterHorizontal;
+
+ /* horizontal resize */
+ if(west && (new->x != a->rect.x)) {
+ west->rect.width = new->x - west->rect.x;
+ a->rect.width += a->rect.x - new->x;
+ a->rect.x = new->x;
+ match_horiz(a, &a->rect);
+ match_horiz(west, &west->rect);
+ relax_column(west);
+ }
+ if(east && (new->x + new->width != a->rect.x + a->rect.width)) {
+ east->rect.width -= new->x + new->width - east->rect.x;
+ east->rect.x = new->x + new->width;
+ a->rect.width = (new->x + new->width) - a->rect.x;
+ match_horiz(a, &a->rect);
+ match_horiz(east, &east->rect);
+ relax_column(east);
+ }
+AfterHorizontal:
+
+ /* skip vertical resize unless the column is in equal mode */
+ if(a->mode != Coldefault)
+ goto AfterVertical;
+
+ /* validate (and trim if necessary) vertical resize */
+ if(new->height < min_height) {
+ if(f->rect.height < min_height
+ && (new->y == f->rect.y || new->y + new->height == f->rect.y + f->rect.height))
+ goto AfterVertical;
+ if(new->y + new->height == f->rect.y + f->rect.height)
+ new->y = f->rect.y + f->rect.height - min_height;
+ new->height = min_height;
+ }
+ if(north && (new->y != f->rect.y))
+ if(new->y < 0 || new->y < (north->rect.y + min_height)) {
+ new->height -= (north->rect.y + min_height) - new->y;
+ new->y = north->rect.y + min_height;
+ }
+ if(south && (new->y + new->height != f->rect.y + f->rect.height)) {
+ if((new->y + new->height) > (south->rect.y + south->rect.height - min_height))
+ new->height = (south->rect.y + south->rect.height - min_height) - new->y;
+ }
+ if(new->height < min_height)
+ goto AfterVertical;
+
+ /* vertical resize */
+ if(north && (new->y != f->rect.y)) {
+ north->rect.height = new->y - north->rect.y;
+ f->rect.height += f->rect.y - new->y;
+ f->rect.y = new->y;
+ //resize_client(north->client, &north->rect, True);
+ //resize_client(f->client, &f->rect, True);
+ }
+ if(south && (new->y + new->height != f->rect.y + f->rect.height)) {
+ south->rect.height -= new->y + new->height - south->rect.y;
+ south->rect.y = new->y + new->height;
+ f->rect.y = new->y;
+ f->rect.height = new->height;
+ //resize_client(f->client, &f->rect, False);
+ //resize_client(south->client, &south->rect, True);
+ }
+AfterVertical:
+
+ relax_column(a);
+ focus_view(screen, v);
+}
+
+static Frame *
+frame_of_point(XPoint *pt)
+{
+ Area *a;
+ View *v = screen->sel;
+ Frame *f = nil;
+
+ if(!v)
+ return nil;
+
+ for(a=v->area->next; a && !ispointinrect(pt->x, pt->y, &a->rect);
+ a=a->next);
+ if(a)
+ for(f=a->frame; f && !ispointinrect(pt->x, pt->y, &f->rect);
+ f=f->anext);
+ return f;
+}
+
+static void
+drop_move(Frame *f, XRectangle *new, XPoint *pt)
+{
+ Area *tgt, *src;
+ Frame *ft;
+ View *v;
+
+ tgt = nil;
+ src = f->area;
+ v = src->view;
+
+ if(!pt)
+ return;
+
+ for(tgt=v->area->next; tgt && !ispointinrect(pt->x, pt->y, &tgt->rect);
+ tgt=tgt->next);
+ if(tgt) {
+ if(pt->x < 16) {
+ if((src->frame && src->frame->anext) || (src != v->area->next)) {
+ tgt = new_column(v, v->area->next, 0);
+ send_to_area(tgt, src, f);
+ }
+ }
+ else if(pt->x >= screen->rect.width - 16) {
+ if((src->frame && src->frame->anext) || src->next) {
+ for(tgt=src; tgt->next; tgt=tgt->next);
+ tgt = new_column(v, tgt, 0);
+ send_to_area(tgt, src, f);
+ }
+ }
+ else if(src != tgt) {
+ Client *c = f->client;
+ Bool before;
+ if(!(ft = frame_of_point(pt)) || (f == ft))
+ return;
+ before = pt->y < (ft->rect.y + ft->rect.height / 2);
+ send_to_area(tgt, src, f);
+
+ f = c->sel;
+ remove_frame(f);
+
+ if(before)
+ insert_frame(ft, f, True);
+ else
+ insert_frame(ft, f, False);
+
+ tgt->sel = f;
+ arrange_column(tgt, False);
+ }
+ else { /* !tgt */
+ if(!(ft = frame_of_point(pt)) || (f == ft))
+ return;
+ remove_frame(f);
+
+ if(pt->y < (ft->rect.y + ft->rect.height / 2))
+ insert_frame(ft, f, True);
+ else
+ insert_frame(ft, f, False);
+
+ tgt->sel = f;
+ arrange_column(tgt, False);
+ }
+ }
+}
+
+void
+resize_column(Client *c, XRectangle *r, XPoint *pt)
+{
+ Frame *f = c->sel;
+ if((f->rect.width == r->width) && (f->rect.height == r->height))
+ drop_move(f, r, pt);
+ else
+ drop_resize(f, r);
+}
+
+Area *
+new_column(View *v, Area *pos, unsigned int w) {
+ Area *a = create_area(v, pos, w);
+ if(!a)
+ return nil;
+ arrange_view(v);
+ return a;
+}
diff --git a/doc/Makefile b/doc/Makefile
@@ -1,25 +0,0 @@
-# window manager improved 2 user guide
-l2h_opts=-local_icons -scalable_fonts -split +1 -subdir -show_section_numbers -address "<br>Last update: $(shell date) by $$USER@$$HOSTNAME"
-
-SRC = guide_en.tex wmii.tex
-DVI = ${SRC:.tex=.dvi}
-PDF = ${SRC:.tex=.pdf}
-PS = ${SRC:.tex=.ps}
-HTML = ${SRC:.tex=.html}
-
-.SUFFIXES: .tex .dvi .pdf .ps .html
-
-all: ${DVI} ${PDF} #${PS} ${HTML}
-.tex.dvi:
- latex -interaction=batchmode $<
- latex -interaction=batchmode $<
-.tex.pdf:
- pdflatex $<
- pdflatex $<
-.dvi.ps:
- dvips -o $@ $<
-.tex.html:
- latex2html ${l2h_opts} $<
-clean:
- rm -f *.pdf *.ps *.dvi *.log *.aux *.out *.toc
- rm -rf guide_en
diff --git a/doc/guide_en.tex b/doc/guide_en.tex
@@ -1,1005 +0,0 @@
-%TODO: please mention the /def/rules mechanism!
-
-
-%guide to wmii-4
-%Copyright (C) 2005, 2006 by Steffen Liebergeld, Salva Peir\'o
-
-%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, version 2
-
-%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, write to the Free Software
-%Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-%02110-1301, USA.
-
-\documentclass[12pt,a4paper]{article} %options given to article are inherited to all packages
-\usepackage[latin1]{inputenc}
-\usepackage[left=3cm,top=2cm,right=2cm,bottom=3cm]{geometry}
-\usepackage{times}
-\usepackage{hyperref} % option [dvipdfm] disables clickable refs
-\hypersetup{pdftex, colorlinks=true, linkcolor=blue, filecolor=blue,
-pagecolor=blue, urlcolor=blue, pdfauthor={Steffen Liebergeld, Salva Pei\'ro},
-pdftitle={A Guide to wmii-4}}
-\usepackage{indentfirst,moreverb}
-% remove this if you want, it's just a matter of imposed imperialist cultures
-% so if I'm given the chance to choose I choose to indent the first paragraph
-% (I learn this way in the school, and don't want to relearn the British way)
-
-%% welcome to the dirty tricks section
-\newcommand{\hrefx}[1]{\href{#1}{#1}} % explicit \href
-% un'% below so latex2html can handle refs correctly (until a better solution is found)
-%\usepackage{html} % gives clickable refs to latex2html
-%\renewcommand{\href}[2]{\htmladdnormallink{#2}{#1}}
-%\renewcommand{\hrefx}[1]{\htmladdnormallink{#1}{#1}}
-%\renewcommand{\verbatiminput}[1]{\input{#1}}
-\renewcommand{\familydefault}{\sfdefault}
-\newcommand{\wmii}{\emph{wmii}}
-
-\newenvironment{itemize*}
- {\begin{itemize}
- \setlength{\itemsep}{0pt}
- \setlength{\parskip}{0pt}}
- {\end{itemize}}
-\date{\today}
-\author{
-Steffen\\Liebergeld \\\\
-\small{with help from}\\
-Salvador\\Peir\'o
-}
-
-\title{A Guide to wmii-4%
-\thanks{Thanks to the wmii community, in particular all the
-people mentioned at \href{http://suckless.org/index.php/WMII/People}{WMII/people}.}
-}
-%\email{stepardo@gmail.com \and saoret.one@gmail.com}
-
-\begin{document}
-
-\maketitle
-
-\tableofcontents
-
-\newpage
-
-\section{Abstract}
-
- \subsection{The purpose of this document}
-
- This document tries to be a good starting point for people new to
- \wmii-4. People who have used wmi, \wmii-2.5 or even ion will get
- to know what is new and different in \wmii-4, and people who have
- never used a tiling window manager before will fall in love with
- the new concept.
-
- \subsection{wmii---the second generation of window manager improved}
-
- \wmii-4 is a new kind of window manager. It is designed to have a
- small memory footprint, be extremely modularised and have as
- little code as possible, thus ensuring as few bugs as possible. In
- fact, one of our official goals is to not exceed $10 k$ lines of
- code~\footnote{ benefit of the $10 k$ SLOC restriction is that
- it's easier to read/understand, thus it's easier to use and get
- used to it}.
-
- \wmii{} tries to be very portable and to give the user as much
- freedom as possible.
-
- \wmii-4 is the third major release of the second generation of the
- window manager improved~\footnote{ the ii is actually the roman
- numeral for 2.}. \wmii{} first introduced a new paradigm in version
- 2.5, namely dynamic window management, that overcomes the
- limitations imposed by the WIMP paradigm (see also the companion
- \emph{wmii.tex}).
-
- \subsection{Target audience}
-
- I presume the reader already has experience with Unix, knows all
- the basic terminology and concepts like files and editors.
-
- I hope you are open minded towards new ideas, and willing to spend some
- time learning \wmii-4~\footnote{remember the refrain: ``nobody
- can teach you what you don't want to know''.}.
-
- If you only want to know how to operate \wmii-4 and are not
- interested in the inner workings or in scripting, you may read
- sections \ref{sec:conf&install}, \ref{sec:terms} and subsection
- \ref{subsec:firststeps} and skip the rest.
-
- However, to get the most out of \wmii-4 you should probably read
- the whole document ``sequentially'', i.e. from beginning to end.
- Another possibility is to read/consume the guide ``on demand'' as
- you notice you need more information or details to understand
- better some concept. We recommend you to read the introductory
- chapters first, use some time to get settled in the \wmii-world
- and read the scripting chapters later on.
-
- \section{Configuration and install}
- \label{sec:conf&install}
-
- \subsection{Obtaining wmii}
-
- \wmii{} is licensed under the MIT/X Consortium License, which
- basically means it is free software, and you are free to download
- it from \hrefx{http://suckless.org} free of charge~\footnote{ please have
- a look at \hrefx{http://suckless.org/repos/wmii/LICENSE} for
- details}.
-
- \subsection{Configuration and installation}
-
- First of all, have a look if there are binary packages of \wmii{} in
- your distribution. Debian, Ubuntu and Gentoo should already have
- good packages. If you found a trustworthy package, you may now safely
- skip this section.
-
- For all those who are still reading this, let me tell you that you are
- on the good side because if you grab the sources and compile them yourself
- you'll benefit from having everything in it's original place, which will
- ease your use of \wmii.
-
- \begin{enumerate}
-
- \item Uninstalling a previous version:
- \begin{verbatim}
- cd /path/to/wmii-previous
- make uninstall && make clean
- \end{verbatim}
-
- In case you're installing a newer version of \wmii, this is the
- first thing you should do otherwise you'll end up mixing
- binaries, configuration files and manual-pages of different and
- potentially incompatible versions.
-
- \item Unpack it:
- \begin{verbatim}
- tar xzf wmii-4.tar.gz
- cd wmii-4
- \end{verbatim}
-
- \item Edit the configuration:
- \begin{verbatim}
- vim config.mk
- \end{verbatim}
-
- The most important variable to set is the \verb+PREFIX+, which
- states, where you want \wmii-4 to be installed to. If you are unsure, keep the
- default, it won't break your system.
-
- \item Run make and make install:
- \begin{verbatim}
- make && make install
- \end{verbatim}
-
- \item Setup the X-server to start \wmii{} as your default window
- manager. You may do that by editing the file \emph{\~{}/.xinitrc}.
-
- \begin{verbatim}
- #!/bin/sh
- exec wmii
- \end{verbatim}
-
- % Not necessary, .xinitrc is sourced by xinit anyways
- %
- % Make sure that the \emph{\~{}/.xinitrc} is executable:
- %
- % \begin{verbatim}
- % chmod u+x ~/.xinitrc
- % \end{verbatim}
-
- \end{enumerate}
-
- Now you are finished. Please note that autoconf tools are not used
- for various reasons~\footnote{ read
- \hrefx{http://www.ohse.de/uwe/articles/aal.html} and
- \hrefx{http://lists.cse.psu.edu/archives/9fans/2003-November/029714.html}
- for further details}. Please don't ask the \wmii{} developers to use autoconf,
- they won't listen to you.
-
-
- \section{Terminology}
- \label{sec:terms}
-
- Before you actually start doing your first steps in \wmii, first the
- terminology has to be clarified.
-
- \subsection{Clients}
-
- A client is a program, that provides you a graphical user interface for
- a special purpose, e.g. a web browser, or a terminal.
-
- \subsection{Focus}
-
- The (input) focus is the client, which currently receives your input.
- In X11 exactly one client can get your input at a time. If you input
- some command into your terminal, the terminal window has the input focus,
- whereas all the other windows do not receive the input you enter.
-
- \subsection{Events}
-
- An event is a message generated by X server to notify X clients about
- states. For instance, X generates a button press event, if you click
- into a window.
-
- \subsection{Tags}
-
- A tag is an alphanumeric string you can associate to clients,
- which allows you to group clients in a natural way.
-
- In \wmii, there are no workspaces anymore. Instead, all clients matching a
- particular tag are displayed at a time. For instance, if you tag your
- browser and a terminal window with the tag ``web-browser'', and you request
- to view all clients matching this tag, \wmii{} will display your browser
- and the terminal on the screen.
- It is also possible to give clients multiple tags, which is described later.
-
- \subsection{View}
-
- A view is the set of displayed clients, which match a specific single tag.
- A view is pretty similar to the ``workspace'' metaphor in other window
- managers, though more powerful.
-
- Only one view can be visible at a time.
-
- Views are related to the tags, which are currently in use. You have exactly
- one view for each single tag, thus you can only view sets of clients which
- match an existing tag.
-
- If you destroy the last client with a tag, the view of this tag is
- destroyed.
-
- \subsection{Column}
-
- A column is a distinct part of a view, where clients are arranged
- automatically in a vertical direction.
-
- In \wmii, you are able to divide each view into different columns.
- You should be aware, that every column holds at least one client.
- As soon as you close the last client of a column, the column is
- destroyed automatically.
-
- % no, a view might contain floating clients only as well
- % Please be aware, that every view has at least one column or a floating window.
-
- \subsection{Layout}
-
- A layout is the arrangement of clients in a column.
- There are three different ways to arrange clients in a column.
- \paragraph{default} This layout arranges each client with
- equally vertical space fitting into the column's height.
- \paragraph{maximum} This layout arranges all clients with
- the same geometry as the column, showing only one of them at a time.
- \paragraph{stacking} This layout arranges all clients
- like a stack, where only the focused client is completely
- visible, and all other clients can be accessed through its title-bars.
- This is an alternative approach to \emph{tabbing}.
-
- \section{Getting started}
-
- Now it is time to start diving into the \wmii{} user experience. I suggest you
- to try everything described by yourself immediately, instead of first reading
- it, to avoid "memory leakage". It is very helpful, if you print this
- document on paper or have it available on a different screen, because you might not
- be able to view it during your first steps in \wmii.
-
- Note, that the \emph{MOD} key I am referring to, may resemble
- different keys on different systems. By default it is the
- \emph{Mod1} or \emph{Alt} key in X11. Normally this is the key labelled with
- \emph{Alt} on your keyboard.
-
- The notation \emph{MOD}-\emph{Key} means to press \emph{MOD} and
- \emph{Key} both at the same time.
-
- All key combinations can be freely configured, but for the sake of
- simplicity I'll stick with the default key combinations for this
- guide. You will learn how to alter the bindings in the
- section \ref{sec:scripting}.
-
- The default key combinations heavily use the home row navigation keys
- \emph{h} (left), \emph{j} (down), \emph{k} (up), and \emph{l} (right),
- which are associated with the specific direction.
-
- \subsection{First steps}
- \label{subsec:firststeps}
-
- Start your X session now. Since it is the first time you
- start \wmii, a window with a little tutorial will show up. You are
- free to read it, but you may also follow this guide :-)
-
- First of all, press \emph{MOD-Enter} to start an xterm. It will
- take half of the vertical space, so you have two equally arranged
- windows. If you press \emph{MOD-Enter} again, you have three
- windows that are arranged equally.
-
- To switch between the three windows, press
- \emph{MOD-j}, which cycles the focus between the three windows.
-
- You can also press \emph{MOD-k} to switch to the window above or
- \emph{MOD-j} to switch to the window below the current.
-
- Now look at the title-bars of those windows. They display
- important information: the first label contains the tag of
- the window. The second label displays the window's title.
-
- Similar information is displayed in the status-bar at the bottom. The
- first labels display the tags currently in use and highlight the currently
- selected view. On the right side some status information is displayed, by
- default the system load and the current time (see
- subsection~\ref{subsec:status} for details).
-
- \subsection{Using columns}
-
- As described earlier, \wmii{} uses columns to arrange your windows.
- Your view already consists of a single column.
- Next, you will create a new column.
-
- In \wmii{} columns always consists of at least a single client,
- thus to create a new column, you need at least two clients at hand.
-
- Now focus a client of your choice and press \emph{MOD-Shift-l},
- which moves the client rightwards. As you see, \wmii{} creates
- a new column by dividing the view horizontally in two equally big
- areas. The focused client has been moved into the new column.
-
- If you close the last client of a column, the column is destroyed
- immediately. If the last client of the current view is closed,
- the view will be removed accordingly as well.
-
- If you press \emph{MOD-j} to change focus, you will see that \wmii
- actually cycles the focus in the current column only.
-
- To change the focus to a different column, you can press \emph{MOD-l}
- (right) and \emph{MOD-h} (left) respectively.
-
- It is also possible to swap adjacent clients among columns. To swap
- clients leftwards, press \emph{MOD-Control-h}. To swap clients
- rightwards, press \emph{MOD-Control-l}.
-
- \subsection{What about layouts?}
-
- Layouts arrange clients in a column. They are related to a single
- column. Thus it is possible to have different columns in one view, each
- using another layout.
-
- The default layout arranges each client of the column with equally
- vertical space. You can enable this layout with \emph{MOD-d}
- (where the ``d'' stands for default) explicitly.
-
- The stacking layout can be enabled with \emph{MOD-s}. As you see now,
- there is only one client using as much space as possible, and only
- title-bars of the other clients displayed in the column. You can
- switch between the clients in the column using \emph{MOD-j}.
-
- The max-layout maximises all clients to the same geometry as the column.
- Only the focused client is displayed at a time, all other clients
- are behind it. You can switch between the clients with \emph{MOD-j}.
-
- \subsection{Floating layer}
-
- To handle clients in the classical way, like in conventional window
- managers, the so-called ``floating layer'' is used. Actually there are a
- bunch of clients which don't fit well into the tiled world, because they
- have been designed with the conventional window management in mind,
- for instance clients like the Gimp or xmms.
-
- While \wmii{} is a dynamic window manager, which does the window arrangement
- for you automatically, those old fashioned programs rely on the
- conventional window managing concept, where all the clients fly around on
- your desktop and you are forced to constantly order the mess.
-
- To attach such broken clients to the floating layer, you can toggle the
- focus between floating and managed layer through pressing \emph{MOD-Space}.
- The \emph{MOD-Shift-Space} shortcut toggles the focused window between
- floating and managed layer.
-
- Note, the floating layer is addressed as the zeroth-column internally.
-
- \subsection{Tags}
-
- Up to now all your clients were tagged with ``1'', and you only had
- this single view. But a single view does not scale well, once
- too many clients appear which are used for different unrelated tasks.
- Thus you might want to have a view per task, e.g. a view
- with your editor and your programming tools, another view
- with your browser, and a third view with your music jukebox.
-
- The good news is, that the tagging concept provides a very dynamic way to
- achieve such kinds of grouping.
-
- You can give the focused client another tag by pressing
- \emph{MOD-Shift-Number}, number being one of the numbers 0 to 9.
-
- You can then switch views through pressing \emph{MOD-Number}.
-
- Normally, whenever a new client appears, it automatically inherits the tag
- of the currently selected view.
-
- %% TODO: better tag handling (this is about to change in \wmii{} till
- %%version 3)
-
- Note, there are more powerful uses of tags you will learn about in the next
- chapter. You will then be able to assign multiple tags to one client and to
- use proper strings as tags.
-
- \subsection{How do I close a window?}
-
- Most X-clients have a menu option or button to be closed. For the rare
- cases they don't provide a mouse-driven way, like in most terminals,
- you can press \emph{MOD-Shift-c} to close a window.
-
- \subsection{How do I start programs?}
-
- You may start programs from a terminal. But \wmii{} contains a special
- keyboard-driven program menu, which is accessible through pressing
- \emph{MOD-p}. Please note, that the content of this menu is provided by a
- simple shell-script.
-
- You will see a list of programs. If you start typing, the
- menu will cut the list and only display items which match
- the input you entered so far (in that order). Whenever there is
- only one item left, the menu highlights it and you can start it
- by pressing \emph{Enter}. You are free to cancel any action by
- pressing \emph{ESC}.
-
- Thus, if you want to start firefox, just type ``fire'' and press
- enter~\footnote{On my system it is sufficient to type ``efo'' to
- start firefox ;-)}.
-
- \subsection{How do I quit wmii?}
- You can quit \wmii, by using the action's menu (\emph{MOD-a})
- and selecting the action ``quit''. That's all.
-
- \section{Looking under the hood}
-
- In this chapter you will learn how \wmii{} was designed, which ideas
- the \wmii{} developers followed and how it was implemented.
-
- \subsection{Dynamic window management}
-
- \wmii{} was designed around the new idea of dynamic window management.
- Dynamic window management means, that the window manager should make all
- decisions about window arrangement, thus taking most extra work away
- from the user and letting him concentrate on his work. This can also be
- seen as tacit window management.
-
- \subsection{Modularity---using distinct tools for distinct tasks}
-
- The developers of \wmii{} know about the most powerful ideas of
- Unix. One of them is the idea to use distinct tools for distinct
- tasks. By carefully designing the window manager, they were able to
- split the task into several smaller binaries, each with a distinct
- job.
-
- \subsection{The glue that puts it all together---9P}
-
- Programs in the Unix world usually communicate via buffers which are
- addressed by (file) descriptors, one of them are sockets.
-
- To create a lightweight but powerful communication protocol, the \wmii
- developers closely looked at the design of Plan9 and chose the 9P
- protocol.
-
- The basic ideas for configuring and running \wmii{} were taken from
- the Acme user interface for programmers of Plan9. Similar to Acme,
- \wmii{} provides a filesystem-interface, which can be accessed by
- 9P clients. This allows to interact with any different kind of
- application through a file system interface, which might be implemented
- in any programming language of choice. This also avoids to force
- client programmers to a specific programming language or paradigm.
-
- The interface of \wmii{} can be compared to the \emph{procfs} file
- system of the Linux kernel.
-
- If you want to interact with a running \wmii{} process, you can access its 9P
- file-system service through either using the bundled tool \emph{wmiir} or
- the 9P2000 kernel module of a Linux kernel later than 2.4.16+. Using the
- kernel module has the advantage to mount the filesystem of \wmii{} into your
- file system hierarchy directly, though it has the drawback that this need
- \emph{root} privileges.
-
- \subsection{Tools}
-
- This section provides a basic overview about the tools which are bundled
- with \wmii. But for a more detailed description, you should read the associated
- man page of the specific tool.
-
- \begin{description}
-
- \item
- \emph{wmiir} is a small tool we is used to access the
- virtual file-system service of \wmii{} remotely. It basically supports four
- operations:
-
- \begin{itemize*}
- \item read
- \item write
- \item remove
- \item create
- \end{itemize*}
-
- wmiir needs to know the address of the file-system service to work
- with. On startup, \wmii{} exports the \verb+WMII_ADDRESS+ environment variable,
- which points to the address of the file-system service of \wmii.
- This address can be:
- \begin{itemize*}
- \item a local unix socket address like \verb+unix!/path/to/socket+
- \item a tcp-capable socket address like \verb+tcp!hostname:port+
- \end{itemize*}
-
- If you want to work on a different filesystem, you may specify it
- manually with the \emph{-a address} command line option.
- A sample invocation looks like:
- \begin{verbatim}
- wmiir read /
- \end{verbatim}
- This command actually prints the contents of the root directory of the
- virtual file-system of \wmii.
-
- \item
- \emph{wmiimenu} is a generic keyboard-driven menu, which matches items
- through providing patterns. You may want to learn more about it by reading
- the man-page.
-
- \item
- \emph{wmiiwarp} is a tiny tool to warp the mouse pointer at specific
- coordinates on your screen.
-
- \item
- \emph{wmiiwm} is the main window manager binary. You may interact
- with its virtual file-system only.
-
- \item
- \emph{wmiipsel} prints the contents of the current X selection to
- STDOUT. This is useful for scripts.
-
- \end{description}
-
- \subsection{Conclusion}
-
- The virtual file-system service of \wmii{} and the tools presented enable
- you to fully control the window manager from scripts.
- You will see some examples in the next section
- \ref{sec:scripting}.
-
- \section{Scripting wmii}
- \label{sec:scripting}
-
- In this chapter you will see how to control \wmii{} through scripts. I will
- give you some pointers, that you can start scripting on your own.
-
- \subsection{Language}
-
- As mentioned earlier, the only requirement for interacting with \wmii{} is to
- access its file-system service. The easiest way to do this, is by using
- the wmiir tool. Thus shell scripts are the easiest way of adapting \wmii
- too fit your needs.
-
- Hence, you can control \wmii{} in any programming language you want. However,
- \wmii's default scripts are written in a subset of ``sh'' that is POSIX
- compliant, to keep \wmii{} as \emph{portable} as possible.
-
- To keep simplicity, the following examples will stick to ``sh'' as well,
- and don't depend on python, tcl, ruby, \dots
-
- % - who doesn't have a shell?, extra! we're giving them for free this week
-
- \subsection{Actions}
-
- In \wmii{} you may group certain tasks into \emph{actions}. Actions
- are nothing more than simple scripts which are located either in
- your local or in the default \wmii{} configuration
- directory~\footnote{ \texttt{\$CONFPREFIX} is set in
- \emph{config.mk} which points to \texttt{/usr/local/etc}
- or \texttt{\$HOME/.wmii-4} by default}.
- Through pressing \emph{MOD-a} you can open the actions menu. It works
- similar to the program menu, but only displays actions.
-
- You might want to add your own actions through writing shell scripts in the
- default \wmii{} configuration directory or in the \texttt{\$HOME/.wmii-4}.
-
- This works, because in the \wmii{} controlling script exports the variable
- \verb+$PATH+ as\\ \verb+$PATH=~/.wmii-4:$CONFPREFIX/wmii:$PATH+ before
- launching the wmiiwm, this way local user actions under
- \verb+~/.wmii-4+ take precedence over the defaults from
- \verb+$CONFPREFIX/wmii+ of the default actions.
-
- You may edit this file on the fly, which means you don't need to
- stop \wmii{} before editing. After you've finished editing, you may
- simply run wmiirc and the changes will take effect immediately.
- To do so, just open the actions menu (with pressing \emph{MOD-a}) and
- choose the \emph{wmiirc} action. It's also possible to launch \wmii{} actions
- directly from a terminal, which is a neat side effect that results from
- exporting \verb+$PATH+ in the \wmii{} startup script.
-
- \subsection{wmiirc}
-
- \emph{wmiirc} is a special ``sh''-script which is launched on startup
- of \wmii{} to take care of configuring and controlling \wmii.
-
- It does so through writing data to several files of the virtual \wmii
- file-system, and through reading events reported by \wmii{} during runtime.
- Events are mostly shortcut presses, mouse clicks or user-defined.
- The events are processed in a loop in the script.
-
- Thus, for the basic configuration of \wmii, like changing the default
- modifier key \emph{MOD=Mod1} or the navigation keys this script is
- the place to look at.
-
- The name \emph{wmiirc} means \wmii{} \emph{run command}, because ``rc'' is an
- old Unix abbreviation for ``run command''.
-
- \subsection{Changing the style}
-
- The style of \wmii-4 is defined through font and colour values, which are
- unobtrusively exported with the following \emph{environment variables}.
-
- \begin{verbatim}
- WMII_SELCOLORS='#000000 #eaffff #8888cc'
- WMII_NORMCOLORS='#000000 #ffffea #bdb76b'
- WMII_FONT=static
- \end{verbatim}
-
- \verb+WMII_SELCOLORS+ defines the colours of the selected client's window
- title and border, whereas \verb+WMII_NORMCOLORS+ defines the colours of all
- unselected clients. The numbers are hexadecimal rgb tuple-values, which you
- might know from HTML. You can grab them with the Gimps colour-chooser for instance.
-
- The first colour defines the text colour of strings in bars and menus.
- The second colour defines the background colour of bars and clients, and
- the third colour defines the 1px borders surrounding bars and clients.
-
- \verb+WMII_FONT+ defines the font which should be used for drawing text.
- in title-bars, the status-bar, and the wmiimenu.
- You can query for different fonts using \emph{xfontsel} for instance.
-
- \subsection{Filling the status-bar}
- \label{subsec:status}
-
- The status bar of \wmii{} has it's own \verb+/bar+ directory with
- a subdirectory for each of the labels created. So while editing
- this document my status-bar looked like:
-
- \begin{verbatim}
- $ wmiir read /bar
- d-r-x------ salva salva 0 Mon Apr 17 14:19:51 2006 1
- d-r-x------ salva salva 0 Mon Apr 17 14:19:51 2006 2
- d-r-x------ salva salva 0 Mon Apr 17 14:19:51 2006 status
- \end{verbatim}
-
- At the same time each of the subdirectories contains two files,
-
- \begin{verbatim}
- $ wmiir read /bar/status
- --rw------- salva salva 23 Mon Apr 17 14:22:14 2006 colors
- --rw------- salva salva 23 Mon Apr 17 14:22:14 2006 data
- \end{verbatim}
-
-
- The first file contains the colour definitions that control how the
- bar will be drawn, while the second contains the data
- which is displayed.
-
- Now you can start your own experiments by creating a new label, and
- exploring and modifying it by reading \& writing values to it's
- \verb+colors+ \& \verb+data+ files. A nice feature of the bar
- (and clients) is that they generate events corresponding to mouse
- clicks on them. You can open a terminal and run
- \verb+wmiir read /event+ to see how the events are generated
- when you click onto the status-bar. This is a mechanism that allows
- controlling applications directly from the bar. If you've
- finished and you want to get rid of your label,
- a \verb+wmiir remove /bar/foo+ command.
-
- If you want to learn more, take a look at the status script and
- visit \hrefx{http://suckless.org} for good examples, like the following:
-
- \begin{itemize*}
- \item \emph{status}: monitoring remaining battery, temperature, \dots on laptops
- \item \emph{status-mpd}: controlling the running mpd
- \item \emph{status-load}: show the machine load
- \item \emph{status-net}: monitoring wireless network signal
- \end{itemize*}
-
- Now open the default status script and try to understand yourself,
- how it works \verbatiminput{../rc/status}. The first line is a handy
- \verb+xwrite+ function declaration, to prevent you from several verbosity
- when issuing a write to some file. The following 3 lines take care of
- creating and setting up the \verb+status+ label. The last section is a
- \verb+while+ loop
- that \emph{tries} to write the system load and date information
- to the bar.\\
-
- The tricky bit is, that it \emph{tries}. So what could make the write
- fail? If \verb+xwrite+ tried to write to a non existent (removed)
- label, then it would fail, thus the condition of the loop would be
- false, and the status script exits cleanly, which makes sense
- because nobody wants a program that updates a nonexistent label.\\
-
- Now go back to the first lines of the script, and note
- that there is a \verb+sleep delay+ between the removal of the
- label and it's creation.
-
- This ensures that the \verb+status+ label will not exist, hence all
- the writes made from any previously running \verb+status+ script
- will fail, thus they will finish. This way we make sure that
- we only run one status script at each time. And thus we keep the
- one-to-one correspondence between label and status script.
-
- \subsection{Assigning new tags}
-
- As mentioned before, you can achieve more powerful things with tags, than
- with the standard key-bindings. You might use any string as a tag. You may
- even use more than one tag per client. To do so, you have to separate the
- tags with a ``+''.
-
- \begin{verbatim}
- echo -n web+code | wmiir write /view/sel/sel/tags
- \end{verbatim}
-
- This command would give the current focused client the tags
- ``web'' and ``code''.
-
- You can now view the ``web'' tag by executing the following:
-
- \begin{verbatim}
- echo -n view web | wmiir write /ctl
- \end{verbatim}
-
- As the development of \wmii-4 progressed, it became clear that this
- action is so common, that it got its own keybinding. By default
- \emph{MOD-t} brings up a menu to choose a view and
- \emph{MOD-Shift-t} brings up a menu enabling you to assign new
- tags to the focused client.
-
- % TODO:
- % I would also like to put/see a recommended readings or bibliography
- % section this will be of course biased by my use of plumber and acme
- % from p9p but i think it's worth mentioning both them here. similar to:
- % http://suckless.org/contrib/guide-es/beginnersguide/node10.html
-
- \section{The End}
- \label{sec:end}
-
- We hope this helps you getting used to \wmii.
- If you've seen something that you think is wrong, confusing or missing in
- this document, feel free to drop us a note:
-
- Contact information is to be found here:
- \href{http://suckless.org/index.php/BeginnersGuide}{direct mail},
- \href{http://suckless.org/index.php/MailingList}{[wmii]} mailing-list,
- \href{http://suckless.org/index.php/IRC}{\#wmii} irc channel or even
- with smoke signals~\footnote{ but don't ask us for advice, here
- you're on your own \texttt{;-P}.}.
-
- Also remember that \wmii{} is written by people with taste, so most
- of the decisions made have strong reasons supporting them, so if
- you think something doesn't make sense or doesn't fit into the
- picture, just try to understand it by yourself first before
- asking, probably you'll end up learning a lot and if its really
- wrong in the end, you'll provide us with much better feedback to
- solve the issue.
-
- \newpage
-
- \section{Appendix}
- \label{sec:appendix}
-
- \subsection{filesystem}
-
- \begin{description}
-
- \item [/bar]
- \begin{itemize*}
- \item the bar namespace
- \item to add a label, create /bar/\verb+label+ (this automatically creates the
- \verb+colors+ and \verb+data+ files.
- \item to delete it, remove /bar/\verb+label+
- \end{itemize*}
-
- \item [/bar/label]
- \begin{itemize*}
- \item each bar has it's own namespace which is named according to it's
- label
- \item label can be any arbitrary string
- \end{itemize*}
-
- \item [/bar/label/data]
- \begin{itemize*}
- \item the data written to the label \verb+label+
- \end{itemize*}
-
- \item [/bar/label/colors]
- \begin{itemize*}
- \item the colours of the bar
- \end{itemize*}
-
- \item [/client]
- \begin{itemize*}
- \item the clients namespace
- \end{itemize*}
-
- \item [/client/n]
- \begin{itemize*}
- \item every client, including ones not shown in the view has a namespace here
- \item \verb+n+ is a non-negative integer and clients are numbered oldest first
- \end{itemize*}
-
- \item [/client/n/class]
- \begin{itemize*}
- \item a file containing the class:instance information for client \verb+n+
- \end{itemize*}
-
- \item [/client/n/ctl]
- \begin{itemize*}
- \item control file for client \verb+n+
- \item accepted commands:
- \begin{itemize*}
- \item kill (removes client)
- \item sendto \verb+area|prev|next|new+
- \item swap \verb+up|down|prev|next+
- \end{itemize*}
- \end{itemize*}
-
- \item [/client/n/geom]
- \begin{itemize*}
- \item the window geometry of client \verb+n+
- \item displayed as four blank separated integers (x y width height?)
- \end{itemize*}
-
- \item [/client/n/index] contains the client's index in the /client namespace, in this case n
-
- \item [/client/n/name]
- \begin{itemize*}
- \item the name of client \verb+n+ as it's seen by \wmii{} (displayed in the tagbar)
- \end{itemize*}
-
- \item [/client/n/tags]
- \begin{itemize*}
- \item a plus(+)-separated list of tags to which client \verb+n+ is related
- \end{itemize*}
-
- \item [/ctl]
- \begin{itemize*}
- \item the \wmii{} control file and command interface
- \item accepted commands:
- \begin{itemize*}
- \item quit
- \item retag
- \item view \verb+tag+
- \end{itemize*}
- \end{itemize*}
-
- \item [/def]
- \item [/def/border] width of the border around clients
- \item [/def/font] the font used by \wmii{} (an xlib font name)
- \item [/def/keys] a newline separated list of the keys to be grabbed by \wmii
- \item [/def/rules]
- \begin{itemize*}
- \item a newline separated list of rules to specify how to automatically tag clients
- \item matches against the class.instance values of a client
- \item syntax: \verb+/$regex/ -> $tag+ (tag might be \~{} to indicate floating mode
- \end{itemize*}
- \item [/def/colwidth] with of newly created columns (in px)
- \item [/def/colmode] mode of newly created columns
-
-
- \item [/view]
- \begin{itemize*}
- \item a manifestation of the collection of clients associated with a specific
- tag
- \end{itemize*}
-
- \item [/view/area]
- \begin{itemize*}
- \item each area has its own namespace in the current view
- \item the areas are numbered starting with 0
- \item 0 is always the floating area, the others are columns
- \end{itemize*}
-
- \item [/view/area/ctl]
- \begin{itemize*}
- \item accepted commands:
- \begin{itemize*}
- \item select \verb+client|prev|next+
- \item client refers to the client number relative to the number and
- age of the clients in \verb+area+
- \end{itemize*}
- \end{itemize*}
-
- \item [/view/area/mode] the current layout for the area, equal, stack or max
- \item [/view/area/sel] the selected client of the area
- \item [/view/area/client] the namespace for each client in \verb+area+
- \item [/view/ctl] accepted commands: select \verb+area|prev|next|toggle+
- \item [/view/sel] the selected area in workspace
- \item [/view/tag] the tag which is common to all clients in the workspace
-
- \end{description}
-
- \newpage
-
- \subsection{keybindings}
- Here are the default keybindings. \verb+$MODKEY+ is a placeholder, which is
- usually mapped to Mod1 or Alt.
- \begin{table}[h]
- \begin{tabular}{|l|l|}
- \hline % Puts in a horizontal line
- Keybinding &Action \\ %Table Headers , columns separated by &,
- %rows ended by \\
- \hline
- \hline
- Moving Focus&\\
- \verb+$MODKEY+-h&move focus to prev column \\
- \verb+$MODKEY+-l&move focus to next column \\
- \verb+$MODKEY+-j&move focus to next client in column \\
- \verb+$MODKEY+-k&move focus to prev client in column \\
- \verb+$MODKEY+-space&toggle to/from floating column 0 \\
- \verb+$MODKEY+-[0-9]&select tag/view [0-9] \\
- Moving Clients&\\
- \verb+$MODKEY+-Control-h&swap client with last client of prev column \\
- \verb+$MODKEY+-Control-l&swap client with last client of next column \\
- \verb+$MODKEY+-Control-j&swap client down in the column \\
- \verb+$MODKEY+-Control-k&swap client up in the column \\
- \verb+$MODKEY+-Shift-h&send client to prev column \\
- \verb+$MODKEY+-Shift-l&send client to next column \\
- \verb+$MODKEY+-Shift-space&send client to/from floating column 0 \\
- \verb+$MODKEY+-Shift-[0-9]&send client to tag/view [0-9] \\
- Layouts&\\
- \verb+$MODKEY+-d&select default layout \\
- \verb+$MODKEY+-s&select stacked layout \\
- \verb+$MODKEY+-m&select max layout \\
- Menu Bar Functions&\\
- \verb+$MODKEY+-a&choose action from menu bar \\
- \verb+$MODKEY+-p&choose program from menu bar \\
- \verb+$MODKEY+-t&choose view from menu bar \\
- \verb+$MODKEY+-Shift-t&assign tag(s) to client from menu bar \\
- Clients and Applications&\\
- \verb+$MODKEY+-Return&open terminal client \\
- \verb+$MODKEY+-Shift-c&kill client \\
- \hline
- \end{tabular}
- \caption{Default keybindings of \wmii}
- \end{table}
-
- \newpage
-
- \section{Credits}
- \label{sec:credits}
-
-
- \begin{description}
- \item [Author] Steffen Liebergeld
- \item [Main critic and inquisitor] Salvador Peir\'o
-
- \item [Helpers]
- Anselm Garbe \\
- Denis Grelich \\
- Ross Mohn \\
- Neptun Florin \\
- Jochen Schwartz \\
- Adrian Ratnapala \\
- \end{description}
-
- \section{Copyright notice}
-
- guide to wmii-4\\
- Copyright (C) 2005, 2006 by Steffen Liebergeld, Salva Peir\'o
-
- 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, version 2
-
- 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, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
-
-\end{document}
diff --git a/doc/wmii.svg b/doc/wmii.svg
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://web.resource.org/cc/"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- sodipodi:docbase="/home/zahod"
- sodipodi:docname="wmii9.svg"
- inkscape:version="0.41"
- sodipodi:version="0.32"
- version="1.0"
- x="0.0000000"
- y="0.0000000"
- width="240.00000px"
- height="120.00000px"
- id="svg2">
- <metadata
- id="metadata1296">
- <rdf:RDF
- id="RDF1298">
- <cc:Work
- id="Work1300"
- rdf:about="">
- <dc:format
- id="format1302">image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage"
- id="type1304" />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <sodipodi:namedview
- inkscape:current-layer="svg2"
- inkscape:window-y="0"
- inkscape:window-x="0"
- inkscape:cy="50.733714"
- inkscape:cx="133.35719"
- inkscape:zoom="1.6877337"
- inkscape:window-height="538"
- inkscape:window-width="640"
- inkscape:pageshadow="2"
- inkscape:pageopacity="0.0"
- borderopacity="1.0"
- bordercolor="#666666"
- pagecolor="#ffffff"
- id="base" />
- <defs
- id="defs3" />
- <g
- transform="translate(0.000000,-932.3622)"
- id="layer1">
- <path
- d="M 0.0000000,971.36227 L 26.999997,971.36227 L 26.999997,1025.3623 L 54.000004,1025.3623 L 54.000004,971.28555 L 81.310128,971.28555 L 81.310128,1025.3623 L 108.00001,1025.3623 L 108.00001,971.36227 L 240.00000,971.36227 L 240.00000,1052.3622 L 213.00001,1052.3622 L 213.00001,998.36218 L 186.00001,998.36218 L 186.00001,1052.3622 L 159.00001,1052.3622 L 159.00001,998.46222 L 132.01646,998.46222 L 132.01646,1052.3622 L 0.0000000,1052.3622 L 0.0000000,971.36227 z M 159.00001,932.36216 L 186.00001,932.36216 L 186.00001,959.36220 L 159.00001,959.36220 L 159.00001,932.36216 z M 213.00001,932.36216 L 240.00000,932.36216 L 240.00000,959.36220 L 213.00001,959.36220 L 213.00001,932.36216 z "
- style="fill:#010101"
- id="path1366" />
- </g>
-</svg>
diff --git a/doc/wmii.tex b/doc/wmii.tex
@@ -1,83 +0,0 @@
-% (C)opyright 2005 by Anselm R. Garbe
-\documentclass{article} \usepackage{times} \begin{document}
-
-\title{Improved GUI concepts for experienced users}
-
-\author{Anselm R. Garbe\\ \small garbeam at gmail dot com}
-
-\maketitle \thispagestyle{empty}
-
-\begin{abstract}
-This article presents the motivation and concepts of the dynamic
-window manager wmii and the graphical toolkit liblitz for the
-\it{X Window System}.
-\end{abstract}
-
-
-%-------------------------------------------------------------------------
-\section{Motivation}
-
-Most common graphical user interfaces are designed after the WIMP\cite{wimp}
-paradigm, which has dominated the desktop environment
-landscape since late 1980s. While research has been done on alternative
-user interfaces, often the focus targeted more in ease of use and low
-learning curves for new computer users rather than in efficiency and
-power of abstraction.
-
-The main reason has been the economical success of computers
-in the normal consumer market, which consists of unexperienced users mainly.
-Our motivation is to change this situation and to provide a graphical
-user interface for experienced users, though we know that this market is
-a niche.
-
-There has been done rarly research in the non-wimp GUI landscape for years.
-Back in 80s and early 90s there has been some research in
-this area for the Plan 9\cite{plan9} operating system at Bell Labs.
-Most recent research has been done by individuals only, like Tuomo Valkonen with
-his Ion\cite{ion} project and Lars Bernhardsson with his
-LarsWM\cite{larswm} project.
-
-The approaches found in the Plan 9 operating system are interesting, because
-on the one hand they cancelled the Unix tradition to work in Teletype emulators
-and on the other hand, they didn't followed the WIMP paradigm propagated by Apple,
-IBM or Microsoft. This makes Plan 9 the most unique approach compared to the
-classical WIMP world.
-
-The main aspects of an improved GUI consists of two things, a powerful
-window management approach and a sane and simple widget set which fits well
-into this window management approach.
-
-In the area of improved window management concepts there has been done more
-research, thus there appeared several different approaches. But the area
-of improved widget sets which form powerful applications with a simple widget
-set has been ignored for long time. Instead, the WIMP world introduced many
-widgets which seem to focus on eye-candy, like progress bars, but don't
-fix the essential problems with WIMP toolkits.
-
-
-
-\section{Future}
-
-
-\section*{Acknowlegdements} Following people provided useful feedback or several
-grammar fixes to this article:
-\begin{itemize}
-\item Frank Boehme (1st version of this article)
-\item Tuncer M zayamut Ayaz (1st version of this article)
-\end{itemize}
-
-\begin{thebibliography}{99}
-\bibitem{wimp} Ashley George Taylor, WIMP Interfaces, CS6751 Topic Report: Winter '97
-\bibitem{x} X Window System - http://www.freedesktop.org
-\bibitem{plan9} plan9 operating system - http://cm.bell-labs.com/plan9dist/
-\bibitem{acme} Rob Pike, Acme: A User Interface for Programmers -
-http://www.cs.bell-labs.com/sys/doc/acme/acme.html
-\bibitem{rat} Ratpoison window manager - http://www.nongnu.org/ratpoison/
-\bibitem{evil} evilwm window manager - http://evilwm.sf.net
-\bibitem{ion} Ion window manager - http://modeemi.cs.tut.fi/~tuomov/ion/
-\bibitem{larswm} LarsWM window manager - http://www.fnurt.net/larswm/
-\bibitem{vi} Vi Improved (VIM) - http://www.vim.org
-\bibitem{9p} 9P protocol - http://www.cs.bell-labs.com/sys/man/5/INDEX.html
-\end{thebibliography}
-
-\end{document}
diff --git a/event.c b/event.c
@@ -0,0 +1,314 @@
+/*
+ * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
+ * See LICENSE file for license details.
+ */
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <X11/keysym.h>
+
+#include "wm.h"
+
+/* local functions */
+static void buttonpress(XEvent *e);
+static void buttonrelease(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 keypress(XEvent *e);
+static void mappingnotify(XEvent *e);
+static void maprequest(XEvent *e);
+static void propertynotify(XEvent *e);
+static void unmapnotify(XEvent *e);
+
+void (*handler[LASTEvent]) (XEvent *) = {
+ [ButtonPress] = buttonpress,
+ [ButtonRelease] = buttonrelease,
+ [ConfigureRequest]= configurerequest,
+ [DestroyNotify] = destroynotify,
+ [EnterNotify] = enternotify,
+ [LeaveNotify] = leavenotify,
+ [Expose] = expose,
+ [KeyPress] = keypress,
+ [MappingNotify] = mappingnotify,
+ [MapRequest] = maprequest,
+ [PropertyNotify]= propertynotify,
+ [UnmapNotify] = unmapnotify
+};
+
+void
+check_x_event(IXPConn *c)
+{
+ XEvent ev;
+ while(XPending(blz.dpy)) { /* main event loop */
+ XNextEvent(blz.dpy, &ev);
+ if(handler[ev.type])
+ (handler[ev.type]) (&ev); /* call handler */
+ }
+}
+
+unsigned int
+flush_masked_events(long even_mask)
+{
+ XEvent ev;
+ unsigned int n = 0;
+ while(XCheckMaskEvent(blz.dpy, even_mask, &ev)) n++;
+ return n;
+}
+
+static void
+buttonrelease(XEvent *e)
+{
+ Frame *f;
+ Bar *b;
+ XButtonPressedEvent *ev = &e->xbutton;
+ if(ev->window == screen->barwin) {
+ for(b=screen->lbar; b; b=b->next)
+ if(ispointinrect(ev->x, ev->y, &b->brush.rect))
+ return write_event("LeftBarClick %d %s\n",
+ ev->button, b->name);
+ for(b=screen->rbar; b; b=b->next)
+ if(ispointinrect(ev->x, ev->y, &b->brush.rect))
+ return write_event("RightBarClick %d %s\n",
+ ev->button, b->name);
+ }
+ else if((f = frame_of_win(ev->window)))
+ write_event("ClientClick %d %d\n", idx_of_client(f->client), ev->button);
+}
+
+static void
+buttonpress(XEvent *e)
+{
+ Frame *f;
+ XButtonPressedEvent *ev = &e->xbutton;
+
+ if((f = frame_of_win(ev->window))) {
+ ev->state &= valid_mask;
+ if((ev->state & def.mod) == def.mod) {
+ focus(f->client, True);
+ switch(ev->button) {
+ case Button1:
+ do_mouse_resize(f->client, CENTER);
+ break;
+ case Button3:
+ do_mouse_resize(f->client, quadofcoord(&f->client->rect, ev->x, ev->y));
+ default:
+ break;
+ }
+ }
+ else if(ev->button == Button1)
+ focus(f->client, True);
+ }
+}
+
+static void
+configurerequest(XEvent *e)
+{
+ XConfigureRequestEvent *ev = &e->xconfigurerequest;
+ XWindowChanges wc;
+ XRectangle *frect;
+ Client *c;
+
+ c = client_of_win(ev->window);
+ ev->value_mask &= ~CWSibling;
+ if(c) {
+ gravitate_client(c, True);
+
+ if(ev->value_mask & CWX)
+ c->rect.x = ev->x;
+ if(ev->value_mask & CWY)
+ c->rect.y = ev->y;
+ if(ev->value_mask & CWWidth)
+ c->rect.width = ev->width;
+ if(ev->value_mask & CWHeight)
+ c->rect.height = ev->height;
+ if(ev->value_mask & CWBorderWidth)
+ c->border = ev->border_width;
+
+ gravitate_client(c, False);
+
+ if(c->frame) {
+ if(c->sel->area->floating)
+ frect=&c->sel->rect;
+ else
+ frect=&c->sel->revert;
+
+ if(c->rect.width >= screen->rect.width && c->rect.height >= screen->rect.height) {
+ frect->y = wc.y = -blitz_labelh(&def.font);
+ frect->x = wc.x = -def.border;
+ }
+ else {
+ frect->y = wc.y = c->rect.y - blitz_labelh(&def.font);
+ frect->x = wc.x = c->rect.x - def.border;
+ }
+ frect->width = wc.width = c->rect.width + 2 * def.border;
+ frect->height = wc.height = c->rect.height + def.border
+ + blitz_labelh(&def.font);
+ wc.border_width = 1;
+ wc.sibling = None;
+ wc.stack_mode = ev->detail;
+ if(c->sel->area->view != screen->sel)
+ wc.x += 2 * screen->rect.width;
+ if(c->sel->area->floating) {
+ XConfigureWindow(blz.dpy, c->framewin, ev->value_mask, &wc);
+ configure_client(c);
+ }
+ }
+ }
+
+ wc.x = ev->x;
+ wc.y = ev->y;
+ wc.width = ev->width;
+ wc.height = ev->height;
+
+ if(c && c->frame) {
+ wc.x = def.border;
+ wc.y = blitz_labelh(&def.font);
+ wc.width = c->sel->rect.width - 2 * def.border;
+ wc.height = c->sel->rect.height - def.border - blitz_labelh(&def.font);
+ }
+
+ wc.border_width = 0;
+ wc.sibling = None;
+ wc.stack_mode = Above;
+ ev->value_mask &= ~CWStackMode;
+ ev->value_mask |= CWBorderWidth;
+ XConfigureWindow(blz.dpy, ev->window, ev->value_mask, &wc);
+
+ XSync(blz.dpy, False);
+}
+
+static void
+destroynotify(XEvent *e)
+{
+ Client *c;
+ XDestroyWindowEvent *ev = &e->xdestroywindow;
+
+ if((c = client_of_win(ev->window)))
+ destroy_client(c);
+}
+
+static void
+enternotify(XEvent *e)
+{
+ XCrossingEvent *ev = &e->xcrossing;
+ Client *c;
+
+ if(ev->mode != NotifyNormal || ev->detail == NotifyInferior)
+ return;
+
+ if((c = client_of_win(ev->window))) {
+ Frame *f = c->sel;
+ Area *a = f->area;
+ if(a->mode == Colmax)
+ c = a->sel->client;
+ focus(c, False);
+ }
+ else if(ev->window == blz.root) {
+ sel_screen = True;
+ draw_frames();
+ }
+}
+
+static void
+leavenotify(XEvent *e)
+{
+ XCrossingEvent *ev = &e->xcrossing;
+
+ if((ev->window == blz.root) && !ev->same_screen) {
+ sel_screen = True;
+ draw_frames();
+ }
+}
+
+static void
+expose(XEvent *e)
+{
+ XExposeEvent *ev = &e->xexpose;
+ static Frame *f;
+
+ if(ev->count == 0) {
+ if(ev->window == screen->barwin)
+ draw_bar(screen);
+ else if((f = frame_of_win(ev->window)) && f->view == screen->sel)
+ draw_frame(f);
+ }
+}
+
+static void
+keypress(XEvent *e)
+{
+ XKeyEvent *ev = &e->xkey;
+ KeySym k = 0;
+ char buf[32];
+ int n;
+ static Frame *f;
+
+
+ ev->state &= valid_mask;
+ if((f = frame_of_win(ev->window))) {
+ buf[0] = 0;
+ n = XLookupString(ev, buf, sizeof(buf), &k, 0);
+ if(IsFunctionKey(k) || IsKeypadKey(k) || IsMiscFunctionKey(k)
+ || IsPFKey(k) || IsPrivateKeypadKey(k))
+ return;
+ buf[n] = 0;
+ }
+ else
+ kpress(blz.root, ev->state, (KeyCode) ev->keycode);
+}
+
+static void
+mappingnotify(XEvent *e)
+{
+ XMappingEvent *ev = &e->xmapping;
+
+ XRefreshKeyboardMapping(ev);
+ if(ev->request == MappingKeyboard)
+ update_keys();
+}
+
+static void
+maprequest(XEvent *e)
+{
+ XMapRequestEvent *ev = &e->xmaprequest;
+ static XWindowAttributes wa;
+
+ if(!XGetWindowAttributes(blz.dpy, ev->window, &wa))
+ return;
+
+ if(wa.override_redirect) {
+ XSelectInput(blz.dpy, ev->window,
+ (StructureNotifyMask | PropertyChangeMask));
+ return;
+ }
+
+ if(!client_of_win(ev->window))
+ manage_client(create_client(ev->window, &wa));
+}
+
+static void
+propertynotify(XEvent *e)
+{
+ XPropertyEvent *ev = &e->xproperty;
+ Client *c;
+
+ if(ev->state == PropertyDelete)
+ return; /* ignore */
+
+ if((c = client_of_win(ev->window)))
+ prop_client(c, ev);
+}
+
+static void
+unmapnotify(XEvent *e)
+{
+ Client *c;
+ XUnmapEvent *ev = &e->xunmap;
+
+ if((c = client_of_win(ev->window)))
+ destroy_client(c);
+}
diff --git a/frame.c b/frame.c
@@ -0,0 +1,117 @@
+/*
+ * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
+ * See LICENSE file for license details.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "wm.h"
+
+Frame *
+create_frame(Client *c, View *v)
+{
+ static unsigned short id = 1;
+ Frame *f = cext_emallocz(sizeof(Frame));
+
+ f->id = id++;
+ f->client = c;
+ f->view = v;
+
+ if(c->frame) {
+ f->revert = c->sel->revert;
+ f->rect = c->sel->rect;
+ }
+ else{
+ f->revert = f->rect = c->rect;
+ f->revert.width = f->rect.width += 2 * def.border;
+ f->revert.height = f->rect.height += def.border + blitz_labelh(&def.font);
+ }
+ f->collapsed = False;
+
+ f->tile.blitz = &blz;
+ f->tile.drawable = pmap;
+ f->tile.gc = c->gc;
+ f->tile.font = &def.font;
+ f->tile.color = def.normcolor;
+ f->tile.border = True;
+ f->grabbox = f->titlebar = f->tile;
+ f->titlebar.align = WEST;
+
+ return f;
+}
+
+void
+remove_frame(Frame *f)
+{
+ Area *a = f->area;
+ Frame **ft = &a->frame;
+
+ for(; *ft && *ft != f; ft=&(*ft)->anext);
+ cext_assert(*ft == f);
+ *ft = f->anext;
+}
+
+void
+insert_frame(Frame *pos, Frame *f, Bool before)
+{
+ Area *a = f->area;
+ if(before) {
+ Frame *ft;
+ for(ft=a->frame; ft && ft->anext != pos; ft=ft->anext);
+ pos=ft;
+ }
+ Frame **p = pos ? &pos->anext : &a->frame;
+ f->anext = *p;
+ *p = f;
+}
+
+void
+update_frame_widget_colors(Frame *f)
+{
+ if(sel_screen && (f->client == sel_client()))
+ f->tile.color = f->titlebar.color = def.selcolor;
+ else
+ f->tile.color = f->titlebar.color = def.normcolor;
+
+ if(f->area->sel == f)
+ f->grabbox.color = def.selcolor;
+ else
+ f->grabbox.color = def.normcolor;
+}
+
+void
+draw_frame(Frame *f)
+{
+ if(def.border) {
+ f->tile.rect = f->rect;
+ f->tile.rect.x = f->tile.rect.y = 0;
+ }
+
+ f->grabbox.rect = f->tile.rect;
+ f->grabbox.rect.height = blitz_labelh(&def.font);
+ f->grabbox.rect.width = def.font.height;
+
+ f->titlebar.rect = f->grabbox.rect;
+ f->titlebar.rect.x = f->grabbox.rect.x + f->grabbox.rect.width;
+ f->titlebar.rect.width = f->rect.width - f->titlebar.rect.x;
+
+ blitz_draw_tile(&f->tile);
+ blitz_draw_tile(&f->grabbox);
+ blitz_draw_label(&f->titlebar, f->client->name);
+ XCopyArea(blz.dpy, pmap, f->client->framewin, f->client->gc,
+ 0, 0, f->rect.width, f->rect.height, 0, 0);
+ XSync(blz.dpy, False);
+}
+
+void
+draw_frames()
+{
+ Client *c;
+ for(c=client; c; c=c->next)
+ if(c->sel && c->sel->view == screen->sel) {
+ update_frame_widget_colors(c->sel);
+ draw_frame(c->sel);
+ }
+}
+
diff --git a/fs.c b/fs.c
@@ -0,0 +1,905 @@
+/*
+ * (C)opyright MMVI Kris Maglione <fbsdaemon at gmail dot com>
+ * See LICENSE file for license details.
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "wm.h"
+
+/* Datatypes: */
+/**************/
+typedef struct Dirtab Dirtab;
+struct Dirtab {
+ char *name;
+ unsigned char qtype;
+ unsigned int type;
+ unsigned int perm;
+};
+
+typedef struct FidLink FidLink;
+struct FidLink {
+ FidLink *next;
+ Fid *fid;
+};
+
+typedef struct FileId FileId;
+struct FileId {
+ FileId *next;
+ union {
+ void *ref;
+ char *buf;
+ Bar *bar;
+ Bar **bar_p;
+ View *view;
+ Client *client;
+ Ruleset *rule;
+ BlitzColor *col;
+ };
+ unsigned int id;
+ unsigned int index;
+ Dirtab tab;
+ unsigned short nref;
+};
+
+/* Constants */
+/*************/
+enum { /* Dirs */
+ FsRoot, FsDClient, FsDClients, FsDBars,
+ FsDTag, FsDTags,
+ /* Files */
+ FsFBar, FsFCctl, FsFColRules,
+ FsFCtags, FsFEvent, FsFKeys, FsFRctl,
+ FsFTagRules, FsFTctl, FsFTindex,
+ FsFprops
+};
+
+/* Error messages */
+static char
+ *Enoperm = "permission denied",
+ *Enofile = "file not found",
+ *Ebadvalue = "bad value",
+ *Einterrupted = "interrupted",
+ *Ebadcmd = "bad command";
+
+/* Macros */
+#define QID(t, i) (((long long)((t)&0xFF)<<32)|((i)&0xFFFFFFFF))
+
+/* Global Vars */
+/***************/
+FileId *free_fileid = nil;
+P9Req *pending_event_reads = nil;
+FidLink *pending_event_fids;
+P9Srv p9srv = {
+ .open= fs_open,
+ .walk= fs_walk,
+ .read= fs_read,
+ .stat= fs_stat,
+ .write= fs_write,
+ .clunk= fs_clunk,
+ .flush= fs_flush,
+ .attach=fs_attach,
+ .create=fs_create,
+ .remove=fs_remove,
+ .freefid=fs_freefid
+};
+
+/* ad-hoc file tree. Empty names ("") indicate dynamic entries to be filled
+ * in by lookup_file */
+static Dirtab
+dirtab_root[]= {{".", P9QTDIR, FsRoot, 0500|P9DMDIR },
+ {"rbar", P9QTDIR, FsDBars, 0700|P9DMDIR },
+ {"lbar", P9QTDIR, FsDBars, 0700|P9DMDIR },
+ {"client", P9QTDIR, FsDClients, 0500|P9DMDIR },
+ {"tag", P9QTDIR, FsDTags, 0500|P9DMDIR },
+ {"ctl", P9QTAPPEND, FsFRctl, 0600|P9DMAPPEND },
+ {"colrules", P9QTFILE, FsFColRules, 0600 },
+ {"event", P9QTFILE, FsFEvent, 0600 },
+ {"keys", P9QTFILE, FsFKeys, 0600 },
+ {"tagrules", P9QTFILE, FsFTagRules, 0600 },
+ {nil}},
+dirtab_clients[]={{".", P9QTDIR, FsDClients, 0500|P9DMDIR },
+ {"", P9QTDIR, FsDClient, 0500|P9DMDIR },
+ {nil}},
+dirtab_client[]= {{".", P9QTDIR, FsDClient, 0500|P9DMDIR },
+ {"ctl", P9QTAPPEND, FsFCctl, 0600|P9DMAPPEND },
+ {"tags", P9QTFILE, FsFCtags, 0600 },
+ {"props", P9QTFILE, FsFprops, 0400 },
+ {nil}},
+dirtab_bars[]= {{".", P9QTDIR, FsDBars, 0700|P9DMDIR },
+ {"", P9QTFILE, FsFBar, 0600 },
+ {nil}},
+dirtab_tags[]= {{".", P9QTDIR, FsDTags, 0500|P9DMDIR },
+ {"", P9QTDIR, FsDTag, 0500|P9DMDIR },
+ {nil}},
+dirtab_tag[]= {{".", P9QTDIR, FsDTag, 0500|P9DMDIR },
+ {"ctl", P9QTAPPEND, FsFTctl, 0600|P9DMAPPEND },
+ {"index", P9QTFILE, FsFTindex, 0400 },
+ {nil}};
+/* Writing the lists separately and using an array of their references
+ * removes the need for casting and allows for C90 conformance,
+ * since otherwise we would need to use compound literals */
+static Dirtab *dirtab[] = {
+ [FsRoot] dirtab_root,
+ [FsDBars] dirtab_bars,
+ [FsDClients] dirtab_clients,
+ [FsDClient] dirtab_client,
+ [FsDTags] dirtab_tags,
+ [FsDTag] dirtab_tag,
+};
+
+/* Utility Functions */
+/*********************/
+
+/* get_file/free_file save and reuse old FileId structs
+ * since so many of them are needed for so many
+ * purposes */
+static FileId *
+get_file() {
+ FileId *temp;
+ if(!free_fileid) {
+ unsigned int i = 15;
+ temp = cext_emallocz(sizeof(FileId) * i);
+ for(; i; i--) {
+ temp->next = free_fileid;
+ free_fileid = temp++;
+ }
+ }
+ temp = free_fileid;
+ free_fileid = temp->next;
+ temp->nref = 1;
+ temp->next = nil;
+ return temp;
+}
+
+static void
+free_file(FileId *f) {
+ if(--f->nref)
+ return;
+ free(f->tab.name);
+ f->next = free_fileid;
+ free_fileid = f;
+}
+
+/* This function's name belies it's true purpose. It increases
+ * the reference counts of the FileId list */
+static void
+clone_files(FileId *f) {
+ for(; f; f=f->next)
+ cext_assert(f->nref++);
+}
+
+/* This should be moved to libixp */
+static void
+write_buf(P9Req *r, void *buf, unsigned int len) {
+ if(r->ifcall.offset >= len)
+ return;
+
+ len -= r->ifcall.offset;
+ if(len > r->ifcall.count)
+ len = r->ifcall.count;
+ r->ofcall.data = cext_emalloc(len);
+ memcpy(r->ofcall.data, buf + r->ifcall.offset, len);
+ r->ofcall.count = len;
+}
+
+/* This should be moved to libixp */
+void
+write_to_buf(P9Req *r, void *buf, unsigned int *len, unsigned int max) {
+ unsigned int offset, count;
+
+ offset = (r->fid->omode&P9OAPPEND) ? *len : r->ifcall.offset;
+ if(offset > *len || r->ifcall.count == 0) {
+ r->ofcall.count = 0;
+ return;
+ }
+
+ count = r->ifcall.count;
+ if(max && (count > max - offset))
+ count = max - offset;
+
+ *len = offset + count;
+
+ if(max == 0) {
+ *(void **)buf = cext_erealloc(*(void **)buf, *len + 1);
+ buf = *(void **)buf;
+ }
+
+ memcpy(buf + offset, r->ifcall.data, count);
+ r->ofcall.count = count;
+ ((char *)buf)[offset+count] = '\0';
+}
+
+/* This should be moved to libixp */
+void
+data_to_cstring(P9Req *r) {
+ unsigned int i;
+ i = r->ifcall.count;
+ if(!i || r->ifcall.data[i - 1] != '\n')
+ r->ifcall.data = cext_erealloc(r->ifcall.data, ++i);
+ cext_assert(r->ifcall.data);
+ r->ifcall.data[i - 1] = '\0';
+}
+
+/* This should be moved to liblitz */
+char *
+parse_colors(char **buf, int *buflen, BlitzColor *col) {
+ unsigned int i;
+ if(*buflen < 23 || 3 != sscanf(*buf, "#%06x #%06x #%06x", &i,&i,&i))
+ return Ebadvalue;
+ (*buflen) -= 23;
+ bcopy(*buf, col->colstr, 23);
+ blitz_loadcolor(&blz, col);
+
+ (*buf) += 23;
+ if(**buf == '\n' || **buf == ' ') {
+ (*buf)++;
+ (*buflen)--;
+ }
+ return nil;
+}
+
+char *
+message_root(char *message)
+{
+ unsigned int n;
+
+ if(!strchr(message, ' ')) {
+ snprintf(buffer, BUFFER_SIZE, "%s ", message);
+ message = buffer;
+ }
+
+ if(!strncmp(message, "quit ", 5))
+ srv.running = 0;
+ else if(!strncmp(message, "view ", 5))
+ select_view(&message[5]);
+ else if(!strncmp(message, "selcolors ", 10)) {
+ message += 10;
+ n = strlen(message);
+ return parse_colors(&message, (int *)&n, &def.selcolor);
+ }else if(!strncmp(message, "normcolors ", 11)) {
+ message += 11;
+ n = strlen(message);
+ return parse_colors(&message, (int *)&n, &def.normcolor);
+ }else if(!strncmp(message, "b1colors ", 9)) {
+ message += 9;
+ n = strlen(message);
+ return parse_colors(&message, (int *)&n, &def.bcolor[0]);
+ }else if(!strncmp(message, "b2colors ", 9)) {
+ message += 9;
+ n = strlen(message);
+ return parse_colors(&message, (int *)&n, &def.bcolor[1]);
+ }else if(!strncmp(message, "b3colors ", 9)) {
+ message += 9;
+ n = strlen(message);
+ return parse_colors(&message, (int *)&n, &def.bcolor[2]);
+ }else if(!strncmp(message, "font ", 5)) {
+ message += 5;
+ free(def.font.fontstr);
+ def.font.fontstr = cext_estrdup(message);
+ blitz_loadfont(&blz, &def.font);
+ }else if(!strncmp(message, "border ", 7)) {
+ message += 7;
+ n = (unsigned int)strtol(message, &message, 10);
+ if(*message)
+ return Ebadvalue;
+ def.border = n;
+ }else if(!strncmp(message, "grabmod ", 8)) {
+ message += 8;
+ unsigned long mod;
+ mod = mod_key_of_str(message);
+ if(!(mod & (Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)))
+ return Ebadvalue;
+ cext_strlcpy(def.grabmod, message, sizeof(def.grabmod));
+ def.mod = mod;
+ if(view)
+ restack_view(screen->sel);
+ }else
+ return Ebadcmd;
+
+ return nil;
+}
+
+char *
+read_root_ctl()
+{
+ unsigned int i = 0;
+ if(screen->sel)
+ i += snprintf(&buffer[i], (BUFFER_SIZE - i), "view %s\n", screen->sel->name);
+ i += snprintf(&buffer[i], (BUFFER_SIZE - i), "selcolors %s\n", def.selcolor.colstr);
+ i += snprintf(&buffer[i], (BUFFER_SIZE - i), "normcolors %s\n", def.normcolor.colstr);
+ i += snprintf(&buffer[i], (BUFFER_SIZE - i), "font %s\n", def.font.fontstr);
+ i += snprintf(&buffer[i], (BUFFER_SIZE - i), "grabmod %s\n", def.grabmod);
+ i += snprintf(&buffer[i], (BUFFER_SIZE - i), "border %d\n", def.border);
+ return buffer;
+}
+
+
+void
+respond_event(P9Req *r) {
+ FileId *f = r->fid->aux;
+ if(f->buf) {
+ r->ofcall.data = (void *)f->buf;
+ r->ofcall.count = strlen(f->buf);
+ respond(r, nil);
+ f->buf = nil;
+ }else{
+ r->aux = pending_event_reads;
+ pending_event_reads = r;
+ }
+}
+
+void
+write_event(char *format, ...) {
+ unsigned int len, slen;
+ va_list ap;
+ FidLink *f;
+ FileId *fi;
+ P9Req *aux;
+
+ va_start(ap, format);
+ vsnprintf(buffer, BUFFER_SIZE, format, ap);
+ va_end(ap);
+
+ if(!(len = strlen(buffer)))
+ return;
+ for(f=pending_event_fids; f; f=f->next) {
+ fi = f->fid->aux;
+ slen = fi->buf ? strlen(fi->buf) : 0;
+ fi->buf = cext_erealloc(fi->buf, slen + len + 1);
+ fi->buf[slen] = '\0';
+ strcat(fi->buf, buffer);
+ }
+ while((aux = pending_event_reads)) {
+ pending_event_reads = pending_event_reads->aux;
+ respond_event(aux);
+ }
+}
+
+static void
+dostat(Stat *s, unsigned int len, FileId *f) {
+ s->type = 0;
+ s->dev = 0;
+ s->qid.path = QID(f->tab.type, f->id);
+ s->qid.version = 0;
+ s->qid.type = f->tab.qtype;
+ s->mode = f->tab.perm;
+ s->atime = time(nil);
+ s->mtime = time(nil);
+ s->length = len;
+ s->name = f->tab.name;
+ s->uid = user;
+ s->gid = user;
+ s->muid = user;
+}
+
+/* lookup_file */
+/***************/
+/* All lookups and directory organization should be performed through
+ * lookup_file, mostly through the dirtabs[] tree. */
+static FileId *
+lookup_file(FileId *parent, char *name)
+{
+ FileId *ret, *file, **last;
+ Dirtab *dir;
+ Client *c;
+ View *v;
+ Bar *b;
+ unsigned int i, id;
+
+ if(!(parent->tab.perm & P9DMDIR))
+ return nil;
+
+ dir = dirtab[parent->tab.type];
+ last = &ret;
+ ret = nil;
+
+ for(; dir->name; dir++) {
+ /* Dynamic dirs */
+ if(!*dir->name) { /* strlen(dir->name) == 0 */
+ switch(parent->tab.type) {
+ case FsDClients:
+ if(!name || !strncmp(name, "sel", 4)) {
+ if((c = sel_client())) {
+ file = get_file();
+ *last = file;
+ last = &file->next;
+ file->ref = c;
+ file->id = c->id;
+ file->index = idx_of_client(c);
+ file->tab = *dir;
+ file->tab.name = cext_estrdup("sel");
+ }if(name) goto LastItem;
+ }
+ if(name) {
+ id = (unsigned int)strtol(name, &name, 10);
+ if(*name) goto NextItem;
+ }
+
+ i=0;
+ for(c=client; c; c=c->next, i++) {
+ if(!name || i == id) {
+ file = get_file();
+ *last = file;
+ last = &file->next;
+ file->ref = c;
+ file->id = c->id;
+ file->tab = *dir;
+ file->tab.name = cext_emallocz(16);
+ snprintf(file->tab.name, 16, "%d", i);
+ if(name) goto LastItem;
+ }
+ }
+ break;
+ case FsDTags:
+ if(!name || !strncmp(name, "sel", 4)) {
+ if(screen->sel) {
+ file = get_file();
+ *last = file;
+ last = &file->next;
+ file->ref = screen->sel;
+ file->id = screen->sel->id;
+ file->tab = *dir;
+ file->tab.name = cext_estrdup("sel");
+ }if(name) goto LastItem;
+ }
+ for(v=view; v; v=v->next) {
+ if(!name || !strcmp(name, v->name)) {
+ file = get_file();
+ *last = file;
+ last = &file->next;
+ file->ref = v;
+ file->id = v->id;
+ file->tab = *dir;
+ file->tab.name = cext_estrdup(v->name);
+ if(name) goto LastItem;
+ }
+ }
+ break;
+ case FsDBars:
+ for(b=*parent->bar_p; b; b=b->next) {
+ if(!name || !strcmp(name, b->name)) {
+ file = get_file();
+ *last = file;
+ last = &file->next;
+ file->ref = b;
+ file->id = b->id;
+ file->tab = *dir;
+ file->tab.name = cext_estrdup(b->name);
+ if(name) goto LastItem;
+ }
+ }
+ break;
+ }
+ }else /* Static dirs */
+ if(!name || !strcmp(name, dir->name)) {
+ file = get_file();
+ *last = file;
+ last = &file->next;
+ file->id = 0;
+ file->ref = parent->ref;
+ file->index = parent->index;
+ file->tab = *dir;
+ file->tab.name = cext_estrdup(file->tab.name);
+
+ /* Special considerations: */
+ switch(file->tab.type) {
+ case FsDBars:
+ if(!strncmp(file->tab.name, "lbar", 5))
+ file->ref = &screen[0].lbar;
+ else
+ file->ref = &screen[0].rbar;
+ break;
+ case FsFColRules:
+ file->ref = &def.colrules;
+ break;
+ case FsFTagRules:
+ file->ref = &def.tagrules;
+ break;
+ }
+ if(name) goto LastItem;
+ }
+ NextItem:
+ continue;
+ }
+LastItem:
+ *last = nil;
+ return ret;
+}
+
+/* Service Functions */
+/*********************/
+void
+fs_attach(P9Req *r) {
+ FileId *f = get_file();
+ f->tab = dirtab[FsRoot][0];
+ f->tab.name = cext_estrdup("/");
+ f->ref = nil; /* shut up valgrind */
+ r->fid->aux = f;
+ r->fid->qid.type = f->tab.qtype;
+ r->fid->qid.path = QID(f->tab.type, 0);
+ r->ofcall.qid = r->fid->qid;
+ respond(r, nil);
+}
+
+void
+fs_walk(P9Req *r) {
+ FileId *f, *nf;
+ int i;
+
+ f = r->fid->aux;
+
+ clone_files(f);
+ for(i=0; i < r->ifcall.nwname; i++) {
+ if(!strncmp(r->ifcall.wname[i], "..", 3)) {
+ if(f->next) {
+ nf=f;
+ f=f->next;
+ free_file(nf);
+ }
+ }else{
+ nf = lookup_file(f, r->ifcall.wname[i]);
+ if(!nf)
+ break;
+ cext_assert(!nf->next);
+ if(strncmp(r->ifcall.wname[i], ".", 2)) {
+ nf->next = f;
+ f = nf;
+ }
+ }
+ r->ofcall.wqid[i].type = f->tab.qtype;
+ r->ofcall.wqid[i].path = QID(f->tab.type, f->id);
+ }
+ /* There should be a way to do this on freefid() */
+ if(i < r->ifcall.nwname) {
+ while((nf = f)) {
+ f=f->next;
+ free_file(nf);
+ }
+ return respond(r, Enofile);
+ }
+
+ /* Remove refs for r->fid if no new fid */
+ /* If Fids were ref counted, this could be
+ * done in their decref function */
+ if(r->ifcall.fid == r->ifcall.newfid) {
+ nf=r->fid->aux;
+ r->fid->aux = f;
+ while((nf = f)) {
+ f=f->next;
+ free_file(nf);
+ }
+ }
+
+ r->newfid->aux = f;
+ r->ofcall.nwqid = i;
+ respond(r, nil);
+}
+
+unsigned int
+fs_size(FileId *f) {
+ switch(f->tab.type) {
+ default:
+ return 0;
+ case FsFColRules:
+ case FsFTagRules:
+ return f->rule->size;
+ case FsFKeys:
+ return def.keyssz;
+ case FsFCtags:
+ return strlen(f->client->tags);
+ case FsFprops:
+ return strlen(f->client->props);
+ }
+}
+
+void
+fs_stat(P9Req *r) {
+ Stat s;
+ int size;
+ unsigned char *buf;
+
+ dostat(&s, fs_size(r->fid->aux), r->fid->aux);
+ r->ofcall.nstat = size = ixp_sizeof_stat(&s);
+ buf = cext_emallocz(size);
+ r->ofcall.stat = buf;
+
+ ixp_pack_stat(&buf, &size, &s);
+ respond(r, nil);
+}
+
+void
+fs_read(P9Req *r) {
+ char *buf;
+ FileId *f, *tf;
+ int n, offset;
+ int size;
+
+ offset = 0;
+ f = r->fid->aux;
+
+ if(f->tab.perm & P9DMDIR && f->tab.perm & 0400) {
+ Stat s;
+ offset = 0;
+ size = r->ifcall.count;
+ buf = cext_emallocz(size);
+ r->ofcall.data = buf;
+
+ tf = f = lookup_file(f, nil);
+ /* Note: f->tab.name == "." so we skip it */
+ for(f=f->next; f; f=f->next) {
+ dostat(&s, fs_size(f), f);
+ n = ixp_sizeof_stat(&s);
+ if(offset >= r->ifcall.offset) {
+ if(size < n)
+ break;
+ ixp_pack_stat((unsigned char **)&buf, &size, &s);
+ }
+ offset += n;
+ }
+
+ while((f = tf)) {
+ tf=tf->next;
+ free_file(f);
+ }
+
+ r->ofcall.count = r->ifcall.count - size;
+ return respond(r, nil);
+ }else{
+ switch(f->tab.type) {
+ case FsFprops:
+ write_buf(r, (void *)f->client->props, strlen(f->client->props));
+ return respond(r, nil);
+ case FsFColRules:
+ case FsFTagRules:
+ write_buf(r, (void *)f->rule->string, f->rule->size);
+ return respond(r, nil);
+ case FsFKeys:
+ write_buf(r, (void *)def.keys, def.keyssz);
+ return respond(r, nil);
+ case FsFCtags:
+ write_buf(r, (void *)f->client->tags, strlen(f->client->tags));
+ return respond(r, nil);
+ case FsFTctl:
+ write_buf(r, (void *)f->view->name, strlen(f->view->name));
+ return respond(r, nil);
+ case FsFBar:
+ write_buf(r, (void *)f->bar->buf, strlen(f->bar->buf));
+ return respond(r, nil);
+ case FsFRctl:
+ buf = read_root_ctl();
+ write_buf(r, buf, strlen(buf));
+ return respond(r, nil);
+ case FsFCctl:
+ if(r->ifcall.offset)
+ return respond(r, nil);
+ r->ofcall.data = cext_emallocz(16);
+ n = snprintf(r->ofcall.data, 16, "%d", f->index);
+ cext_assert(n >= 0);
+ r->ofcall.count = n;
+ return respond(r, nil);
+ case FsFTindex:
+ buf = (char *)view_index(f->view);
+ n = strlen(buf);
+ write_buf(r, (void *)buf, n);
+ return respond(r, nil);
+ case FsFEvent:
+ respond_event(r);
+ return;
+ }
+ }
+ /* This is an assert because it should this should not be called if
+ * the file is not open for reading. */
+ cext_assert(!"Read called on an unreadable file");
+}
+
+/* This function needs to be seriously cleaned up */
+void
+fs_write(P9Req *r) {
+ FileId *f;
+ char *errstr = nil;
+ unsigned int i;
+
+ if(r->ifcall.count == 0)
+ return respond(r, nil);
+
+ f = r->fid->aux;
+ switch(f->tab.type) {
+ case FsFColRules:
+ case FsFTagRules:
+ write_to_buf(r, &f->rule->string, &f->rule->size, 0);
+ return respond(r, nil);
+ case FsFKeys:
+ write_to_buf(r, &def.keys, &def.keyssz, 0);
+ return respond(r, nil);
+ case FsFCtags:
+ data_to_cstring(r);
+ i=strlen(f->client->tags);
+ write_to_buf(r, &f->client->tags, &i, 255);
+ r->ofcall.count = i- r->ifcall.offset;
+ return respond(r, nil);
+ case FsFBar:
+ /* XXX: This should validate after each write */
+ i = strlen(f->bar->buf);
+ write_to_buf(r, &f->bar->buf, &i, 279);
+ r->ofcall.count = i - r->ifcall.offset;
+ return respond(r, nil);
+ case FsFCctl:
+ data_to_cstring(r);
+ if((errstr = message_client(f->client, r->ifcall.data)))
+ return respond(r, errstr);
+ r->ofcall.count = r->ifcall.count;
+ return respond(r, nil);
+ case FsFTctl:
+ data_to_cstring(r);
+ if((errstr = message_view(f->view, r->ifcall.data)))
+ return respond(r, errstr);
+ r->ofcall.count = r->ifcall.count;
+ return respond(r, nil);
+ case FsFRctl:
+ data_to_cstring(r);
+ { unsigned int n;
+ char *toks[32];
+ n = cext_tokenize(toks, 32, r->ifcall.data, '\n');
+ for(i = 0; i < n; i++) {
+ if(errstr)
+ message_root(toks[i]);
+ else
+ errstr = message_root(toks[i]);
+ }
+ }
+ if(errstr)
+ return respond(r, errstr);
+ r->ofcall.count = r->ifcall.count;
+ return respond(r, nil);
+ case FsFEvent:
+ if(r->ifcall.data[r->ifcall.count-1] == '\n')
+ write_event("%.*s", r->ifcall.count, r->ifcall.data);
+ else
+ write_event("%.*s\n", r->ifcall.count, r->ifcall.data);
+ r->ofcall.count = r->ifcall.count;
+ return respond(r, nil);
+ }
+ /* This is an assert because it should this should not be called if
+ * the file is not open for writing. */
+ cext_assert(!"Write called on an unwritable file");
+}
+
+void
+fs_open(P9Req *r) {
+ FidLink *fl;
+ FileId *f = r->fid->aux;
+ switch(f->tab.type) {
+ case FsFEvent:
+ fl = cext_emallocz(sizeof(FidLink));
+ fl->fid = r->fid;
+ fl->next = pending_event_fids;
+ pending_event_fids = fl;
+ break;
+ }
+ if((r->ifcall.mode&3) == P9OEXEC)
+ return respond(r, Enoperm);
+ if((r->ifcall.mode&3) != P9OREAD && !(f->tab.perm & 0200))
+ return respond(r, Enoperm);
+ if((r->ifcall.mode&3) != P9OWRITE && !(f->tab.perm & 0400))
+ return respond(r, Enoperm);
+ if((r->ifcall.mode&~(3|P9OAPPEND|P9OTRUNC)))
+ return respond(r, Enoperm);
+
+ respond(r, nil);
+}
+
+void
+fs_create(P9Req *r) {
+ FileId *f = r->fid->aux;
+ switch(f->tab.type) {
+ default:
+ /* XXX: This should be taken care of by the library */
+ return respond(r, Enoperm);
+ case FsDBars:
+ if(!strlen(r->ifcall.name))
+ return respond(r, Ebadvalue);
+ create_bar(f->bar_p, r->ifcall.name);
+ f = lookup_file(f, r->ifcall.name);
+ if(!f)
+ return respond(r, Enofile);
+
+ r->ofcall.qid.type = f->tab.qtype;
+ r->ofcall.qid.path = QID(f->tab.type, f->id);
+ f->next = r->fid->aux;
+ r->fid->aux = f;
+ respond(r, nil);
+ break;
+ }
+}
+
+void
+fs_remove(P9Req *r) {
+ FileId *f = r->fid->aux;
+ switch(f->tab.type) {
+ default:
+ /* XXX: This should be taken care of by the library */
+ return respond(r, Enoperm);
+ case FsFBar:
+ destroy_bar(f->next->bar_p, f->bar);
+ draw_bar(screen);
+ respond(r, nil);
+ break;
+ }
+}
+
+void
+fs_clunk(P9Req *r) {
+ Client *c;
+ FidLink **fl, *ft;
+ char *buf;
+ int i;
+ FileId *f = r->fid->aux;
+
+ switch(f->tab.type) {
+ case FsFColRules:
+ update_rules(&f->rule->rule, f->rule->string);
+ break;
+ case FsFTagRules:
+ update_rules(&f->rule->rule, f->rule->string);
+ for(c=client; c; c=c->next)
+ apply_rules(c);
+ update_views();
+ break;
+ case FsFKeys:
+ update_keys();
+ break;
+ case FsFCtags:
+ apply_tags(f->client, f->client->tags);
+ update_views();
+ draw_frame(f->client->sel);
+ break;
+ case FsFBar:
+ buf = f->bar->buf;
+ i = strlen(f->bar->buf);
+ parse_colors(&buf, &i, &f->bar->brush.color);
+ while(i > 0 && buf[i - 1] == '\n')
+ buf[--i] = '\0';
+ cext_strlcpy(f->bar->text, buf, sizeof(f->bar->text));
+ draw_bar(screen);
+ break;
+ case FsFEvent:
+ for(fl=&pending_event_fids; *fl; fl=&(*fl)->next)
+ if((*fl)->fid == r->fid) {
+ ft = *fl;
+ *fl = (*fl)->next;
+ f = ft->fid->aux;
+ free(f->buf);
+ free(ft);
+ break;
+ }
+ break;
+ }
+ respond(r, nil);
+}
+
+void
+fs_flush(P9Req *r) {
+ P9Req **t;
+ for(t=&pending_event_reads; *t; t=(P9Req **)&(*t)->aux)
+ if(*t == r->oldreq) {
+ *t = (*t)->aux;
+ respond(r->oldreq, Einterrupted);
+ break;
+ }
+ respond(r, nil);
+}
+
+void
+fs_freefid(Fid *f) {
+ FileId *id, *tid;
+
+ for(id=f->aux; id; id = tid) {
+ tid = id->next;
+ free_file(id);
+ }
+}
diff --git a/geom.c b/geom.c
@@ -0,0 +1,49 @@
+/*
+ * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
+ * See LICENSE file for license details.
+ */
+
+#include <math.h>
+
+#include "wm.h"
+
+Bool
+ispointinrect(int x, int y, XRectangle * r)
+{
+ return (x >= r->x) && (x <= r->x + r->width)
+ && (y >= r->y) && (y <= r->y + r->height);
+}
+
+BlitzAlign
+quadofcoord(XRectangle *rect, int x, int y)
+{
+ BlitzAlign ret = 0;
+ x -= rect->x;
+ y -= rect->y;
+
+ if(x >= rect->width * .5)
+ ret |= EAST;
+ if(x <= rect->width * .5)
+ ret |= WEST;
+ if(y <= rect->height * .5)
+ ret |= NORTH;
+ if(y >= rect->height * .5)
+ ret |= SOUTH;
+
+ return ret;
+}
+
+/* Syntax: <x> <y> <width> <height> */
+int
+strtorect(XRectangle *r, const char *val)
+{
+ XRectangle new;
+ if (!val)
+ return -1;
+
+ if(sscanf(val, "%hd %hd %hu %hu", &new.x, &new.y, &new.width, &new.height) != 4)
+ return -1;
+
+ *r = new;
+ return 0;
+}
diff --git a/key.c b/key.c
@@ -0,0 +1,265 @@
+/*
+ * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
+ * See LICENSE file for license details.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <X11/Xlib.h>
+#include <X11/keysym.h>
+
+#include "wm.h"
+
+void
+init_lock_keys()
+{
+ XModifierKeymap *modmap;
+ KeyCode num_lock;
+ static int masks[] = {
+ ShiftMask, LockMask, ControlMask, Mod1Mask,
+ Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
+ };
+ int i;
+
+ num_lock_mask = 0;
+ modmap = XGetModifierMapping(blz.dpy);
+ num_lock = XKeysymToKeycode(blz.dpy, XStringToKeysym("Num_Lock"));
+
+ if(modmap && modmap->max_keypermod > 0) {
+ int max = (sizeof(masks) / sizeof(int)) * modmap->max_keypermod;
+ for(i = 0; i < max; i++)
+ if(num_lock && (modmap->modifiermap[i] == num_lock))
+ num_lock_mask = masks[i / modmap->max_keypermod];
+ }
+ XFreeModifiermap(modmap);
+
+ valid_mask = 255 & ~(num_lock_mask | LockMask);
+}
+
+unsigned long
+mod_key_of_str(char *val)
+{
+ unsigned long mod = 0;
+ if (strstr(val, "Shift"))
+ mod |= ShiftMask;
+ if (strstr(val, "Control"))
+ mod |= ControlMask;
+ if (strstr(val, "Mod1"))
+ mod |= Mod1Mask;
+ if (strstr(val, "Mod2"))
+ mod |= Mod2Mask;
+ if (strstr(val, "Mod3"))
+ mod |= Mod3Mask;
+ if (strstr(val, "Mod4"))
+ mod |= Mod4Mask;
+ if (strstr(val, "Mod5"))
+ mod |= Mod5Mask;
+ return mod;
+}
+
+static void
+grab_key(Key *k)
+{
+ XGrabKey(blz.dpy, k->key, k->mod, blz.root,
+ True, GrabModeAsync, GrabModeAsync);
+ if(num_lock_mask) {
+ XGrabKey(blz.dpy, k->key, k->mod | num_lock_mask, blz.root,
+ True, GrabModeAsync, GrabModeAsync);
+ XGrabKey(blz.dpy, k->key, k->mod | num_lock_mask | LockMask, blz.root,
+ True, GrabModeAsync, GrabModeAsync);
+ }
+ XSync(blz.dpy, False);
+}
+
+static void
+ungrab_key(Key *k)
+{
+ XUngrabKey(blz.dpy, k->key, k->mod, blz.root);
+ if(num_lock_mask) {
+ XUngrabKey(blz.dpy, k->key, k->mod | num_lock_mask, blz.root);
+ XUngrabKey(blz.dpy, k->key, k->mod | num_lock_mask | LockMask, blz.root);
+ }
+ XSync(blz.dpy, False);
+}
+
+static Key *
+name2key(const char *name)
+{
+ Key *k;
+ for(k=key; k && strncmp(k->name, name, sizeof(k->name)); k=k->lnext);
+ return k;
+}
+
+static Key *
+get_key(const char *name)
+{
+ char buf[128];
+ char *seq[8];
+ char *kstr;
+ unsigned int i, toks;
+ static unsigned short id = 1;
+ Key *k = 0, *r = 0;
+
+ if((k = name2key(name))) {
+ ungrab_key(k);
+ return k;
+ }
+
+ cext_strlcpy(buf, name, sizeof(buf));
+ toks = cext_tokenize(seq, 8, buf, ',');
+
+ for(i = 0; i < toks; i++) {
+ if(!k)
+ r = k = cext_emallocz(sizeof(Key));
+ else {
+ k->next = cext_emallocz(sizeof(Key));
+ k = k->next;
+ }
+ cext_strlcpy(k->name, name, sizeof(k->name));
+ kstr = strrchr(seq[i], '-');
+ if(kstr)
+ kstr++;
+ else
+ kstr = seq[i];
+ k->key = XKeysymToKeycode(blz.dpy, XStringToKeysym(kstr));
+ k->mod = mod_key_of_str(seq[i]);
+ }
+ if(r) {
+ r->id = id++;
+ r->lnext = key;
+ key = r;
+ }
+
+ return r;
+}
+
+static void
+next_keystroke(unsigned long *mod, KeyCode *code)
+{
+ XEvent e;
+ KeySym sym;
+ *mod = 0;
+ do {
+ XMaskEvent(blz.dpy, KeyPressMask, &e);
+ *mod |= e.xkey.state & valid_mask;
+ *code = (KeyCode) e.xkey.keycode;
+ sym = XKeycodeToKeysym(blz.dpy, e.xkey.keycode, 0);
+ } while(IsModifierKey(sym));
+}
+
+static void
+emulate_key_press(unsigned long mod, KeyCode key)
+{
+ XEvent e;
+ Window client_win;
+ int revert;
+
+ XGetInputFocus(blz.dpy, &client_win, &revert);
+
+ e.xkey.type = KeyPress;
+ e.xkey.time = CurrentTime;
+ e.xkey.window = client_win;
+ e.xkey.display = blz.dpy;
+ e.xkey.state = mod;
+ e.xkey.keycode = key;
+ XSendEvent(blz.dpy, client_win, True, KeyPressMask, &e);
+ e.xkey.type = KeyRelease;
+ XSendEvent(blz.dpy, client_win, True, KeyReleaseMask, &e);
+ XSync(blz.dpy, False);
+}
+
+static Key *
+match_keys(Key *k, unsigned long mod, KeyCode keycode, Bool seq)
+{
+ Key *ret = nil, *next;
+ for(next = k->tnext; k; (k=next) && (next=k->tnext)) {
+ if(seq)
+ k = k->next;
+ if(k && (k->mod == mod) && (k->key == keycode)) {
+ k->tnext = ret;
+ ret = k;
+ }
+ }
+ return ret;
+}
+
+static void
+kpress_seq(Window w, Key *done)
+{
+ unsigned long mod;
+ KeyCode key;
+ Key *found;
+
+ next_keystroke(&mod, &key);
+
+ found = match_keys(done, mod, key, True);
+ if((done->mod == mod) && (done->key == key))
+ emulate_key_press(mod, key); /* double key */
+ else {
+ if(!found) {
+ XBell(blz.dpy, 0);
+ } /* grabbed but not found */
+ else if(!found->tnext && !found->next)
+ write_event("Key %s\n", found->name);
+ else
+ kpress_seq(w, found);
+ }
+}
+
+void
+kpress(Window w, unsigned long mod, KeyCode keycode)
+{
+ Key *k;
+
+ for(k=key; k; k->tnext=k->lnext, k=k->lnext);
+ Key *found = match_keys(key, mod, keycode, False);
+
+ if(!found) {
+ XBell(blz.dpy, 0);
+ } /* grabbed but not found */
+ else if(!found->tnext && !found->next)
+ write_event("Key %s\n", found->name);
+ else {
+ XGrabKeyboard(blz.dpy, w, True, GrabModeAsync, GrabModeAsync, CurrentTime);
+ kpress_seq(w, found);
+ XUngrabKeyboard(blz.dpy, CurrentTime);
+ XSync(blz.dpy, False);
+ }
+}
+
+void
+update_keys()
+{
+ Key *k, *n;
+ char *l, *p;
+
+ init_lock_keys();
+
+ while((k = key)) {
+ key = key->lnext;
+ ungrab_key(k);
+
+ while((n = k)) {
+ k = k->next;
+ free(n);
+ }
+ }
+
+ for(l = p = def.keys; p && *p;) {
+ if(*p == '\n') {
+ *p = 0;
+ if((k = get_key(l)))
+ grab_key(k);
+ *p = '\n';
+ l = ++p;
+ }
+ else
+ p++;
+ }
+ if(l < p && strlen(l)) {
+ if((k = get_key(l)))
+ grab_key(k);
+ }
+
+ XSync(blz.dpy, False);
+}
diff --git a/libcext/Makefile b/libcext/Makefile
@@ -1,28 +0,0 @@
-# libcext - c extensions for wmii project
-# (C)opyright MMIV-MMVI Anselm R. Garbe
-
-include ../config.mk
-
-SRC = assert.c malloc.c strlcat.c strlcpy.c tokenize.c \
- trim.c
-
-OBJ = ${SRC:.c=.o}
-
-.PREFIXES = .c .o
-
-all: libcext.a
- @echo built libcext
-
-.c.o:
- @echo CC $<
- @${CC} -c ${CFLAGS} $<
-
-$(OBJ): cext.h
-
-libcext.a: ${OBJ}
- @echo AR $@
- @${AR} $@ ${OBJ}
- @${RANLIB} $@
-
-clean:
- rm -f libcext.a *.o
diff --git a/libcext/assert.c b/libcext/assert.c
@@ -1,10 +0,0 @@
-/* Public Domain */
-#include <stdio.h>
-#include <stdlib.h>
-
-void
-cext_failed_assert(char *a, char *file, int line)
-{
- fprintf(stderr, "Assertion \"%s\" failed at %s:%d\n", a, file, line);
- abort();
-}
diff --git a/libcext/cext.h b/libcext/cext.h
@@ -1,38 +0,0 @@
-/*
- * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
- * See LICENSE file for license details.
- */
-
-#include <sys/types.h>
-
-#ifndef nil
-#define nil (void *)0
-#endif
-
-/* asprintf.c */
-extern int cext_asprintf(char **str, char const *fmt, ...);
-
-/* malloc.c */
-extern void *cext_emallocz(unsigned int size);
-extern void *cext_emalloc(unsigned int size);
-extern void *cext_erealloc(void *ptr, unsigned int size);
-extern char *cext_estrdup(const char *str);
-
-/* strlcat.c */
-extern unsigned int cext_strlcat(char *dst, const char *src, unsigned int siz);
-
-/* strlcpy.c */
-extern unsigned int cext_strlcpy(char *dst, const char *src, unsigned int siz);
-
-/* tokenize.c */
-extern unsigned int cext_tokenize(char **result, unsigned int reslen, char *str, char delim);
-
-/* trim.c */
-extern void cext_trim(char *str, const char *chars);
-
-/* assert.c */
-#define cext_assert(a) do { \
- if(!(a)) \
- cext_failed_assert(#a, __FILE__, __LINE__); \
- } while (0)
-extern void cext_failed_assert(char *a, char *file, int line);
diff --git a/libcext/malloc.c b/libcext/malloc.c
@@ -1,54 +0,0 @@
-/*
- * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
- * See LICENSE file for license details.
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "cext.h"
-
-static void
-bad_malloc(unsigned int size)
-{
- fprintf(stderr, "fatal: could not malloc() %d bytes\n",
- (int) size);
- exit(1);
-}
-
-void *
-cext_emallocz(unsigned int size)
-{
- void *res = calloc(1, size);
- if(!res)
- bad_malloc(size);
- return res;
-}
-
-void *
-cext_emalloc(unsigned int size)
-{
- void *res = malloc(size);
- if(!res)
- bad_malloc(size);
- return res;
-}
-
-void *
-cext_erealloc(void *ptr, unsigned int size)
-{
- void *res = realloc(ptr, size);
- if(!res)
- bad_malloc(size);
- return res;
-}
-
-char *
-cext_estrdup(const char *str)
-{
- void *res = strdup(str);
- if(!res)
- bad_malloc(strlen(str));
- return res;
-}
diff --git a/libcext/strlcat.c b/libcext/strlcat.c
@@ -1,55 +0,0 @@
-/*
- * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <sys/types.h>
-#include <string.h>
-
-#include "cext.h"
-
-/*
- * Appends src to string dst of size siz (unlike strncat, siz is the
- * full size of dst, not space left). At most siz-1 characters
- * will be copied. Always NUL terminates (unless siz <= strlen(dst)).
- * Returns strlen(src) + MIN(siz, strlen(initial dst)).
- * If retval >= siz, truncation occurred.
- */
-unsigned int
-cext_strlcat(char *dst, const char *src, unsigned int siz)
-{
- register char *d = dst;
- register const char *s = src;
- register unsigned int n = siz;
- unsigned int dlen;
-
- /* Find the end of dst and adjust bytes left but don't go past end */
- while(n-- != 0 && *d != 0)
- d++;
- dlen = d - dst;
- n = siz - dlen;
-
- if(n == 0)
- return (dlen + strlen(s));
- while(*s != 0) {
- if(n != 1) {
- *d++ = *s;
- n--;
- }
- s++;
- }
- *d = 0;
-
- return (dlen + (s - src)); /* count does not include NUL */
-}
diff --git a/libcext/strlcpy.c b/libcext/strlcpy.c
@@ -1,47 +0,0 @@
-/*
- * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <sys/types.h>
-
-#include "cext.h"
-
-/*
- * Copy src to string dst of size siz. At most siz-1 characters
- * will be copied. Always NUL terminates (unless siz == 0).
- * Returns strlen(src); if retval >= siz, truncation occurred.
- */
-unsigned int
-cext_strlcpy(char *dst, const char *src, unsigned int siz)
-{
- register char *d = dst;
- register const char *s = src;
- register unsigned int n = siz;
-
- /* Copy as many bytes as will fit */
- if(n != 0 && --n != 0) {
- do {
- if((*d++ = *s++) == 0)
- break;
- } while(--n != 0);
- }
- /* Not enough room in dst, add NUL and traverse rest of src */
- if(n == 0) {
- if(siz != 0)
- *d = '\0'; /* NUL-terminate dst */
- while(*s++);
- }
- return (s - src - 1); /* count does not include NUL */
-}
diff --git a/libcext/tokenize.c b/libcext/tokenize.c
@@ -1,34 +0,0 @@
-/*
- * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
- * See LICENSE file for license details.
- */
-
-#include <string.h>
-
-#include "cext.h"
-
-unsigned int
-cext_tokenize(char **result, unsigned int reslen, char *str, char delim)
-{
- char *p, *n;
- unsigned int i = 0;
-
- if(!str)
- return 0;
- for(n = str; *n == delim; n++);
- p = n;
- for(i = 0; *n != 0;) {
- if(i == reslen)
- return i;
- if(*n == delim) {
- *n = 0;
- if(strlen(p))
- result[i++] = p;
- p = ++n;
- } else
- n++;
- }
- if((i < reslen) && (p < n) && strlen(p))
- result[i++] = p;
- return i; /* number of tokens */
-}
diff --git a/libcext/trim.c b/libcext/trim.c
@@ -1,35 +0,0 @@
-/*
- * Copyright (c) MMVI Anselm R. Garbe <arg@suckless.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "cext.h"
-
-/*
- * Removes all characters in chars from str.
- */
-void
-cext_trim(char *str, const char *chars)
-{
- const char *cp;
- char *sp, *sn;
-
- for(cp = chars; *cp; cp++) {
- for(sp = sn = str; *sn; sn++) {
- if(*sn != *cp)
- *(sp++) = *sn;
- }
- *sp = 0;
- }
-}
diff --git a/libixp/Makefile b/libixp/Makefile
@@ -1,25 +0,0 @@
-# libixp - lib ixp protocol
-# (C)opyright MMIV-MMVI Anselm R. Garbe
-
-include ../config.mk
-
-CFLAGS += -I ../libcext
-
-SRC = client.c convert.c message.c server.c socket.c transport.c intmap.c request.c
-
-OBJ = ${SRC:.c=.o}
-
-all: libixp.a
- @echo built libixp
-
-.c.o:
- @echo CC $<
- @${CC} -c ${CFLAGS} $<
-
-libixp.a: ${OBJ}
- @echo AR $@
- @${AR} $@ ${OBJ}
- @${RANLIB} $@
-
-clean:
- rm -f libixp.a *.o
diff --git a/libixp/client.c b/libixp/client.c
@@ -1,220 +0,0 @@
-/*
- * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
- * See LICENSE file for license details.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "ixp.h"
-
-int
-ixp_client_do_fcall(IXPClient *c)
-{
- static unsigned char msg[IXP_MAX_MSG];
- unsigned int msize = ixp_fcall2msg(msg, &c->ifcall, IXP_MAX_MSG);
-
- c->errstr = 0;
- if(ixp_send_message(c->fd, msg, msize, &c->errstr) != msize)
- return -1;
- if(!ixp_recv_message(c->fd, msg, IXP_MAX_MSG, &c->errstr))
- return -1;
- if(!(msize = ixp_msg2fcall(&c->ofcall, msg, IXP_MAX_MSG))) {
- c->errstr = "received bad message";
- return -1;
- }
- if(c->ofcall.type == RERROR) {
- c->errstr = c->ofcall.ename;
- return -1;
- }
-
- return 0;
-}
-
-int
-ixp_client_dial(IXPClient *c, char *sockfile, unsigned int rootfid)
-{
-
- if((c->fd = ixp_connect_sock(sockfile)) < 0) {
- c->errstr = "cannot connect server";
- return -1;
- }
-
- c->ifcall.type = TVERSION;
- c->ifcall.tag = IXP_NOTAG;
- c->ifcall.msize = IXP_MAX_MSG;
- c->ifcall.version = IXP_VERSION;
- if(ixp_client_do_fcall(c) == -1) {
- fprintf(stderr, "error: %s\n", c->errstr);
- ixp_client_hangup(c);
- return -1;
- }
- if(strncmp(c->ofcall.version, IXP_VERSION, strlen(IXP_VERSION))) {
- fprintf(stderr, "error: %s\n", c->errstr);
- c->errstr = "9P versions differ";
- ixp_client_hangup(c);
- return -1; /* we cannot handle this version */
- }
- free(c->ofcall.version);
- c->root_fid = rootfid;
-
- c->ifcall.type = TATTACH;
- c->ifcall.tag = IXP_NOTAG;
- c->ifcall.fid = c->root_fid;
- c->ifcall.afid = IXP_NOFID;
- c->ifcall.uname = getenv("USER");
- c->ifcall.aname = "";
- if(ixp_client_do_fcall(c) == -1) {
- fprintf(stderr, "error: %s\n", c->errstr);
- ixp_client_hangup(c);
- return -1;
- }
- c->root_qid = c->ofcall.qid;
-
- return 0;
-}
-
-int
-ixp_client_remove(IXPClient *c, unsigned int newfid, char *filepath)
-{
-
- if(ixp_client_walk(c, newfid, filepath) == -1)
- return -1;
- c->ifcall.type = TREMOVE;
- c->ifcall.tag = IXP_NOTAG;
- c->ifcall.fid = newfid;
-
- return ixp_client_do_fcall(c);
-}
-
-int
-ixp_client_create(IXPClient *c, unsigned int dirfid, char *name,
- unsigned int perm, unsigned char mode)
-{
- c->ifcall.type = TCREATE;
- c->ifcall.tag = IXP_NOTAG;
- c->ifcall.fid = dirfid;
- c->ifcall.name = name;
- c->ifcall.perm = perm;
- c->ifcall.mode = mode;
- return ixp_client_do_fcall(c);
-}
-
-int
-ixp_client_walk(IXPClient *c, unsigned int newfid, char *filepath)
-{
- unsigned int i;
- char *wname[IXP_MAX_WELEM];
-
- c->ifcall.type = TWALK;
- c->ifcall.fid = c->root_fid;
- c->ifcall.newfid = newfid;
- if(filepath) {
- c->ifcall.name = filepath;
- c->ifcall.nwname =
- cext_tokenize(wname, IXP_MAX_WELEM, c->ifcall.name, '/');
- for(i = 0; i < c->ifcall.nwname; i++)
- c->ifcall.wname[i] = wname[i];
- }
- return ixp_client_do_fcall(c);
-}
-
-int
-ixp_client_stat(IXPClient *c, unsigned int newfid, char *filepath)
-{
-
- if(ixp_client_walk(c, newfid, filepath) == -1)
- return -1;
-
- c->ifcall.type = TSTAT;
- c->ifcall.tag = IXP_NOTAG;
- c->ifcall.fid = newfid;
- return ixp_client_do_fcall(c);
-}
-
-int
-ixp_client_open(IXPClient *c, unsigned int newfid, unsigned char mode)
-{
-
- c->ifcall.type = TOPEN;
- c->ifcall.tag = IXP_NOTAG;
- c->ifcall.fid = newfid;
- c->ifcall.mode = mode;
- return ixp_client_do_fcall(c);
-}
-
-int
-ixp_client_walkopen(IXPClient *c, unsigned int newfid, char *filepath,
- unsigned char mode)
-{
-
- if(ixp_client_walk(c, newfid, filepath) == -1)
- return -1;
- return ixp_client_open(c, newfid, mode);
-}
-
-int
-ixp_client_read(IXPClient *c, unsigned int fid, unsigned long long offset,
- void *result, unsigned int res_len)
-{
- unsigned int bytes = c->ofcall.iounit;
-
- c->ifcall.type = TREAD;
- c->ifcall.tag = IXP_NOTAG;
- c->ifcall.fid = fid;
- c->ifcall.offset = offset;
- c->ifcall.count = res_len < bytes ? res_len : bytes;
- if(ixp_client_do_fcall(c) == -1)
- return -1;
- memcpy(result, c->ofcall.data, c->ofcall.count);
- free(c->ofcall.data);
-
- return c->ofcall.count;
-}
-
-int
-ixp_client_write(IXPClient *c, unsigned int fid,
- unsigned long long offset, unsigned int count,
- unsigned char *data)
-{
-
- if(count > c->ofcall.iounit) {
- c->errstr = "iounit exceeded";
- return -1;
- }
-
- c->ifcall.type = TWRITE;
- c->ifcall.tag = IXP_NOTAG;
- c->ifcall.fid = fid;
- c->ifcall.offset = offset;
- c->ifcall.count = count;
- c->ifcall.data = (void *)data;
- if(ixp_client_do_fcall(c) == -1)
- return -1;
-
- return c->ofcall.count;
-}
-
-int
-ixp_client_close(IXPClient *c, unsigned int fid)
-{
-
- c->ifcall.type = TCLUNK;
- c->ifcall.tag = IXP_NOTAG;
- c->ifcall.fid = fid;
- return ixp_client_do_fcall(c);
-}
-
-void
-ixp_client_hangup(IXPClient *c)
-{
- /* session finished, now shutdown */
- if(c->fd) {
- shutdown(c->fd, SHUT_RDWR);
- close(c->fd);
- }
-}
diff --git a/libixp/convert.c b/libixp/convert.c
@@ -1,236 +0,0 @@
-/*
- * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
- * See LICENSE file for license details.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "ixp.h"
-
-/* packode/unpackode stuff */
-
-void
-ixp_pack_u8(unsigned char **msg, int *msize, unsigned char val)
-{
- if(!msize || (*msize -= 1) >= 0)
- *(*msg)++ = val;
-}
-
-void
-ixp_unpack_u8(unsigned char **msg, int *msize, unsigned char *val)
-{
- if(!msize || (*msize -= 1) >= 0)
- *val = *(*msg)++;
-}
-
-void
-ixp_pack_u16(unsigned char **msg, int *msize, unsigned short val)
-{
- if(!msize || (*msize -= 2) >= 0) {
- *(*msg)++ = val;
- *(*msg)++ = val >> 8;
- }
-}
-
-void
-ixp_unpack_u16(unsigned char **msg, int *msize, unsigned short *val)
-{
- if(!msize || (*msize -= 2) >= 0) {
- *val = *(*msg)++;
- *val |= *(*msg)++ << 8;
- }
-}
-
-void
-ixp_pack_u32(unsigned char **msg, int *msize, unsigned int val)
-{
- if(!msize || (*msize -= 4) >= 0) {
- *(*msg)++ = val;
- *(*msg)++ = val >> 8;
- *(*msg)++ = val >> 16;
- *(*msg)++ = val >> 24;
- }
-}
-
-void
-ixp_unpack_u32(unsigned char **msg, int *msize, unsigned int *val)
-{
- if(!msize || (*msize -= 4) >= 0) {
- *val = *(*msg)++;
- *val |= *(*msg)++ << 8;
- *val |= *(*msg)++ << 16;
- *val |= *(*msg)++ << 24;
- }
-}
-
-void
-ixp_pack_u64(unsigned char **msg, int *msize, unsigned long long val)
-{
- if(!msize || (*msize -= 8) >= 0) {
- *(*msg)++ = val;
- *(*msg)++ = val >> 8;
- *(*msg)++ = val >> 16;
- *(*msg)++ = val >> 24;
- *(*msg)++ = val >> 32;
- *(*msg)++ = val >> 40;
- *(*msg)++ = val >> 48;
- *(*msg)++ = val >> 56;
- }
-}
-
-void
-ixp_unpack_u64(unsigned char **msg, int *msize, unsigned long long *val)
-{
- if(!msize || (*msize -= 8) >= 0) {
- *val |= *(*msg)++;
- *val |= *(*msg)++ << 8;
- *val |= *(*msg)++ << 16;
- *val |= *(*msg)++ << 24;
- *val |= (unsigned long long)*(*msg)++ << 32;
- *val |= (unsigned long long)*(*msg)++ << 40;
- *val |= (unsigned long long)*(*msg)++ << 48;
- *val |= (unsigned long long)*(*msg)++ << 56;
- }
-}
-
-void
-ixp_pack_string(unsigned char **msg, int *msize, const char *s)
-{
- unsigned short len = s ? strlen(s) : 0;
- ixp_pack_u16(msg, msize, len);
- if(s)
- ixp_pack_data(msg, msize, (void *)s, len);
-}
-
-void
-ixp_unpack_strings(unsigned char **msg, int *msize, unsigned short n, char **strings) {
- unsigned char *s = *msg;
- unsigned int i, size = 0;
- unsigned short len;
- for(i=0; i<n; i++) {
- ixp_unpack_u16(&s, msize, &len);
- s += len;
- size += len + 1; /* for '\0' */
- }
- if(!size) {
- /* So we don't try to free some random value */
- *strings = nil;
- return;
- }
- s = cext_emalloc(size);
- for(i=0; i < n; i++) {
- ixp_unpack_u16(msg, msize, &len);
- if(!msize || (*msize -= len) < 0)
- return;
-
- memcpy(s, *msg, len);
- s[len] = '\0';
- strings[i] = (char *)s;
- *msg += len;
- s += len + 1;
- }
-}
-
-void
-ixp_unpack_string(unsigned char **msg, int *msize, char **string, unsigned short *len)
-{
- ixp_unpack_u16(msg, msize, len);
- *string = nil;
- if (!msize || (*msize -= *len) >= 0) {
- *string = cext_emalloc(*len+1);
- if(*len)
- memcpy(*string, *msg, *len);
- (*string)[*len] = 0;
- *msg += *len;
- }
-}
-
-void
-ixp_pack_data(unsigned char **msg, int *msize, unsigned char *data, unsigned int datalen)
-{
- if(!msize || (*msize -= datalen) >= 0) {
- memcpy(*msg, data, datalen);
- *msg += datalen;
- }
-}
-
-void
-ixp_unpack_data(unsigned char **msg, int *msize, unsigned char **data, unsigned int datalen)
-{
- if(!msize || (*msize -= datalen) >= 0) {
- *data = cext_emallocz(datalen);
- memcpy(*data, *msg, datalen);
- *msg += datalen;
- }
-}
-
-void
-ixp_pack_prefix(unsigned char *msg, unsigned int size, unsigned char id,
- unsigned short tag)
-{
- ixp_pack_u32(&msg, 0, size);
- ixp_pack_u8(&msg, 0, id);
- ixp_pack_u16(&msg, 0, tag);
-}
-
-void
-ixp_unpack_prefix(unsigned char **msg, unsigned int *size, unsigned char *id,
- unsigned short *tag)
-{
- int msize;
- ixp_unpack_u32(msg, nil, size);
- msize = *size;
- ixp_unpack_u8(msg, &msize, id);
- ixp_unpack_u16(msg, &msize, tag);
-}
-
-void
-ixp_pack_qid(unsigned char **msg, int *msize, Qid * qid)
-{
- ixp_pack_u8(msg, msize, qid->type);
- ixp_pack_u32(msg, msize, qid->version);
- ixp_pack_u64(msg, msize, qid->path);
-}
-
-void
-ixp_unpack_qid(unsigned char **msg, int *msize, Qid * qid)
-{
- ixp_unpack_u8(msg, msize, &qid->type);
- ixp_unpack_u32(msg, msize, &qid->version);
- ixp_unpack_u64(msg, msize, &qid->path);
-}
-
-void
-ixp_pack_stat(unsigned char **msg, int *msize, Stat * stat)
-{
- ixp_pack_u16(msg, msize, ixp_sizeof_stat(stat) - sizeof(unsigned short));
- ixp_pack_u16(msg, msize, stat->type);
- ixp_pack_u32(msg, msize, stat->dev);
- ixp_pack_qid(msg, msize, &stat->qid);
- ixp_pack_u32(msg, msize, stat->mode);
- ixp_pack_u32(msg, msize, stat->atime);
- ixp_pack_u32(msg, msize, stat->mtime);
- ixp_pack_u64(msg, msize, stat->length);
- ixp_pack_string(msg, msize, stat->name);
- ixp_pack_string(msg, msize, stat->uid);
- ixp_pack_string(msg, msize, stat->gid);
- ixp_pack_string(msg, msize, stat->muid);
-}
-
-void
-ixp_unpack_stat(unsigned char **msg, int *msize, Stat * stat)
-{
- unsigned short dummy;
- *msg += sizeof(unsigned short);
- ixp_unpack_u16(msg, msize, &stat->type);
- ixp_unpack_u32(msg, msize, &stat->dev);
- ixp_unpack_qid(msg, msize, &stat->qid);
- ixp_unpack_u32(msg, msize, &stat->mode);
- ixp_unpack_u32(msg, msize, &stat->atime);
- ixp_unpack_u32(msg, msize, &stat->mtime);
- ixp_unpack_u64(msg, msize, &stat->length);
- ixp_unpack_string(msg, msize, &stat->name, &dummy);
- ixp_unpack_string(msg, msize, &stat->uid, &dummy);
- ixp_unpack_string(msg, msize, &stat->gid, &dummy);
- ixp_unpack_string(msg, msize, &stat->muid, &dummy);
-}
diff --git a/libixp/intmap.c b/libixp/intmap.c
@@ -1,144 +0,0 @@
-/* This file is derived from src/lib9p/intmap.c from plan9port */
-/* See LICENCE.p9p for terms of use */
-#include <stdlib.h>
-#include "ixp.h"
-#define USED(v) if(v){}else{}
-
-struct Intlist {
- unsigned long id;
- void* aux;
- Intlist* link;
- unsigned int ref;
-};
-
-static unsigned long
-hashid(Intmap *map, unsigned long id)
-{
- return id%map->nhash;
-}
-
-static void
-nop(void *v)
-{
- USED(v);
-}
-
-void
-initmap(Intmap *m, unsigned long nhash, void *hash)
-{
- m->nhash = nhash;
- m->hash = hash;
-}
-
-static Intlist**
-llookup(Intmap *map, unsigned long id)
-{
- Intlist **lf;
-
- for(lf=&map->hash[hashid(map, id)]; *lf; lf=&(*lf)->link)
- if((*lf)->id == id)
- break;
- return lf;
-}
-
-void
-freemap(Intmap *map, void (*destroy)(void*))
-{
- int i;
- Intlist *p, *nlink;
-
- if(destroy == nil)
- destroy = nop;
- for(i=0; i<map->nhash; i++){
- for(p=map->hash[i]; p; p=nlink){
- nlink = p->link;
- destroy(p->aux);
- free(p);
- }
- }
-}
-void
-execmap(Intmap *map, void (*run)(void*))
-{
- int i;
- Intlist *p, *nlink;
-
- for(i=0; i<map->nhash; i++){
- for(p=map->hash[i]; p; p=nlink){
- nlink = p->link;
- run(p->aux);
- }
- }
-}
-
-void*
-lookupkey(Intmap *map, unsigned long id)
-{
- Intlist *f;
- void *v;
-
- if((f = *llookup(map, id)))
- v = f->aux;
- else
- v = nil;
- return v;
-}
-
-void*
-insertkey(Intmap *map, unsigned long id, void *v)
-{
- Intlist *f;
- void *ov;
- unsigned long h;
-
- if((f = *llookup(map, id))){
- /* no decrement for ov because we're returning it */
- ov = f->aux;
- f->aux = v;
- }else{
- f = cext_emallocz(sizeof(*f));
- f->id = id;
- f->aux = v;
- h = hashid(map, id);
- f->link = map->hash[h];
- map->hash[h] = f;
- ov = nil;
- }
- return ov;
-}
-
-int
-caninsertkey(Intmap *map, unsigned long id, void *v)
-{
- Intlist *f;
- int rv;
- unsigned long h;
-
- if(*llookup(map, id))
- rv = 0;
- else{
- f = cext_emallocz(sizeof *f);
- f->id = id;
- f->aux = v;
- h = hashid(map, id);
- f->link = map->hash[h];
- map->hash[h] = f;
- rv = 1;
- }
- return rv;
-}
-
-void*
-deletekey(Intmap *map, unsigned long id)
-{
- Intlist **lf, *f;
- void *ov;
-
- if((f = *(lf = llookup(map, id)))){
- ov = f->aux;
- *lf = f->link;
- free(f);
- }else
- ov = nil;
- return ov;
-}
diff --git a/libixp/ixp.h b/libixp/ixp.h
@@ -1,369 +0,0 @@
-/*
- *(C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
- *See LICENSE file for license details.
- */
-
-#include <sys/types.h>
-#include <cext.h>
-
-#define IXP_VERSION "9P2000"
-#define IXP_NOTAG (unsigned short)~0U /*Dummy tag */
-#define IXP_NOFID (unsigned int)~0 /*No auth */
-
-enum { IXP_MAX_VERSION = 32,
- IXP_MAX_ERROR = 128,
- IXP_MAX_CACHE = 32,
- IXP_MAX_MSG = 8192,
- IXP_MAX_FLEN = 128,
- IXP_MAX_ULEN = 32,
- IXP_MAX_WELEM = 16 };
-
-/* 9P message types */
-enum { TVERSION = 100,
- RVERSION,
- TAUTH = 102,
- RAUTH,
- TATTACH = 104,
- RATTACH,
- TERROR = 106, /* illegal */
- RERROR,
- TFLUSH = 108,
- RFLUSH,
- TWALK = 110,
- RWALK,
- TOPEN = 112,
- ROPEN,
- TCREATE = 114,
- RCREATE,
- TREAD = 116,
- RREAD,
- TWRITE = 118,
- RWRITE,
- TCLUNK = 120,
- RCLUNK,
- TREMOVE = 122,
- RREMOVE,
- TSTAT = 124,
- RSTAT,
- TWSTAT = 126,
- RWSTAT,
-};
-
-/* borrowed from libc.h of Plan 9 */
-enum { IXP_DMDIR = 0x80000000, /* mode bit for directories */
- IXP_DMAPPEND = 0x40000000, /* mode bit for append only files */
- IXP_DMEXCL = 0x20000000, /* mode bit for exclusive use files */
- IXP_DMMOUNT = 0x10000000, /* mode bit for mounted channel */
- IXP_DMAUTH = 0x08000000, /* mode bit for authentication file */
- IXP_DMTMP = 0x04000000, /* mode bit for non-backed-up file */
- IXP_DMREAD = 0x4<<6, /* mode bit for read permission */
- IXP_DMWRITE = 0x2<<6, /* mode bit for write permission */
- IXP_DMEXEC = 0x1<<6 /* mode bit for execute permission */
-};
-
-/* modes */
-enum { IXP_OREAD = 0x00,
- IXP_OWRITE = 0x01,
- IXP_ORDWR = 0x02,
- IXP_OEXEC = 0x03,
- IXP_OEXCL = 0x04,
- IXP_OTRUNC = 0x10,
- IXP_OREXEC = 0x20,
- IXP_ORCLOSE = 0x40,
- IXP_OAPPEND = 0x80,
-};
-
-/* qid.types */
-enum { IXP_QTDIR = 0x80,
- IXP_QTAPPEND = 0x40,
- IXP_QTEXCL = 0x20,
- IXP_QTMOUNT = 0x10,
- IXP_QTAUTH = 0x08,
- IXP_QTTMP = 0x04,
- IXP_QTSYMLINK = 0x02,
- IXP_QTLINK = 0x01,
- IXP_QTFILE = 0x00,
-};
-
-/* from libc.h in p9p */
-enum { P9OREAD = 0, /* open for read */
- P9OWRITE = 1, /* write */
- P9ORDWR = 2, /* read and write */
- P9OEXEC = 3, /* execute, == read but check execute permission */
- P9OTRUNC = 16, /* or'ed in (except for exec), truncate file first */
- P9OCEXEC = 32, /* or'ed in, close on exec */
- P9ORCLOSE = 64, /* or'ed in, remove on close */
- P9ODIRECT = 128, /* or'ed in, direct access */
- P9ONONBLOCK = 256, /* or'ed in, non-blocking call */
- P9OEXCL = 0x1000, /* or'ed in, exclusive use (create only) */
- P9OLOCK = 0x2000, /* or'ed in, lock after opening */
- P9OAPPEND = 0x4000 /* or'ed in, append only */
-};
-
-/* bits in Qid.type */
-enum { P9QTDIR = 0x80, /* type bit for directories */
- P9QTAPPEND = 0x40, /* type bit for append only files */
- P9QTEXCL = 0x20, /* type bit for exclusive use files */
- P9QTMOUNT = 0x10, /* type bit for mounted channel */
- P9QTAUTH = 0x08, /* type bit for authentication file */
- P9QTTMP = 0x04, /* type bit for non-backed-up file */
- P9QTSYMLINK = 0x02, /* type bit for symbolic link */
- P9QTFILE = 0x00 /* type bits for plain file */
-};
-
-/* bits in Dir.mode */
-#define P9DMDIR 0x80000000 /* mode bit for directories */
-#define P9DMAPPEND 0x40000000 /* mode bit for append only files */
-#define P9DMEXCL 0x20000000 /* mode bit for exclusive use files */
-#define P9DMMOUNT 0x10000000 /* mode bit for mounted channel */
-#define P9DMAUTH 0x08000000 /* mode bit for authentication file */
-#define P9DMTMP 0x04000000 /* mode bit for non-backed-up file */
-#define P9DMSYMLINK 0x02000000 /* mode bit for symbolic link (Unix, 9P2000.u) */
-#define P9DMDEVICE 0x00800000 /* mode bit for device file (Unix, 9P2000.u) */
-#define P9DMNAMEDPIPE 0x00200000 /* mode bit for named pipe (Unix, 9P2000.u) */
-#define P9DMSOCKET 0x00100000 /* mode bit for socket (Unix, 9P2000.u) */
-#define P9DMSETUID 0x00080000 /* mode bit for setuid (Unix, 9P2000.u) */
-#define P9DMSETGID 0x00040000 /* mode bit for setgid (Unix, 9P2000.u) */
-
-enum { P9DMREAD = 0x4, /* mode bit for read permission */
- P9DMWRITE = 0x2, /* mode bit for write permission */
- P9DMEXEC = 0x1 /* mode bit for execute permission */
-};
-
-
-typedef struct Qid Qid;
-struct Qid {
- unsigned char type;
- unsigned int version;
- unsigned long long path;
- /* internal use only */
- unsigned char dir_type;
-};
-
-/* stat structure */
-typedef struct Stat {
- unsigned short type;
- unsigned int dev;
- Qid qid;
- unsigned int mode;
- unsigned int atime;
- unsigned int mtime;
- unsigned long long length;
- char *name;
- char *uid;
- char *gid;
- char *muid;
-} Stat;
-
-/* from fcall(3) in plan9port */
-typedef struct Fcall {
- unsigned char type;
- unsigned short tag;
- unsigned int fid;
- union {
- struct { /* Tversion, Rversion */
- unsigned int msize;
- char *version;
- };
- struct { /* Tflush */
- unsigned short oldtag;
- };
- struct { /* Rerror */
- char *ename;
- };
- struct { /* Ropen, Rcreate */
- Qid qid; /* +Rattach */
- unsigned int iounit;
- };
- struct { /* Rauth */
- Qid aqid;
- };
- struct { /* Tauth, Tattach */
- unsigned int afid;
- char *uname;
- char *aname;
- };
- struct { /* Tcreate */
- unsigned int perm;
- char *name;
- unsigned char mode; /* +Topen */
- };
- struct { /* Twalk */
- unsigned int newfid;
- unsigned short nwname;
- char *wname[IXP_MAX_WELEM];
- };
- struct { /* Rwalk */
- unsigned short nwqid;
- Qid wqid[IXP_MAX_WELEM];
- };
- struct { /* Twrite */
- unsigned long long offset; /* +Tread */
- /* +Rread */
- unsigned int count; /* +Tread */
- char *data;
- };
- struct { /* Twstat, Rstat */
- unsigned short nstat;
- unsigned char *stat;
- };
- };
-} Fcall;
-
-typedef struct IXPServer IXPServer;
-typedef struct IXPConn IXPConn;
-typedef struct Intmap Intmap;
-
-typedef struct Intlist Intlist;
-struct Intmap {
- unsigned long nhash;
- Intlist **hash;
-};
-
-struct IXPConn {
- IXPServer *srv;
- void *aux;
- int fd;
- void (*read) (IXPConn *);
- void (*close) (IXPConn *);
- char closed;
-
- /* Implementation details */
- /* do not use */
- IXPConn *next;
-};
-
-struct IXPServer {
- int running;
- IXPConn *conn;
- int maxfd;
- fd_set rd;
-};
-
-typedef struct IXPClient {
- int fd;
- unsigned int root_fid;
- Qid root_qid;
- Fcall ifcall;
- Fcall ofcall;
- char *errstr;
-} IXPClient;
-
-typedef struct P9Conn P9Conn;
-typedef struct Fid {
- P9Conn *conn;
- Intmap *map;
- char *uid;
- void *aux;
- unsigned long fid;
- Qid qid;
- signed char omode;
-} Fid;
-
-typedef struct P9Req P9Req;
-struct P9Req {
- P9Conn *conn;
- Fid *fid;
- Fid *newfid;
- P9Req *oldreq;
- Fcall ifcall;
- Fcall ofcall;
- void *aux;
-};
-
-typedef struct P9Srv {
- void (*attach)(P9Req *r);
- void (*clunk)(P9Req *r);
- void (*create)(P9Req *r);
- void (*flush)(P9Req *r);
- void (*open)(P9Req *r);
- void (*read)(P9Req *r);
- void (*remove)(P9Req *r);
- void (*stat)(P9Req *r);
- void (*walk)(P9Req *r);
- void (*write)(P9Req *r);
- void (*freefid)(Fid *f);
-} P9Srv;
-
-/* client.c */
-extern int ixp_client_dial(IXPClient *c, char *address, unsigned int rootfid);
-extern void ixp_client_hangup(IXPClient *c);
-extern int ixp_client_remove(IXPClient *c, unsigned int newfid, char *filepath);
-extern int ixp_client_create(IXPClient *c, unsigned int dirfid, char *name,
- unsigned int perm, unsigned char mode);
-extern int ixp_client_walk(IXPClient *c, unsigned int newfid, char *filepath);
-extern int ixp_client_stat(IXPClient *c, unsigned int newfid, char *filepath);
-extern int ixp_client_walkopen(IXPClient *c, unsigned int newfid, char *filepath,
- unsigned char mode);
-extern int ixp_client_open(IXPClient *c, unsigned int newfid, unsigned char mode);
-extern int ixp_client_read(IXPClient *c, unsigned int fid,
- unsigned long long offset, void *result,
- unsigned int res_len);
-extern int ixp_client_write(IXPClient *c, unsigned int fid,
- unsigned long long offset,
- unsigned int count, unsigned char *data);
-extern int ixp_client_close(IXPClient *c, unsigned int fid);
-extern int ixp_client_do_fcall(IXPClient * c);
-
-/* convert.c */
-extern void ixp_pack_u8(unsigned char **msg, int *msize, unsigned char val);
-extern void ixp_unpack_u8(unsigned char **msg, int *msize, unsigned char *val);
-extern void ixp_pack_u16(unsigned char **msg, int *msize, unsigned short val);
-extern void ixp_unpack_u16(unsigned char **msg, int *msize, unsigned short *val);
-extern void ixp_pack_u32(unsigned char **msg, int *msize, unsigned int val);
-extern void ixp_unpack_u32(unsigned char **msg, int *msize, unsigned int *val);
-extern void ixp_pack_u64(unsigned char **msg, int *msize, unsigned long long val);
-extern void ixp_unpack_u64(unsigned char **msg, int *msize, unsigned long long *val);
-extern void ixp_pack_string(unsigned char **msg, int *msize, const char *s);
-extern void ixp_unpack_strings(unsigned char **msg, int *msize, unsigned short n, char **strings);
-extern void ixp_unpack_string(unsigned char **msg, int *msize, char **string, unsigned short *len);
-extern void ixp_pack_data(unsigned char **msg, int *msize, unsigned char *data,
- unsigned int datalen);
-extern void ixp_unpack_data(unsigned char **msg, int *msize, unsigned char **data,
- unsigned int datalen);
-extern void ixp_pack_prefix(unsigned char *msg, unsigned int size,
- unsigned char id, unsigned short tag);
-extern void ixp_unpack_prefix(unsigned char **msg, unsigned int *size,
- unsigned char *id, unsigned short *tag);
-extern void ixp_pack_qid(unsigned char **msg, int *msize, Qid *qid);
-extern void ixp_unpack_qid(unsigned char **msg, int *msize, Qid *qid);
-extern void ixp_pack_stat(unsigned char **msg, int *msize, Stat *stat);
-extern void ixp_unpack_stat(unsigned char **msg, int *msize, Stat *stat);
-
-/* request.c */
-extern void respond(P9Req *r, char *error);
-extern void serve_9pcon(IXPConn *c);
-
-/* intmap.c */
-extern void initmap(Intmap *m, unsigned long nhash, void *hash);
-extern void incref_map(Intmap *m);
-extern void decref_map(Intmap *m);
-extern void freemap(Intmap *map, void (*destroy)(void*));
-extern void execmap(Intmap *map, void (*destroy)(void*));
-extern void* lookupkey(Intmap *map, unsigned long id);
-extern void* insertkey(Intmap *map, unsigned long id, void *v);
-extern int caninsertkey(Intmap *map, unsigned long id, void *v);
-extern void* deletekey(Intmap *map, unsigned long id);
-
-/* message.c */
-extern unsigned short ixp_sizeof_stat(Stat *stat);
-extern unsigned int ixp_fcall2msg(void *msg, Fcall *fcall, unsigned int msglen);
-extern unsigned int ixp_msg2fcall(Fcall *call, void *msg, unsigned int msglen);
-
-/* server.c */
-extern IXPConn *ixp_server_open_conn(IXPServer *s, int fd, void *aux,
- void (*read)(IXPConn *c), void (*close)(IXPConn *c));
-extern void ixp_server_close_conn(IXPConn *c);
-extern char *ixp_server_loop(IXPServer *s);
-extern unsigned int ixp_server_receive_fcall(IXPConn *c, Fcall *fcall);
-extern int ixp_server_respond_fcall(IXPConn *c, Fcall *fcall);
-extern int ixp_server_respond_error(IXPConn *c, Fcall *fcall, char *errstr);
-extern void ixp_server_close(IXPServer *s);
-
-/* socket.c */
-extern int ixp_connect_sock(char *address);
-extern int ixp_create_sock(char *address, char **errstr);
-
-/* transport.c */
-extern unsigned int ixp_send_message(int fd, void *msg, unsigned int msize, char **errstr);
-extern unsigned int ixp_recv_message(int fd, void *msg, unsigned int msglen, char **errstr);
diff --git a/libixp/message.c b/libixp/message.c
@@ -1,243 +0,0 @@
-/*
- * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
- * See LICENSE file for license details.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "ixp.h"
-
-#define IXP_QIDSZ (sizeof(unsigned char) + sizeof(unsigned int)\
- + sizeof(unsigned long long))
-
-static unsigned short
-sizeof_string(const char *s)
-{
- return sizeof(unsigned short) + strlen(s);
-}
-
-unsigned short
-ixp_sizeof_stat(Stat * stat)
-{
- return IXP_QIDSZ
- + 2 * sizeof(unsigned short)
- + 4 * sizeof(unsigned int)
- + sizeof(unsigned long long)
- + sizeof_string(stat->name)
- + sizeof_string(stat->uid)
- + sizeof_string(stat->gid)
- + sizeof_string(stat->muid);
-}
-
-unsigned int
-ixp_fcall2msg(void *msg, Fcall *fcall, unsigned int msglen)
-{
- unsigned int i = sizeof(unsigned char) +
- sizeof(unsigned short) + sizeof(unsigned int);
- int msize = msglen - i;
- unsigned char *p = msg + i;
-
- switch (fcall->type) {
- case TVERSION:
- case RVERSION:
- ixp_pack_u32(&p, &msize, fcall->msize);
- ixp_pack_string(&p, &msize, fcall->version);
- break;
- case TAUTH:
- ixp_pack_u32(&p, &msize, fcall->afid);
- ixp_pack_string(&p, &msize, fcall->uname);
- ixp_pack_string(&p, &msize, fcall->aname);
- break;
- case RAUTH:
- ixp_pack_qid(&p, &msize, &fcall->aqid);
- break;
- case RATTACH:
- ixp_pack_qid(&p, &msize, &fcall->qid);
- break;
- case TATTACH:
- ixp_pack_u32(&p, &msize, fcall->fid);
- ixp_pack_u32(&p, &msize, fcall->afid);
- ixp_pack_string(&p, &msize, fcall->uname);
- ixp_pack_string(&p, &msize, fcall->aname);
- break;
- case RERROR:
- ixp_pack_string(&p, &msize, fcall->ename);
- break;
- case TFLUSH:
- ixp_pack_u16(&p, &msize, fcall->oldtag);
- break;
- case TWALK:
- ixp_pack_u32(&p, &msize, fcall->fid);
- ixp_pack_u32(&p, &msize, fcall->newfid);
- ixp_pack_u16(&p, &msize, fcall->nwname);
- for(i = 0; i < fcall->nwname; i++)
- ixp_pack_string(&p, &msize, fcall->wname[i]);
- break;
- case RWALK:
- ixp_pack_u16(&p, &msize, fcall->nwqid);
- for(i = 0; i < fcall->nwqid; i++)
- ixp_pack_qid(&p, &msize, &fcall->wqid[i]);
- break;
- case TOPEN:
- ixp_pack_u32(&p, &msize, fcall->fid);
- ixp_pack_u8(&p, &msize, fcall->mode);
- break;
- case ROPEN:
- case RCREATE:
- ixp_pack_qid(&p, &msize, &fcall->qid);
- ixp_pack_u32(&p, &msize, fcall->iounit);
- break;
- case TCREATE:
- ixp_pack_u32(&p, &msize, fcall->fid);
- ixp_pack_string(&p, &msize, fcall->name);
- ixp_pack_u32(&p, &msize, fcall->perm);
- ixp_pack_u8(&p, &msize, fcall->mode);
- break;
- case TREAD:
- ixp_pack_u32(&p, &msize, fcall->fid);
- ixp_pack_u64(&p, &msize, fcall->offset);
- ixp_pack_u32(&p, &msize, fcall->count);
- break;
- case RREAD:
- ixp_pack_u32(&p, &msize, fcall->count);
- ixp_pack_data(&p, &msize, (unsigned char *)fcall->data, fcall->count);
- break;
- case TWRITE:
- ixp_pack_u32(&p, &msize, fcall->fid);
- ixp_pack_u64(&p, &msize, fcall->offset);
- ixp_pack_u32(&p, &msize, fcall->count);
- ixp_pack_data(&p, &msize, (unsigned char *)fcall->data, fcall->count);
- break;
- case RWRITE:
- ixp_pack_u32(&p, &msize, fcall->count);
- break;
- case TCLUNK:
- case TREMOVE:
- case TSTAT:
- ixp_pack_u32(&p, &msize, fcall->fid);
- break;
- case RSTAT:
- ixp_pack_u16(&p, &msize, fcall->nstat);
- ixp_pack_data(&p, &msize, fcall->stat, fcall->nstat);
- break;
- case TWSTAT:
- ixp_pack_u32(&p, &msize, fcall->fid);
- ixp_pack_u16(&p, &msize, fcall->nstat);
- ixp_pack_data(&p, &msize, fcall->stat, fcall->nstat);
- break;
- }
-
- if(msize < 0)
- return 0;
-
- msize = msglen - msize;
- ixp_pack_prefix(msg, msize, fcall->type, fcall->tag);
- return msize;
-}
-
-unsigned int
-ixp_msg2fcall(Fcall *fcall, void *msg, unsigned int msglen)
-{
- int msize;
- unsigned int i, tsize;
- unsigned short len;
- unsigned char *p = msg;
- ixp_unpack_prefix(&p, (unsigned int *)&msize, &fcall->type, &fcall->tag);
- tsize = msize;
-
- if(msize > msglen) /* bad message */
- return 0;
- switch (fcall->type) {
- case TVERSION:
- case RVERSION:
- ixp_unpack_u32(&p, &msize, &fcall->msize);
- ixp_unpack_string(&p, &msize, &fcall->version, &len);
- break;
- case TAUTH:
- ixp_unpack_u32(&p, &msize, &fcall->afid);
- ixp_unpack_string(&p, &msize, &fcall->uname, &len);
- ixp_unpack_string(&p, &msize, &fcall->aname, &len);
- break;
- case RAUTH:
- ixp_unpack_qid(&p, &msize, &fcall->aqid);
- break;
- case RATTACH:
- ixp_unpack_qid(&p, &msize, &fcall->qid);
- break;
- case TATTACH:
- ixp_unpack_u32(&p, &msize, &fcall->fid);
- ixp_unpack_u32(&p, &msize, &fcall->afid);
- ixp_unpack_string(&p, &msize, &fcall->uname, &len);
- ixp_unpack_string(&p, &msize, &fcall->aname, &len);
- break;
- case RERROR:
- ixp_unpack_string(&p, &msize, &fcall->ename, &len);
- break;
- case TFLUSH:
- ixp_unpack_u16(&p, &msize, &fcall->oldtag);
- break;
- case TWALK:
- ixp_unpack_u32(&p, &msize, &fcall->fid);
- ixp_unpack_u32(&p, &msize, &fcall->newfid);
- ixp_unpack_u16(&p, &msize, &fcall->nwname);
- ixp_unpack_strings(&p, &msize, fcall->nwname, fcall->wname);
- break;
- case RWALK:
- ixp_unpack_u16(&p, &msize, &fcall->nwqid);
- for(i = 0; i < fcall->nwqid; i++)
- ixp_unpack_qid(&p, &msize, &fcall->wqid[i]);
- break;
- case TOPEN:
- ixp_unpack_u32(&p, &msize, &fcall->fid);
- ixp_unpack_u8(&p, &msize, &fcall->mode);
- break;
- case ROPEN:
- case RCREATE:
- ixp_unpack_qid(&p, &msize, &fcall->qid);
- ixp_unpack_u32(&p, &msize, &fcall->iounit);
- break;
- case TCREATE:
- ixp_unpack_u32(&p, &msize, &fcall->fid);
- ixp_unpack_string(&p, &msize, &fcall->name, &len);
- ixp_unpack_u32(&p, &msize, &fcall->perm);
- ixp_unpack_u8(&p, &msize, &fcall->mode);
- break;
- case TREAD:
- ixp_unpack_u32(&p, &msize, &fcall->fid);
- ixp_unpack_u64(&p, &msize, &fcall->offset);
- ixp_unpack_u32(&p, &msize, &fcall->count);
- break;
- case RREAD:
- ixp_unpack_u32(&p, &msize, &fcall->count);
- ixp_unpack_data(&p, &msize, (void *)&fcall->data, fcall->count);
- break;
- case TWRITE:
- ixp_unpack_u32(&p, &msize, &fcall->fid);
- ixp_unpack_u64(&p, &msize, &fcall->offset);
- ixp_unpack_u32(&p, &msize, &fcall->count);
- ixp_unpack_data(&p, &msize, (void *)&fcall->data, fcall->count);
- break;
- case RWRITE:
- ixp_unpack_u32(&p, &msize, &fcall->count);
- break;
- case TCLUNK:
- case TREMOVE:
- case TSTAT:
- ixp_unpack_u32(&p, &msize, &fcall->fid);
- break;
- case RSTAT:
- ixp_unpack_u16(&p, &msize, &len);
- ixp_unpack_data(&p, &msize, &fcall->stat, len);
- break;
- case TWSTAT:
- ixp_unpack_u32(&p, &msize, &fcall->fid);
- ixp_unpack_u16(&p, &msize, &len);
- ixp_unpack_data(&p, &msize, &fcall->stat, len);
- break;
- }
-
- if(msize > 0)
- return tsize;
- return 0;
-}
diff --git a/libixp/request.c b/libixp/request.c
@@ -1,394 +0,0 @@
-/*
- * (C)opyright MMVI Kris Maglione <fbsdaemon at gmail dot com>
- * See LICENSE file for license details.
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/socket.h>
-#include "ixp.h"
-
-static void ixp_handle_req(P9Req *r);
-
-/* We use string literals rather than arrays here because
- * they're allocated in a readonly section */
-static char
- *Eduptag = "tag in use",
- *Edupfid = "fid in use",
- *Enofunc = "function not implemented",
- *Ebotch = "9P protocol botch",
- *Enofile = "file does not exist",
- *Enofid = "fid does not exist",
- *Enotag = "tag does not exist",
- *Enotdir = "not a directory",
- *Einterrupted = "interrupted",
- *Eisdir = "cannot perform operation on a directory";
-
-enum { TAG_BUCKETS = 64,
- FID_BUCKETS = 64 };
-
-struct P9Conn {
- Intmap tagmap;
- void *taghash[TAG_BUCKETS];
- Intmap fidmap;
- void *fidhash[FID_BUCKETS];
- P9Srv *srv;
- IXPConn *conn;
- unsigned int msize;
- unsigned char *buf;
- unsigned int ref;
-};
-
-static void
-free_p9conn(P9Conn *pc) {
- free(pc->buf);
- free(pc);
-}
-
-static void *
-createfid(Intmap *map, int fid, P9Conn *pc) {
- Fid *f = cext_emallocz(sizeof(Fid));
- f->fid = fid;
- f->omode = -1;
- f->map = map;
- f->conn = pc;
- if(caninsertkey(map, fid, f))
- return f;
- free(f);
- return nil;
-}
-
-static int
-destroyfid(P9Conn *pc, unsigned long fid) {
- Fid *f;
- if(!(f = deletekey(&pc->fidmap, fid)))
- return 0;
- if(pc->srv->freefid)
- pc->srv->freefid(f);
- free(f);
- return 1;
-}
-
-void
-ixp_server_handle_fcall(IXPConn *c)
-{
- Fcall fcall = {0};
- P9Conn *pc = c->aux;
- P9Req *req;
- unsigned int msize;
- char *errstr = nil;
-
- if(!(msize = ixp_recv_message(c->fd, pc->buf, pc->msize, &errstr)))
- goto Fail;
- if(!(msize = ixp_msg2fcall(&fcall, pc->buf, IXP_MAX_MSG)))
- goto Fail;
-
- req = cext_emallocz(sizeof(P9Req));
- req->conn = pc;
- req->ifcall = fcall;
- pc->conn = c;
-
- if(lookupkey(&pc->tagmap, fcall.tag))
- return respond(req, Eduptag);
-
- insertkey(&pc->tagmap, fcall.tag, req);
- return ixp_handle_req(req);
-
-Fail:
- ixp_server_close_conn(c);
-}
-
-static void
-ixp_handle_req(P9Req *r)
-{
- P9Conn *pc = r->conn;
- P9Srv *srv = pc->srv;
-
- switch(r->ifcall.type) {
- default:
- respond(r, Enofunc);
- break;
- case TVERSION:
- if(!strncmp(r->ifcall.version, "9P", 3)) {
- r->ofcall.version = "9P";
- }else
- if(!strncmp(r->ifcall.version, "9P2000", 7)) {
- r->ofcall.version = "9P2000";
- }else{
- r->ofcall.version = "unknown";
- }
- r->ofcall.msize = r->ifcall.msize;
- respond(r, nil);
- break;
- case TATTACH:
- if(!(r->fid = createfid(&pc->fidmap, r->ifcall.fid, pc)))
- return respond(r, Edupfid);
- /* attach is a required function */
- srv->attach(r);
- break;
- case TCLUNK:
- if(!(r->fid = lookupkey(&pc->fidmap, r->ifcall.fid)))
- return respond(r, Enofid);
- if(!srv->clunk)
- return respond(r, nil);
- srv->clunk(r);
- break;
- case TFLUSH:
- if(!(r->oldreq = lookupkey(&pc->tagmap, r->ifcall.oldtag)))
- return respond(r, Enotag);
- if(!srv->flush)
- return respond(r, Enofunc);
- srv->flush(r);
- break;
- case TCREATE:
- if(!(r->fid = lookupkey(&pc->fidmap, r->ifcall.fid)))
- return respond(r, Enofid);
- if(r->fid->omode != -1)
- return respond(r, Ebotch);
- if(!(r->fid->qid.type&P9QTDIR))
- return respond(r, Enotdir);
- if(!pc->srv->create)
- return respond(r, Enofunc);
- pc->srv->create(r);
- break;
- case TOPEN:
- if(!(r->fid = lookupkey(&pc->fidmap, r->ifcall.fid)))
- return respond(r, Enofid);
- if((r->fid->qid.type&P9QTDIR) && (r->ifcall.mode|P9ORCLOSE) != (P9OREAD|P9ORCLOSE))
- return respond(r, Eisdir);
- r->ofcall.qid = r->fid->qid;
- if(!pc->srv->open)
- return respond(r, Enofunc);
- pc->srv->open(r);
- break;
- case TREAD:
- if(!(r->fid = lookupkey(&pc->fidmap, r->ifcall.fid)))
- return respond(r, Enofid);
- if(r->fid->omode == -1 || r->fid->omode == P9OWRITE)
- return respond(r, Ebotch);
- if(!pc->srv->read)
- return respond(r, Enofunc);
- pc->srv->read(r);
- break;
- case TREMOVE:
- if(!(r->fid = lookupkey(&pc->fidmap, r->ifcall.fid)))
- return respond(r, Enofid);
- if(!pc->srv->remove)
- return respond(r, Enofunc);
- pc->srv->remove(r);
- break;
- case TSTAT:
- if(!(r->fid = lookupkey(&pc->fidmap, r->ifcall.fid)))
- return respond(r, Enofid);
- if(!pc->srv->stat)
- return respond(r, Enofunc);
- pc->srv->stat(r);
- break;
- case TWALK:
- if(!(r->fid = lookupkey(&pc->fidmap, r->ifcall.fid)))
- return respond(r, Enofid);
- if(r->fid->omode != -1)
- return respond(r, "cannot walk from an open fid");
- if(r->ifcall.nwname && !(r->fid->qid.type&P9QTDIR))
- return respond(r, Enotdir);
- if((r->ifcall.fid != r->ifcall.newfid)) {
- if(!(r->newfid = createfid(&pc->fidmap, r->ifcall.newfid, pc)))
- return respond(r, Edupfid);
- }else
- r->newfid = r->fid;
- if(!pc->srv->walk)
- return respond(r, Enofunc);
- pc->srv->walk(r);
- break;
- case TWRITE:
- if(!(r->fid = lookupkey(&pc->fidmap, r->ifcall.fid)))
- return respond(r, Enofid);
- if((r->fid->omode&3) != P9OWRITE && (r->fid->omode&3) != P9ORDWR)
- return respond(r, "write on fid not opened for writing");
- if(!pc->srv->write)
- return respond(r, Enofunc);
- pc->srv->write(r);
- break;
- /* Still to be implemented: flush, wstat, auth */
- }
-}
-
-void
-respond(P9Req *r, char *error) {
- P9Conn *pc = r->conn;
- switch(r->ifcall.type) {
- default:
- if(!error)
- cext_assert(!"Respond called on unsupported fcall type");
- break;
- case TVERSION:
- cext_assert(!error);
- free(r->ifcall.version);
- pc->msize = (r->ofcall.msize < IXP_MAX_MSG) ? r->ofcall.msize : IXP_MAX_MSG;
- free(pc->buf);
- pc->buf = cext_emallocz(r->ofcall.msize);
- break;
- case TATTACH:
- if(error)
- destroyfid(pc, r->fid->fid);
- free(r->ifcall.uname);
- free(r->ifcall.aname);
- break;
- case TOPEN:
- case TCREATE:
- if(!error) {
- r->fid->omode = r->ifcall.mode;
- r->fid->qid = r->ofcall.qid;
- }
- free(r->ifcall.name);
- r->ofcall.iounit = pc->msize - sizeof(unsigned long);
- break;
- case TWALK:
- if(error || r->ofcall.nwqid < r->ifcall.nwname) {
- if(r->ifcall.fid != r->ifcall.newfid && r->newfid)
- destroyfid(pc, r->newfid->fid);
- if(!error && r->ofcall.nwqid == 0)
- error = Enofile;
- }else{
- if(r->ofcall.nwqid == 0)
- r->newfid->qid = r->fid->qid;
- else
- r->newfid->qid = r->ofcall.wqid[r->ofcall.nwqid-1];
- }
- free(*r->ifcall.wname);
- break;
- case TWRITE:
- free(r->ifcall.data);
- break;
- case TREMOVE:
- if(r->fid)
- destroyfid(pc, r->fid->fid);
- break;
- case TCLUNK:
- if(r->fid)
- destroyfid(pc, r->fid->fid);
- if(!pc->conn && r->ifcall.tag == IXP_NOTAG)
- pc->ref--;
- break;
- case TFLUSH:
- if((r->oldreq = lookupkey(&pc->tagmap, r->ifcall.oldtag)))
- respond(r->oldreq, Einterrupted);
- if(!pc->conn && r->ifcall.tag == IXP_NOTAG)
- pc->ref--;
- break;
- case TREAD:
- case TSTAT:
- break;
- /* Still to be implemented: flush, wstat, auth */
- }
-
- r->ofcall.tag = r->ifcall.tag;
- if(!error)
- r->ofcall.type = r->ifcall.type + 1;
- else {
- r->ofcall.type = RERROR;
- r->ofcall.ename = error;
- }
-
- if(pc->conn)
- ixp_server_respond_fcall(pc->conn, &r->ofcall);
-
- switch(r->ofcall.type) {
- case RSTAT:
- free(r->ofcall.stat);
- break;
- case RREAD:
- free(r->ofcall.data);
- break;
- }
-
- deletekey(&pc->tagmap, r->ifcall.tag);;
- free(r);
-
- if(!pc->conn && pc->ref == 0)
- free_p9conn(pc);
-}
-
-/* Pending request cleanup */
-static void
-ixp_void_request(void *t) {
- P9Req *r, *tr;
- P9Conn *pc;
-
- r = t;
- pc = r->conn;
-
- tr = cext_emallocz(sizeof(P9Req));
- tr->conn = pc;
- tr->ifcall.type = TFLUSH;
- tr->ifcall.tag = IXP_NOTAG;
- tr->ifcall.oldtag = r->ifcall.tag;
- ixp_handle_req(tr);
-}
-
-/* Open FID cleanup */
-static void
-ixp_void_fid(void *t) {
- P9Conn *pc;
- P9Req *tr;
- Fid *f;
-
- f = t;
- pc = f->conn;
-
- tr = cext_emallocz(sizeof(P9Req));
- tr->fid = f;
- tr->conn = pc;
- tr->ifcall.type = TCLUNK;
- tr->ifcall.tag = IXP_NOTAG;
- tr->ifcall.fid = f->fid;
- ixp_handle_req(tr);
-}
-
-static void
-ixp_p9conn_incref(void *r) {
- P9Conn *pc = *(P9Conn **)r;
- pc->ref++;
-}
-
-/* To cleanup a connction, we increase the ref count for
- * each open FID and pending request and generate clunk and
- * flush requests. As each request is responded to and each
- * FID is clunked, we decrease the ref count. When the ref
- * count is 0, we free the P9Conn and its buf. The IXPConn
- * is taken care of in server.c */
-static void
-ixp_cleanup_conn(IXPConn *c) {
- P9Conn *pc = c->aux;
- pc->conn = nil;
- pc->ref = 1;
- execmap(&pc->tagmap, ixp_p9conn_incref);
- execmap(&pc->fidmap, ixp_p9conn_incref);
- if(pc->ref > 1) {
- execmap(&pc->tagmap, ixp_void_request);
- execmap(&pc->fidmap, ixp_void_fid);
- }
- if(--pc->ref == 0)
- free_p9conn(pc);
-}
-
-/* Handle incoming 9P connections */
-void
-serve_9pcon(IXPConn *c) {
- int fd = accept(c->fd, nil, nil);
- if(fd < 0)
- return;
-
- P9Conn *pc = cext_emallocz(sizeof(P9Conn));
- pc->srv = c->aux;
-
- /* XXX */
- pc->msize = 1024;
- pc->buf = cext_emallocz(pc->msize);
-
- initmap(&pc->tagmap, TAG_BUCKETS, &pc->taghash);
- initmap(&pc->fidmap, FID_BUCKETS, &pc->fidhash);
-
- ixp_server_open_conn(c->srv, fd, pc, ixp_server_handle_fcall, ixp_cleanup_conn);
-}
diff --git a/libixp/server.c b/libixp/server.c
@@ -1,130 +0,0 @@
-/*
- * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
- * See LICENSE file for license details.
- */
-
-#include <errno.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <unistd.h>
-
-#include "ixp.h"
-
-static unsigned char *msg[IXP_MAX_MSG];
-
-IXPConn *
-ixp_server_open_conn(IXPServer *s, int fd, void *aux,
- void (*read)(IXPConn *c), void (*close)(IXPConn *c))
-{
- IXPConn *c = cext_emallocz(sizeof(IXPConn));
- c->fd = fd;
- c->aux = aux;
- c->srv = s;
- c->read = read;
- c->close = close;
- c->next = s->conn;
- s->conn = c;
- return c;
-}
-
-void
-ixp_server_close_conn(IXPConn *c)
-{
- IXPServer *s = c->srv;
- IXPConn **tc;
- for(tc=&s->conn; *tc && *tc != c; tc=&(*tc)->next);
- cext_assert(*tc == c);
- *tc = c->next;
- c->closed = 1;
- if(c->close)
- c->close(c);
- else
- shutdown(c->fd, SHUT_RDWR);
- close(c->fd);
- free(c);
-}
-
-static void
-prepare_select(IXPServer *s)
-{
- IXPConn **c;
- FD_ZERO(&s->rd);
- for(c=&s->conn; *c; *c && (c=&(*c)->next)) {
- if(s->maxfd < (*c)->fd)
- s->maxfd = (*c)->fd;
- if((*c)->read)
- FD_SET((*c)->fd, &s->rd);
- }
-}
-
-static void
-handle_conns(IXPServer *s)
-{
- IXPConn *c, *n;
- for((c=s->conn) && (n=c->next); c; (c=n) && (n=c->next))
- if(FD_ISSET(c->fd, &s->rd) && c->read)
- c->read(c);
-}
-
-char *
-ixp_server_loop(IXPServer *s)
-{
- int r;
- s->running = 1;
-
- /* main loop */
- while(s->running) {
- prepare_select(s);
-
- r = select(s->maxfd + 1, &s->rd, 0, 0, 0);
- if(r == -1 && errno == EINTR)
- continue;
- if(r < 0)
- return "fatal select error";
- else if(r > 0)
- handle_conns(s);
- }
- return nil;
-}
-
-unsigned int
-ixp_server_receive_fcall(IXPConn *c, Fcall *fcall)
-{
- unsigned int msize;
- char *errstr = 0;
- if(!(msize = ixp_recv_message(c->fd, msg, IXP_MAX_MSG, &errstr))) {
- ixp_server_close_conn(c);
- return 0;
- }
- return ixp_msg2fcall(fcall, msg, IXP_MAX_MSG);
-}
-
-int
-ixp_server_respond_fcall(IXPConn *c, Fcall *fcall)
-{
- char *errstr;
- unsigned int msize = ixp_fcall2msg(msg, fcall, IXP_MAX_MSG);
- if(c->closed)
- return 0;
- if(ixp_send_message(c->fd, msg, msize, &errstr) != msize) {
- ixp_server_close_conn(c);
- return -1;
- }
- return 0;
-}
-
-void
-ixp_server_close(IXPServer *s)
-{
- IXPConn *c, *next;
- for(c=s->conn; c; c=next) {
- next=c->next;
- ixp_server_close_conn(c);
- }
-}
diff --git a/libixp/socket.c b/libixp/socket.c
@@ -1,200 +0,0 @@
-/*
- * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
- * See LICENSE file for license details.
- */
-
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <netinet/in.h>
-#include <netdb.h>
-#include <sys/un.h>
-#include <unistd.h>
-
-#include "ixp.h"
-
-static int
-connect_unix_sock(char *address)
-{
- int fd = 0;
- struct sockaddr_un addr = { 0 };
- socklen_t su_len;
-
- /* init */
- addr.sun_family = AF_UNIX;
- strncpy(addr.sun_path, address, sizeof(addr.sun_path));
- su_len = sizeof(struct sockaddr) + strlen(addr.sun_path);
-
- if((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
- return -1;
- if(connect(fd, (struct sockaddr *) &addr, su_len)) {
- close(fd);
- return -1;
- }
- return fd;
-}
-
-static int
-connect_inet_sock(char *host)
-{
- int fd = 0;
- struct sockaddr_in addr = { 0 };
- struct hostent *hp;
- char *port = strrchr(host, '!');
- unsigned int prt;
-
- if(!port)
- return -1;
- *port = 0;
- port++;
- if(sscanf(port, "%d", &prt) != 1)
- return -1;
-
- /* init */
- if((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
- return -1;
- hp = gethostbyname(host);
- addr.sin_family = AF_INET;
- addr.sin_port = htons(prt);
- bcopy(hp->h_addr, &addr.sin_addr, hp->h_length);
-
- if(connect(fd, (struct sockaddr *) &addr, sizeof(struct sockaddr_in))) {
- close(fd);
- return -1;
- }
- return fd;
-}
-
-int
-ixp_connect_sock(char *address)
-{
- char *p;
-
- if((p = strchr(address, '!'))) {
- *p = 0;
- p++;
-
- if(!strncmp(address, "unix", 5))
- return connect_unix_sock(p);
- else if(!strncmp(address, "tcp", 4))
- return connect_inet_sock(p);
- }
- return -1;
-}
-
-static int
-create_inet_sock(char *host, char **errstr)
-{
- int fd;
- struct sockaddr_in addr = { 0 };
- struct hostent *hp;
- char *port = strrchr(host, '!');
- unsigned int prt;
-
- if(!port) {
- *errstr = "no port provided in address";
- return -1;
- }
- *port = 0;
- port++;
- if(sscanf(port, "%d", &prt) != 1) {
- *errstr = "invalid port number";
- return -1;
- }
- signal(SIGPIPE, SIG_IGN);
- if((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
- *errstr = "cannot open socket";
- return -1;
- }
- addr.sin_family = AF_INET;
- addr.sin_port = htons(prt);
-
- if(!strncmp(host, "*", 2))
- addr.sin_addr.s_addr = htonl(INADDR_ANY);
- else if((hp = gethostbyname(host)))
- bcopy(hp->h_addr, &addr.sin_addr, hp->h_length);
- else {
- *errstr = "cannot translate hostname to an address";
- return -1;
- }
-
- if(bind(fd, (struct sockaddr *) &addr, sizeof(struct sockaddr_in)) < 0) {
- *errstr = "cannot bind socket";
- close(fd);
- return -1;
- }
-
- if(listen(fd, IXP_MAX_CACHE) < 0) {
- *errstr = "cannot listen on socket";
- close(fd);
- return -1;
- }
- return fd;
-}
-
-static int
-create_unix_sock(char *file, char **errstr)
-{
- int fd;
- int yes = 1;
- struct sockaddr_un addr = { 0 };
- socklen_t su_len;
-
- signal(SIGPIPE, SIG_IGN);
- if((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
- *errstr = "cannot open socket";
- return -1;
- }
- if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
- (char *) &yes, sizeof(yes)) < 0) {
- *errstr = "cannot set socket options";
- close(fd);
- return -1;
- }
- addr.sun_family = AF_UNIX;
- strncpy(addr.sun_path, file, sizeof(addr.sun_path));
- su_len = sizeof(struct sockaddr) + strlen(addr.sun_path);
-
- unlink(file); /* remove old socket, if any */
- if(bind(fd, (struct sockaddr *) &addr, su_len) < 0) {
- *errstr = "cannot bind socket";
- close(fd);
- return -1;
- }
- chmod(file, S_IRWXU);
-
- if(listen(fd, IXP_MAX_CACHE) < 0) {
- *errstr = "cannot listen on socket";
- close(fd);
- return -1;
- }
- return fd;
-}
-
-int
-ixp_create_sock(char *address, char **errstr)
-{
- char *p = strchr(address, '!');
- char *addr, *type;
-
- if(!p) {
- *errstr = "no socket type defined";
- return -1;
- }
- *p = 0;
-
- addr = &p[1];
- type = address; /* unix, inet */
-
- if(!strncmp(type, "unix", 5))
- return create_unix_sock(addr, errstr);
- else if(!strncmp(type, "tcp", 4))
- return create_inet_sock(addr, errstr);
- else
- *errstr = "unkown socket type";
- return -1;
-}
diff --git a/libixp/transport.c b/libixp/transport.c
@@ -1,77 +0,0 @@
-/*
- * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
- * See LICENSE file for license details.
- */
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <unistd.h>
-
-#include "ixp.h"
-
-unsigned int
-ixp_send_message(int fd, void *msg, unsigned int msize, char **errstr)
-{
- unsigned int num = 0;
- int r;
-
- /* send message */
- while(num < msize) {
- r = write(fd, msg + num, msize - num);
- if(r == -1 && errno == EINTR)
- continue;
- if(r < 1) {
- *errstr = "broken pipe";
- return 0;
- }
- num += r;
- }
- return num;
-}
-
-static unsigned int
-ixp_recv_data(int fd, void *msg, unsigned int msize, char **errstr)
-{
- unsigned int num = 0;
- int r = 0;
-
- /* receive data */
- while(num < msize) {
- r = read(fd, msg + num, msize - num);
- if(r == -1 && errno == EINTR)
- continue;
- if(r < 1) {
- *errstr = "broken pipe";
- return 0;
- }
- num += r;
- }
- return num;
-}
-
-unsigned int
-ixp_recv_message(int fd, void *msg, unsigned int msglen, char **errstr)
-{
- unsigned int msize;
-
- /* receive header */
- if(ixp_recv_data(fd, msg, sizeof(unsigned int), errstr) !=
- sizeof(unsigned int))
- return 0;
- ixp_unpack_u32((void *)&msg, nil, &msize);
- if(msize > msglen) {
- *errstr = "invalid message header";
- return 0;
- }
- /* receive message */
- if(ixp_recv_data(fd, msg, msize - sizeof(unsigned int), errstr)
- != msize - sizeof(unsigned int))
- return 0;
- return msize;
-}
diff --git a/liblitz/brush.c b/liblitz/brush.c
@@ -6,7 +6,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <cext.h>
#include "blitz.h"
void
@@ -30,7 +29,7 @@ blitz_draw_label(BlitzBrush *b, char *text)
return;
shortened = 0;
- cext_strlcpy(buf, text, sizeof(buf));
+ strncpy(buf, text, sizeof(buf));
len = strlen(buf);
gcv.foreground = b->color.fg;
gcv.background = b->color.bg;
diff --git a/liblitz/color.c b/liblitz/color.c
@@ -4,7 +4,6 @@
*/
#include <string.h>
-#include <cext.h>
#include "blitz.h"
@@ -14,7 +13,7 @@ xloadcolor(Blitz *blitz, char *colstr)
XColor color;
char col[8];
- cext_strlcpy(col, colstr, sizeof(col));
+ strncpy(col, colstr, sizeof(col));
col[7] = 0;
XAllocNamedColor(blitz->dpy,
DefaultColormap(blitz->dpy, blitz->screen), col, &color, &color);
diff --git a/liblitz/font.c b/liblitz/font.c
@@ -7,8 +7,6 @@
#include <stdlib.h>
#include <string.h>
#include <locale.h>
-#include <cext.h>
-
#include "blitz.h"
unsigned int
@@ -16,7 +14,7 @@ blitz_textwidth_l(BlitzFont *font, char *text, unsigned int len)
{
if(font->set) {
XRectangle r;
- XmbTextExtents(font->set, text, len, nil, &r);
+ XmbTextExtents(font->set, text, len, NULL, &r);
return r.width;
}
return XTextWidth(font->xfont, text, len);
@@ -32,7 +30,7 @@ void
blitz_loadfont(Blitz *blitz, BlitzFont *font)
{
char *fontname = font->fontstr;
- char **missing = nil, *def = "?";
+ char **missing = NULL, *def = "?";
int n;
setlocale(LC_ALL, "");
@@ -45,7 +43,7 @@ blitz_loadfont(Blitz *blitz, BlitzFont *font)
XFreeStringList(missing);
if(font->set) {
XFreeFontSet(blitz->dpy, font->set);
- font->set = nil;
+ font->set = NULL;
}
}
if(font->set) {
@@ -68,7 +66,7 @@ blitz_loadfont(Blitz *blitz, BlitzFont *font)
else {
if(font->xfont)
XFreeFont(blitz->dpy, font->xfont);
- font->xfont = nil;
+ font->xfont = NULL;
font->xfont = XLoadQueryFont(blitz->dpy, fontname);
if (!font->xfont) {
fontname = "fixed";
diff --git a/mouse.c b/mouse.c
@@ -0,0 +1,287 @@
+/*
+ * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
+ * (C)opyright MMVI Kris Maglione <fbsdaemon@gmail.com>
+ * See LICENSE file for license details.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "wm.h"
+
+#define ButtonMask (ButtonPressMask | ButtonReleaseMask)
+#define MouseMask (ButtonMask | PointerMotionMask)
+
+static void
+rect_morph_xy(XRectangle *rect, int dx, int dy, BlitzAlign *mask)
+{
+ BlitzAlign new_mask = 0;
+ if(*mask & NORTH) {
+ if(rect->height - dy >= 0 || *mask & SOUTH) {
+ rect->y += dy;
+ rect->height -= dy;
+ }
+ else {
+ rect->y += rect->height;
+ rect->height = dy - rect->height;
+ new_mask ^= NORTH|SOUTH;
+ }
+ }
+ if(*mask & SOUTH) {
+ if(rect->height + dy >= 0 || *mask & NORTH)
+ rect->height += dy;
+ else {
+ rect->height = -dy - rect->height;
+ rect->y -= rect->height;
+ new_mask ^= NORTH|SOUTH;
+ }
+ }
+ if(*mask & EAST) {
+ if(rect->width + dx >= 0 || *mask & WEST)
+ rect->width += dx;
+ else {
+ rect->width = -dx - rect->width;
+ rect->x -= rect->width;
+ new_mask ^= EAST|WEST;
+ }
+ }
+ if(*mask & WEST) {
+ if(rect->width - dx >= 0 || *mask & EAST) {
+ rect->x += dx;
+ rect->width -= dx;
+ }
+ else {
+ rect->x += rect->width;
+ rect->width = dx - rect->width;
+ new_mask ^= EAST|WEST;
+ }
+ }
+ *mask ^= new_mask;
+}
+
+typedef struct {
+ XRectangle *rects;
+ int num;
+ int x1, y1, x2, y2;
+ BlitzAlign mask;
+ int *delta;
+} SnapArgs;
+
+static void
+snap_line(SnapArgs *a)
+{
+ int i, t_xy;
+
+ /* horizontal */
+ if(a->y1 == a->y2 && (a->mask & (NORTH|SOUTH))) {
+ for(i=0; i < a->num; i++) {
+ if(!((a->rects[i].x + a->rects[i].width < a->x1) ||
+ (a->rects[i].x > a->x2))) {
+
+ if(abs(a->rects[i].y - a->y1) <= abs(*a->delta))
+ *a->delta = a->rects[i].y - a->y1;
+
+ t_xy = a->rects[i].y + a->rects[i].height;
+ if(abs(t_xy - a->y1) < abs(*a->delta))
+ *a->delta = t_xy - a->y1;
+ }
+ }
+ }
+ else if (a->mask & (EAST|WEST)) {
+ /* This is the same as above, tr/xy/yx/,
+ * s/width/height/, s/height/width/ */
+ for(i=0; i < a->num; i++) {
+ if(!((a->rects[i].y + a->rects[i].height < a->y1) ||
+ (a->rects[i].y > a->y2))) {
+
+ if(abs(a->rects[i].x - a->x1) <= abs(*a->delta))
+ *a->delta = a->rects[i].x - a->x1;
+
+ t_xy = a->rects[i].x + a->rects[i].width;
+ if(abs(t_xy - a->x1) < abs(*a->delta))
+ *a->delta = t_xy - a->x1;
+ }
+ }
+ }
+}
+
+BlitzAlign
+snap_rect(XRectangle *rects, int num, XRectangle *current,
+ BlitzAlign *mask, int snap)
+{
+ SnapArgs a = { rects, num, 0, 0, 0, 0, *mask, nil };
+ int dx = snap + 1, dy = snap + 1;
+ BlitzAlign ret;
+
+ a.x1 = current->x;
+ a.x2 = current->x + current->width;
+ a.delta = &dy;
+ if(*mask & NORTH) {
+ a.y2 = a.y1 = current->y;
+ snap_line(&a);
+ }
+ if(*mask & SOUTH) {
+ a.y2 = a.y1 = current->y + current->height;
+ snap_line(&a);
+ }
+
+ a.y1 = current->y;
+ a.y2 = current->y + current->height;
+ a.delta = &dx;
+ if(*mask & EAST) {
+ a.x1 = a.x2 = current->x + current->width;
+ snap_line(&a);
+ }
+ if(*mask & WEST) {
+ a.x1 = a.x2 = current->x;
+ snap_line(&a);
+ }
+
+ rect_morph_xy(current, abs(dx) <= snap ? dx : 0,
+ abs(dy) <= snap ? dy : 0, mask);
+
+ ret = *mask;
+ if(abs(dx) <= snap)
+ ret ^= EAST|WEST;
+ if(abs(dy) <= snap)
+ ret ^= NORTH|SOUTH;
+
+ return ret ^ CENTER;
+}
+
+static void
+draw_xor_border(XRectangle *r)
+{
+ XRectangle xor = *r;
+
+ xor.x += 2;
+ xor.y += 2;
+ xor.width = xor.width > 4 ? xor.width - 4 : 0;
+ xor.height = xor.height > 4 ? xor.height - 4 : 0;
+ XSetLineAttributes(blz.dpy, xorgc, 1, LineSolid, CapNotLast, JoinMiter);
+ XDrawLine(blz.dpy, blz.root, xorgc, xor.x + 2, xor.y + xor.height / 2,
+ xor.x + xor.width - 2, xor.y + xor.height / 2);
+ XDrawLine(blz.dpy, blz.root, xorgc, xor.x + xor.width / 2, xor.y + 2,
+ xor.x + xor.width / 2, xor.y + xor.height - 2);
+ XSetLineAttributes(blz.dpy, xorgc, 4, LineSolid, CapNotLast, JoinMiter);
+ XDrawRectangles(blz.dpy, blz.root, xorgc, &xor, 1);
+ XSync(blz.dpy, False);
+}
+
+void
+do_mouse_resize(Client *c, BlitzAlign align)
+{
+ BlitzAlign grav;
+ int px, py, ox, oy, i;
+ float rx, ry;
+ Window dummy;
+ XEvent ev;
+ unsigned int num = 0, di;
+ Frame *f = c->sel;
+ Bool floating = f->area->floating;
+ int snap = floating ? screen->rect.height / 66 : 0;
+ XRectangle *rects = floating ? rects_of_view(f->area->view, &num) : nil;
+ XRectangle frect = f->rect, ofrect;
+ XRectangle origin = frect;
+ XPoint pt;
+
+ XQueryPointer(blz.dpy, c->framewin, &dummy, &dummy, &i, &i, &ox, &oy, &di);
+ rx = (float)ox / frect.width;
+ ry = (float)oy / frect.height;
+
+ if (floating || align != CENTER) {
+ px = ox = frect.width / 2;
+ py = oy = frect.height / 2;
+ if(align&NORTH)
+ oy -= py;
+ if(align&SOUTH)
+ oy += py;
+ if(align&EAST)
+ ox += px;
+ if(align&WEST)
+ ox -= px;
+
+ XWarpPointer(blz.dpy, None, c->framewin, 0, 0, 0, 0, ox, oy);
+ }
+
+ XTranslateCoordinates(blz.dpy, c->framewin, blz.root, ox, oy, &ox, &oy, &dummy);
+ pt.x = ox; pt.y = oy;
+
+ XSync(blz.dpy, False);
+ if(XGrabPointer(blz.dpy, c->framewin, False, MouseMask, GrabModeAsync, GrabModeAsync,
+ None, cursor[CurResize], CurrentTime) != GrabSuccess)
+ return;
+
+ XGrabServer(blz.dpy);
+ draw_xor_border(&frect);
+ for(;;) {
+ XMaskEvent(blz.dpy, MouseMask | ExposureMask, &ev);
+ switch (ev.type) {
+ case ButtonRelease:
+ draw_xor_border(&frect);
+ if(!floating)
+ resize_column(c, &frect, (align == CENTER) ? &pt : nil);
+ else
+ resize_client(c, &frect, False);
+ if(rects)
+ free(rects);
+ XUngrabServer(blz.dpy);
+ XUngrabPointer(blz.dpy, CurrentTime);
+ XSync(blz.dpy, False);
+
+ XWarpPointer(blz.dpy, None, c->framewin, 0, 0, 0, 0,
+ frect.width * rx, frect.height * ry);
+ return;
+ break;
+ case MotionNotify:
+ ofrect = frect;
+
+ pt.x = ev.xmotion.x;
+ pt.y = ev.xmotion.y;
+ XTranslateCoordinates(blz.dpy, c->framewin, blz.root, ev.xmotion.x,
+ ev.xmotion.y, &px, &py, &dummy);
+
+ rect_morph_xy(&origin, px-ox, py-oy, &align);
+ frect=origin;
+ ox=px; oy=py;
+
+ if(floating)
+ grav = snap_rect(rects, num, &frect, &align, snap);
+ else
+ grav = align ^ CENTER;
+ match_sizehints(c, &frect, floating, grav);
+
+ draw_xor_border(&ofrect);
+ draw_xor_border(&frect);
+ break;
+ case Expose:
+ (handler[Expose])(&ev);
+ break;
+ default: break;
+ }
+ }
+}
+
+void
+grab_mouse(Window w, unsigned long mod, unsigned int button)
+{
+ XGrabButton(blz.dpy, button, mod, w, False, ButtonMask,
+ GrabModeAsync, GrabModeSync, None, None);
+ if((mod != AnyModifier) && num_lock_mask) {
+ XGrabButton(blz.dpy, button, mod | num_lock_mask, w, False, ButtonMask,
+ GrabModeAsync, GrabModeSync, None, None);
+ XGrabButton(blz.dpy, button, mod | num_lock_mask | LockMask, w, False,
+ ButtonMask, GrabModeAsync, GrabModeSync, None, None);
+ }
+}
+
+void
+ungrab_mouse(Window w, unsigned long mod, unsigned int button)
+{
+ XUngrabButton(blz.dpy, button, mod, w);
+ if(mod != AnyModifier && num_lock_mask) {
+ XUngrabButton(blz.dpy, button, mod | num_lock_mask, w);
+ XUngrabButton(blz.dpy, button, mod | num_lock_mask | LockMask, w);
+ }
+}
diff --git a/rule.c b/rule.c
@@ -0,0 +1,76 @@
+/*
+ * (C)opyright MMVI Anselm R. Garbe <garbeam at gmail dot com>
+ * See LICENSE file for license details.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include "wm.h"
+
+/* basic rule matching language /regex/ -> value
+ * regex might contain POSIX regex syntax defined in regex(3) */
+enum {
+ IGNORE,
+ REGEX,
+ VALUE
+};
+
+void
+update_rules(Rule **rule, const char *data)
+{
+ int mode = IGNORE;
+ Rule *rul;
+ char *p, *r = nil, *v = nil, regex[256], value[256];
+
+ if(!data || !strlen(data))
+ return;
+
+ while((rul = *rule)) {
+ *rule = rul->next;
+ regfree(&rul->regex);
+ free(rul);
+ }
+
+ for(p = (char *)data; *p; p++)
+ switch(mode) {
+ case IGNORE:
+ if(*p == '/') {
+ mode = REGEX;
+ r = regex;
+ }
+ else if(*p == '>') {
+ mode = VALUE;
+ value[0] = 0;
+ v = value;
+ }
+ break;
+ case REGEX:
+ if(*p == '/') {
+ mode = IGNORE;
+ *r = 0;
+ }
+ else {
+ *r = *p;
+ r++;
+ }
+ break;
+ case VALUE:
+ if(*p == '\n' || *p == 0) {
+ *rule = cext_emallocz(sizeof(Rule));
+ *v = 0;
+ cext_trim(value, " \t/");
+ if(!regcomp(&(*rule)->regex, regex, 0)) {
+ cext_strlcpy((*rule)->value, value, sizeof(rul->value));
+ rule = &(*rule)->next;
+ }
+ else free(*rule);
+ mode = IGNORE;
+ }
+ else {
+ *v = *p;
+ v++;
+ }
+ break;
+ }
+}
diff --git a/test/test_fs.sh b/test/test_fs.sh
@@ -1,19 +0,0 @@
-#!/bin/sh
-# Heavy access test of wmiiwm's fs
-
-dump_fs() {
- echo $1
- wmiir read $1|wmiir write $1;
- for i in `wmiir read $1|awk '{print $10}'`
- do
- if test $i != "event"
- then
- dump_fs $1/$i
- fi
- done
-}
-
-while true
-do
- dump_fs /
-done
diff --git a/view.c b/view.c
@@ -0,0 +1,444 @@
+/*
+ * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
+ * See LICENSE file for license details.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "wm.h"
+
+static Bool
+is_empty(View *v)
+{
+ Area *a;
+ for(a=v->area; a; a=a->next)
+ if(a->frame)
+ return False;
+ return True;
+}
+
+Frame *
+clientframe_of_view(View *v, Client *c)
+{
+ Frame *f;
+ for(f=c->frame; f; f=f->cnext)
+ if(f->area->view == v)
+ break;
+ return f;
+}
+
+static void
+assign_sel_view(View *v)
+{
+ if(screen->sel != v) {
+ if(screen->sel)
+ write_event("UnfocusTag %s\n",screen->sel->name);
+ screen->sel = v;
+ write_event("FocusTag %s\n", screen->sel->name);
+ }
+}
+
+Client *
+sel_client_of_view(View *v) {
+ return v->sel && v->sel->sel ? v->sel->sel->client : nil;
+}
+
+View *
+get_view(const char *name)
+{
+ View *v;
+ int cmp;
+ for(v = view; v; v=v->next)
+ if((cmp=strcmp(name, v->name)) >= 0)
+ break;
+ if(!v || cmp != 0)
+ v = create_view(name);
+ return v;
+}
+
+View *
+create_view(const char *name)
+{
+ static unsigned short id = 1;
+ View **i, *v = cext_emallocz(sizeof(View));
+
+ v->id = id++;
+ cext_strlcpy(v->name, name, sizeof(v->name));
+ create_area(v, nil, 0);
+ create_area(v, v->area, 0);
+ v->area->floating = True;
+
+ for(i=&view; *i; i=&(*i)->next)
+ if(strcmp((*i)->name, name) < 0) break;
+ v->next = *i;
+ *i = v;
+
+ write_event("CreateTag %s\n", v->name);
+ if(!screen->sel)
+ assign_sel_view(v);
+
+ return v;
+}
+
+void
+destroy_view(View *v)
+{
+ Area *a;
+ View **i;
+
+ while((a = v->area)) {
+ v->area = a->next;
+ destroy_area(a);
+ };
+
+ for(i=&view; *i; i=&(*i)->next)
+ if(*i == v) break;
+ *i = v->next;
+
+ if(screen->sel == v)
+ for(screen->sel=view; screen->sel && screen->sel->next; screen->sel=screen->sel->next)
+ if(screen->sel->next == *i) break;
+
+ write_event("DestroyTag %s\n", v->name);
+ free(v);
+}
+
+static void
+update_frame_selectors(View *v)
+{
+ Area *a;
+ Frame *f;
+ for(a=v->area; a; a=a->next)
+ for(f=a->frame; f; f=f->anext)
+ f->client->sel = f;
+}
+
+void
+focus_view(WMScreen *s, View *v)
+{
+ Frame *f;
+ Client *c;
+
+ XGrabServer(blz.dpy);
+ assign_sel_view(v);
+
+ update_frame_selectors(v);
+
+ /* gives all(!) clients proper geometry (for use of different tags) */
+ for(c=client; c; c=c->next)
+ if((f = c->sel)) {
+ if(f->view == v) {
+ resize_client(c, &f->rect, False);
+ //XMoveWindow(blz.dpy, c->framewin, f->rect.x, f->rect.y);
+ }else
+ XMoveWindow(blz.dpy, c->framewin, 2 * s->rect.width + f->rect.x,
+ f->rect.y);
+ }
+
+ if((c = sel_client()))
+ focus_client(c, True);
+
+ draw_frames();
+ XSync(blz.dpy, False);
+ XUngrabServer(blz.dpy);
+ flush_masked_events(EnterWindowMask);
+}
+
+void
+select_view(const char *arg)
+{
+ char buf[256];
+ cext_strlcpy(buf, arg, sizeof(buf));
+ cext_trim(buf, " \t+");
+ if(!strlen(buf))
+ return;
+ assign_sel_view(get_view(arg));
+ update_views(); /* performs focus_view */
+}
+
+void
+attach_to_view(View *v, Frame *f)
+{
+ Area *a;
+ Client *c = f->client;
+
+ c->revert = nil;
+
+ if(c->trans || c->floating || c->fixedsize
+ || (c->rect.width == screen->rect.width && c->rect.height == screen->rect.height))
+ a = v->area;
+ else if(starting && v->sel->floating)
+ a = v->area->next;
+ else
+ a = v->sel;
+ attach_to_area(a, f, False);
+ v->sel = a;
+}
+
+void
+restack_view(View *v)
+{
+ Area *a;
+ Frame *f;
+ Client *c;
+ unsigned int n=0, i=0;
+ static Window *wins = nil;
+ static unsigned int winssz = 0;
+
+ for(c=client; c; c=c->next, i++);
+ if(i > winssz) {
+ winssz = 2 * i;
+ wins = cext_erealloc(wins, sizeof(Window) * winssz);
+ }
+
+ for(a=v->area; a; a=a->next) {
+ if(a->frame) {
+ wins[n++] = a->sel->client->framewin;
+ for(f=a->frame; f; f=f->anext)
+ if(f != a->sel) n++;
+ i=n;
+ for(f=a->frame; f; f=f->anext) {
+ Client *c = f->client;
+ update_client_grab(c, (v->sel == a) && (a->sel == f));
+ if(f != a->sel)
+ wins[--i] = c->framewin;
+ }
+ }
+ }
+
+ if(n)
+ XRestackWindows(blz.dpy, wins, n);
+}
+
+void
+scale_view(View *v, float w)
+{
+ unsigned int xoff, col_size = 0;
+ unsigned int min_width = screen->rect.width/NCOL;
+ Area *a;
+ float scale, dx = 0;
+ int wdiff = 0;
+
+ if(!v->area->next)
+ return;
+
+ for(a=v->area->next; a; a=a->next, col_size++)
+ dx += a->rect.width;
+ scale = w / dx;
+ xoff = 0;
+ for(a=v->area->next; a; a=a->next) {
+ a->rect.width *= scale;
+ if(!a->next)
+ a->rect.width = w - xoff;
+ xoff += a->rect.width;
+ }
+
+ /* min_width can only be respected when there is enough space; the caller should guarantee this */
+ if(col_size * min_width > w)
+ return;
+ xoff = 0;
+ for(a=v->area->next, col_size--; a; a=a->next, col_size--) {
+ if(a->rect.width < min_width)
+ a->rect.width = min_width;
+ else if((wdiff = xoff + a->rect.width - w + col_size * min_width) > 0)
+ a->rect.width -= wdiff;
+ if(!a->next)
+ a->rect.width = w - xoff;
+ xoff += a->rect.width;
+ }
+}
+
+void
+arrange_view(View *v)
+{
+ unsigned int xoff = 0;
+ Area *a;
+
+ if(!v->area->next)
+ return;
+
+ scale_view(v, screen->rect.width);
+ for(a=v->area->next; a; a=a->next) {
+ a->rect.x = xoff;
+ a->rect.y = 0;
+ a->rect.height = screen->rect.height - screen->brect.height;
+ xoff += a->rect.width;
+ arrange_column(a, False);
+ }
+}
+
+XRectangle *
+rects_of_view(View *v, unsigned int *num)
+{
+ XRectangle *result;
+ Frame *f;
+
+ *num = 2;
+ for(f=v->area->frame; f; f=f->anext, (*num)++);
+
+ result = cext_emallocz(*num * sizeof(XRectangle));
+ for(f=v->area->frame; f; f=f->anext)
+ *result++ = f->rect;
+ *result++ = screen->rect;
+ *result++ = screen->brect;
+
+ return result - *num;
+}
+
+/* XXX: This will need cleanup */
+unsigned char *
+view_index(View *v) {
+ unsigned int a_i, buf_i, n;
+ int len;
+ Frame *f;
+ Area *a;
+
+ len = BUFFER_SIZE;
+ buf_i = 0;
+ for((a = v->area), (a_i = 0); a && len > 0; (a=a->next), (a_i++)) {
+ if(a->floating)
+ n = snprintf(&buffer[buf_i], len, "# ~ %d %d\n",
+ a->rect.width, a->rect.height);
+ else
+ n = snprintf(&buffer[buf_i], len, "# %d %d %d\n",
+ a_i, a->rect.x, a->rect.width);
+ buf_i += n;
+ len -= n;
+ for(f=a->frame; f && len > 0; f=f->anext) {
+ XRectangle *r = &f->rect;
+ if(a->floating)
+ n = snprintf(&buffer[buf_i], len, "~ %d %d %d %d %d %s\n",
+ idx_of_client(f->client),
+ r->x, r->y, r->width, r->height,
+ f->client->props);
+ else
+ n = snprintf(&buffer[buf_i], len, "%d %d %d %d %s\n",
+ a_i, idx_of_client(f->client), r->y,
+ r->height, f->client->props);
+ if(len - n < 0)
+ return (unsigned char *)buffer;
+ buf_i += n;
+ len -= n;
+ }
+ }
+ return (unsigned char *)buffer;
+}
+
+Client *
+client_of_message(View *v, char *message, unsigned int *next)
+{
+ unsigned int i;
+ Client *c;
+
+ if(!strncmp(message, "sel ", 4)) {
+ *next = 4;
+ return sel_client_of_view(v);
+ }
+ if((1 != sscanf(message, "%d %n", &i, next)))
+ return nil;
+ for(c=client; i && c; c=c->next, i--);
+ return c;
+}
+
+Area *
+area_of_message(View *v, char *message, unsigned int *next) {
+ unsigned int i;
+ Area *a;
+
+ if(!strncmp(message, "sel ", 4)) {
+ *next = 4;
+ return v->sel;
+ }
+ if(!strncmp(message, "~ ", 2))
+ return v->area;
+ if(1 != sscanf(message, "%d %n", &i, next) || i == 0)
+ return nil;
+ for(a=v->area; i && a; a=a->next, i--);
+ return a;
+}
+
+char *
+message_view(View *v, char *message) {
+ unsigned int n, i;
+ Client *c;
+ Frame *f;
+ Area *a;
+ static char Ebadvalue[] = "bad value";
+
+ if(!strncmp(message, "send ", 5)) {
+ message += 5;
+ if(!(c = client_of_message(v, message, &n)))
+ return Ebadvalue;
+ if(!(f = clientframe_of_view(v, c)))
+ return Ebadvalue;
+ return send_client(f, &message[n]);
+ }
+ if(!strncmp(message, "select ", 7)) {
+ message += 7;
+ return select_area(v->sel, message);
+ }
+ if(!strncmp(message, "colmode ", 8)) {
+ message += 8;
+ if(!(a = area_of_message(v, message, &n)) || a == v->area)
+ return Ebadvalue;
+ if((i = column_mode_of_str(&message[n])) == -1)
+ return Ebadvalue;
+ a->mode = i;
+ arrange_column(a, True);
+ restack_view(v);
+ if(v == screen->sel)
+ focus_view(screen, v);
+ draw_frames();
+ return nil;
+ }
+ return Ebadvalue;
+}
+
+void
+update_views()
+{
+ View *n, *v;
+ View *old = screen->sel;
+
+ for(v=view; v; v=v->next)
+ update_frame_selectors(v);
+
+ if(old && !strncmp(old->name, "nil", 4))
+ old = nil;
+
+ for((v=view) && (n=v->next); v; (v=n) && (n=v->next))
+ if((v != old) && is_empty(v))
+ destroy_view(v);
+
+ if(old)
+ focus_view(screen, old);
+ else if(screen->sel)
+ focus_view(screen, screen->sel);
+}
+
+unsigned int
+newcolw_of_view(View *v)
+{
+ Rule *r;
+ Area *a;
+ unsigned int i, n;
+ regmatch_t tmpregm;
+
+ for(r=def.colrules.rule; r; r=r->next) {
+ if(!regexec(&r->regex, v->name, 1, &tmpregm, 0)) {
+ char buf[256];
+ char *toks[16];
+ cext_strlcpy(buf, r->value, sizeof(buf));
+ n = cext_tokenize(toks, 16, buf, '+');
+ for(a=v->area, i=0; a; a=a->next, i++);
+ if(n && n >= i) {
+ if(sscanf(toks[i - 1], "%u", &n) == 1)
+ return (screen->rect.width * n) / 100;
+ }
+ break;
+ }
+ }
+ return 0;
+}
diff --git a/wm.c b/wm.c
@@ -0,0 +1,410 @@
+/*
+ * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
+ * See LICENSE file for license details.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <X11/cursorfont.h>
+#include <X11/Xproto.h>
+#include <X11/keysym.h>
+#include <X11/Xatom.h>
+#include <X11/Xproto.h>
+
+#include "wm.h"
+
+static int other_wm_running;
+static int (*x_error_handler) (Display *, XErrorEvent *);
+static char version[] = "wmiiwm - " VERSION ", (C)opyright MMIV-MMVI Anselm R. Garbe\n";
+
+static void
+usage()
+{
+ fputs("usage: wmiiwm -a <address> [-r <wmiirc>] [-v]\n", stderr);
+ exit(1);
+}
+
+static void
+error(char *errstr, ...) {
+ va_list ap;
+ va_start(ap, errstr);
+ vfprintf(stderr, errstr, ap);
+ va_end(ap);
+ exit(1);
+}
+
+static void
+scan_wins()
+{
+ int i;
+ unsigned int num;
+ Window *wins;
+ XWindowAttributes wa;
+ Window d1, d2;
+
+ if(XQueryTree(blz.dpy, blz.root, &d1, &d2, &wins, &num)) {
+ for(i = 0; i < num; i++) {
+ if(!XGetWindowAttributes(blz.dpy, wins[i], &wa))
+ continue;
+ if(wa.override_redirect || XGetTransientForHint(blz.dpy, wins[i], &d1))
+ continue;
+ if(wa.map_state == IsViewable)
+ manage_client(create_client(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(blz.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)
+{
+ Atom *protocols;
+ long res;
+ int protos = 0;
+ int i;
+
+ res = win_property(w, wm_atom[WMProtocols], XA_ATOM, 20L,
+ ((unsigned char **) &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;
+}
+
+
+static void
+init_atoms()
+{
+ wm_atom[WMProtocols] = XInternAtom(blz.dpy, "WM_PROTOCOLS", False);
+ wm_atom[WMDelete] = XInternAtom(blz.dpy, "WM_DELETE_WINDOW", False);
+ net_atom[NetSupported] = XInternAtom(blz.dpy, "_NET_SUPPORTED", False);
+ net_atom[NetWMName] = XInternAtom(blz.dpy, "_NET_WM_NAME", False);
+ tags_atom = XInternAtom(blz.dpy, "_WIN_TAGS", False);
+
+ XChangeProperty(blz.dpy, blz.root, net_atom[NetSupported], XA_ATOM, 32,
+ PropModeReplace, (unsigned char *) net_atom, NetLast);
+}
+
+static void
+init_cursors()
+{
+ cursor[CurNormal] = XCreateFontCursor(blz.dpy, XC_left_ptr);
+ cursor[CurResize] = XCreateFontCursor(blz.dpy, XC_sizing);
+ cursor[CurMove] = XCreateFontCursor(blz.dpy, XC_fleur);
+ cursor[CurInput] = XCreateFontCursor(blz.dpy, XC_xterm);
+}
+
+static void
+init_screen(WMScreen *screen)
+{
+ Window w;
+ int ret;
+ unsigned mask;
+ XGCValues gcv;
+
+ gcv.subwindow_mode = IncludeInferiors;
+ gcv.function = GXxor;
+ gcv.foreground = def.selcolor.bg;
+ gcv.plane_mask = AllPlanes;
+ gcv.graphics_exposures = False;
+ xorgc = XCreateGC(blz.dpy, blz.root, GCForeground | GCGraphicsExposures |
+ GCFunction | GCSubwindowMode | GCPlaneMask, &gcv);
+
+ screen->rect.x = screen->rect.y = 0;
+ screen->rect.width = DisplayWidth(blz.dpy, blz.screen);
+ screen->rect.height = DisplayHeight(blz.dpy, blz.screen);
+ def.snap = screen->rect.height / 63;
+
+ sel_screen = XQueryPointer(blz.dpy, blz.root, &w, &w, &ret, &ret, &ret, &ret, &mask);
+}
+
+/*
+ * 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
+wmii_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, "wmiiwm: fatal error: Xrequest code=%d, Xerror code=%d\n",
+ error->request_code, error->error_code);
+ return x_error_handler(blz.dpy, error); /* calls 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 = 1;
+ return -1;
+}
+
+static void
+cleanup()
+{
+ Client *c;
+ for(c=client; c; c=c->next)
+ reparent_client(c, blz.root, c->sel->rect.x, c->sel->rect.y);
+ XSetInputFocus(blz.dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
+ XSync(blz.dpy, False);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int i;
+ char *address = nil, *wmiirc = nil, *namespace, *errstr;
+ WMScreen *s;
+ struct passwd *passwd;
+ XSetWindowAttributes wa;
+
+ /* command line args */
+ for(i = 1; (i < argc) && (argv[i][0] == '-'); i++) {
+ switch (argv[i][1]) {
+ case 'v':
+ fprintf(stdout, "%s", version);
+ exit(0);
+ break;
+ case 'a':
+ if(i + 1 < argc)
+ address = argv[++i];
+ else
+ usage();
+ break;
+ case 'r':
+ if(i + 1 < argc)
+ wmiirc = argv[++i];
+ else
+ usage();
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+ starting = True;
+
+ blz.dpy = XOpenDisplay(0);
+ if(!blz.dpy)
+ error("wmiiwm: cannot open dpy\n");
+ blz.screen = DefaultScreen(blz.dpy);
+ blz.root = RootWindow(blz.dpy, blz.screen);
+
+ /* check if another WM is already running */
+ other_wm_running = 0;
+ XSetErrorHandler(startup_error_handler);
+ /* this causes an error if some other WM is running */
+ XSelectInput(blz.dpy, blz.root, SubstructureRedirectMask | EnterWindowMask);
+ XSync(blz.dpy, False);
+
+ if(other_wm_running)
+ error("wmiiwm: another window manager is already running\n");
+
+ if(!address)
+ usage();
+
+ /* Check namespace permissions */
+ if(!strncmp(address, "unix!", 5)) {
+ struct stat st;
+ namespace = cext_estrdup(&address[5]);
+
+ for(i = strlen(namespace) - 1; i >= 0; i--)
+ if(namespace[i] == '/') break;
+ namespace[i+1] = '\0';
+ if(stat(namespace, &st))
+ error("wmiiwm: can't stat namespace directory \"%s\": %s\n",
+ namespace, strerror(errno));
+ if(getuid() != st.st_uid)
+ error("wmiiwm: namespace directory \"%s\" exists, but is not owned by you",
+ namespace);
+ if(st.st_mode & 077)
+ error("wmiiwm: namespace directory \"%s\" exists, "
+ "but has group or world permissions",
+ namespace);
+ free(namespace);
+ }
+
+ XSetErrorHandler(0);
+ x_error_handler = XSetErrorHandler(wmii_error_handler);
+ errstr = nil;
+ i = ixp_create_sock(address, &errstr);
+ if(i < 0)
+ error("wmiiwm: fatal: %s\n", errstr);
+
+ /* start wmiirc */
+ if(wmiirc) {
+ int name_len = strlen(wmiirc) + 6;
+ char execstr[name_len];
+ switch(fork()) {
+ case 0:
+ if(setsid() == -1)
+ error("wmiim: can't setsid: %s\n", strerror(errno));
+ close(i);
+ close(ConnectionNumber(blz.dpy));
+ snprintf(execstr, name_len, "exec %s", wmiirc);
+ execl("/bin/sh", "sh", "-c", execstr, nil);
+ error("wmiiwm: can't exec \"%s\": %s\n", wmiirc, strerror(errno));
+ case -1:
+ perror("wmiiwm: cannot fork wmiirc");
+ default:
+ break;
+ }
+ }
+
+ /* IXP server */
+ ixp_server_open_conn(&srv, i, &p9srv, serve_9pcon, nil);
+
+ /* X server */
+ ixp_server_open_conn(&srv, ConnectionNumber(blz.dpy), nil, check_x_event, nil);
+
+ view = nil;
+ client = nil;
+ key = nil;
+
+ passwd = getpwuid(getuid());
+ user = cext_estrdup(passwd->pw_name);
+
+ def.colrules.string = nil;
+ def.colrules.size = 0;
+ def.tagrules.string = nil;
+ def.tagrules.size = 0;
+ def.keys = nil;
+ def.keyssz = 0;
+ def.font.fontstr = cext_estrdup(BLITZ_FONT);
+ def.border = 2;
+ def.colmode = Coldefault;
+ cext_strlcpy(def.selcolor.colstr, BLITZ_SELCOLORS, sizeof(def.selcolor.colstr));
+ blitz_loadcolor(&blz, &def.selcolor);
+ cext_strlcpy(def.normcolor.colstr, BLITZ_NORMCOLORS, sizeof(def.normcolor.colstr));
+ blitz_loadcolor(&blz, &def.normcolor);
+ cext_strlcpy(def.bcolor[0].colstr, BLITZ_B1COLORS, sizeof(def.bcolor[0].colstr));
+ cext_strlcpy(def.bcolor[1].colstr, BLITZ_B2COLORS, sizeof(def.bcolor[1].colstr));
+ cext_strlcpy(def.bcolor[2].colstr, BLITZ_B3COLORS, sizeof(def.bcolor[2].colstr));
+ blitz_loadcolor(&blz, &def.bcolor[0]);
+ blitz_loadcolor(&blz, &def.bcolor[1]);
+ blitz_loadcolor(&blz, &def.bcolor[2]);
+ cext_strlcpy(def.grabmod, "Mod1", sizeof(def.grabmod));
+ def.mod = Mod1Mask;
+
+ init_atoms();
+ init_cursors();
+ blitz_loadfont(&blz, &def.font);
+ init_lock_keys();
+
+ num_screens = 1;
+ screens = cext_emallocz(num_screens * sizeof(*screens));
+
+ for(i = 0; i < num_screens; i++) {
+ s = &screens[i];
+ s->lbar = nil;
+ s->rbar = nil;
+ s->sel = nil;
+
+ init_screen(s);
+
+ pmap = XCreatePixmap(blz.dpy, blz.root, s->rect.width, s->rect.height,
+ DefaultDepth(blz.dpy, blz.screen));
+
+ wa.event_mask = SubstructureRedirectMask | EnterWindowMask | LeaveWindowMask;
+ wa.cursor = cursor[CurNormal];
+ XChangeWindowAttributes(blz.dpy, blz.root, CWEventMask | CWCursor, &wa);
+
+ wa.override_redirect = 1;
+ wa.background_pixmap = ParentRelative;
+ wa.event_mask = ExposureMask | ButtonReleaseMask
+ | SubstructureRedirectMask | SubstructureNotifyMask;
+
+ s->brect = s->rect;
+ s->brect.height = blitz_labelh(&def.font);
+ s->brect.y = s->rect.height - s->brect.height;
+ s->barwin = XCreateWindow(blz.dpy, RootWindow(blz.dpy, blz.screen),
+ s->brect.x, s->brect.y,
+ s->brect.width, s->brect.height, 0,
+ DefaultDepth(blz.dpy, blz.screen),
+ CopyFromParent, DefaultVisual(blz.dpy, blz.screen),
+ CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
+ XSync(blz.dpy, False);
+
+ s->bbrush.blitz = &blz;
+ s->bbrush.gc = XCreateGC(blz.dpy, s->barwin, 0, 0);
+ s->bbrush.drawable = pmap;
+ s->bbrush.rect = s->brect;
+ s->bbrush.rect.x = 0;
+ s->bbrush.rect.y = 0;
+ s->bbrush.color = def.normcolor;
+ s->bbrush.font = &def.font;
+ s->bbrush.border = True;
+
+ draw_bar(s);
+ XMapRaised(blz.dpy, s->barwin);
+ }
+
+ screen = &screens[0];
+ scan_wins();
+ update_views();
+
+ starting = False;
+
+ /* main event loop */
+ errstr = ixp_server_loop(&srv);
+ if(errstr)
+ fprintf(stderr, "wmii: fatal: %s\n", errstr);
+
+ cleanup();
+ XCloseDisplay(blz.dpy);
+ ixp_server_close(&srv);
+
+ return errstr ? 1 : 0;
+}
diff --git a/wm.h b/wm.h
@@ -0,0 +1,309 @@
+/*
+ * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
+ * See LICENSE file for license details.
+ */
+
+#include <stdio.h>
+#include <regex.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+#include <ixp.h>
+#include <blitz.h>
+
+/* WM atoms */
+enum { WMProtocols, WMDelete, WMLast };
+
+/* NET atoms */
+enum { NetSupported, NetWMName, NetLast };
+
+/* Column modes */
+enum { Coldefault, Colstack, Colmax };
+
+/* Cursor */
+enum { CurNormal, CurResize, CurMove, CurInput, CurLast };
+
+enum { NCOL = 16 };
+enum { WM_PROTOCOL_DELWIN = 1 };
+
+/* Data Structures */
+typedef struct View View;
+typedef struct Area Area;
+typedef struct Frame Frame;
+typedef struct Client Client;
+typedef struct Key Key;
+typedef struct Bar Bar;
+typedef struct Rule Rule;
+typedef struct Ruleset Ruleset;
+typedef struct WMScreen WMScreen;
+
+struct View {
+ View *next;
+ char name[256];
+ unsigned short id;
+ Area *area;
+ Area *sel;
+ Area *revert;
+};
+
+struct Area {
+ Area *next;
+ Frame *frame;
+ Frame *sel;
+ View *view;
+ Bool floating;
+ unsigned short id;
+ int mode;
+ XRectangle rect;
+};
+
+struct Frame {
+ Frame *cnext;
+ Frame *anext;
+ View *view;
+ Area *area;
+ unsigned short id;
+ XRectangle rect;
+ XRectangle revert;
+ Client *client;
+ Bool collapsed;
+ BlitzBrush tile;
+ BlitzBrush grabbox;
+ BlitzBrush titlebar;
+};
+
+struct Client {
+ Client *next;
+ Area *revert;
+ Frame *frame;
+ Frame *sel;
+ char name[256];
+ char tags[256];
+ char props[512];
+ unsigned short id;
+ unsigned int border;
+ int proto;
+ Bool floating;
+ Bool fixedsize;
+ Window win;
+ Window trans;
+ Window framewin;
+ XRectangle rect;
+ XSizeHints size;
+ GC gc;
+};
+
+struct Key {
+ Key *next;
+ Key *lnext;
+ Key *tnext;
+ unsigned short id;
+ char name[128];
+ unsigned long mod;
+ KeyCode key;
+};
+
+struct Bar {
+ Bar *next;
+ Bar *smaller;
+ char buf[280];
+ char text[256];
+ char name[256];
+ unsigned short id;
+ BlitzBrush brush;
+};
+
+struct Rule {
+ Rule *next;
+ regex_t regex;
+ char value[256];
+};
+
+struct Ruleset {
+ Rule *rule;
+ char *string;
+ unsigned int size;
+};
+
+/* global variables */
+struct {
+ BlitzColor selcolor;
+ BlitzColor normcolor;
+ BlitzColor bcolor[3];
+ BlitzFont font;
+ unsigned int border;
+ unsigned int snap;
+ char *keys;
+ unsigned int keyssz;
+ Ruleset tagrules;
+ Ruleset colrules;
+ char grabmod[5];
+ unsigned long mod;
+ int colmode;
+} def;
+
+struct WMScreen {
+ Bar *lbar;
+ Bar *rbar;
+ View *sel;
+ Window barwin;
+
+ XRectangle rect;
+ XRectangle brect;
+ BlitzBrush bbrush;
+} *screens, *screen;
+
+Client *client;
+View *view;
+Key *key;
+
+enum { BUFFER_SIZE = 8092 };
+char buffer[BUFFER_SIZE];
+
+/* IXP */
+IXPServer srv;
+P9Srv p9srv;
+
+/* X11 */
+unsigned int num_screens;
+Blitz blz;
+GC xorgc;
+char *user;
+Atom wm_atom[WMLast];
+Atom net_atom[NetLast];
+Atom tags_atom;
+Cursor cursor[CurLast];
+unsigned int valid_mask;
+unsigned int num_lock_mask;
+Bool sel_screen;
+Pixmap pmap;
+void (*handler[LASTEvent]) (XEvent *);
+
+/* Misc */
+Bool starting;
+
+/* wm.c */
+extern char *message_root(char *message);
+
+/* area.c */
+extern Area *create_area(View *v, Area *pos, unsigned int w);
+extern void destroy_area(Area *a);
+extern Area *area_of_id(View *t, unsigned short id);
+extern char *select_area(Area *a, char *arg);
+extern void send_to_area(Area *to, Area *from, Frame *f);
+extern void attach_to_area(Area *a, Frame *f, Bool send);
+extern void detach_from_area(Area *a, Frame *f);
+extern Client *sel_client_of_area(Area *a);
+
+/* bar.c */
+extern Bar *create_bar(Bar **b_link, char *name);
+extern void destroy_bar(Bar **b_link, Bar *b);
+extern void draw_bar(WMScreen *s);
+extern void resize_bar();
+extern Bar *bar_of_name(Bar *b_link, const char *name);
+
+/* client.c */
+extern Client *create_client(Window w, XWindowAttributes *wa);
+extern void destroy_client(Client *c);
+extern void configure_client(Client *c);
+extern void prop_client(Client *c, XPropertyEvent *e);
+extern void kill_client(Client *c);
+extern void gravitate_client(Client *c, Bool invert);
+extern void unmap_client(Client *c);
+extern void map_client(Client *c);
+extern void reparent_client(Client *c, Window w, int x, int y);
+extern void manage_client(Client *c);
+extern void focus_client(Client *c, Bool restack);
+extern void focus(Client *c, Bool restack);
+extern void resize_client(Client *c, XRectangle *r, Bool ignore_xcall);
+extern void match_sizehints(Client *c, XRectangle *r, Bool floating, BlitzAlign sticky);
+extern char *send_client(Frame *f, char *arg);
+extern char * message_client(Client *c, char *message);
+extern void move_client(Client *c, char *arg);
+extern void size_client(Client *c, char *arg);
+extern void newcol_client(Client *c, char *arg);
+extern Client *sel_client();
+extern Frame *frame_of_win(Window w);
+extern Client *client_of_win(Window w);
+extern int idx_of_client(Client *c);
+extern void update_client_grab(Client *c, Bool is_sel);
+extern void apply_rules(Client *c);
+extern void apply_tags(Client *c, const char *tags);
+
+/* column.c */
+extern void arrange_column(Area *a, Bool dirty);
+extern void scale_column(Area *a, float h);
+extern void resize_column(Client *c, XRectangle *r, XPoint *pt);
+extern int column_mode_of_str(char *arg);
+extern char *str_of_column_mode(int mode);
+extern Area *new_column(View *v, Area *pos, unsigned int w);
+
+/* event.c */
+extern void check_x_event(IXPConn *c);
+extern unsigned int flush_masked_events(long even_mask);
+
+/* frame.c */
+extern Frame *create_frame(Client *c, View *v);
+extern void remove_frame(Frame *f);
+extern void insert_frame(Frame *pos, Frame *f, Bool before);
+extern void draw_frame(Frame *f);
+extern void draw_frames();
+extern void update_frame_widget_colors(Frame *f);
+
+/* fs.c */
+extern void fs_attach(P9Req *r);
+extern void fs_clunk(P9Req *r);
+extern void fs_create(P9Req *r);
+extern void fs_flush(P9Req *r);
+extern void fs_freefid(Fid *f);
+extern void fs_open(P9Req *r);
+extern void fs_read(P9Req *r);
+extern void fs_remove(P9Req *r);
+extern void fs_stat(P9Req *r);
+extern void fs_walk(P9Req *r);
+extern void fs_write(P9Req *r);
+extern void write_event(char *format, ...);
+
+/* geom.c */
+extern Bool ispointinrect(int x, int y, XRectangle * r);
+extern BlitzAlign quadofcoord(XRectangle *rect, int x, int y);
+extern int strtorect(XRectangle *r, const char *val);
+
+/* key.c */
+extern void kpress(Window w, unsigned long mod, KeyCode keycode);
+extern void update_keys();
+extern void init_lock_keys();
+extern unsigned long mod_key_of_str(char *val);
+
+/* mouse.c */
+extern void do_mouse_resize(Client *c,BlitzAlign align);
+extern void grab_mouse(Window w, unsigned long mod, unsigned int button);
+extern void ungrab_mouse(Window w, unsigned long mod, unsigned int button);
+extern BlitzAlign snap_rect(XRectangle *rects, int num, XRectangle *current,
+ BlitzAlign *mask, int snap);
+
+/* rule.c */
+extern void update_rules(Rule **rule, const char *data);
+
+/* view.c */
+extern void arrange_view(View *v);
+extern void scale_view(View *v, float w);
+extern View *get_view(const char *name);
+extern View *create_view(const char *name);
+extern void focus_view(WMScreen *s, View *v);
+extern void update_client_views(Client *c, char **tags);
+extern XRectangle *rects_of_view(View *v, unsigned int *num);
+extern View *view_of_id(unsigned short id);
+extern void select_view(const char *arg);
+extern void attach_to_view(View *v, Frame *f);
+extern Client *sel_client_of_view(View *v);
+extern char *message_view(View *v, char *message);
+extern void restack_view(View *v);
+extern unsigned char *view_index(View *v);
+extern void destroy_view(View *v);
+extern void update_views();
+extern unsigned int newcolw_of_view(View *v);
+
+/* wm.c */
+extern int wmii_error_handler(Display *dpy, XErrorEvent *error);
+extern int win_proto(Window w);
diff --git a/wmii b/wmii
@@ -0,0 +1,10 @@
+#!/bin/sh
+# start wmiiwm and wait for its termination
+
+WMII_CONFPATH="$HOME/.wmii-4:CONFPREFIX/wmii-4" export WMII_CONFPATH
+WMII_NS_DIR="/tmp/ns.$USER.${DISPLAY%.0}" export WMII_NS_DIR
+WMII_ADDRESS="unix!$WMII_NS_DIR/wmii" export WMII_ADDRESS
+mkdir -m 700 "$WMII_NS_DIR" 2>/dev/null
+
+mkdir $HOME/.wmii-4 2>/dev/null && CONFPREFIX/wmii-4/welcome &
+exec wmiiwm -a $WMII_ADDRESS -r `PATH="$WMII_CONFPATH:$PATH" which wmiirc`
diff --git a/wmii.1 b/wmii.1
@@ -0,0 +1,209 @@
+.TH WMII 1 wmii-4
+.SH NAME
+wmii \- window manager improved 2
+.SH SYNOPSIS
+.B wmii
+.SH DESCRIPTION
+.SS Overview
+.B wmii
+is a script that launches the wmii window manager and its various utilities and
+makes sure that they are configured for use.
+.SS Actions
+An action is a shell script in the default setup, but it can actually be
+any executable file. It is executed usually by selecting it from the
+actions menu.
+You can customize an action by copying it from the global action
+directory CONFPREFIX/wmii-4 to $HOME/.wmii-4 and then editing the copy to
+fit your needs. Of course you can also create your own actions there; make
+sure that they are executable.
+.P
+Here is a list of the default actions:
+.TP 2
+quit
+leave the window manager nicely
+.TP 2
+status
+periodically print date and load average to the bar
+.TP 2
+welcome
+display a welcome message that contains the wmii tutorial
+.TP 2
+wmiirc
+configure wmii
+.SS Default Key Bindings
+.PD 0
+.B Moving Around
+.RS 2
+.TP 16
+.I Key
+.I Action
+.TP
+.B Mod-h
+Move to a
+.B window
+to the
+.B left
+of the one currently focused
+.TP
+.B Mod-l
+Move to a
+.B window
+to the
+.B right
+of the one currently focused
+.TP
+.B Mod-j
+Move to a
+.B window below
+the one currently focused
+.TP
+.B Mod-k
+Move to a
+.B window above
+the one currently focused
+.TP
+.B Mod-space
+.B Toggle
+between the managed and floating
+.B layer
+.TP
+.BI Mod-t \ tag
+Move to the
+.B view
+of the given
+.I tag
+.TP
+.B Mod-[0-9]
+Move to the
+.B view
+with the given number
+.PD 1
+.P
+.RE
+.B Moving Things Around
+.RS 2
+.PD 0
+.TP 16
+.I Key
+.I Action
+.TP
+.B Mod-Shift-h
+Move the current window
+.B window
+to a column on the
+.B left
+.TP
+.B Mod-Shift-l
+Move the current window
+.B window
+to a column on the
+.B right
+.TP
+.B Mod-Shift-j
+Move the current
+.B window below
+the one beneath it
+.TP
+.B Mod-Shift-k
+Move the current
+.B window above
+the one above it
+.TP
+.B Mod-Shift-space
+.B Toggle
+the current
+.B window
+between the managed and floating
+.B layer
+.TP
+.BI Mod-Shift-t \ tag
+Move the current window to the
+.B view
+of the given
+.I tag
+.TP
+.B Mod-Shift-[0-9]
+Move to the current window to the
+.B view
+with the given number
+.PD 1
+.P
+.RE
+.B Miscellaneous
+.RS 2
+.PD 0
+.TP 16
+.I Key
+.I Action
+.TP
+.B Mod-m
+Switch the current column to
+.B max mode
+.TP
+.B Mod-s
+Switch the current column to
+.B stack mode
+.TP
+.B Mod-d
+Switch the current column to
+.B default mode
+.TP
+.B Mod-Shift-c
+.B Kill
+the selected client
+.TP
+.BI Mod-p \ program
+.B Execute
+.I program
+.TP
+.BI Mod-a \ action
+Execute the
+.B named action
+.TP
+.B Mod-Enter
+Start an
+.B xterm
+
+.SS Configuration
+If you feel the need to change the default configuration, then customize (as
+described above) the
+.B wmiirc
+action. This action is executed at the end of the
+.B wmii
+script and does all the work of setting up the window manager, the key
+bindings, the bar labels, etc.
+.SH FILES
+.TP
+/tmp/ns.$USER.${DISPLAY%.0}/wmii
+The wmii socket file which provides a 9P service.
+.TP
+CONFPREFIX/wmii-4
+Global action directory.
+.TP
+$HOME/.wmii-4
+User-specific action directory. Actions are first searched here.
+.SH ENVIRONMENT
+.TP
+HOME, DISPLAY
+See the section
+.B FILES
+above.
+.P
+The following variables are set and exported within
+.B wmii
+and thus can be used in actions:
+.TP
+PATH, OLD_PATH
+.B wmii
+adds the local and global actions directory to the front of the existing PATH.
+The original value is saved in OLD_PATH.
+.TP
+WMII_ADDRESS
+Socket file of
+.BR wmiiwm (1).
+Used by
+.BR wmiir (1).
+.SH SEE ALSO
+.BR wmiiwm (1),
+.BR dmenu (1),
+.BR wmiir (1)
diff --git a/wmii.svg b/wmii.svg
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ sodipodi:docbase="/home/zahod"
+ sodipodi:docname="wmii9.svg"
+ inkscape:version="0.41"
+ sodipodi:version="0.32"
+ version="1.0"
+ x="0.0000000"
+ y="0.0000000"
+ width="240.00000px"
+ height="120.00000px"
+ id="svg2">
+ <metadata
+ id="metadata1296">
+ <rdf:RDF
+ id="RDF1298">
+ <cc:Work
+ id="Work1300"
+ rdf:about="">
+ <dc:format
+ id="format1302">image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage"
+ id="type1304" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ inkscape:current-layer="svg2"
+ inkscape:window-y="0"
+ inkscape:window-x="0"
+ inkscape:cy="50.733714"
+ inkscape:cx="133.35719"
+ inkscape:zoom="1.6877337"
+ inkscape:window-height="538"
+ inkscape:window-width="640"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ borderopacity="1.0"
+ bordercolor="#666666"
+ pagecolor="#ffffff"
+ id="base" />
+ <defs
+ id="defs3" />
+ <g
+ transform="translate(0.000000,-932.3622)"
+ id="layer1">
+ <path
+ d="M 0.0000000,971.36227 L 26.999997,971.36227 L 26.999997,1025.3623 L 54.000004,1025.3623 L 54.000004,971.28555 L 81.310128,971.28555 L 81.310128,1025.3623 L 108.00001,1025.3623 L 108.00001,971.36227 L 240.00000,971.36227 L 240.00000,1052.3622 L 213.00001,1052.3622 L 213.00001,998.36218 L 186.00001,998.36218 L 186.00001,1052.3622 L 159.00001,1052.3622 L 159.00001,998.46222 L 132.01646,998.46222 L 132.01646,1052.3622 L 0.0000000,1052.3622 L 0.0000000,971.36227 z M 159.00001,932.36216 L 186.00001,932.36216 L 186.00001,959.36220 L 159.00001,959.36220 L 159.00001,932.36216 z M 213.00001,932.36216 L 240.00000,932.36216 L 240.00000,959.36220 L 213.00001,959.36220 L 213.00001,932.36216 z "
+ style="fill:#010101"
+ id="path1366" />
+ </g>
+</svg>
diff --git a/wmiiwm.1 b/wmiiwm.1
@@ -0,0 +1,162 @@
+.TH WMIIWM 1 wmii-4
+.SH NAME
+wmiiwm \- window manager improved 2 (core)
+.SH SYNOPSIS
+.B wmiiwm
+.B \-a
+.I <address>
+.RB [ \-c ]
+.RB [ \-v ]
+.SH DESCRIPTION
+
+.PD 0
+.I The information in this manual page is
+.IR obselete .
+.P
+An update is not likely util
+.I wmii\-4
+nears release.
+
+.SS Overview
+.BR wmiiwm (1)
+is the core of window manager improved 2.
+.P
+.B wmii
+is a dynamic window manager for X11. In contrast to static window management
+the user rarely has to think about how to organize windows, no matter what he
+is doing or how many applications are used at the same time. The window manager
+adapts to the current environment and fits to the needs of the user, rather
+than forcing him to use a preset, fixed layout and trying to shoehorn all
+windows and applications into it.
+.P
+.B wmii
+supports classic and tiled window management with extended keyboard and mouse
+control. The classic window management arranges windows in a floating layer
+in which windows can be moved and resized freely. The tiled window management
+is based on columns which split up the screen horizontally. Each column handles
+arbitrary windows and arranges them vertically in a non\-overlapping way. They
+can then be moved and resized between and within columns at will.
+.P
+.B wmii
+provides a virtual filesystem which represents the internal state similar to
+the procfs of Unix operating systems. Modifying this virtual filesystem results
+in changing the state of the window manager. The virtual filesystem service can
+be accessed through 9P\-capable client programs, like
+.BR wmiir (1) .
+This allows simple and powerful remote control of the core window manager.
+.P
+.B wmii
+basically consists of clients, columns, views, and the bar, which are described
+in detail in the
+.B Terminology
+section.
+.SS Options
+.TP
+.BI \-a " address"
+Lets you specify the address which
+.B wmiiwm
+uses to listen for connections. The syntax for
+.I address
+is taken (along with many other profound ideas) from the Plan 9 operating
+system and has the form
+.B unix!/path/to/socket
+for unix socket files, and
+.B tcp!hostname!port
+for tcp sockets.
+.TP
+.B \-c
+Checks if another window manager is running. If not it exits with termination code
+0.
+.TP
+.B \-v
+Prints version information to stdout, then exits.
+.SS Terminology
+.TP 2
+Display
+A running X server instance consisting of input devices and screens.
+.TP 2
+Screen
+A physical or virtual (Xinerama or
+.BR Xnest (1))
+screen of an X display. A screen displays a bar window and a view at a time.
+.TP 2
+Window
+A (rectangular) drawable X object which is displayed on a screen, usually an
+application window.
+.TP 2
+Client
+An application window surrounded by a frame window containing a border and a
+title\-bar. The title\-bar contains three labels. The first one displays the tags
+of a client, the second one displays the client's title, and the third one
+displays the client's index and the total number of clients within the column.
+If the client is focused within the column, the third label is highlighted.
+If the client is attached to the floating layer, a ~ character is prepended in
+the third label as well.
+.TP 2
+Floating layer
+A screen layer of
+.B wmii
+on top of all other layers, where clients are arranged in a classic (floating)
+way. They can be resized or moved freely.
+.TP 2
+Managed layer
+A screen layer of
+.B wmii
+behind the floating layer, where clients are arranged in a non\-overlapping
+(managed) way. Here, the window manager dynamically assigns each client a
+size and position. The managed layer consists of columns.
+.TP 2
+Tag
+Alphanumeric strings which can be assigned to a client. This provides a
+mechanism to group clients with similar properties. Clients can have one
+tag, e.g.
+.IR work ,
+or several tags, e.g.
+.IR work+mail .
+Tags are separated with the
+.I +
+character.
+.TP 2
+View
+A set of clients containing a specific tag, quite similiar to a workspace in
+other window managers. It consists of the floating and managed layers.
+.TP 2
+Column
+A column is a screen area which arranges clients vertically in a
+non\-overlapping way. Columns provide three different modes, which arrange
+clients with equal size, stacked, or maximized respectively. Clients can be
+moved and resized between and within columns freely.
+.TP 2
+Bar
+The bar at the bottom of the screen displays a label for each view and
+allows the creation of arbitrary user\-defined labels.
+.TP 2
+Event
+An event is a message which can be read from a special file in the filesystem
+of
+.BR wmiiwm ,
+such as a mouse button press, a key press, or a message written by a different
+9P\-client.
+.SS Basic window management
+Running a raw
+.B wmiiwm
+process without the
+.BR wmii (1)
+script provides basic window management capabilities already. However to use
+it effectively, remote control through its filesystem interface is necessary.
+By default it is only usable with the mouse in conjunction with the
+.I Mod1 (Alt)
+modifier key. Other interactions like customizing the style, killing or
+retagging clients, or grabbing keys cannot be achieved without accessing the
+filesystem.
+.P
+The filesystem can be accessed by connecting to the
+.I address
+of
+.B wmiiwm
+with any 9P\-capable client, like
+.BR wmiir (1).
+.SH SEE ALSO
+.BR wmii (1),
+.BR dmenu (1),
+.BR wmiir (1)