wmii

git clone git://oldgit.suckless.org/wmii/
Log | Files | Refs | README | LICENSE

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)