added initial files

This commit is contained in:
garbeam 2005-11-18 17:54:58 +02:00
commit caaad63c0d
89 changed files with 14369 additions and 0 deletions

92
CHANGES Normal file
View File

@ -0,0 +1,92 @@
3 - (20051101): Tini
2 - (20050701): Boyd
* new sloppy focus
* new ahwm-alike moving and resizing of frames
* new tabbing through un-/locking frames
* new builtin pager layer
* new builtin icon layer
* new layers per page:
- floating layercontains floating frames
- managed layer contains tiled frames
* new native layout mechanism: C interface implements functions:
init, deinit, manage, unmanage, resize, select
* new default layouts: tiled, float, grid, vsplit
* renamed wmiinput into wmimenu, several cleanups and added
double-buffered rendering
* changed wmikeys to allow grabbing the keyboard for special modes
like resize mode, move mode
* changed wmibar to calculate label sizes dynamically and added
double-buffered rendering
* removed limited generic layout mechanism
* removed nesting from layout mechanism
* fixed following issues:
- http://wmi.modprobe.de/index.php/PITS/00001
- http://wmi.modprobe.de/index.php/PITS/00002
- http://wmi.modprobe.de/index.php/PITS/00003
- http://wmi.modprobe.de/index.php/PITS/00004
- http://wmi.modprobe.de/index.php/PITS/00005
- http://wmi.modprobe.de/index.php/PITS/00006
- http://wmi.modprobe.de/index.php/PITS/00007
- http://wmi.modprobe.de/index.php/PITS/00008
- http://wmi.modprobe.de/index.php/PITS/00009
- http://wmi.modprobe.de/index.php/PITS/00010
- http://wmi.modprobe.de/index.php/PITS/00011
- http://wmi.modprobe.de/index.php/PITS/00012
- http://wmi.modprobe.de/index.php/PITS/00013
- http://wmi.modprobe.de/index.php/PITS/00014
- http://wmi.modprobe.de/index.php/PITS/00015
- http://wmi.modprobe.de/index.php/PITS/00016
- http://wmi.modprobe.de/index.php/PITS/00017
- http://wmi.modprobe.de/index.php/PITS/00018
- http://wmi.modprobe.de/index.php/PITS/00019
- http://wmi.modprobe.de/index.php/PITS/00020
- http://wmi.modprobe.de/index.php/PITS/00021
- http://wmi.modprobe.de/index.php/PITS/00022
- http://wmi.modprobe.de/index.php/PITS/00023
- http://wmi.modprobe.de/index.php/PITS/00024
- http://wmi.modprobe.de/index.php/PITS/00025
- http://wmi.modprobe.de/index.php/PITS/00026
- http://wmi.modprobe.de/index.php/PITS/00027
- http://wmi.modprobe.de/index.php/PITS/00028
- http://wmi.modprobe.de/index.php/PITS/00029
- http://wmi.modprobe.de/index.php/PITS/00030
- http://wmi.modprobe.de/index.php/PITS/00031
- http://wmi.modprobe.de/index.php/PITS/00032
- http://wmi.modprobe.de/index.php/PITS/00033
- http://wmi.modprobe.de/index.php/PITS/00034
- http://wmi.modprobe.de/index.php/PITS/00035
- http://wmi.modprobe.de/index.php/PITS/00036
- http://wmi.modprobe.de/index.php/PITS/00037
- http://wmi.modprobe.de/index.php/PITS/00038
- http://wmi.modprobe.de/index.php/PITS/00039
- http://wmi.modprobe.de/index.php/PITS/00040
- http://wmi.modprobe.de/index.php/PITS/00041
- http://wmi.modprobe.de/index.php/PITS/00042
- http://wmi.modprobe.de/index.php/PITS/00043
- http://wmi.modprobe.de/index.php/PITS/00044
- http://wmi.modprobe.de/index.php/PITS/00045
- http://wmi.modprobe.de/index.php/PITS/00046
- http://wmi.modprobe.de/index.php/PITS/00047
- http://wmi.modprobe.de/index.php/PITS/00048
- http://wmi.modprobe.de/index.php/PITS/00049
- http://wmi.modprobe.de/index.php/PITS/00050
- http://wmi.modprobe.de/index.php/PITS/00051
- http://wmi.modprobe.de/index.php/PITS/00052
- http://wmi.modprobe.de/index.php/PITS/00053
1.1 - (20050613): Zadkiel
* fixed following issues:
- http://wmi.modprobe.de/index.php/PITS/00001
- http://wmi.modprobe.de/index.php/PITS/00004
- http://wmi.modprobe.de/index.php/PITS/00007
- http://wmi.modprobe.de/index.php/PITS/00033
- http://wmi.modprobe.de/index.php/PITS/00019
- http://wmi.modprobe.de/index.php/PITS/00028
- http://wmi.modprobe.de/index.php/PITS/00034
- http://wmi.modprobe.de/index.php/PITS/00039
- http://wmi.modprobe.de/index.php/PITS/00027
- http://wmi.modprobe.de/index.php/PITS/00032
1 - (20050601): Uriel
initial release

22
LICENSE Normal file
View File

@ -0,0 +1,22 @@
MIT/X Consortium License
(C)opyright MMIII-MMV Anselm R. Garbe <garbeam at wmii dot de>
(C)opyright MMV Georg Neis <gn at oglaroon dot de>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

70
Makefile Normal file
View File

@ -0,0 +1,70 @@
# wmii - window manager improved 2
# (C)opyright MMIV-MMV Anselm R. Garbe
include config.mk
SUBDIRS = libcext liblitz libixp libixp2 libwmii cmd
BIN = cmd/wm/wmii cmd/wm/wmiiwm cmd/wmibar cmd/wmifs \
cmd/wmikeys cmd/wmimenu cmd/wmiplumb cmd/wmir cmd/wmiwarp
MAN1 = cmd/wm/wmii.1 cmd/wm/wmiiwm.1 cmd/wmibar.1 cmd/wmifs.1 \
cmd/wmikeys.1 cmd/wmimenu.1 cmd/wmir.1
all:
@echo wmii build options:
@echo "LIBS = ${LIBS}"
@echo "INCLUDES = ${INCLUDES}"
@echo "CFLAGS = ${CFLAGS}"
@echo "LDFLAGS = ${LDFLAGS}"
@echo "CC = ${CC}"
@for i in ${SUBDIRS} cmd/wm; do \
(cd $$i; ${MAKE}); \
done
dist: clean
@mkdir -p wmii-${VERSION}
@cp -R Makefile README LICENSE config.mk rc ${SUBDIRS} extra doc wmii-${VERSION}
@tar -cf wmii-${VERSION}.tar wmii-${VERSION}
@gzip wmii-${VERSION}.tar
@rm -rf wmii-${VERSION}
@echo created distribution wmii-${VERSION}.tar.gz
clean:
rm -f *.o
@for i in ${SUBDIRS} cmd/wm; do \
(cd $$i; ${MAKE} clean); \
done
rm -rf wmii-${VERSION}*
install: all
@mkdir -p ${DESTDIR}${PREFIX}/bin
@cp -f ${BIN} ${DESTDIR}${PREFIX}/bin
@sed 's|CONFPREFIX|${CONFPREFIX}|; s|9PREFIX|${9PREFIX}|' <cmd/wm/wmii >${DESTDIR}${PREFIX}/bin/wmii
@for i in ${BIN}; do \
chmod 755 ${DESTDIR}${PREFIX}/bin/`basename $$i`; \
done
@echo installed executable files to ${DESTDIR}${PREFIX}/bin
@mkdir -p ${DESTDIR}${CONFPREFIX}/wmii-3
@cd rc; for i in *; do \
sed 's|9PREFIX|${9PREFIX}|' <$$i >${DESTDIR}${CONFPREFIX}/wmii-3/$$i; \
chmod 755 ${DESTDIR}${CONFPREFIX}/wmii-3/$$i; \
done
@echo installed rc scripts to ${DESTDIR}${CONFPREFIX}/wmii-3
@mkdir -p ${DESTDIR}${MANPREFIX}/man1
@cp -f ${MAN1} ${DESTDIR}${MANPREFIX}/man1
@sed 's|CONFPREFIX|${CONFPREFIX}|' <cmd/wm/wmii.1 >${DESTDIR}${MANPREFIX}/man1/wmii.1
@for i in ${MAN1}; do \
chmod 444 ${DESTDIR}${MANPREFIX}/man1/`basename $$i`; \
done
@echo installed manual pages to ${DESTDIR}${MANPREFIX}/man1
uninstall:
@for i in ${BIN}; do \
rm -f ${DESTDIR}${PREFIX}/bin/`basename $$i`; \
done
@for i in ${MAN1}; do \
rm -f ${DESTDIR}${MANPREFIX}/man1/`basename $$i`; \
done
@rm -rf ${DESTDIR}${CONFPREFIX}/wmii-3
@echo "uninstalled wmii"

86
README Normal file
View File

@ -0,0 +1,86 @@
Abstract
--------
window manager improved 2 (wmii) [1] is an improved, modularized and
lightweight X11 window manager which supports tabbed, tiled and
conventional window management through layouts.
wmii consists of components that are independent processes and
communicate via a socket-based virtual filesystem which is oriented
on the "everything is file" paradigm of the Plan 9 [2] operating system.
The core distribution of wmii contains the window manager itself,
a master file system routing utility (wmifs), a generic bar (wmibar),
a shortcut handler (wmikeys), and a generic interaction menu (wmimenu)
beside several tiny utilities like wmir, wmiplumb, and wmiwarp.
Requirements
------------
In order to build wmii you need the Xlib header files.
Furthermore, the wmii configuration scripts rely on the 9rc package [3]
which contains ports of various Plan 9 standard tools, in particular the
rc shell.
Installation
------------
Edit config.mk to match your local setup. wmii is installed into
the /usr/local hierarchy by default.
Afterwards enter the following command to build and install wmii (if
necessary as root):
$ make clean install
Running wmii
------------
Add the following line to your .xinitrc to start wmii using startx:
exec wmii
In order to connect wmii or wmir to a specific display, make sure that
DISPLAY environment variable is set correctly, e.g.:
$ setenv DISPLAY foo.bar:1
$ wmii
This will start wmii on display :1 of the host foo.bar if it is present
in your .xinitrc.
Configuration
-------------
The configuration of wmii is done by customizing the existing actions.
Customizing an action means copying the appropriate file from the directory
$WMII_CONFDIR (usually /usr/local/etc/wmii-3) to
$HOME/.wmii-3 and editing it to fit your needs. The action of main
interest is called 'wmirc': it is executed on startup.
Credits
-------
Beside all thanks to the wmi contributors, the following people have
contributed especially to wmii in various ways:
- Christoph Wegscheider <christoph (dot) wegscheider (at) wegi (dot) net>
- Georg Neis <gn (at) oglaroon (dot) de>
- Uwe Zeisberger <zeisberg (at) informatik (dot) uni-freiburg (dot) de>
- Uriel <uriell (at) binarydream (dot) org>
- Scot Doyle <scot (at) scotdoyle (dot) com>
- Sebastian Hartmann <seb (dot) wmi (at) gmx (dot) de>
- Bernhard Leiner <bleiner (at) gmail (dot) com>
- Jonas Domeij <jonas (dot) domeij (at) gmail (dot) com>
- Vincent <10 (dot) 50 (at) free (dot) fr>
- Oliver Kopp <olly (at) flupp (dot) de>
- Sebastian Roth <sebastian (dot) roth (at) gmail (dot) com>
- Steve Hoffman <steveh (at) g2switchworks (dot) com>
- Christof Musik <christof (at) senfdax (dot) de>
- Steffen Liebergeld <perl (at) gmx (dot) org>
References
----------
[1] http://wmii.de
[2] http://www.cs.bell-labs.com/plan9dist/
[3] http://wmii.de/repos/9base/
--Anselm R. Garbe

86
cmd/Makefile Normal file
View File

@ -0,0 +1,86 @@
# window manager improved 2 utilities
# (C)opyright MMIV-MMV Anselm R. Garbe
include ../config.mk
CFLAGS += -I../liblitz -I../libixp -I../libwmii -I../libcext
LDFLAGS += -L../liblitz -llitz -L../libixp -lixp \
-L../libwmii -lwmii -L../libcext -lcext
LDFLAGS2 += ${LIBS} -L../liblitz -llitz -L../libixp2 -lixp \
-L../libwmii -lwmii -L../libcext -lcext
SRC_bar = wmibar.c
OBJ_bar = ${SRC_bar:.c=.o}
SRC_bar2 = wmibar2.c
OBJ_bar2 = ${SRC_bar2:.c=.o}
SRC_menu = wmimenu.c
OBJ_menu = ${SRC_menu:.c=.o}
SRC_r2 = wmir2.c
OBJ_r2 = ${SRC_r2:.c=.o}
SRC_r = wmir.c
OBJ_r = ${SRC_r:.c=.o}
SRC_fs = wmifs.c
OBJ_fs = ${SRC_fs:.c=.o}
SRC_keys = wmikeys.c
OBJ_keys = ${SRC_keys:.c=.o}
SRC_plumb = wmiplumb.c
OBJ_plumb = ${SRC_plumb:.c=.o}
SRC_warp = wmiwarp.c
OBJ_warp = ${SRC_warp:.c=.o}
all: wmibar wmibar2 wmimenu wmir wmir2 wmifs wmikeys wmiplumb wmiwarp
@echo built wmi commands
.c.o:
@echo CC $<
@${CC} -c ${CFLAGS} $<
wmibar: ${OBJ_bar}
@echo LD $@
@${CC} -o $@ ${OBJ_bar} ${LDFLAGS}
wmibar2: ${OBJ_bar2}
@echo LD $@
@${CC} -o $@ ${OBJ_bar2} ${LDFLAGS2}
wmimenu: ${OBJ_menu}
@echo LD $@
@${CC} -o $@ ${OBJ_menu} ${LDFLAGS}
wmir2: ${OBJ_r2}
@echo LD $@
@${CC} -o $@ ${OBJ_r2} -g -static -L${LIBDIR} -L/usr/lib -lc \
-L../libixp2 -lixp -L../libcext -lcext
wmir: ${OBJ_r}
@echo LD $@
@${CC} -o $@ ${OBJ_r} -g -static -L${LIBDIR} -L/usr/lib -lc \
-L../libixp -lixp -L../libcext -lcext
wmifs: ${OBJ_fs}
@echo LD $@
@${CC} -o $@ ${OBJ_fs} ${LDFLAGS}
wmikeys: ${OBJ_keys}
@echo LD $@
@${CC} -o $@ ${OBJ_keys} ${LDFLAGS}
wmiplumb: ${OBJ_plumb}
@echo LD $@
@${CC} -o $@ ${OBJ_plumb} ${LDFLAGS}
wmiwarp: ${OBJ_warp}
@echo LD $@
@${CC} -o $@ ${OBJ_warp} ${LDFLAGS}
clean:
rm -f wmibar wmibar2 wmimenu wmir wmir2 wmikeys wmiplumb wmifs\
wmiwarp *.o

15
cmd/wm/Makefile Normal file
View File

@ -0,0 +1,15 @@
# wm - window manager improved 2
# (C)opyright MMIV-MMV Anselm R. Garbe
include ../../config.mk
all: layout.c
@${MAKE} -f Makefile.wm
@echo built core wm
layout.c:
@echo SH mklayout.sh
@sh mklayout.sh
clean:
rm -f wmiiwm *.o layout.*

25
cmd/wm/Makefile.wm Normal file
View File

@ -0,0 +1,25 @@
# wm - window manager improved 2
# (C)opyright MMIV-MMV Anselm R. Garbe
include ../../config.mk
CFLAGS += -I../../liblitz -I../../libixp -I../../libwmii \
-I../../libcext
LDFLAGS += -L../../liblitz -llitz -L../../libixp -lixp \
-L../../libwmii -lwmii -L../../libcext -lcext
SRC = wm.c core.c client.c event.c mouse.c frame.c page.c layout.c
include layout.mk
OBJ = ${SRC:.c=.o}
all: wmiiwm
.c.o:
@echo CC $<
@${CC} -c ${CFLAGS} $<
wmiiwm: ${OBJ}
@echo LD $@
@${CC} -o $@ ${OBJ} ${LDFLAGS}

67
cmd/wm/area.c Normal file
View File

@ -0,0 +1,67 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "wm.h"
#include <cext.h>
static Area zero_area = {0};
void
free_area(Area* a)
{
ixp_remove_file(ixps, a->files[A_PREFIX]);
free(f);
}
void
destroy_area(Area *a)
{
unsigned int i;
a->layout->deinit(a);
for(i = 0; a->frames && a->frames[i]; i++);
destroy_frame(i);
free_area(a);
}
void
focus_area(Area *a, int raise, int up, int down)
{
Page *p = f->page;
Frame *old;
if (!p)
return;
old = get_selected(p);
if (down && f->clients)
focus_client(f->clients[f->sel], raise, 0);
if (is_managed_frame(f)) {
p->managed_stack = (Frame **)
attach_item_begin(detach_item
((void **) p->managed_stack, f,
sizeof(Frame *)), f, sizeof(Frame *));
p->files[P_MANAGED_SELECTED]->content =
f->files[F_PREFIX]->content;
p->files[P_MODE]->content = p->files[P_MANAGED_PREFIX]->content;
} else {
p->floating_stack = (Frame **)
attach_item_begin(detach_item
((void **) p->floating_stack, f,
sizeof(Frame *)), f, sizeof(Frame *));
p->files[P_FLOATING_SELECTED]->content =
f->files[F_PREFIX]->content;
p->files[P_MODE]->content = p->files[P_FLOATING_PREFIX]->content;
if (raise)
XRaiseWindow(dpy, f->win);
}
if (up)
focus_page(p, raise, 0);
}

366
cmd/wm/client.c Normal file
View File

@ -0,0 +1,366 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Xatom.h>
#include "wm.h"
#include <cext.h>
static Client zero_client = {0};
Client *
alloc_client(Window w)
{
static int id = 0;
char buf[MAX_BUF];
char buf2[MAX_BUF];
XClassHint ch;
Client *c = (Client *) emalloc(sizeof(Client));
*c = zero_client;
c->win = w;
snprintf(buf, MAX_BUF, "/detached/client/%d", id);
c->files[C_PREFIX] = ixp_create(ixps, buf);
win_prop(dpy, c->win, XA_WM_NAME, buf2, MAX_BUF);
snprintf(buf, MAX_BUF, "/detached/client/%d/name", id);
c->files[C_NAME] = wmii_create_ixpfile(ixps, buf, buf2);
if (XGetClassHint(dpy, c->win, &ch)) {
snprintf(buf, MAX_BUF, "/detached/client/%d/class", id);
c->files[C_CLASS] = wmii_create_ixpfile(ixps, buf, ch.res_class);
snprintf(buf, MAX_BUF, "/detached/client/%d/instance", id);
c->files[C_INSTANCE] = wmii_create_ixpfile(ixps, buf, ch.res_name);
} else {
snprintf(buf, MAX_BUF, "/detached/client/%d/class", id);
c->files[C_CLASS] = ixp_create(ixps, buf);
snprintf(buf, MAX_BUF, "/detached/client/%d/instance", id);
c->files[C_INSTANCE] = ixp_create(ixps, buf);
}
id++;
clients = (Client **) attach_item_end((void **) clients, c, sizeof(Client *));
XSelectInput(dpy, c->win, CLIENT_MASK);
return c;
}
void
set_client_state(Client * c, int state)
{
long data[2];
data[0] = (long) state;
data[1] = (long) None;
XChangeProperty(dpy, c->win, wm_state, wm_state, 32,
PropModeReplace, (unsigned char *) data, 2);
}
void
show_client(Client * c)
{
XSelectInput(dpy, c->win, CLIENT_MASK & ~StructureNotifyMask);
XMapWindow(dpy, c->win);
XSelectInput(dpy, c->win, CLIENT_MASK);
set_client_state(c, NormalState);
grab_client(c, Mod1Mask, Button1);
grab_client(c, Mod1Mask, Button3);
}
void
hide_client(Client * c)
{
ungrab_client(c, AnyModifier, AnyButton);
XSelectInput(dpy, c->win, CLIENT_MASK & ~StructureNotifyMask);
XUnmapWindow(dpy, c->win);
XSelectInput(dpy, c->win, CLIENT_MASK);
set_client_state(c, WithdrawnState);
}
void
reparent_client(Client * c, Window w, int x, int y)
{
XSelectInput(dpy, c->win, CLIENT_MASK & ~StructureNotifyMask);
XReparentWindow(dpy, c->win, w, x, y);
XSelectInput(dpy, c->win, CLIENT_MASK);
}
void
grab_client(Client * c, unsigned long mod, unsigned int button)
{
XSelectInput(dpy, c->win, CLIENT_MASK & ~StructureNotifyMask);
XGrabButton(dpy, button, mod, c->win, False,
ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);
if ((mod != AnyModifier) && num_lock_mask) {
XGrabButton(dpy, button, mod | num_lock_mask, c->win,
False, ButtonPressMask, GrabModeAsync, GrabModeAsync,
None, None);
XGrabButton(dpy, button, mod | num_lock_mask | LockMask,
c->win, False, ButtonPressMask, GrabModeAsync,
GrabModeAsync, None, None);
}
XSelectInput(dpy, c->win, CLIENT_MASK);
XSync(dpy, False);
}
void
ungrab_client(Client * c, unsigned long mod, unsigned int button)
{
XSelectInput(dpy, c->win, CLIENT_MASK & ~StructureNotifyMask);
XUngrabButton(dpy, button, mod, c->win);
if (mod != AnyModifier && num_lock_mask) {
XUngrabButton(dpy, button, mod | num_lock_mask, c->win);
XUngrabButton(dpy, button, mod | num_lock_mask | LockMask, c->win);
}
XSelectInput(dpy, c->win, CLIENT_MASK);
XSync(dpy, False);
}
void
configure_client(Client * c)
{
XConfigureEvent e;
e.type = ConfigureNotify;
e.event = c->win;
e.window = c->win;
e.x = c->rect.x;
e.y = c->rect.y;
if (c->frame) {
XRectangle *frect = rect_of_frame(c->frame);
e.x += frect->x;
e.y += frect->y;
}
e.width = c->rect.width;
e.height = c->rect.height;
e.border_width = c->border;
e.above = None;
e.override_redirect = False;
XSelectInput(dpy, c->win, CLIENT_MASK & ~StructureNotifyMask);
XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *) & e);
XSelectInput(dpy, c->win, CLIENT_MASK);
XSync(dpy, False);
}
void
close_client(Client * c)
{
if (c->proto & PROTO_DEL)
send_message(dpy, c->win, wm_protocols, wm_delete);
else
XKillClient(dpy, c->win);
}
void
_init_client(Client * c, XWindowAttributes * wa)
{
long msize;
c->rect.x = wa->x;
c->rect.y = wa->y;
c->border = wa->border_width;
c->rect.width = wa->width + 2 * c->border;
c->rect.height = wa->height + 2 * c->border;
XSetWindowBorderWidth(dpy, c->win, 0);
XSelectInput(dpy, c->win, PropertyChangeMask);
c->proto = win_proto(c->win);
XGetTransientForHint(dpy, c->win, &c->trans);
/* size hints */
if (!XGetWMNormalHints(dpy, c->win, &c->size, &msize)
|| !c->size.flags)
c->size.flags = PSize;
XAddToSaveSet(dpy, c->win);
}
void
handle_client_property(Client * c, XPropertyEvent * e)
{
char buf[1024];
long msize;
buf[0] = '\0';
if (e->state == PropertyDelete)
return; /* ignore */
if (e->atom == wm_protocols) {
/* update */
c->proto = win_proto(c->win);
return;
}
switch (e->atom) {
case XA_WM_NAME:
win_prop(dpy, c->win, XA_WM_NAME, buf, sizeof(buf));
if (strlen(buf)) {
if (c->files[C_NAME]->content)
free(c->files[C_NAME]->content);
c->files[C_NAME]->content = estrdup(buf);
c->files[C_NAME]->size = strlen(buf);
}
if (c->frame)
draw_client(c);
invoke_core_event(defaults[WM_EVENT_CLIENT_UPDATE]);
break;
case XA_WM_TRANSIENT_FOR:
XGetTransientForHint(dpy, c->win, &c->trans);
break;
case XA_WM_NORMAL_HINTS:
if (!XGetWMNormalHints(dpy, c->win, &c->size, &msize)
|| !c->size.flags) {
c->size.flags = PSize;
}
break;
}
}
void
free_client(Client * c)
{
clients =
(Client **) detach_item((void **) clients, c, sizeof(Client *));
ixp_remove_file(ixps, c->files[C_PREFIX]);
if (ixps->errstr)
fprintf(stderr, "wmiiwm: free_client(): %s\n", ixps->errstr);
free(c);
}
/* speed reasoned function for client property change */
void
draw_client(Client * c)
{
Frame *f = c->frame;
unsigned int tabh = tab_height(f);
int i, size;
XRectangle *frect;
int tw;
if (!tabh)
return;
size = count_items((void **) f->clients);
frect = rect_of_frame(f);
tw = frect->width;
if (size)
tw /= size;
for (i = 0; f->clients[i] && f->clients[i] != c; i++);
if (!f->clients[i + 1]) {
int xoff = i * tw;
draw_tab(f, c->files[C_NAME]->content, xoff, 0,
frect->width - xoff, tabh, ISSELFRAME(f)
&& f->clients[f->sel] == c);
} else
draw_tab(f, c->files[C_NAME]->content, i * tw, 0, tw, tabh,
is_selected(f) && f->clients[f->sel] == c);
}
void
draw_clients(Frame * f)
{
unsigned int tabh = tab_height(f);
int i, size = count_items((void **) f->clients);
XRectangle *frect = rect_of_frame(f);
int tw = frect->width;
if (!tabh || !size)
return;
if (size)
tw /= size;
for (i = 0; f->clients[i]; i++) {
if (!f->clients[i + 1]) {
int xoff = i * tw;
draw_tab(f, f->clients[i]->files[C_NAME]->content,
xoff, 0, frect->width - xoff, tabh, is_selected(f)
&& f->clients[f->sel] == f->clients[i]);
break;
} else
draw_tab(f, f->clients[i]->files[C_NAME]->content,
i * tw, 0, tw, tabh, is_selected(f)
&& f->clients[f->sel] == f->clients[i]);
}
XSync(dpy, False);
}
int
manage_class_instance(Client * c)
{
char buf[MAX_BUF];
File *f;
char *class = (char *) c->files[C_CLASS]->content;
char *inst = (char *) c->files[C_INSTANCE]->content;
if (!c->files[C_CLASS]->content || !c->files[C_INSTANCE]->content)
return 1;
snprintf(buf, sizeof(buf), "/default/client/%s:%s/manage",
class ? class : "", inst ? inst : "");
f = ixp_walk(ixps, buf);
if (!f) {
snprintf(buf, sizeof(buf), "/default/client/%s:%s/manage",
class ? class : "", "*");
f = ixp_walk(ixps, buf);
}
if (f && f->content)
return _strtonum(f->content, 0, 1);
return 1;
}
void
gravitate(Client * c, unsigned int tabh, unsigned int bw, int 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 = tabh;
break;
case EastGravity:
case CenterGravity:
case WestGravity:
dy = -(c->rect.height / 2) + tabh;
break;
case SouthEastGravity:
case SouthGravity:
case SouthWestGravity:
dy = -c->rect.height;
break;
default: /* don't care */
break;
}
/* x */
switch (gravity) {
case StaticGravity:
case NorthWestGravity:
case WestGravity:
case SouthWestGravity:
dx = bw;
break;
case NorthGravity:
case CenterGravity:
case SouthGravity:
dx = -(c->rect.width / 2) + bw;
break;
case NorthEastGravity:
case EastGravity:
case SouthEastGravity:
dx = -(c->rect.width + bw);
break;
default: /* don't care */
break;
}
if (invert) {
dx = -dx;
dy = -dy;
}
c->rect.x += dx;
c->rect.y += dy;
}

582
cmd/wm/core.c Normal file
View File

@ -0,0 +1,582 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <X11/Xatom.h>
#include <X11/Xproto.h>
#include <X11/keysym.h>
#include "wm.h"
static void new_page(void *obj, char *cmd);
static void _select_page(void *obj, char *cmd);
static void _destroy_page(void *obj, char *cmd);
static void quit(void *obj, char *cmd);
static void _attach_client(void *obj, char *cmd);
static void _detach_client(void *obj, char *cmd);
static void _close_client(void *obj, char *cmd);
static void pager(void *obj, char *cmd);
static void icons(void *obj, char *cmd);
/* action table for /ctl namespace */
Action core_acttbl[] = {
{"new", new_page},
{"destroy", _destroy_page},
{"select", _select_page},
{"attach", _attach_client},
{"detach", _detach_client},
{"close", _close_client},
{"quit", quit},
{"pager", pager},
{"icons", icons},
{0, 0}
};
int
comp_obj(void *c1, void *c2)
{
return c1 == c2;
}
void
run_action(File * f, void *obj, Action * acttbl)
{
int i;
size_t len;
if (!f->content)
return;
for (i = 0; acttbl[i].name; i++) {
len = strlen(acttbl[i].name);
if (!strncmp(acttbl[i].name, (char *) f->content, len)) {
if (f->size > len)
acttbl[i].func(obj, &((char *) f->content)[len + 1]);
else
acttbl[i].func(obj, 0);
return;
break;
}
}
fprintf(stderr, "wmiiwm: unknown action '%s'\n"
" or invalid ctl device\n", (char *) f->content);
}
static void
quit(void *obj, char *cmd)
{
ixps->runlevel = SHUTDOWN;
}
void
invoke_core_event(File * f)
{
if (!f->content)
return;
spawn(dpy, f->content);
}
void
focus_page(Page * p, int raise, int down)
{
if (!pages)
return;
if (p != pages[sel]) {
hide_page(pages[sel]);
sel = index_item((void **) pages, p);
show_page(pages[sel]);
defaults[WM_SEL_PAGE]->content = p->files[P_PREFIX]->content;
invoke_core_event(defaults[WM_EVENT_PAGE_UPDATE]);
}
if (down)
focus_area(p->areas[p->sel], raise, 0, down);
}
unsigned int
tab_height(Frame *f)
{
if (_strtonum(f->files[F_TAB]->content, 0, 1))
return font->ascent + font->descent + 4;
return 0;
}
unsigned int
border_width(Frame *f)
{
if(_strtonum(f->files[F_BORDER]->content, 0, 1))
return BORDER_WIDTH;
return 0;
}
static void
scale_rect(XRectangle * from_dim, XRectangle * to_dim,
XRectangle * src, XRectangle * tgt)
{
double wfact = (double) to_dim->width / (double) from_dim->width;
double hfact = (double) to_dim->height / (double) from_dim->height;
tgt->x = to_dim->x + (src->x * wfact);
tgt->y = to_dim->y + (src->y * hfact);
tgt->width = (src->width * wfact);
tgt->height = (src->height * hfact);
if (tgt->width < 1)
tgt->width = 1;
if (tgt->height < 1)
tgt->height = 1;
}
static void
draw_pager_page(Page * p, Draw * d)
{
unsigned int i, j;
XRectangle r = d->rect;
char name[4];
if (p == pages[sel]) {
d->bg = blitz_loadcolor(dpy, screen_num, defaults[WM_SEL_BG_COLOR]->content);
d->fg = blitz_loadcolor(dpy, screen_num, defaults[WM_SEL_FG_COLOR]->content);
d->border = blitz_loadcolor(dpy, screen_num, defaults[WM_SEL_BORDER_COLOR]->content);
d->font = font;
} else {
d->bg = blitz_loadcolor(dpy, screen_num, defaults[WM_NORM_BG_COLOR]->content);
d->fg = blitz_loadcolor(dpy, screen_num, defaults[WM_NORM_FG_COLOR]->content);
d->border = blitz_loadcolor(dpy, screen_num, defaults[WM_NORM_BORDER_COLOR]->content);
d->font = font;
}
snprintf(name, sizeof(name), "%d", index_item((void **)pages, p));
d->data = name;
blitz_drawlabel(dpy, d);
XSync(dpy, False);
for (i = 0; p->areas[i]; i++) {
for(j = 0; p->areas[i]->frames[j]; j++) {
if (i == p->sel && j == p->areas[i]->sel) {
d->bg = blitz_loadcolor(dpy, screen_num, defaults[WM_SEL_BG_COLOR]->content);
d->fg = blitz_loadcolor(dpy, screen_num, defaults[WM_SEL_FG_COLOR]->content);
d->border = blitz_loadcolor(dpy, screen_num, defaults[WM_SEL_BORDER_COLOR]->content);
d->font = font;
} else {
d->bg = blitz_loadcolor(dpy, screen_num, defaults[WM_NORM_BG_COLOR]->content);
d->fg = blitz_loadcolor(dpy, screen_num, defaults[WM_NORM_FG_COLOR]->content);
d->border = blitz_loadcolor(dpy, screen_num, defaults[WM_NORM_BORDER_COLOR]->content);
d->font = font;
}
d->data = p->areas[i]->frames[j]->clients[p->areas[i]->frames[j]->sel]->files[C_NAME]->content;
scale_rect(&rect, &r, &p->areas[i]->rect, &d->rect);
blitz_drawlabel(dpy, d);
XSync(dpy, False); /* do not clear upwards */
}
}
}
static void
draw_pager()
{
unsigned int ic, ir, tw, th, rows, cols, size;
int i = 0;
int dx;
Draw d = {0};
blitz_getbasegeometry((void **) pages, &size, &cols, &rows);
dx = (cols - 1) * GAP; /* GAPpx space */
tw = (rect.width - dx) / cols;
th = ((double) tw / rect.width) * rect.height;
d.drawable = transient;
d.gc = transient_gc;
for (ir = 0; ir < rows; ir++) {
for (ic = 0; ic < cols; ic++) {
d.rect.x = ic * tw + (ic * GAP);
d.rect.width = tw;
if (rows == 1)
d.rect.y = 0;
else
d.rect.y = ir * (rect.height - th) / (rows - 1);
d.rect.height = th;
if (!pages[i])
return;
draw_pager_page(pages[i], &d);
i++;
}
}
}
static Page *
xy_to_pager_page(int x, int y)
{
unsigned int ic, ir, tw, th, rows, cols, size;
int i = 0;
int dx;
XRectangle r;
if (!pages)
return 0;
blitz_getbasegeometry((void **) pages, &size, &cols, &rows);
dx = (cols - 1) * GAP; /* GAPpx space */
tw = (rect.width - dx) / cols;
th = ((double) tw / rect.width) * rect.height;
for (ir = 0; ir < rows; ir++) {
for (ic = 0; ic < cols; ic++) {
r.x = ic * tw + (ic * GAP);
r.width = tw;
if (rows == 1)
r.y = 0;
else
r.y = ir * (rect.height - th) / (rows - 1);
r.height = th;
if (!pages[i])
return 0;
if (blitz_ispointinrect(x, y, &r))
return pages[i];
i++;
}
}
return 0;
}
static int
handle_kpress(XKeyEvent * e)
{
KeySym ksym = XKeycodeToKeysym(dpy, e->keycode, 0);
if (ksym >= XK_1 && ksym <= XK_9)
return ksym - XK_1;
else if (ksym == XK_0)
return 9;
else if (ksym >= XK_a && ksym <= XK_z)
return 10 + ksym - XK_a;
return -1;
}
static void
pager(void *obj, char *cmd)
{
XEvent ev;
int i;
if (!pages)
return;
XClearWindow(dpy, transient);
XMapRaised(dpy, transient);
draw_pager();
while (XGrabKeyboard(dpy, transient, True, GrabModeAsync, GrabModeAsync, CurrentTime) != GrabSuccess)
usleep(1000);
for (;;) {
while (!XCheckWindowEvent(dpy, transient, ButtonPressMask | KeyPressMask, &ev)) {
usleep(20000);
continue;
}
switch (ev.type) {
case KeyPress:
XUnmapWindow(dpy, transient);
if ((i = handle_kpress(&ev.xkey)) != -1)
if (i < count_items((void **) pages))
focus_page(pages[i], 0, 1);
XUngrabKeyboard(dpy, CurrentTime);
return;
break;
case ButtonPress:
XUnmapWindow(dpy, transient);
if (ev.xbutton.button == Button1) {
Page *p = xy_to_pager_page(ev.xbutton.x, ev.xbutton.y);
if (p)
focus_page(p, 0, 1);
}
return;
break;
}
}
}
static void
draw_icons()
{
unsigned int i, ic, ir, tw, th, rows, cols, size;
int dx, dy;
if (!detached)
return;
blitz_getbasegeometry((void **) detached, &size, &cols, &rows);
dx = (cols - 1) * GAP; /* GAPpx space */
dy = (rows - 1) * GAP; /* GAPpx space */
tw = (rect.width - dx) / cols;
th = (rect.height - dy) / rows;
XClearWindow(dpy, transient);
XMapRaised(dpy, transient);
i = 0;
for (ir = 0; ir < rows; ir++) {
for (ic = 0; ic < cols; ic++) {
Client *c = detached[i++];
XRectangle cr;
if (!c)
return;
cr.x = ic * tw + (ic * GAP);
cr.y = ir * th + (ir * GAP);
cr.width = tw;
cr.height = th;
XMoveResizeWindow(dpy, c->win, cr.x, cr.y, cr.width,
cr.height);
configure_client(c);
show_client(c);
XRaiseWindow(dpy, c->win);
grab_client(c, AnyModifier, AnyButton);
XSync(dpy, False);
}
}
}
static void
icons(void *obj, char *cmd)
{
XEvent ev;
int i, n;
Client *c;
if (!detached)
return;
XClearWindow(dpy, transient);
XMapRaised(dpy, transient);
draw_icons();
while (XGrabKeyboard(dpy, transient, True, GrabModeAsync, GrabModeAsync, CurrentTime) != GrabSuccess)
usleep(1000);
for (;;) {
while (!XCheckMaskEvent(dpy, ButtonPressMask | KeyPressMask, &ev)) {
usleep(20000);
continue;
}
switch (ev.type) {
case KeyPress:
XUnmapWindow(dpy, transient);
if ((n = handle_kpress(&ev.xkey)) != -1) {
for (i = 0; detached && detached[i]; i++)
hide_client(detached[i]);
if (n - 1 < i) {
c = detached[n];
detached = (Client **) detach_item((void **) detached, c,
sizeof(Client *));
attach_client(c);
}
} else {
for (i = 0; detached && detached[i]; i++)
hide_client(detached[i]);
}
XUngrabKeyboard(dpy, CurrentTime);
return;
break;
case ButtonPress:
if (ev.xbutton.button == Button1) {
XUnmapWindow(dpy, transient);
for (i = 0; detached && detached[i]; i++)
hide_client(detached[i]);
if ((c = win_to_client(ev.xbutton.window))) {
detached =
(Client **) detach_item((void **) detached, c,
sizeof(Client *));
attach_client(c);
}
XUngrabKeyboard(dpy, CurrentTime);
}
return;
break;
}
}
}
static void
_close_client(void *obj, char *cmd)
{
if (clients && clients[sel_client])
close_client(clients[sel_client]);
}
static void
_attach_client(void *obj, char *cmd)
{
if (detached) {
Client *c = detached[0];
detached =
(Client **) detach_item((void **) detached, c,
sizeof(Client *));
attach_client(c);
}
}
static void
_detach_client(void *obj, char *cmd)
{
Frame *f;
if (!pages)
return;
f = SELFRAME(pages[sel]);
if (!f)
return;
detach_client_from_frame(f->clients[f->sel], 0, 0);
}
static void
_select_page(void *obj, char *cmd)
{
if (!pages || !cmd)
return;
if (!strncmp(cmd, "prev", 5))
sel = index_prev_item((void **) pages, pages[sel]);
else if (!strncmp(cmd, "next", 5))
sel = index_next_item((void **) pages, pages[sel]);
else
sel = _strtonum(cmd, 0, count_items((void **)pages));
focus_page(pages[sel], 0, 1);
}
void
destroy_page(Page * p)
{
unsigned int i;
for (i = 0; p->areas[i]; i++)
destroy_area(p->areas[i]);
free_page(p);
if (pages) {
show_page(pages[sel]);
defaults[WM_SEL_PAGE]->content = pages[sel]->files[P_PREFIX]->content;
focus_page(pages[sel], 0, 1);
invoke_core_event(defaults[WM_EVENT_PAGE_UPDATE]);
}
}
static void
_destroy_page(void *obj, char *cmd)
{
if (!pages)
return;
destroy_page(pages[sel]);
}
static void
new_page(void *obj, char *cmd)
{
if (pages)
hide_page(pages[sel]);
alloc_page("0");
}
Client *
win_to_client(Window w)
{
int i;
for (i = 0; clients && clients[i]; i++)
if (clients[i]->win == w)
return clients[i];
return 0;
}
void
scan_wins()
{
int i;
unsigned int num;
Window *wins;
XWindowAttributes wa;
Window d1, d2;
Client *c;
if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) {
for (i = 0; i < num; i++) {
if (!XGetWindowAttributes(dpy, wins[i], &wa))
continue;
if (wa.override_redirect
|| XGetTransientForHint(dpy, wins[i], &d1))
continue;
if (wa.map_state == IsViewable) {
c = alloc_client(wins[i]);
_init_client(c, &wa);
attach_client(c);
}
}
}
if (wins)
XFree(wins);
}
void *
get_func(void *acttbl[][2], int rows, char *fname)
{
int i;
for (i = 0; i < rows; i++) {
if (!strncmp((char *) acttbl[i][0], fname, MAX_BUF)) {
return acttbl[i][1];
}
}
return 0;
}
int
win_proto(Window w)
{
Atom *protocols;
long res;
int protos = 0;
int i;
res = property(dpy, w, wm_protocols, XA_ATOM,
20L, ((unsigned char **) &protocols));
if (res <= 0) {
return protos;
}
for (i = 0; i < res; i++) {
if (protocols[i] == wm_delete) {
protos |= PROTO_DEL;
}
}
free((char *) protocols);
return protos;
}
int
win_state(Window w)
{
/* state hints */
XWMHints *hints = XGetWMHints(dpy, w);
int res;
long *prop = 0;
if (property(dpy, w, wm_state, wm_state,
2L, ((unsigned char **) &prop)) > 0) {
res = (int) *prop;
free((long *) prop);
} else {
res = hints ? hints->initial_state : NormalState;
}
if (hints) {
free(hints);
}
return res;
}
void
handle_after_write(IXPServer * s, File * f)
{
if (f == defaults[WM_CTL])
run_action(f, 0, core_acttbl);
else if (f == defaults[WM_TRANS_COLOR]) {
unsigned long col[1];
col[0] = xorcolor.pixel;
XFreeColors(dpy, DefaultColormap(dpy, screen_num), col, 1, 0);
XAllocNamedColor(dpy, DefaultColormap(dpy, screen_num),
defaults[WM_TRANS_COLOR]->content,
&xorcolor, &xorcolor);
XSetForeground(dpy, xorgc, xorcolor.pixel);
}
check_event(0);
}

329
cmd/wm/event.c Normal file
View File

@ -0,0 +1,329 @@
/*
* (C)opyright MMIV-MMV 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 handle_buttonpress(XEvent * e);
static void handle_configurerequest(XEvent * e);
static void handle_destroynotify(XEvent * e);
static void handle_expose(XEvent * e);
static void handle_maprequest(XEvent * e);
static void handle_motionnotify(XEvent * e);
static void handle_propertynotify(XEvent * e);
static void handle_unmapnotify(XEvent * e);
static void handle_enternotify(XEvent * e);
static void update_ignore_enternotify_hack(XEvent * e);
static unsigned int ignore_enternotify_hack = 0;
void (*handler[LASTEvent]) (XEvent *);
void
init_event_hander()
{
int i;
/* init event handler */
for (i = 0; i < LASTEvent; i++) {
handler[i] = 0;
}
handler[ButtonPress] = handle_buttonpress;
handler[CirculateNotify] = update_ignore_enternotify_hack;
handler[ConfigureRequest] = handle_configurerequest;
handler[DestroyNotify] = handle_destroynotify;
handler[EnterNotify] = handle_enternotify;
handler[Expose] = handle_expose;
handler[GravityNotify] = update_ignore_enternotify_hack;
handler[MapRequest] = handle_maprequest;
handler[MapNotify] = update_ignore_enternotify_hack;
handler[MotionNotify] = handle_motionnotify;
handler[PropertyNotify] = handle_propertynotify;
handler[UnmapNotify] = handle_unmapnotify;
}
void
check_event(Connection * c)
{
XEvent ev;
while (XPending(dpy)) {
XNextEvent(dpy, &ev);
/* main evet loop */
if (handler[ev.type]) {
/* call handler */
(handler[ev.type]) (&ev);
}
}
}
static void
handle_buttonpress(XEvent * e)
{
Client *c;
XButtonPressedEvent *ev = &e->xbutton;
Frame *f = win_to_frame(ev->window);
if (f) {
handle_frame_buttonpress(ev, f);
return;
}
if (ev->window == root) {
XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
XSync(dpy, False);
}
if ((c = win_to_client(ev->window))) {
if (c->frame) {
ev->state &= valid_mask;
if (ev->state & Mod1Mask) {
if (!is_managed_frame(c->frame))
XRaiseWindow(dpy, c->frame->win);
switch (ev->button) {
case Button1:
mouse_move(c->frame);
break;
case Button3:
{
Align align = xy_to_align(&c->rect, ev->x, ev->y);
if (align == CENTER)
mouse_move(c->frame);
else
mouse_resize(c->frame, align);
}
break;
default:
break;
}
}
}
}
}
static void
handle_configurerequest(XEvent * e)
{
XConfigureRequestEvent *ev = &e->xconfigurerequest;
XWindowChanges wc;
Client *c;
unsigned int bw = 0, tabh = 0;
Frame *f = 0;
update_ignore_enternotify_hack(e);
/* fprintf(stderr, "%s", "configure request\n"); */
c = win_to_client(ev->window);
ev->value_mask &= ~CWSibling;
if (c) {
/* fprintf(stderr, "%s", "configure request client\n"); */
f = c->frame;
if (f) {
bw = border_width(f);
tabh = tab_height(f);
}
if (ev->value_mask & CWStackMode) {
if (wc.stack_mode == Above)
XRaiseWindow(dpy, c->win);
else
ev->value_mask &= ~CWStackMode;
}
gravitate(c, tabh ? tabh : bw, bw, 1);
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(c, tabh ? tabh : bw, bw, 0);
if (f) {
XRectangle *frect = rect_of_frame(f);
frect->x = wc.x = c->rect.x - bw;
frect->y = wc.y = c->rect.y - (tabh ? tabh : bw);
frect->width = wc.width = c->rect.width + 2 * bw;
frect->height = wc.height =
c->rect.height + bw + (tabh ? tabh : bw);
wc.border_width = 1;
wc.sibling = None;
wc.stack_mode = ev->detail;
XConfigureWindow(dpy, f->win, ev->value_mask, &wc);
configure_client(c);
}
}
wc.x = ev->x;
wc.y = ev->y;
if (f) {
/* if so, then bw and tabh are already initialized */
wc.x = bw;
wc.y = tabh ? tabh : bw;
}
wc.width = ev->width;
wc.height = ev->height;
wc.border_width = 0;
wc.sibling = None;
wc.stack_mode = Above;
ev->value_mask &= ~CWStackMode;
ev->value_mask |= CWBorderWidth;
XConfigureWindow(dpy, e->xconfigurerequest.window, ev->value_mask,
&wc);
XSync(dpy, False);
/*
fprintf(stderr, "%d,%d,%d,%d\n", wc.x, wc.y, wc.width, wc.height);
*/
}
static void
handle_destroynotify(XEvent * e)
{
XDestroyWindowEvent *ev = &e->xdestroywindow;
Client *c = win_to_client(ev->window);
/* fprintf(stderr, "destroy: client 0x%x\n", (int)ev->window); */
if (!c)
return;
if (c->frame)
detach_client_from_frame(c, 0, 1);
else if (detached && (index_item((void **) detached, c) >= 0))
detached = (Client **) detach_item((void **) detached, c,
sizeof(Client *));
free_client(c);
}
static void
handle_expose(XEvent * e)
{
static Frame *f;
if (e->xexpose.count == 0) {
f = win_to_frame(e->xbutton.window);
if (f)
draw_frame(f);
}
}
static void
handle_maprequest(XEvent * e)
{
XMapRequestEvent *ev = &e->xmaprequest;
static XWindowAttributes wa;
static Client *c;
/* fprintf(stderr, "map: window 0x%x\n", (int)ev->window); */
if (!XGetWindowAttributes(dpy, ev->window, &wa))
return;
if (wa.override_redirect)
return;
/* there're clients which send map requests twice */
c = win_to_client(ev->window);
if (!c)
c = alloc_client(ev->window);
if (!c->frame) {
_init_client(c, &wa);
attach_client(c);
}
}
static void
handle_motionnotify(XEvent * e)
{
Frame *f = win_to_frame(e->xmotion.window);
Cursor cursor;
if (f) {
Frame *old = SELFRAME(pages[sel]);
if (old != f) {
focus_frame(f, 0, 0, 1);
draw_frame(old);
draw_frame(f);
} else if (f->clients) {
/* multihead assumption */
XSetInputFocus(dpy, f->clients[f->sel]->win,
RevertToPointerRoot, CurrentTime);
XSync(dpy, False);
}
cursor = cursor_for_motion(f, e->xmotion.x, e->xmotion.y);
if (cursor != f->cursor) {
f->cursor = cursor;
XDefineCursor(dpy, f->win, cursor);
}
}
}
static void
handle_propertynotify(XEvent * e)
{
XPropertyEvent *ev = &e->xproperty;
Client *c = win_to_client(ev->window);
if (c) {
handle_client_property(c, ev);
return;
}
}
static void
handle_unmapnotify(XEvent * e)
{
XUnmapEvent *ev = &e->xunmap;
Client *c;
update_ignore_enternotify_hack(e);
if (ev->event == root)
return;
if ((c = win_to_client(ev->window))) {
if (c->frame) {
detach_client_from_frame(c, 1, 0);
if (pages)
draw_page(pages[sel]);
free_client(c);
} else if (detached) {
if (index_item((void **) detached, c) == -1)
free_client(c);
}
}
}
static void
handle_enternotify(XEvent * e)
{
XCrossingEvent *ev = &e->xcrossing;
Client *c;
if (ev->mode != NotifyNormal)
return;
/* mouse is not in the focus window */
if (ev->detail == NotifyInferior)
return;
c = win_to_client(ev->window);
if (c && c->frame && (ev->serial != ignore_enternotify_hack)) {
Frame *old = SELFRAME(pages[sel]);
XUndefineCursor(dpy, c->frame->win);
if (old != c->frame) {
focus_frame(c->frame, 0, 0, 1);
draw_frame(old);
draw_frame(c->frame);
} else {
/* multihead assumption */
XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
XSync(dpy, False);
}
}
}
static void
update_ignore_enternotify_hack(XEvent * e)
{
ignore_enternotify_hack = e->xany.serial;
XSync(dpy, False);
}

659
cmd/wm/frame.c Normal file
View File

@ -0,0 +1,659 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <assert.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "wm.h"
#include <cext.h>
static Frame zero_frame = {0};
static void mouse();
static void select_client(void *obj, char *cmd);
static void handle_after_write_frame(IXPServer * s, File * f);
static void handle_before_read_frame(IXPServer * s, File * f);
/* action table for /frame/?/ namespace */
Action frame_acttbl[] = {
{"select", select_client},
{0, 0}
};
Frame *
alloc_frame(XRectangle * r, int add_frame_border, int floating)
{
XSetWindowAttributes wa;
static int id = 0;
char buf[MAX_BUF];
Frame *f = (Frame *) emalloc(sizeof(Frame));
*f = zero_frame;
f->rect = *r;
f->cursor = normal_cursor;
snprintf(buf, MAX_BUF, "/detached/frame/%d", id);
f->files[F_PREFIX] = ixp_create(ixps, buf);
snprintf(buf, MAX_BUF, "/detached/frame/%d/client", id);
f->files[F_CLIENT_PREFIX] = ixp_create(ixps, buf);
snprintf(buf, MAX_BUF, "/detached/frame/%d/client/sel", id);
f->files[F_SEL_CLIENT] = ixp_create(ixps, buf);
f->files[F_SEL_CLIENT]->bind = 1;
snprintf(buf, MAX_BUF, "/detached/frame/%d/ctl", id);
f->files[F_CTL] = ixp_create(ixps, buf);
f->files[F_CTL]->after_write = handle_after_write_frame;
snprintf(buf, MAX_BUF, "/detached/frame/%d/size", id);
f->files[F_SIZE] = ixp_create(ixps, buf);
f->files[F_SIZE]->before_read = handle_before_read_frame;
f->files[F_SIZE]->after_write = handle_after_write_frame;
snprintf(buf, MAX_BUF, "/detached/frame/%d/border", id);
f->files[F_BORDER] = wmii_create_ixpfile(ixps, buf, defaults[WM_BORDER]->content);
f->files[F_BORDER]->after_write = handle_after_write_frame;
snprintf(buf, MAX_BUF, "/detached/frame/%d/tab", id);
f->files[F_TAB] = wmii_create_ixpfile(ixps, buf, defaults[WM_TAB]->content);
f->files[F_TAB]->after_write = handle_after_write_frame;
snprintf(buf, MAX_BUF, "/detached/frame/%d/handleinc", id);
f->files[F_HANDLE_INC] = wmii_create_ixpfile(ixps, buf, defaults[WM_HANDLE_INC]->content);
f->files[F_HANDLE_INC]->after_write = handle_after_write_frame;
snprintf(buf, MAX_BUF, "/detached/frame/%d/locked", id);
f->files[F_LOCKED] = wmii_create_ixpfile(ixps, buf, defaults[WM_LOCKED]->content);
snprintf(buf, MAX_BUF, "/detached/frame/%d/selstyle/bgcolor", id);
f->files[F_SEL_BG_COLOR] = wmii_create_ixpfile(ixps, buf, defaults[WM_SEL_BG_COLOR]->content);
snprintf(buf, MAX_BUF, "/detached/frame/%d/selstyle/fgcolor", id);
f->files[F_SEL_FG_COLOR] = wmii_create_ixpfile(ixps, buf, defaults[WM_SEL_FG_COLOR]->content);
snprintf(buf, MAX_BUF, "/detached/frame/%d/selstyle/bordercolor", id);
f->files[F_SEL_BORDER_COLOR] = wmii_create_ixpfile(ixps, buf, defaults[WM_SEL_BORDER_COLOR]->content);
snprintf(buf, MAX_BUF, "/detached/frame/%d/normstyle/bgcolor", id);
f->files[F_NORM_BG_COLOR] = wmii_create_ixpfile(ixps, buf, defaults[WM_NORM_BG_COLOR]->content);
snprintf(buf, MAX_BUF, "/detached/frame/%d/normstyle/fgcolor", id);
f->files[F_NORM_FG_COLOR] = wmii_create_ixpfile(ixps, buf, defaults[WM_NORM_FG_COLOR]->content);
snprintf(buf, MAX_BUF, "/detached/frame/%d/normstyle/bordercolor", id);
f->files[F_NORM_BORDER_COLOR] = wmii_create_ixpfile(ixps, buf, defaults[WM_NORM_BORDER_COLOR]->content);
snprintf(buf, MAX_BUF, "/detached/frame/%d/event/b2press", id);
f->files[F_EVENT_B2PRESS] = wmii_create_ixpfile(ixps, buf, defaults[WM_EVENT_B2PRESS]->content);
snprintf(buf, MAX_BUF, "/detached/frame/%d/event/b3press", id);
f->files[F_EVENT_B3PRESS] = wmii_create_ixpfile(ixps, buf, defaults[WM_EVENT_B3PRESS]->content);
snprintf(buf, MAX_BUF, "/detached/frame/%d/event/b4press", id);
f->files[F_EVENT_B4PRESS] = wmii_create_ixpfile(ixps, buf, defaults[WM_EVENT_B4PRESS]->content);
snprintf(buf, MAX_BUF, "/detached/frame/%d/event/b5press", id);
f->files[F_EVENT_B5PRESS] = wmii_create_ixpfile(ixps, buf, defaults[WM_EVENT_B5PRESS]->content);
id++;
wa.override_redirect = 1;
wa.background_pixmap = ParentRelative;
wa.event_mask = ExposureMask | ButtonPressMask | ButtonReleaseMask
| PointerMotionMask | SubstructureRedirectMask
| SubstructureNotifyMask;
if (add_frame_border) {
int bw = border_width(f);
int th = tab_height(f);
f->rect.width += 2 * bw;
f->rect.height += bw + (th ? th : bw);
}
f->win =
XCreateWindow(dpy, root, f->rect.x, f->rect.y, f->rect.width, f->rect.height, 0,
DefaultDepth(dpy, screen_num), CopyFromParent,
DefaultVisual(dpy, screen_num),
CWOverrideRedirect | CWBackPixmap | CWEventMask,
&wa);
XDefineCursor(dpy, f->win, f->cursor);
f->gc = XCreateGC(dpy, f->win, 0, 0);
XSync(dpy, False);
frames =
(Frame **) attach_item_end((void **) frames, f, sizeof(Frame *));
return f;
}
void
focus_frame(Frame * f, int raise, int up, int down)
{
Area *a = f->area;
if (down && f->clients)
focus_client(f->clients[f->sel], raise, 0);
a->sel = index_item((void **)a->frames, f);
a->files[A_SEL_FRAME]->content = f->files[F_PREFIX]->content;
if (raise && a->page->sel == 0) /* only floating windows are raised */
XRaiseWindow(dpy, f->win);
if (up)
focus_area(a, raise, up, 0);
}
Frame *
win_to_frame(Window w)
{
int i;
for (i = 0; frames && frames[i]; i++)
if (frames[i]->win == w)
return frames[i];
return 0;
}
void
toggle_frame(Frame * f)
{
Page *p = f->page;
int was_managed = is_managed_frame(f);
detach_frame_from_page(f, 1);
attach_Frameo_page(p, f, !was_managed);
resize_frame(f, rect_of_frame(f), 0, 0);
draw_page(p);
}
void
free_frame(Frame * f)
{
frames = (Frame **) detach_item((void **) frames, f, sizeof(Frame *));
XFreeGC(dpy, f->gc);
XDestroyWindow(dpy, f->win);
ixp_remove_file(ixps, f->files[F_PREFIX]);
if (ixps->errstr)
fprintf(stderr, "wmiiwm: free_frame(): %s\n", ixps->errstr);
free(f);
}
XRectangle *
rect_of_frame(Frame * f)
{
return is_managed_frame(f) ? &f->managed_rect : &f->floating_rect;
}
void
focus_client(Client * c, int raise, int up)
{
Frame *f = 0;
sel = c;
/* focus client */
if (c) {
f = c->frame;
for (f->sel = 0; f->clients && f->clients[f->sel] != c; f->sel++);
f->files[F_CLIENT_SELECTED]->content = c->files[C_PREFIX]->content;
if (raise)
XRaiseWindow(dpy, c->win);
XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
} else
XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
invoke_core_event(core_files[CORE_EVENT_CLIENT_UPDATE]);
if (up && f)
focus_frame(f, raise, up, 0);
}
static void
resize_clients(Frame * f, int tabh, int bw)
{
int i;
XRectangle *frect = rect_of_frame(f);
for (i = 0; f->clients && f->clients[i]; i++) {
Client *c = f->clients[i];
c->rect.x = bw;
c->rect.y = tabh ? tabh : bw;
c->rect.width = frect->width - 2 * bw;
c->rect.height = frect->height - bw - (tabh ? tabh : bw);
XMoveResizeWindow(dpy, c->win, c->rect.x, c->rect.y,
c->rect.width, c->rect.height);
configure_client(c);
}
}
int
is_managed_frame(Frame * f)
{
Page *p = f->page;
if (p->managed) {
int i;
for (i = 0; p->managed[i] && p->managed[i] != f; i++);
if (p->managed[i])
return 1;
}
return 0;
}
static void
check_dimensions(Frame * f, unsigned int tabh, unsigned int bw)
{
XRectangle *frect = rect_of_frame(f);
Client *c = f->clients ? f->clients[f->sel] : 0;
if (!c)
return;
if (c->size.flags & PMinSize) {
if (frect->width - 2 * bw < c->size.min_width) {
frect->width = c->size.min_width + 2 * bw;
}
if (frect->height - bw - (tabh ? tabh : bw) < c->size.min_height) {
frect->height = c->size.min_height + bw + (tabh ? tabh : bw);
}
}
if (c->size.flags & PMaxSize) {
if (frect->width - 2 * bw > c->size.max_width) {
frect->width = c->size.max_width + 2 * bw;
}
if (frect->height - bw - (tabh ? tabh : bw) > c->size.max_height) {
frect->height = c->size.max_height + bw + (tabh ? tabh : bw);
}
}
}
static void
resize_incremental(Frame * f, unsigned int tabh, unsigned int bw)
{
XRectangle *frect = rect_of_frame(f);
Client *c = f->clients ? f->clients[f->sel] : 0;
if (!c)
return;
/*
* increment stuff, see chapter 4.1.2.3 of the Inter-Client
* Communication Conventions Manual
*/
if (c->size.flags & PResizeInc) {
int base_width = 0, base_height = 0;
if (c->size.flags & PBaseSize) {
base_width = c->size.base_width;
base_height = c->size.base_height;
} else if (c->size.flags & PMinSize) {
/* base_{width,height} defaults to min_{width,height} */
base_width = c->size.min_width;
base_height = c->size.min_height;
}
/*
* client_width = base_width + i * c->size.width_inc for an
* integer i
*/
frect->width -=
(frect->width - 2 * bw - base_width) % c->size.width_inc;
frect->height -=
(frect->height - bw - (tabh ? tabh : bw) -
base_height) % c->size.height_inc;
}
}
void
resize_frame(Frame * f, XRectangle * r, XPoint * pt, int ignore_layout)
{
unsigned int tabh = _strtonum(f->files[F_TAB_H]->content, 0, 30);
unsigned int bw = _strtonum(f->files[F_BORDER_W]->content, 0, 10);
XRectangle *frect = rect_of_frame(f);
/* do layout stuff if necessary */
if (!ignore_layout && is_managed_frame(f)) {
Page *p = f->page;
if (p && p->layout) {
p->layout->resize(f, r, pt);
} else
*frect = *r;
} else
*frect = *r;
/* resize if client requests special size */
check_dimensions(f, tabh, bw);
if (f->files[F_HANDLE_INC]->content
&& ((char *) f->files[F_HANDLE_INC]->content)[0] == '1')
resize_incremental(f, tabh, bw);
XMoveResizeWindow(dpy, f->win, frect->x, frect->y, frect->width,
frect->height);
resize_clients(f, (tabh ? tabh : bw), bw);
}
void
draw_tab(Frame * f, char *text, int x, int y, int w, int h, int sel)
{
Draw d = {0};
XFontStruct *font = blitz_getfont(dpy, f->files[F_SELECTED_TEXT_FONT]->content);
d.drawable = f->win;
d.gc = f->gc;
d.rect.x = x;
d.rect.y = y;
d.rect.width = w;
d.rect.height = h;
d.data = text;
if (sel) {
d.bg =
blitz_loadcolor(dpy, screen_num,
f->files[F_SELECTED_BG_COLOR]->content);
d.fg =
blitz_loadcolor(dpy, screen_num,
f->files[F_SELECTED_FG_COLOR]->content);
d.border =
blitz_loadcolor(dpy, screen_num,
f->files[F_SELECTED_BORDER_COLOR]->content);
d.font = font;
} else {
d.bg =
blitz_loadcolor(dpy, screen_num,
f->files[F_NORMAL_BG_COLOR]->content);
d.fg =
blitz_loadcolor(dpy, screen_num,
f->files[F_NORMAL_FG_COLOR]->content);
d.border =
blitz_loadcolor(dpy, screen_num,
f->files[F_NORMAL_BORDER_COLOR]->content);
d.font = font;
}
blitz_drawlabel(dpy, &d);
XSync(dpy, False);
XFreeFont(dpy, font);
}
/**
* Assumes following files:
*
* ./sel-style/text-font "<value>"
* ./sel-style/text-size "<int>"
* ./sel-style/text-color "#RRGGBBAA"
* ./sel-style/bg-color "#RRGGBBAA"
* ./sel-style/border-color "#RRGGBBAA [#RRGGBBAA [#RRGGBBAA [#RRGGBBAA]]]"
* ./norm-style/text-font "<value>"
* ./norm-style/text-size "<int>"
* ./norm-style/text-color "#RRGGBBAA"
* ./norm-style/bg-color "#RRGGBBAA"
* ./norm-style/border-color "#RRGGBBAA [#RRGGBBAA [#RRGGBBAA [#RRGGBBAA]]]"
*/
void
draw_frame(Frame * f)
{
Draw d = {0};
int bw = _strtonum(f->files[F_BORDER_W]->content, 0, 10);
XRectangle notch;
XRectangle *frect = rect_of_frame(f);
if (bw) {
notch.x = bw;
notch.y = bw;
notch.width = frect->width - 2 * bw;
notch.height = frect->height - 2 * bw;
d.drawable = f->win;
d.gc = f->gc;
/* define ground plate (i = 0) */
if (is_selected(f)) {
d.bg =
blitz_loadcolor(dpy, screen_num,
f->files[F_SELECTED_BG_COLOR]->content);
d.fg =
blitz_loadcolor(dpy, screen_num,
f->files[F_SELECTED_FG_COLOR]->content);
d.border =
blitz_loadcolor(dpy, screen_num,
f->files[F_SELECTED_BORDER_COLOR]->
content);
} else {
d.bg =
blitz_loadcolor(dpy, screen_num,
f->files[F_NORMAL_BG_COLOR]->content);
d.fg =
blitz_loadcolor(dpy, screen_num,
f->files[F_NORMAL_FG_COLOR]->content);
d.border =
blitz_loadcolor(dpy, screen_num,
f->files[F_NORMAL_BORDER_COLOR]->content);
}
d.rect = *frect;
d.rect.x = d.rect.y = 0;
d.notch = &notch;
blitz_drawlabel(dpy, &d);
}
draw_clients(f);
XSync(dpy, False);
}
void
handle_frame_buttonpress(XButtonEvent * e, Frame * f)
{
int bindex;
int size = count_items((void **) f->clients);
int cindex = e->x / (rect_of_frame(f)->width / size);
if (!is_managed_frame(f))
XRaiseWindow(dpy, f->win);
if (cindex != f->sel) {
focus_client(f->clients[cindex], 1, 0);
draw_frame(f);
return;
}
if (e->button == Button1) {
mouse();
return;
}
bindex = F_EVENT_B2PRESS - 2 + e->button;
/* frame mouse handling */
if (f->files[bindex]->content)
spawn(dpy, f->files[bindex]->content);
draw_frame(f);
}
void
attach_Cliento_frame(Frame * f, Client * c)
{
int size = count_items((void **) f->clients);
wmii_move_ixpfile(c->files[C_PREFIX], f->files[F_CLIENT_PREFIX]);
f->files[F_CLIENT_SELECTED]->content = c->files[C_PREFIX]->content;
f->clients =
(Client **) attach_item_end((void **) f->clients, c,
sizeof(Client *));
f->sel = size;
c->frame = f;
reparent_client(c, f->win,
_strtonum(f->files[F_BORDER_W]->content, 0, 10),
_strtonum(f->files[F_TAB_H]->content, 0, 30));
resize_frame(f, rect_of_frame(f), 0, 1);
show_client(c);
focus_client(c, 1, 1);
}
static void
attach_client_fullscreen(Client * c)
{
Page *p;
Frame *f;
int bw, th;
if (pages)
hide_page(pages[sel_page]);
p = alloc_page("1");
c->rect = rect;
f = alloc_frame(&c->rect, 1, 1);
bw = _strtonum(f->files[F_BORDER_W]->content, 0, 10);
th = _strtonum(f->files[F_TAB_H]->content, 0, 30);
f->floating_rect.x -= bw;
f->floating_rect.y -= (th ? th : bw);
attach_Frameo_page(p, f, 0);
attach_Cliento_frame(f, c);
draw_frame(f);
invoke_core_event(core_files[CORE_EVENT_PAGE_UPDATE]);
}
void
attach_client(Client * c)
{
Page *p = 0;
Frame *f = 0;
/* fullscreen app support */
if ((c->rect.x == rect.x) && (c->rect.y == rect.y)
&& (c->rect.width == rect.width)
&& (c->rect.height == rect.height)) {
attach_client_fullscreen(c);
return;
}
if (!pages)
alloc_page("0");
/* transient stuff */
if (c && c->trans && !f) {
Client *t = win_to_client(c->trans);
if (t && t->frame) {
focus_client(t, 1, 1);
f = alloc_frame(&c->rect, 1, 1);
}
}
p = pages[sel_page];
if (!f) {
/* check if we shall manage it */
if (!manage_class_instance(c))
f = alloc_frame(&c->rect, 1, 1);
}
if (!f) {
/* check for tabbing? */
f = get_selected(p);
if (f && (f->floating
|| (((char *) f->files[F_LOCKED]->content)[0] == '1')))
f = 0;
}
if (!f)
f = alloc_frame(&c->rect, 1, 0);
if (!f->page)
attach_Frameo_page(p, f, !f->floating);
attach_Cliento_frame(f, c);
draw_frame(f);
invoke_core_event(core_files[CORE_EVENT_PAGE_UPDATE]);
}
void
detach_client_from_frame(Client * c, int unmapped, int destroyed)
{
Frame *f = c->frame;
wmii_move_ixpfile(c->files[C_PREFIX],
core_files[CORE_DETACHED_CLIENT]);
c->frame = 0;
f->clients =
(Client **) detach_item((void **) f->clients, c, sizeof(Client *));
if (f->sel)
f->sel--;
else
f->sel = 0;
if (!destroyed) {
if (!unmapped) {
hide_client(c);
detached =
(Client **) attach_item_begin((void **) detached, c,
sizeof(Client *));
}
reparent_client(c, root,
_strtonum(f->files[F_BORDER_W]->content, 0, 10),
_strtonum(f->files[F_TAB_H]->content, 0, 30));
}
if (f->clients) {
focus_client(f->clients[f->sel], 1, 1);
draw_frame(f);
} else {
detach_frame_from_page(f, 0);
free_frame(f);
if (pages)
focus_page(pages[sel_page], 0, 1);
}
invoke_core_event(core_files[CORE_EVENT_PAGE_UPDATE]);
}
static void
mouse()
{
Frame *f;
Align align;
if (!pages)
return;
if (!(f = SELFRAME(pages[sel])))
return;
align = cursor_to_align(f->cursor);
if (align == CENTER)
mouse_move(f);
else
mouse_resize(f, align);
}
static void
select_client(void *obj, char *cmd)
{
Frame *f = obj;
int size = count_items((void **) f->clients);
if (!f || !cmd || size == 1)
return;
if (!strncmp(cmd, "prev", 5)) {
if (f->sel > 0)
f->sel--;
else
f->sel = size - 1;
} else if (!strncmp(cmd, "next", 5)) {
if (f->sel + 1 == size)
f->sel = 0;
else
f->sel++;
}
focus_client(f->clients[f->sel], 1, 0);
draw_frame(f);
}
static void
handle_before_read_frame(IXPServer * s, File * f)
{
int i = 0;
for (i = 0; frames && frames[i]; i++) {
if (f == frames[i]->files[F_SIZE]) {
XRectangle *frect = rect_of_frame(frames[i]);
char buf[64];
snprintf(buf, 64, "%d,%d,%d,%d", frect->x, frect->y,
frect->width, frect->height);
if (f->content)
free(f->content);
f->content = estrdup(buf);
f->size = strlen(buf);
return;
}
}
}
static void
handle_after_write_frame(IXPServer * s, File * f)
{
int i;
for (i = 0; frames && frames[i]; i++) {
if (f == frames[i]->files[F_CTL]) {
run_action(f, frames[i], frame_acttbl);
return;
}
if (f == frames[i]->files[F_TAB_H]
|| f == frames[i]->files[F_BORDER_W]
|| f == frames[i]->files[F_HANDLE_INC]) {
if (frames[i]->page && frames[i]->page->layout) {
frames[i]->page->layout->arrange(frames[i]->page);
draw_page(frames[i]->page);
} else {
resize_frame(frames[i], rect_of_frame(frames[i]), 0, 0);
draw_frame(frames[i]);
}
return;
} else if (f == frames[i]->files[F_SIZE]) {
char *size = frames[i]->files[F_SIZE]->content;
if (size && strrchr(size, ',')) {
XRectangle frect;
frect = *rect_of_frame(frames[i]);
blitz_strtorect(dpy, &rect, &frect, size);
resize_frame(frames[i], &frect, 0, 0);
draw_page(frames[i]->page);
}
return;
}
}
}

393
cmd/wm/layout_column.c Normal file
View File

@ -0,0 +1,393 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "wm.h"
#include "layout.h"
#include <cext.h>
typedef struct Acme Acme;
typedef struct Column Column;
struct Acme {
int sel;
Column **column;
};
struct Column {
int sel;
int refresh;
Frame **frames;
XRectangle rect;
};
static void init_col(Page * p, int argc, char **argv);
static void deinit_col(Page * p);
static void arrange_col(Page * p);
static void attach_col(Page * p, Client * c);
static void detach_col(Page *, Client * c, int unmapped, int destroyed);
static void resize_col(Frame * f, XRectangle * new, XPoint * pt);
static void aux_col(Frame * f, char *what);
static LayoutImpl lcol = {"col", init_col, deinit_col, arrange_col, attach_col,
detach_col, resize_col, aux_col
};
static Column zero_column = {0};
static Acme zero_acme = {0};
void
init_layout_col()
{
layouts =
(Layout **) attach_item_end((void **) layouts, &lcol,
sizeof(Layout *));
}
static void
arrange_column(Page * p, Column * col)
{
int i;
int n = count_items((void **) col->frames);
unsigned int height = p->managed_rect.height / n;
for (i = 0; col->frames && col->frames[i]; i++) {
if (col->refresh) {
col->frames[i]->rect = col->rect;
col->frames[i]->rect.height = height;
col->frames[i]->rect.y = i * height;
}
resize_frame(col->frames[i], &col->frames[i]->rect, 0, 1);
}
}
static void
arrange_col(Page * p)
{
Acme *acme = p->aux;
int i;
if (!acme) {
fprintf(stderr, "%s", "wmiiwm: fatal, page has no layout\n");
exit(1);
}
for (i = 0; acme->column && acme->column[i]; i++)
arrange_column(p, acme->column[i]);
}
static void
init_col(Page * p, int argc, char **argv)
{
Acme *acme = emalloc(sizeof(Acme));
int i, j, n, cols = 1;
unsigned int width = 1;
Column *col;
*acme = zero_acme;
p->aux = acme;
/* processing argv */
for (i = 1; (i < argc) && (argv[i][0] == '-'); i++) {
switch (argv[i][1]) {
case 'c':
cols = _strtonum(argv[++i], 0, 32);
if (cols < 1)
cols = 1;
break;
}
}
width = p->managed_rect.width / cols;
acme->column = emalloc((cols + 1) * sizeof(Column *));
for (i = 0; i < cols; i++) {
acme->column[i] = emalloc(sizeof(Column));
*acme->column[i] = zero_column;
acme->column[i]->rect = p->managed_rect;
acme->column[i]->rect.x = i * width;
acme->column[i]->rect.width = width;
acme->column[i]->refresh = 1;
}
acme->column[cols] = 0; /* null termination of array */
/*
* Frame attaching strategy works as follows: 1. If more clients than
* column exist, then each column gets one client, except eastmost
* column, which gets all remaining clients. 2. If lesser clients
* than column exist, than filling begins from eastmost to westmost
* column until no more clients exist.
*/
n = count_items((void **) clients);
if (n > cols) {
/* 1st. case */
j = 0;
for (i = 0; i < (cols - 1); i++) {
col = acme->column[i];
col->frames = (Frame **) attach_item_end((void **) col->frames,
alloc_frame(&clients[i]->rect, 1, 1), sizeof(Frame *));
col->frames[0]->aux = col;
attach_Frameo_page(p, col->frames[0], 1);
attach_Cliento_frame(col->frames[0], clients[j]);
j++;
}
col = acme->column[cols - 1];
col->frames = emalloc((n - j + 1) * sizeof(Frame *));
for (i = 0; i + j < n; i++) {
col->frames[i] = alloc_frame(&clients[j + i]->rect, 1, 1);
col->frames[i]->aux = col;
attach_Frameo_page(p, col->frames[i], 1);
attach_Cliento_frame(col->frames[i], clients[j + i]);
}
col->frames[i] = 0;
} else {
/* 2nd case */
j = 0;
for (i = cols - 1; j < n; i--) {
col = acme->column[i];
col->frames = (Frame **) attach_item_end((void **) col->frames,
alloc_frame(&clients[i]->rect, 1, 1), sizeof(Frame *));
col->frames[0]->aux = col;
attach_Frameo_page(p, col->frames[0], 1);
attach_Cliento_frame(col->frames[0], clients[j]);
j++;
}
}
arrange_col(p);
}
static void
deinit_col(Page * p)
{
Acme *acme = p->aux;
int i;
for (i = 0; acme->column && acme->column[i]; i++) {
Column *col = acme->column[i];
int j;
for (j = 0; col->frames && col->frames[j]; j++) {
Frame *f = col->frames[j];
while (f->clients && f->clients[0])
detach_client_from_frame(f->clients[0], 0, 0);
detach_frame_from_page(f, 1);
free_frame(f);
}
free(col->frames);
}
free(acme->column);
free(acme);
p->aux = 0;
}
static void
attach_col(Page * p, Client * c)
{
Acme *acme = p->aux;
Column *col;
Frame *f;
col = acme->column[acme->sel];
f = alloc_frame(&c->rect, 1, 1);
col->frames = (Frame **) attach_item_end((void **) col->frames, f,
sizeof(Frame *));
f->aux = col;
col->refresh = 1;
attach_Frameo_page(p, f, 1);
attach_Cliento_frame(f, c);
arrange_col(p);
}
static void
detach_col(Page * p, Client * c, int unmapped, int destroyed)
{
Frame *f = c->frame;
Column *col = f->aux;
if (!col)
return; /* client was not attached, maybe exit(1) in
* such case */
col->frames = (Frame **) detach_item((void **) col->frames, c->frame,
sizeof(Frame *));
col->refresh = 1;
detach_client_from_frame(c, unmapped, destroyed);
detach_frame_from_page(f, 1);
free_frame(f);
arrange_col(p);
}
static void
drop_resize(Frame * f, XRectangle * new)
{
Column *col = f->aux;
Acme *acme = f->page->aux;
int i, idx, n = 0;
if (!col) {
fprintf(stderr, "%s",
"wmii: fatal: frame has no associated column\n");
exit(1);
}
for (i = 0; acme->column && acme->column[i]; i++) {
if (acme->column[i] == col)
idx = i;
n++;
}
/* horizontal resizals are */
if (new->x < f->rect.x) {
if (idx && new->x > acme->column[idx - 1]->rect.x) {
Column *west = acme->column[idx - 1];
west->rect.width = new->x - west->rect.x;
col->rect.width += f->rect.x - new->x;
col->rect.x = new->x;
for (i = 0; west->frames && west->frames[i]; i++) {
Frame *f = west->frames[i];
f->rect.x = west->rect.x;
f->rect.width = west->rect.width;
resize_frame(f, &f->rect, 0, 1);
}
for (i = 0; col->frames && col->frames[i]; i++) {
Frame *f = col->frames[i];
f->rect.x = col->rect.x;
f->rect.width = col->rect.width;
resize_frame(f, &f->rect, 0, 1);
}
}
}
if (new->x + new->width > f->rect.x + f->rect.width) {
if ((idx + 1 < n) &&
(new->x + new->width < acme->column[idx + 1]->rect.x
+ acme->column[idx + 1]->rect.width)) {
Column *east = acme->column[idx + 1];
east->rect.width -= new->x + new->width - east->rect.x;
east->rect.x = new->x + new->width;
col->rect.x = new->x;
col->rect.width = new->width;
for (i = 0; col->frames && col->frames[i]; i++) {
Frame *f = col->frames[i];
f->rect.x = col->rect.x;
f->rect.width = col->rect.width;
resize_frame(f, &f->rect, 0, 1);
}
for (i = 0; east->frames && east->frames[i]; i++) {
Frame *f = east->frames[i];
f->rect.x = east->rect.x;
f->rect.width = east->rect.width;
resize_frame(f, &f->rect, 0, 1);
}
}
}
/* vertical stuff */
n = 0;
for (i = 0; col->frames && col->frames[i]; i++) {
if (col->frames[i] == f)
idx = i;
n++;
}
if (new->y < f->rect.y) {
if (idx && new->y > col->frames[idx - 1]->rect.y) {
Frame *north = col->frames[idx - 1];
north->rect.height = new->y - north->rect.y;
f->rect.width += new->y - north->rect.y;
f->rect.y = new->y;
resize_frame(north, &north->rect, 0, 1);
resize_frame(f, &f->rect, 0, 1);
}
}
if (new->y + new->height > f->rect.y + f->rect.height) {
if ((idx + 1 < n) &&
(new->y + new->height < col->frames[idx + 1]->rect.y
+ col->frames[idx + 1]->rect.height)) {
Frame *south = col->frames[idx + 1];
south->rect.width -= new->x + new->width - south->rect.x;
south->rect.x = new->x + new->width;
f->rect.x = new->x;
f->rect.width = new->width;
resize_frame(f, &f->rect, 0, 1);
resize_frame(south, &south->rect, 0, 1);
}
}
}
static void
_drop_move(Frame * f, XRectangle * new, XPoint * pt)
{
Column *tgt = 0, *src = f->aux;
Acme *acme = f->page->aux;
int i;
if (!src) {
fprintf(stderr, "%s",
"wmii: fatal: frame has no associated column\n");
exit(1);
}
for (i = 0; acme->column && acme->column[i]; i++) {
Column *colp = acme->column[i];
if (blitz_ispointinrect(pt->x, pt->y, &colp->rect)) {
tgt = colp;
break;
}
}
/* use pointer as fixpoint */
if (tgt == src) {
/* only change order within column */
for (i = 0; tgt->frames && tgt->frames[i]; i++) {
Frame *fp = tgt->frames[i];
if (blitz_ispointinrect(pt->x, pt->y, &fp->rect)) {
if (fp == f)
return; /* just ignore */
else {
int idxf = index_item((void **) tgt->frames, f);
int idxfp = index_item((void **) tgt->frames, fp);
Frame *tmpf = f;
XRectangle tmpr = f->rect;
f->rect = fp->rect;
fp->rect = tmpr;
tgt->frames[idxf] = tgt->frames[idxfp];
tgt->frames[idxfp] = tmpf;
resize_frame(f, &f->rect, 0, 1);
resize_frame(fp, &fp->rect, 0, 1);
}
return;
}
}
} else {
/* detach, attach and change order in target column */
src->frames = (Frame **) detach_item((void **) src->frames,
f, sizeof(Frame *));
tgt->frames =
(Frame **) attach_item_end((void **) tgt->frames, f,
sizeof(Frame *));
tgt->refresh = 1;
arrange_column(f->page, tgt);
/* TODO: implement a better target placing strategy */
}
}
static void
resize_col(Frame * f, XRectangle * new, XPoint * pt)
{
if ((f->rect.width == new->width)
&& (f->rect.height == new->height))
_drop_move(f, new, pt);
else
drop_resize(f, new);
}
static void
aux_col(Frame * f, char *what)
{
}

12
cmd/wm/mklayout.sh Normal file
View File

@ -0,0 +1,12 @@
#!/bin/sh
rm -f layout.h layout.c layout.mk
echo "#include \"layout.h\"" > layout.c
echo "#include \"wm.h\"" >> layout.c
echo "void init_layouts() {" >> layout.c
for i in `ls layout_*.c`; do
FUNC="`echo \`basename $i\` | sed 's/\.c//g'`"
echo "void init_$FUNC();" >> layout.h
echo " init_$FUNC();" >> layout.c;
echo "SRC += $i" >>layout.mk
done
echo "}" >> layout.c

620
cmd/wm/mouse.c Normal file
View File

@ -0,0 +1,620 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <stdlib.h>
#include "wm.h"
Cursor
cursor_for_motion(Frame * f, int x, int y)
{
int n, e, w, s, tn, te, tw, ts;
int tabh, bw;
bw = border_width(f);
tabh = tab_height(f);
if (!f || !bw)
return normal_cursor;
/* rectangle attributes of client are used */
w = x < bw;
e = x >= rect_of_frame(f)->width - bw;
n = y < bw;
s = y >= rect_of_frame(f)->height - bw;
tw = x < (tabh ? tabh : 2 * bw);
te = x > rect_of_frame(f)->width - (tabh ? tabh : 2 * bw);
tn = y < (tabh ? tabh : 2 * bw);
ts = s > rect_of_frame(f)->height - (tabh ? tabh : 2 * bw);
if ((w && n) || (w && tn) || (n && tw))
return nw_cursor;
else if ((e && n) || (e && tn) || (n && te))
return ne_cursor;
else if ((w && s) || (w && ts) || (s && tw))
return sw_cursor;
else if ((e && s) || (e && ts) || (s && te))
return se_cursor;
else if (w)
return w_cursor;
else if (e)
return e_cursor;
else if (n)
return n_cursor;
else if (s)
return s_cursor;
return normal_cursor;
}
Align
xy_to_align(XRectangle * rect, int x, int y)
{
int w = x <= rect->x + rect->width / 2;
int n = y <= rect->y + rect->height / 2;
int e = x > rect->x + rect->width / 2;
int s = y > rect->y + rect->height / 2;
int nw = w && n;
int ne = e && n;
int sw = w && s;
int se = e && s;
if (nw)
return NWEST;
else if (ne)
return NEAST;
else if (se)
return SEAST;
else if (sw)
return SWEST;
else if (w)
return WEST;
else if (e)
return EAST;
else if (n)
return NORTH;
else if (s)
return SOUTH;
return CENTER;
}
Align
cursor_to_align(Cursor cursor)
{
if (cursor == w_cursor)
return WEST;
else if (cursor == nw_cursor)
return NWEST;
else if (cursor == n_cursor)
return NORTH;
else if (cursor == ne_cursor)
return NEAST;
else if (cursor == e_cursor)
return EAST;
else if (cursor == se_cursor)
return SEAST;
else if (cursor == s_cursor)
return SOUTH;
else if (cursor == sw_cursor)
return SWEST;
return CENTER; /* should not happen */
}
static int
check_vert_match(XRectangle * r, XRectangle * neighbor)
{
/* check if neighbor matches edge */
return (((neighbor->y <= r->y)
&& (neighbor->y + neighbor->height >= r->y))
|| ((neighbor->y >= r->y)
&& (r->y + r->height >= neighbor->y)));
}
static int
check_horiz_match(XRectangle * r, XRectangle * neighbor)
{
/* check if neighbor matches edge */
return (((neighbor->x <= r->x)
&& (neighbor->x + neighbor->width >= r->x))
|| ((neighbor->x >= r->x)
&& (r->x + r->width >= neighbor->x)));
}
static void
snap_move(XRectangle * r, XRectangle * rects,
unsigned int num, int snapw, int snaph)
{
int i, j, w = 0, n = 0, e = 0, s = 0;
/* snap to other windows */
for (i = 0; i <= snapw && !(w && e); i++) {
for (j = 0; j < num && !(w && e); j++) {
/* check west neighbors leftwards */
if (!w) {
if (r->x - i == (rects[j].x + rects[j].width)) {
/*
* west edge of neighbor found, check
* vert match
*/
w = check_vert_match(r, &rects[j]);
if (w)
r->x = rects[j].x + rects[j].width;
}
}
/* check west neighbors rightwards */
if (!w) {
if (r->x + i == (rects[j].x + rects[j].width)) {
/*
* west edge of neighbor found, check
* vert match
*/
w = check_vert_match(r, &rects[j]);
if (w)
r->x = rects[j].x + rects[j].width;
}
}
/* check east neighbors leftwards */
if (!e) {
if (r->x + r->width - i == rects[j].x) {
/*
* east edge of neighbor found, check
* vert match
*/
e = check_vert_match(r, &rects[j]);
if (e)
r->x = rects[j].x - r->width;
}
}
/* check east neighbors rightwards */
if (!e) {
if (r->x + r->width + i == rects[j].x) {
/*
* east edge of neighbor found, check
* vert match
*/
e = check_vert_match(r, &rects[j]);
if (e)
r->x = rects[j].x - r->width;
}
}
}
/* snap to west screen border */
if (!w && (r->x - i == rect.x)) {
w = 1;
r->x = rect.x;
}
/* snap to west screen border */
if (!w && (r->x + i == rect.x)) {
w = 1;
r->x = rect.x;
}
/* snap to east screen border */
if (!e && (r->x + r->width - i == rect.width)) {
e = 1;
r->x = rect.x + rect.width - r->width;
}
if (!e && (r->x + r->width + i == rect.width)) {
e = 1;
r->x = rect.x + rect.width - r->width;
}
}
for (i = 0; i <= snaph && !(n && s); i++) {
for (j = 0; j < num && !(n && s); j++) {
/* check north neighbors upwards */
if (!n) {
if (r->y - i == (rects[j].y + rects[j].height)) {
/*
* north edge of neighbor found,
* check horiz match
*/
n = check_horiz_match(r, &rects[j]);
if (n)
r->y = rects[j].y + rects[j].height;
}
}
/* check north neighbors downwards */
if (!n) {
if (r->y + i == (rects[j].y + rects[j].height)) {
/*
* north edge of neighbor found,
* check horiz match
*/
n = check_horiz_match(r, &rects[j]);
if (n)
r->y = rects[j].y + rects[j].height;
}
}
/* check south neighbors upwards */
if (!s) {
if (r->y + r->height - i == rects[j].y) {
/*
* south edge of neighbor found,
* check horiz match
*/
s = check_horiz_match(r, &rects[j]);
if (s)
r->y = rects[j].y - r->height;
}
}
/* check south neighbors downwards */
if (!s) {
if (r->y + r->height + i == rects[j].y) {
/*
* south edge of neighbor found,
* check horiz match
*/
s = check_horiz_match(r, &rects[j]);
if (s)
r->y = rects[j].y - r->height;
}
}
}
/* snap to north screen border */
if (!n && (r->y - i == rect.y)) {
n = 1;
r->y = rect.y;
}
if (!n && (r->y + i == rect.y)) {
n = 1;
r->y = rect.y;
}
/* snap to south screen border */
if (!s && (r->y + r->height - i == rect.height)) {
s = 1;
r->y = rect.y + rect.height - r->height;
}
if (!s && (r->y + r->height + i == rect.height)) {
s = 1;
r->y = rect.y + rect.height - r->height;
}
}
}
static void
draw_pseudo_border(XRectangle * r)
{
XRectangle pseudo = *r;
pseudo.x += 2;
pseudo.y += 2;
pseudo.width -= 4;
pseudo.height -= 4;
XDrawRectangles(dpy, root, xorgc, &pseudo, 1);
XSync(dpy, False);
}
void
mouse_move(Frame * f)
{
int px = 0, py = 0, wex, wey, ex, ey, first = 1, i;
Window dummy;
XEvent ev;
/* borders */
int snapw = rect.width * _strtonum(defaults[WM_SNAP_VALUE]->content, 0, 1000) / 1000;
int snaph = rect.height * _strtonum(defaults[WM_SNAP_VALUE]->content, 0, 1000) / 1000;
unsigned int num;
unsigned int dmask;
XRectangle *rects = rectangles(&num);
XRectangle frect = *rect_of_frame(f);
XPoint pt;
XQueryPointer(dpy, f->win, &dummy, &dummy, &i, &i, &wex, &wey, &dmask);
XTranslateCoordinates(dpy, f->win, root, wex, wey, &ex, &ey, &dummy);
pt.x = ex;
pt.y = ey;
XSync(dpy, False);
XGrabServer(dpy);
XGrabPointer(dpy, root, False, ButtonMotionMask | ButtonReleaseMask,
GrabModeAsync, GrabModeAsync, None, move_cursor,
CurrentTime);
for (;;) {
while (!XCheckMaskEvent(dpy,
ButtonReleaseMask | ButtonMotionMask, &ev)) {
usleep(20000);
continue;
}
switch (ev.type) {
case ButtonRelease:
if (!first) {
draw_pseudo_border(&frect);
resize_frame(f, &frect, &pt, 0);
}
draw_page(pages[sel]);
free(rects);
XUngrabPointer(dpy, CurrentTime /* ev.xbutton.time */ );
XUngrabServer(dpy);
XSync(dpy, False);
return;
break;
case MotionNotify:
pt.x = ev.xmotion.x;
pt.y = ev.xmotion.y;
XTranslateCoordinates(dpy, f->win, root, ev.xmotion.x,
ev.xmotion.y, &px, &py, &dummy);
if (first)
first = 0;
else
draw_pseudo_border(&frect);
frect.x = px - ex;
frect.y = py - ey;
snap_move(&frect, rects, num, snapw, snaph);
draw_pseudo_border(&frect);
break;
}
}
}
static void
snap_resize(XRectangle * r, XRectangle * o, Align align,
XRectangle * rects, unsigned int num, int px, int ox, int py,
int oy, int snapw, int snaph)
{
int i, j, pend = 0;
int w, h;
/* x */
switch (align) {
case NEAST:
case EAST:
case SEAST:
w = px - r->x + (o->width - ox);
if (w < 10)
break;
r->width = w;
if (w <= snapw)
break;
/* snap to border */
for (i = 0; !pend && (i < snapw); i++) {
if (r->x + r->width - i == rect.x + rect.width) {
r->width -= i;
break;
}
if (r->x + r->width + i == rect.x + rect.width) {
r->width += i;
break;
}
for (j = 0; j < num; j++) {
if (r->x + r->width - i == rects[j].x) {
pend = check_vert_match(r, &rects[j]);
if (pend) {
r->width -= i;
break;
}
}
if (r->x + r->width + i == rects[j].x) {
pend = check_vert_match(r, &rects[j]);
if (pend) {
r->width += i;
break;
}
}
}
}
break;
case NWEST:
case WEST:
case SWEST:
w = r->width + r->x - px + ox;
if (w < 10)
break;
r->width = w;
r->x = px - ox;
if (w <= snapw)
break;
/* snap to border */
for (i = 0; !pend && (i < snapw); i++) {
if (r->x - i == rect.x) {
r->x -= i;
r->width += i;
break;
}
if (r->x + i == rect.x) {
r->x += i;
r->width -= i;
break;
}
for (j = 0; j < num; j++) {
if (r->x - i == rects[j].x + rects[j].width) {
pend = check_vert_match(r, &rects[j]);
if (pend) {
r->x -= i;
r->width += i;
break;
}
}
if (r->x + i == rects[j].x + rects[j].width) {
pend = check_vert_match(r, &rects[j]);
if (pend) {
r->x += i;
r->width -= i;
break;
}
}
}
}
break;
default:
break;
}
/* y */
pend = 0;
switch (align) {
case SWEST:
case SOUTH:
case SEAST:
h = py - r->y + (o->height - oy);
if (h < 10)
break;
r->height = h;
if (h <= snaph)
break;
/* snap to border */
for (i = 0; !pend && (i < snaph); i++) {
if (r->y + r->height - i == rect.y + rect.height) {
r->height -= i;
break;
}
if (r->y + r->height + i == rect.y + rect.height) {
r->height += i;
break;
}
for (j = 0; j < num; j++) {
if (r->y + r->height - i == rects[j].y) {
pend = check_horiz_match(r, &rects[j]);
if (pend) {
r->height -= i;
break;
}
}
if (r->y + r->height + i == rects[j].y) {
pend = check_horiz_match(r, &rects[j]);
if (pend) {
r->height += i;
break;
}
}
}
}
break;
case NWEST:
case NORTH:
case NEAST:
h = r->height + r->y - py + oy;
if (h < 10)
break;
r->height = h;
r->y = py - oy;
if (h <= snaph)
break;
/* snap to border */
for (i = 0; !pend && (i < snaph); i++) {
if (r->y - i == rect.y) {
r->y -= i;
r->height += i;
break;
}
if (r->y + i == rect.y) {
r->y += i;
r->height -= i;
break;
}
for (j = 0; j < num; j++) {
if (r->y - i == rects[j].y + rects[j].height) {
pend = check_horiz_match(r, &rects[j]);
if (pend) {
r->y -= i;
r->height += i;
break;
}
}
if (r->y + i == rects[j].y + rects[j].height) {
pend = check_horiz_match(r, &rects[j]);
if (pend) {
r->y += i;
r->height -= i;
break;
}
}
}
}
break;
default:
break;
}
}
void
mouse_resize(Frame * f, Align align)
{
int px = 0, py = 0, i, ox, oy, first = 1;
Window dummy;
XEvent ev;
/* borders */
int snapw = rect.width * _strtonum(defaults[WM_SNAP_VALUE]->content, 0, 1000) / 1000;
int snaph = rect.height * _strtonum(defaults[WM_SNAP_VALUE]->content, 0, 1000) / 1000;
unsigned int dmask;
unsigned int num;
XRectangle *rects = rectangles(&num);
XRectangle frect = *rect_of_frame(f);
XRectangle origin = frect;
XQueryPointer(dpy, f->win, &dummy, &dummy, &i, &i, &ox, &oy, &dmask);
XSync(dpy, False);
XGrabServer(dpy);
XGrabPointer(dpy, f->win, False, ButtonMotionMask | ButtonReleaseMask,
GrabModeAsync, GrabModeAsync, None, resize_cursor,
CurrentTime);
for (;;) {
while (!XCheckMaskEvent(dpy,
ButtonReleaseMask | ButtonMotionMask, &ev)) {
usleep(20000);
continue;
}
switch (ev.type) {
case ButtonRelease:
if (!first) {
XPoint pt;
draw_pseudo_border(&frect);
pt.x = px;
pt.y = py;
resize_frame(f, &frect, &pt, 0);
}
XUngrabPointer(dpy, CurrentTime /* ev.xbutton.time */ );
XUngrabServer(dpy);
XSync(dpy, False);
return;
break;
case MotionNotify:
XTranslateCoordinates(dpy, f->win, root, ev.xmotion.x,
ev.xmotion.y, &px, &py, &dummy);
if (first)
first = 0;
else
draw_pseudo_border(&frect);
snap_resize(&frect, &origin, align, rects, num, px,
ox, py, oy, snapw, snaph);
draw_pseudo_border(&frect);
break;
}
}
}
void
drop_move(Frame * f, XRectangle * new, XPoint * pt)
{
Area *a = f->area;
int cx, cy;
unsigned int i, idx = index_item((void **) a->frames, f);
if ((f->rect.x == new->x) && (f->rect.y == new->y))
return;
cx = (pt ? pt->x : new->x + new->width / 2);
cy = (pt ? pt->y : new->y + new->height / 2);
for (i = 0; a->frames[i]; i++) {
if ((a->frames[i] != f) && blitz_ispointinrect(cx, cy, &a->frames[i]->rect)) {
swap((void **) &a->frames[i], (void **) &a->frames[idx]);
a->sel = i;
a->layout->arrange(a);
return;
}
}
}

316
cmd/wm/old/layout_grid.c Normal file
View File

@ -0,0 +1,316 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "wm.h"
#include "layout.h"
#include <cext.h>
/* grid layout definition */
static void arrange_grid(Page * p);
static void init_grid(Page * p);
static void deinit_grid(Page * p);
static void manage_grid(Frame * f);
static void unmanage_grid(Frame * f);
static void resize_grid(Frame * f, XRectangle * new, XPoint * pt);
static Frame *select_grid(Frame * f, char *what);
static Layout grid =
{"grid", init_grid, deinit_grid, arrange_grid, manage_grid,
unmanage_grid, resize_grid, select_grid
};
void
init_layout_grid()
{
layouts =
(Layout **) attach_item_end((void **) layouts, &grid,
sizeof(Layout *));
}
static void
arrange_grid(Page * p)
{
unsigned int i, ic, ir, tw, th, rows, cols;
if (!p->managed)
return;
blitz_getbasegeometry((void **) p->managed, &ic, &cols, &rows);
th = p->managed_rect.height / rows;
tw = p->managed_rect.width / cols;
i = 0;
for (ir = 0; ir < rows; ir++) {
for (ic = 0; ic < cols; ic++) {
if (p->managed[i]) {
XRectangle *r = (XRectangle *) p->managed[i]->aux;
r->x = p->managed_rect.x + ic * tw;
r->y = p->managed_rect.y + ir * th;
r->width = tw;
r->height = th;
p->managed[i]->managed_rect = *r;
resize_frame(p->managed[i], &p->managed[i]->managed_rect,
0, 1);
} else
break;
i++;
}
}
}
static void
init_grid(Page * p)
{
int i;
for (i = 0; p->managed && p->managed[i]; i++)
p->managed[i]->aux = emalloc(sizeof(XRectangle));
}
static void
deinit_grid(Page * p)
{
int i;
for (i = 0; p->managed && p->managed[i]; i++) {
if (p->managed[i]->aux) {
free(p->managed[i]->aux);
p->managed[i]->aux = 0;
}
}
}
static void
manage_grid(Frame * f)
{
f->aux = emalloc(sizeof(XRectangle));
if (f->page)
arrange_grid(f->page);
}
static void
unmanage_grid(Frame * f)
{
if (f->aux) {
free(f->aux);
f->aux = 0;
}
if (f->page)
arrange_grid(f->page);
}
#define THRESHOLD 30
static void
drop_resize(Frame * f, XRectangle * new)
{
int diff;
unsigned int i, rows, cols, cr, cc, num, idx;
Page *p = f->page;
XRectangle *r;
if (!p || !p->managed)
return;
blitz_getbasegeometry((void **) p->managed, &i, &cols, &rows);
num = index_item((void **) p->managed, f);
cr = num / cols;
cc = num - cr * cols;
/* horizontal resize */
if (f->managed_rect.x == new->x && f->managed_rect.width != new->width) {
/* east direction resize */
if (cc == cols - 1)
return;
if (p->managed[cc + 1]
&& (new->x + new->width > p->managed[cc + 1]->managed_rect.x
+ p->managed[cc + 1]->managed_rect.width - THRESHOLD))
return;
diff = new->width - ((XRectangle *) f->aux)->width;
for (i = 0; i < rows; i++) {
idx = (i * cols) + cc;
if (p->managed[idx]) {
r = (XRectangle *) p->managed[idx]->aux;
r->width = new->width;
p->managed[idx]->managed_rect = *r;
resize_frame(p->managed[idx],
&p->managed[idx]->managed_rect, 0, 1);
}
idx = (i * cols) + (cc + 1);
if (p->managed[idx]) {
r = (XRectangle *) p->managed[idx]->aux;
r->x += diff;
r->width -= diff;
p->managed[idx]->managed_rect = *r;
resize_frame(p->managed[idx],
&p->managed[idx]->managed_rect, 0, 1);
}
}
}
if (f->managed_rect.x != new->x) {
/* west direction resize */
if (!cc)
return;
if (new->x < p->managed[cc - 1]->managed_rect.x + THRESHOLD)
return;
diff = new->width - ((XRectangle *) f->aux)->width;
for (i = 0; i < rows; i++) {
idx = (i * cols) + cc;
if (p->managed[idx]) {
r = (XRectangle *) p->managed[idx]->aux;
r->x -= diff;
r->width += diff;
p->managed[idx]->managed_rect = *r;
resize_frame(p->managed[idx],
&p->managed[idx]->managed_rect, 0, 1);
}
idx = (i * cols) + (cc - 1);
if (p->managed[idx]) {
r = (XRectangle *) p->managed[idx]->aux;
r->width -= diff;
p->managed[idx]->managed_rect = *r;
resize_frame(p->managed[idx],
&p->managed[idx]->managed_rect, 0, 1);
}
}
}
/* vertical resize */
if (f->managed_rect.y == new->y
&& f->managed_rect.height != new->height) {
/* south direction resize */
if (cr == rows - 1)
return;
if (p->managed[cr + 1]
&& (new->y + new->height >
p->managed[(cr + 1) * rows]->managed_rect.y +
p->managed[(cr + 1) * rows]->managed_rect.height -
THRESHOLD))
return;
diff = new->height - ((XRectangle *) f->aux)->height;
for (i = 0; i < cols; i++) {
idx = (cr * rows) + i;
if (p->managed[idx]) {
r = (XRectangle *) p->managed[idx]->aux;
r->height = new->height;
p->managed[idx]->managed_rect = *r;
resize_frame(p->managed[idx],
&p->managed[idx]->managed_rect, 0, 1);
}
idx = (cr + 1) * rows + i;
if (p->managed[idx]) {
r = (XRectangle *) p->managed[idx]->aux;
r->y += diff;
r->height -= diff;
p->managed[idx]->managed_rect = *r;
resize_frame(p->managed[idx],
&p->managed[idx]->managed_rect, 0, 1);
}
}
} else if (f->managed_rect.y != new->y) {
/* north direction resize */
if (!cr)
return;
if (new->y <
p->managed[(cr - 1) * rows]->managed_rect.y + THRESHOLD)
return;
diff = new->height - ((XRectangle *) f->aux)->height;
for (i = 0; i < cols; i++) {
idx = (cr * rows) + i;
if (p->managed[idx]) {
r = (XRectangle *) p->managed[idx]->aux;
r->y -= diff;
r->height += diff;
p->managed[idx]->managed_rect = *r;
resize_frame(p->managed[idx],
&p->managed[idx]->managed_rect, 0, 1);
}
idx = (cr - 1) * rows + i;
if (p->managed[idx]) {
r = (XRectangle *) p->managed[idx]->aux;
r->height -= diff;
p->managed[idx]->managed_rect = *r;
resize_frame(p->managed[idx],
&p->managed[idx]->managed_rect, 0, 1);
}
}
}
}
static void
resize_grid(Frame * f, XRectangle * new, XPoint * pt)
{
if ((f->managed_rect.width == new->width)
&& (f->managed_rect.height == new->height))
drop_move(f, new, pt);
else
drop_resize(f, new);
}
static unsigned int
get_current_frame_position(Frame * f)
{
Page *p = f->page;
unsigned int i;
for (i = 0; i < count_items((void **) p->managed); ++i)
if (p->managed[i] == f)
return i;
return -1;
}
static Frame *
select_grid(Frame * f, char *what)
{
Page *p = f->page;
unsigned int ic, cols, rows, pos;
int idx;
blitz_getbasegeometry((void **) p->managed, &ic, &cols, &rows);
pos = get_current_frame_position(f);
if (!strncmp(what, "prev", 5)) {
idx = index_prev_item((void **) p->managed, f);
if (idx >= 0)
return p->managed[idx];
} else if (!strncmp(what, "next", 5)) {
idx = index_next_item((void **) p->managed, f);
if (idx >= 0)
return p->managed[idx];
} else if (!strncmp(what, "south", 6)) {
if ((pos + cols) <= ic)
pos += cols;
else
pos %= cols;
return p->managed[pos];
} else if (!strncmp(what, "north", 6)) {
if (pos < cols)
pos = (rows - 1) * cols + pos;
else
pos -= cols;
return p->managed[pos];
} else if (!strncmp(what, "west", 5)) {
if (pos % cols == 0) {
pos += (cols - 1);
if (pos >= ic)
pos = ic - 1;
} else
pos--;
return p->managed[pos];
} else if (!strncmp(what, "east", 5)) {
if (pos % cols == (cols - 1))
pos -= (cols - 1);
else {
pos++;
if (pos >= ic)
pos = ic - 1;
}
return p->managed[pos];
}
return 0;
}

115
cmd/wm/old/layout_max.c Normal file
View File

@ -0,0 +1,115 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "wm.h"
#include "layout.h"
static void arrange_max(Page * p);
static void init_max(Page * p);
static void deinit_max(Page * p);
static void manage_max(Frame * f);
static void unmanage_max(Frame * f);
static void resize_max(Frame * f, XRectangle * new, XPoint * pt);
static Frame *select_max(Frame * f, char *what);
static Layout max = {"max", init_max, deinit_max, arrange_max, manage_max,
unmanage_max, resize_max, select_max
};
void
init_layout_max()
{
layouts =
(Layout **) attach_item_end((void **) layouts, &max,
sizeof(Layout *));
}
static void
arrange_max(Page * p)
{
int i = 0;
if (!p->managed)
return;
for (i = 0; p->managed[i]; i++) {
p->managed[i]->managed_rect = p->managed_rect;
resize_frame(p->managed[i], &p->managed[i]->managed_rect, 0, 1);
}
if (p->managed_stack)
XRaiseWindow(dpy, p->managed_stack[0]->win);
/* raise floatings */
for (i = 0; p->floating && p->floating[i]; i++)
XRaiseWindow(dpy, p->floating[i]->win);
}
static void
init_max(Page * p)
{
/* max has nothing to init */
arrange_max(p);
}
static void
deinit_max(Page * p)
{
/* max has nothing to free */
}
static void
manage_max(Frame * f)
{
Page *p = f->page;
int idx;
if (!p)
return;
idx = index_next_item((void **) p->managed, f);
if (idx > 0)
swap((void **) &p->managed[0], (void **) &p->managed[idx]);
arrange_max(f->page);
}
static void
unmanage_max(Frame * f)
{
/* nothing todo */
}
static void
resize_max(Frame * f, XRectangle * new, XPoint * pt)
{
if (f->page)
f->managed_rect = f->page->managed_rect;
}
static Frame *
select_max(Frame * f, char *what)
{
Page *p = f->page;
Frame *res = 0;
int i, idx;
if (!strncmp(what, "prev", 5)) {
idx = index_prev_item((void **) p->managed, f);
if (idx >= 0)
res = p->managed[idx];
} else if (!strncmp(what, "next", 5)) {
idx = index_next_item((void **) p->managed, f);
if (idx >= 0)
res = p->managed[idx];
}
if (res)
XRaiseWindow(dpy, res->win);
/* raise floatings */
for (i = 0; p->floating && p->floating[i]; i++)
XRaiseWindow(dpy, p->floating[i]->win);
return res;
}

176
cmd/wm/old/layout_tiled.c Normal file
View File

@ -0,0 +1,176 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <stdlib.h>
#include <string.h>
#include "wm.h"
#include "layout.h"
#include <cext.h>
/* tiled layout definition */
static void arrange_tiled(Page * p);
static void init_tiled(Page * p);
static void deinit_tiled(Page * p);
static void manage_tiled(Frame * f);
static void unmanage_tiled(Frame * f);
static void resize_tiled(Frame * f, XRectangle * new, XPoint * pt);
static Frame *select_tiled(Frame * f, char *what);
static Layout tiled =
{"tiled", init_tiled, deinit_tiled, arrange_tiled, manage_tiled,
unmanage_tiled, resize_tiled, select_tiled
};
void
init_layout_tiled()
{
layouts =
(Layout **) attach_item_end((void **) layouts, &tiled,
sizeof(Layout *));
}
static void
arrange_tiled(Page * p)
{
int tw, th, i, size = 0;
if (!p->managed)
return;
/* determing num of frame and size of page */
size = count_items((void **) p->managed);
if (size > 1) {
tw = (p->managed_rect.width * *((int *) p->aux)) / 100;
th = p->managed_rect.height / (size - 1);
} else {
tw = p->managed_rect.width;
th = p->managed_rect.height;
}
/* master tile */
p->managed[0]->managed_rect.x = p->managed_rect.x;
p->managed[0]->managed_rect.y = p->managed_rect.y;
p->managed[0]->managed_rect.width = tw;
p->managed[0]->managed_rect.height = p->managed_rect.height;
resize_frame(p->managed[0], &p->managed[0]->managed_rect, 0, 1);
if (size == 1)
return;
for (i = 1; i < size; i++) {
p->managed[i]->managed_rect.x = p->managed_rect.x + tw;
p->managed[i]->managed_rect.y = p->managed_rect.y + (i - 1) * th;
p->managed[i]->managed_rect.width = p->managed_rect.width - tw;
p->managed[i]->managed_rect.height = th;
resize_frame(p->managed[i], &p->managed[i]->managed_rect, 0, 1);
}
}
static void
init_tiled(Page * p)
{
p->aux = emalloc(sizeof(int));
*((int *) p->aux) =
_strtonum(core_files[CORE_PAGE_TILE_WIDTH]->content, 5, 95);
}
static void
deinit_tiled(Page * p)
{
p->aux = 0;
}
static void
manage_tiled(Frame * f)
{
Page *p = f->page;
if (!p)
return;
arrange_tiled(p);
}
static void
unmanage_tiled(Frame * f)
{
manage_tiled(f);
}
static void
drop_resize(Frame * f, XRectangle * new)
{
Page *p = f->page;
int num = 0;
int rearrange = 0;
if (!p)
return;
/* determing num of frame and size of page */
num = index_item((void **) p->managed, f);
if (!num) { /* master tile */
if ((f->managed_rect.x == new->x)
&& (f->managed_rect.width != new->width)) {
f->managed_rect = *new;
rearrange = 1;
}
} else if (f->managed_rect.x != new->x) {
int diff = f->managed_rect.width - new->width;
p->managed[0]->managed_rect.width += diff;
rearrange = 1;
}
if (rearrange) {
int tw =
(p->managed[0]->managed_rect.width * 100) /
p->managed_rect.width;
if (tw > 95)
tw = 95;
else if (tw < 5)
tw = 5;
*((int *) p->aux) = tw;
arrange_tiled(p);
}
}
static void
resize_tiled(Frame * f, XRectangle * new, XPoint * pt)
{
if ((f->managed_rect.width == new->width)
&& (f->managed_rect.height == new->height))
drop_move(f, new, pt);
else
drop_resize(f, new);
}
static Frame *
select_tiled(Frame * f, char *what)
{
Page *p = f->page;
int idx;
if (!strncmp(what, "prev", 5)) {
idx = index_prev_item((void **) p->managed, f);
if (idx >= 0)
return p->managed[idx];
} else if (!strncmp(what, "next", 5)) {
idx = index_next_item((void **) p->managed, f);
if (idx >= 0)
return p->managed[idx];
} else if (!strncmp(what, "zoomed", 7)) {
idx = index_item((void **) p->managed, f);
if (idx == 0 && p->managed[1])
idx = 1;
if (idx > 0)
swap((void **) &p->managed[0], (void **) &p->managed[idx]);
p->managed_stack = (Frame **)
attach_item_begin(detach_item
((void **) p->managed_stack, p->managed[0],
sizeof(Frame *)), p->managed[0],
sizeof(Frame *));
arrange_tiled(p);
return p->managed_stack[0];
}
return 0;
}

223
cmd/wm/old/layout_vsplit.c Normal file
View File

@ -0,0 +1,223 @@
/*
* (C)opyright 2005 Jonas WUSTRACK <doez at supinfo dot com>
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "wm.h"
#include "layout.h"
#include <cext.h>
static void arrange_vsplit(Page * p);
static void init_vsplit(Page * p);
static void deinit_vsplit(Page * p);
static void manage_vsplit(Frame * f);
static void unmanage_vsplit(Frame * f);
static void resize_vsplit(Frame * f, XRectangle * new, XPoint * pt);
static Frame *select_vsplit(Frame * f, char *what);
static Layout vsplit =
{"vsplit", init_vsplit, deinit_vsplit, arrange_vsplit, manage_vsplit,
unmanage_vsplit, resize_vsplit, select_vsplit
};
void
init_layout_vsplit()
{
layouts =
(Layout **) attach_item_end((void **) layouts, &vsplit,
sizeof(Layout *));
}
static void
get_base_geometry_vsplit(void **items, unsigned int *size,
unsigned int *cols, unsigned int *rows)
{
/* float sq, dummy; */
*size = count_items((void **) items);
*cols = 1;
*rows = *size;
}
static void
arrange_vsplit(Page * p)
{
unsigned int i, ic, tw, th, rows, cols;
if (!p->managed)
return;
get_base_geometry_vsplit((void **) p->managed, &ic, &cols, &rows);
th = p->managed_rect.height / rows;
tw = p->managed_rect.width;
for (i = 0; i < rows; i++) {
if (p->managed[i]) {
XRectangle *r = (XRectangle *) p->managed[i]->aux;
r->x = p->managed_rect.x;
r->y = p->managed_rect.y + i * th;
r->width = tw;
r->height = th;
p->managed[i]->managed_rect = *r;
resize_frame(p->managed[i], &p->managed[i]->managed_rect, 0,
1);
}
}
}
static void
init_vsplit(Page * p)
{
int i;
for (i = 0; p->managed && p->managed[i]; i++)
p->managed[i]->aux = emalloc(sizeof(XRectangle));
}
static void
deinit_vsplit(Page * p)
{
int i;
for (i = 0; p->managed && p->managed[i]; i++) {
if (p->managed[i]->aux) {
free(p->managed[i]->aux);
p->managed[i]->aux = 0;
}
}
}
static void
manage_vsplit(Frame * f)
{
f->aux = emalloc(sizeof(XRectangle));
if (f->page)
arrange_vsplit(f->page);
}
static void
unmanage_vsplit(Frame * f)
{
if (f->aux) {
free(f->aux);
f->aux = 0;
}
if (f->page)
arrange_vsplit(f->page);
}
#define THRESHOLD 30
static void
drop_resize(Frame * f, XRectangle * new)
{
int diff;
unsigned int i, rows, cols, num, idx;
Page *p = f->page;
XRectangle *r;
if (!p || !p->managed)
return;
get_base_geometry_vsplit((void **) p->managed, &i, &cols, &rows);
num = index_item((void **) p->managed, f);
/* vertical resize */
if (f->managed_rect.y == new->y
&& f->managed_rect.height != new->height) {
/* south direction resize */
if (num == rows - 1)
return;
if (p->managed[num + 1]
&& (new->y + new->height > p->managed[num + 1]->managed_rect.y
+ p->managed[num + 1]->managed_rect.height - THRESHOLD))
return;
diff = new->height - ((XRectangle *) f->aux)->height;
idx = num;
if (p->managed[idx]) {
r = (XRectangle *) p->managed[idx]->aux;
r->height = new->height;
p->managed[idx]->managed_rect = *r;
resize_frame(p->managed[idx], &p->managed[idx]->managed_rect,
0, 1);
}
idx = num + 1;
if (p->managed[idx]) {
r = (XRectangle *) p->managed[idx]->aux;
r->y += diff;
r->height -= diff;
p->managed[idx]->managed_rect = *r;
resize_frame(p->managed[idx], &p->managed[idx]->managed_rect,
0, 1);
}
} else if (f->managed_rect.y != new->y) {
/* north direction resize */
if (!num)
return;
if (new->y < p->managed[num - 1]->managed_rect.y + THRESHOLD)
return;
diff = new->height - ((XRectangle *) f->aux)->height;
idx = num;
if (p->managed[idx]) {
r = (XRectangle *) p->managed[idx]->aux;
r->y -= diff;
r->height += diff;
p->managed[idx]->managed_rect = *r;
resize_frame(p->managed[idx], &p->managed[idx]->managed_rect,
0, 1);
}
idx = num - 1;
if (p->managed[idx]) {
r = (XRectangle *) p->managed[idx]->aux;
r->height -= diff;
p->managed[idx]->managed_rect = *r;
resize_frame(p->managed[idx], &p->managed[idx]->managed_rect,
0, 1);
}
}
}
static void
resize_vsplit(Frame * f, XRectangle * new, XPoint * pt)
{
if ((f->managed_rect.width == new->width)
&& (f->managed_rect.height == new->height))
drop_move(f, new, pt);
else
drop_resize(f, new);
}
static Frame *
select_vsplit(Frame * f, char *what)
{
Page *p = f->page;
int idx;
if (!strncmp(what, "prev", 5)) {
idx = index_prev_item((void **) p->managed, f);
if (idx >= 0)
return p->managed[idx];
} else if (!strncmp(what, "next", 5)) {
idx = index_next_item((void **) p->managed, f);
if (idx >= 0)
return p->managed[idx];
} else if (!strncmp(what, "zoomed", 7)) {
idx = index_item((void **) p->managed, f);
if (idx == 0 && p->managed[1])
idx = 1;
if (idx > 0)
swap((void **) &p->managed[0], (void **) &p->managed[idx]);
p->managed_stack = (Frame **)
attach_item_begin(detach_item
((void **) p->managed_stack, p->managed[0],
sizeof(Frame *)), p->managed[0],
sizeof(Frame *));
arrange_vsplit(p);
return p->managed_stack[0];
}
return 0;
}

463
cmd/wm/page.c Normal file
View File

@ -0,0 +1,463 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "wm.h"
#include <cext.h>
static Page zero_page = {0};
static void select_frame(void *obj, char *cmd);
static void _toggle_frame(void *obj, char *cmd);
static void handle_after_write_page(IXPServer * s, File * f);
static void handle_before_read_page(IXPServer * s, File * f);
/* action table for /page/?/ namespace */
Action page_acttbl[] = {
{"select", select_frame},
{"toggle", _toggle_frame},
{0, 0}
};
Page *
alloc_page(char *autodestroy)
{
Page *p = emalloc(sizeof(Page));
char buf[MAX_BUF], buf2[MAX_BUF];
int id = count_items((void **) pages) + 1;
*p = zero_page;
p->managed_rect = rect;
if (core_files[CORE_PAGE_MANAGED_SIZE]->content)
blitz_strtorect(dpy, &rect, &p->managed_rect,
core_files[CORE_PAGE_MANAGED_SIZE]->content);
snprintf(buf, sizeof(buf), "/page/%d", id);
p->files[P_PREFIX] = ixp_create(ixps, buf);
snprintf(buf, sizeof(buf), "/page/%d/floating", id);
p->files[P_FLOATING_PREFIX] = ixp_create(ixps, buf);
snprintf(buf, sizeof(buf), "/page/%d/floating/sel", id);
p->files[P_FLOATING_SELECTED] = ixp_create(ixps, buf);
p->files[P_FLOATING_SELECTED]->bind = 1; /* mount point */
snprintf(buf, sizeof(buf), "/page/%d/floating/name", id);
p->files[P_FLOATING_LAYOUT] = wmii_create_ixpfile(ixps, buf, "float");
snprintf(buf, sizeof(buf), "/page/%d/managed", id);
p->files[P_MANAGED_PREFIX] = ixp_create(ixps, buf);
snprintf(buf, sizeof(buf), "/page/%d/managed/sel", id);
p->files[P_MANAGED_SELECTED] = ixp_create(ixps, buf);
p->files[P_MANAGED_SELECTED]->bind = 1; /* mount point */
snprintf(buf, sizeof(buf), "/page/%d/ctl", id);
p->files[P_CTL] = ixp_create(ixps, buf);
p->files[P_CTL]->after_write = handle_after_write_page;
snprintf(buf, sizeof(buf), "/page/%d/name", id);
snprintf(buf2, sizeof(buf2), "%d", id);
p->files[P_NAME] = wmii_create_ixpfile(ixps, buf, buf2);
snprintf(buf, sizeof(buf), "/page/%d/managed/name", id);
p->files[P_MANAGED_LAYOUT] =
wmii_create_ixpfile(ixps, buf,
core_files[CORE_PAGE_LAYOUT]->content);
p->files[P_MANAGED_LAYOUT]->after_write = handle_after_write_page;
snprintf(buf, sizeof(buf), "/page/%d/mode", id);
p->files[P_MODE] = ixp_create(ixps, buf);
p->files[P_MODE]->bind = 1;
snprintf(buf, sizeof(buf), "/page/%d/managed/size", id);
p->files[P_MANAGED_SIZE] = wmii_create_ixpfile(ixps, buf, core_files[CORE_PAGE_MANAGED_SIZE]->content);
p->files[P_MANAGED_SIZE]->after_write = handle_after_write_page;
p->files[P_MANAGED_SIZE]->before_read = handle_before_read_page;
snprintf(buf, sizeof(buf), "/page/%d/auto-destroy", id);
p->files[P_AUTO_DESTROY] = wmii_create_ixpfile(ixps, buf, autodestroy);
p->layout = get_layout(p->files[P_MANAGED_LAYOUT]->content);
if (p->layout) {
p->layout->init(p);
p->files[P_MODE]->content = p->files[P_MANAGED_PREFIX]->content;
} else
p->files[P_MODE]->content = p->files[P_FLOATING_PREFIX]->content;
pages = (Page **) attach_item_end((void **) pages, p, sizeof(Page *));
sel_page = index_item((void **) pages, p);
core_files[CORE_PAGE_SELECTED]->content = p->files[P_PREFIX]->content;
invoke_core_event(core_files[CORE_EVENT_PAGE_UPDATE]);
return p;
}
void
free_page(Page * p)
{
pages = (Page **) detach_item((void **) pages, p, sizeof(Page *));
if (pages) {
int i;
char buf[8];
if (sel_page - 1 >= 0)
sel_page--;
else
sel_page = 0;
for (i = 0; pages[i]; i++) {
snprintf(buf, sizeof(buf), "%d", i + 1);
free(pages[i]->files[P_PREFIX]->name);
pages[i]->files[P_PREFIX]->name = estrdup(buf);
free(pages[i]->files[P_NAME]->content);
pages[i]->files[P_NAME]->content = estrdup(buf);
pages[i]->files[P_NAME]->size = strlen(buf);
}
}
if (p->layout)
p->layout->deinit(p);
core_files[CORE_PAGE_SELECTED]->content = 0;
ixp_remove_file(ixps, p->files[P_PREFIX]);
if (ixps->errstr)
fprintf(stderr, "wmiiwm: free_page(): %s\n", ixps->errstr);
free(p);
}
void
draw_page(Page * p)
{
int i;
if (!p)
return;
for (i = 0; p->floating && p->floating[i]; i++)
draw_frame(p->floating[i]);
for (i = 0; p->managed && p->managed[i]; i++)
draw_frame(p->managed[i]);
}
XRectangle *
rectangles(unsigned int *num)
{
XRectangle *result = 0;
int i, j = 0;
Window d1, d2;
Window *wins;
XWindowAttributes wa;
XRectangle r;
if (XQueryTree(dpy, root, &d1, &d2, &wins, num)) {
result = emalloc(*num * sizeof(XRectangle));
for (i = 0; i < *num; i++) {
if (!XGetWindowAttributes(dpy, wins[i], &wa))
continue;
if (wa.override_redirect && (wa.map_state == IsViewable)) {
r.x = wa.x;
r.y = wa.y;
r.width = wa.width;
r.height = wa.height;
result[j++] = r;
}
}
}
if (wins) {
XFree(wins);
}
*num = j;
return result;
}
static void
_toggle_frame(void *obj, char *cmd)
{
Page *p = obj;
Frame *f;
if (!p || !p->layout)
return;
f = get_selected(p);
if (!f)
return;
toggle_frame(f);
}
static Frame *
select_toggled(Page * p, Frame * f)
{
if (is_managed_frame(f))
return p->floating_stack ? p->floating_stack[0] : 0;
else
return f = p->managed_stack ? p->managed_stack[0] : 0;
return 0;
}
Frame *
select_floating(Page * p, Frame * f, char *what)
{
int idx;
if (!strncmp(what, "prev", 5)) {
idx = index_prev_item((void **) p->floating, f);
if (idx >= 0)
return p->floating[idx];
} else if (!strncmp(what, "next", 5)) {
idx = index_next_item((void **) p->floating, f);
if (idx >= 0)
return p->floating[idx];
}
return 0;
}
static void
center_pointer(Frame * f)
{
Window dummy;
int wex, wey, ex, ey, i;
unsigned int dmask;
XRectangle *r;
if (!f)
return;
r = rect_of_frame(f);
XQueryPointer(dpy, f->win, &dummy, &dummy, &i, &i, &wex, &wey, &dmask);
XTranslateCoordinates(dpy, f->win, root, wex, wey, &ex, &ey, &dummy);
if (blitz_ispointinrect(ex, ey, r))
return;
/* suppress EnterNotify's while mouse warping */
XSelectInput(dpy, root, ROOT_MASK & ~StructureNotifyMask);
XWarpPointer(dpy, None, f->win, 0, 0, 0, 0,
r->width / 2, r->height / 2);
XSync(dpy, False);
XSelectInput(dpy, root, ROOT_MASK);
}
static void
select_frame(void *obj, char *cmd)
{
Page *p = (Page *) obj;
Frame *f, *old, *old2;
if (!p || !cmd)
return;
old2 = old = f = get_selected(p);
if (!f)
return;
if (is_managed_frame(f))
old2 = p->managed[0];
if (!strncmp(cmd, "toggled", 8))
f = select_toggled(p, f);
else if (is_managed_frame(f))
f = p->layout->select(f, cmd);
else
f = select_floating(p, f, cmd);
if (!f)
return;
focus_frame(f, 1, 1, 1);
center_pointer(f);
if (old)
draw_frame(old);
if (old2 != old)
draw_frame(old2); /* on zoom */
draw_frame(f);
}
void
hide_page(Page * p)
{
int i;
for (i = 0; p->floating && p->floating[i]; i++)
XUnmapWindow(dpy, p->floating[i]->win);
for (i = 0; p->managed && p->managed[i]; i++)
XUnmapWindow(dpy, p->managed[i]->win);
XSync(dpy, False);
}
void
show_page(Page * p)
{
int i;
for (i = 0; p->floating && p->floating[i]; i++)
XMapWindow(dpy, p->floating[i]->win);
for (i = 0; p->managed && p->managed[i]; i++)
XMapWindow(dpy, p->managed[i]->win);
XSync(dpy, False);
}
static void
handle_before_read_page(IXPServer * s, File * f)
{
int i;
XRectangle *rct = 0;
for (i = 0; pages && pages[i]; i++) {
if (pages[i]->files[P_MANAGED_SIZE] == f) {
rct = &pages[i]->managed_rect;
break;
}
}
if (rct) {
char buf[64];
snprintf(buf, 64, "%d,%d,%d,%d", rct->x, rct->y,
rct->width, rct->height);
if (f->content)
free(f->content);
f->content = estrdup(buf);
f->size = strlen(buf);
}
}
Layout *
get_layout(char *name)
{
int i = 0;
size_t len;
if (!name)
return 0;
len = strlen(name);
for (i = 0; layouts[i]; i++) {
if (!strncmp(name, layouts[i]->name, len))
return layouts[i];
}
return 0;
}
static void
handle_after_write_page(IXPServer * s, File * f)
{
int i;
for (i = 0; pages && pages[i]; i++) {
Page *p = pages[i];
if (p->files[P_CTL] == f) {
run_action(f, p, page_acttbl);
return;
} else if (p->files[P_MANAGED_SIZE] == f) {
/* resize stuff */
blitz_strtorect(dpy, &rect, &p->managed_rect,
p->files[P_MANAGED_SIZE]->content);
if (!p->managed_rect.width)
p->managed_rect.width = 10;
if (!p->managed_rect.height)
p->managed_rect.height = 10;
if (p->layout)
p->layout->arrange(p);
draw_page(p);
return;
} else if (p->files[P_MANAGED_LAYOUT] == f) {
int had_valid_layout = p->layout ? 1 : 0;
if (p->layout)
p->layout->deinit(p);
p->layout = get_layout(p->files[P_MANAGED_LAYOUT]->content);
if (p->layout) {
p->layout->init(p);
p->layout->arrange(p);
if (!had_valid_layout) {
int j;
Frame **tmp = 0;
for (j = 0; p->floating && p->floating[j]; j++) {
if (!p->floating[j]->floating)
tmp =
(Frame **) attach_item_begin((void **) tmp,
p->
floating[j],
sizeof(Frame
*));
}
for (j = 0; tmp && tmp[j]; j++)
toggle_frame(tmp[j]);
free(tmp);
}
}
if (!p->layout) {
/* make all managed clients floating */
int j;
Frame **tmp = 0;
while (p->managed) {
tmp = (Frame **) attach_item_begin((void **) tmp,
p->managed[0],
sizeof(Frame *));
detach_frame_from_page(p->managed[0], 1);
}
for (j = 0; tmp && tmp[j]; j++) {
attach_Frameo_page(p, tmp[j], 0);
resize_frame(tmp[j], rect_of_frame(tmp[j]), 0, 1);
}
free(tmp);
}
draw_page(p);
invoke_core_event(core_files[CORE_EVENT_PAGE_UPDATE]);
return;
}
}
}
void
attach_Frameo_page(Page * p, Frame * f, int managed)
{
Frame *old = get_selected(p);
XSelectInput(dpy, root, ROOT_MASK & ~StructureNotifyMask);
XMapRaised(dpy, f->win);
if (!f->floating && managed && p->layout) {
int i;
p->managed = (Frame **) attach_item_end((void **) p->managed, f,
sizeof(Frame *));
p->managed_stack =
(Frame **) attach_item_begin((void **) p->managed_stack, f,
sizeof(Frame *));
wmii_move_ixpfile(f->files[F_PREFIX], p->files[P_MANAGED_PREFIX]);
p->files[P_MANAGED_SELECTED]->content =
f->files[F_PREFIX]->content;
if (p == pages[sel_page])
for (i = 0; p->floating && p->floating[i]; i++)
XRaiseWindow(dpy, p->floating[i]->win);
} else {
p->floating = (Frame **) attach_item_end((void **) p->floating, f,
sizeof(Frame *));
p->floating_stack =
(Frame **) attach_item_begin((void **) p->floating_stack, f,
sizeof(Frame *));
wmii_move_ixpfile(f->files[F_PREFIX], p->files[P_FLOATING_PREFIX]);
p->files[P_FLOATING_SELECTED]->content =
f->files[F_PREFIX]->content;
p->files[P_MODE]->content = p->files[P_FLOATING_PREFIX]->content;
}
f->page = p;
focus_frame(f, 1, 0, 1);
if (is_managed_frame(f) && p->layout)
p->layout->manage(f);
center_pointer(f);
if (old)
draw_frame(old);
draw_frame(f);
}
void
detach_frame_from_page(Frame * f, int ignore_focus_and_destroy)
{
Page *p = f->page;
wmii_move_ixpfile(f->files[F_PREFIX], core_files[CORE_DETACHED_FRAME]);
if (is_managed_frame(f)) {
p->managed = (Frame **) detach_item((void **) p->managed, f,
sizeof(Frame *));
p->managed_stack =
(Frame **) detach_item((void **) p->managed_stack, f,
sizeof(Frame *));
p->files[P_MANAGED_SELECTED]->content = 0;
} else {
p->floating = (Frame **) detach_item((void **) p->floating, f,
sizeof(Frame *));
p->floating_stack =
(Frame **) detach_item((void **) p->floating_stack, f,
sizeof(Frame *));
p->files[P_FLOATING_SELECTED]->content = 0;
}
XUnmapWindow(dpy, f->win);
if (is_managed_mode(p) && p->layout)
p->layout->unmanage(f);
f->page = 0;
if (!ignore_focus_and_destroy) {
Frame *fr;
if (!p->managed && !p->floating
&& _strtonum(p->files[P_AUTO_DESTROY]->content, 0, 1)) {
destroy_page(p);
return;
}
focus_page(p, 0, 1);
fr = get_selected(p);
if (fr) {
center_pointer(fr);
draw_frame(fr);
}
}
}

290
cmd/wm/wm.c Normal file
View File

@ -0,0 +1,290 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <X11/cursorfont.h>
#include <X11/Xproto.h>
#include "wm.h"
static int other_wm_running;
static int (*x_error_handler) (Display *, XErrorEvent *);
char *version[] = {
"wmiiwm - window manager improved 2 - " VERSION "\n"
" (C)opyright MMIV-MMV Anselm R. Garbe\n", 0
};
static void
usage()
{
fprintf(stderr, "%s",
"usage: wmiiwm -s <socket file> [-c] [-v]\n"
" -s socket file\n"
" -c checks if another WM is already running\n"
" -v version info\n");
exit(1);
}
static void
init_atoms()
{
wm_state = XInternAtom(dpy, "WM_STATE", False);
wm_change_state = XInternAtom(dpy, "WM_CHANGE_STATE", False);
wm_protocols = XInternAtom(dpy, "WM_PROTOCOLS", False);
wm_delete = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
motif_wm_hints = XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
net_wm_desktop = XInternAtom(dpy, "_NET_WM_DESKTOP", False);
}
static void
init_cursors()
{
normal_cursor = XCreateFontCursor(dpy, XC_left_ptr);
resize_cursor = XCreateFontCursor(dpy, XC_sizing);
move_cursor = XCreateFontCursor(dpy, XC_fleur);
drag_cursor = XCreateFontCursor(dpy, XC_cross);
w_cursor = XCreateFontCursor(dpy, XC_left_side);
e_cursor = XCreateFontCursor(dpy, XC_right_side);
n_cursor = XCreateFontCursor(dpy, XC_top_side);
s_cursor = XCreateFontCursor(dpy, XC_bottom_side);
nw_cursor = XCreateFontCursor(dpy, XC_top_left_corner);
ne_cursor = XCreateFontCursor(dpy, XC_top_right_corner);
sw_cursor = XCreateFontCursor(dpy, XC_bottom_left_corner);
se_cursor = XCreateFontCursor(dpy, XC_bottom_right_corner);
}
static void
init_defaults()
{
defaults[WM_DETACHED_FRAME] = ixp_create(ixps, "/detached/frame");
defaults[WM_DETACHED_CLIENT] = ixp_create(ixps, "/detached/client");
defaults[WM_TRANS_COLOR] = wmii_create_ixpfile(ixps, "/default/transcolor", BLITZ_SEL_FG_COLOR);
defaults[WM_TRANS_COLOR]->after_write = handle_after_write;
defaults[WM_SEL_BG_COLOR] = wmii_create_ixpfile(ixps, "/default/selstyle/bgcolor", BLITZ_SEL_BG_COLOR);
defaults[WM_SEL_FG_COLOR] = wmii_create_ixpfile(ixps, "/default/selstyle/fgcolor", BLITZ_SEL_FG_COLOR);
defaults[WM_SEL_BORDER_COLOR] = wmii_create_ixpfile(ixps, "/default/selstyle/fgcolor", BLITZ_SEL_BORDER_COLOR);
defaults[WM_NORM_BG_COLOR] = wmii_create_ixpfile(ixps, "/default/normstyle/bgcolor", BLITZ_NORM_BG_COLOR);
defaults[WM_NORM_FG_COLOR] = wmii_create_ixpfile(ixps, "/default/normstyle/fgcolor", BLITZ_NORM_FG_COLOR);
defaults[WM_NORM_BORDER_COLOR] = wmii_create_ixpfile(ixps, "/default/normstyle/fgcolor", BLITZ_NORM_BORDER_COLOR);
defaults[WM_FONT] = wmii_create_ixpfile(ixps, "/default/font", BLITZ_FONT);
defaults[WM_FONT]->after_write = handle_after_write;
defaults[WM_PAGE_SIZE] = wmii_create_ixpfile(ixps, "/default/pagesize", "0,0,east,south-16");
defaults[WM_SNAP_VALUE] = wmii_create_ixpfile(ixps, "/default/snapvalue", "20"); /* 0..1000 */
defaults[WM_BORDER] = wmii_create_ixpfile(ixps, "/default/border", "1");
defaults[WM_TAB] = wmii_create_ixpfile(ixps, "/default/tab", "1");
defaults[WM_HANDLE_INC] = wmii_create_ixpfile(ixps, "/default/handleinc", "1");
defaults[WM_LOCKED] = wmii_create_ixpfile(ixps, "/default/locked", "1");
defaults[WM_LAYOUT] = wmii_create_ixpfile(ixps, "/default/layout", LAYOUT);
defaults[WM_SEL_PAGE] = ixp_create(ixps, "/page/sel");
defaults[WM_EVENT_PAGE_UPDATE] = ixp_create(ixps, "/default/event/pageupdate");
defaults[WM_EVENT_CLIENT_UPDATE] = ixp_create(ixps, "/default/event/clientupdate");
defaults[WM_EVENT_B1PRESS] = ixp_create(ixps, "/defaults/event/b1press");
defaults[WM_EVENT_B2PRESS] = ixp_create(ixps, "/defaults/event/b2press");
defaults[WM_EVENT_B3PRESS] = ixp_create(ixps, "/defaults/event/b3press");
defaults[WM_EVENT_B4PRESS] = ixp_create(ixps, "/defaults/event/b4press");
defaults[WM_EVENT_B5PRESS] = ixp_create(ixps, "/defaults/event/b5press");
}
static void
init_screen()
{
XGCValues gcv;
XSetWindowAttributes wa;
XAllocNamedColor(dpy, DefaultColormap(dpy, screen_num),
defaults[WM_TRANS_COLOR]->content,
&xorcolor, &xorcolor);
gcv.subwindow_mode = IncludeInferiors;
gcv.function = GXxor;
gcv.foreground = xorcolor.pixel;
gcv.line_width = 4;
gcv.plane_mask = AllPlanes;
gcv.graphics_exposures = False;
xorgc = XCreateGC(dpy, root, GCForeground | GCGraphicsExposures
| GCFunction | GCSubwindowMode | GCLineWidth
| GCPlaneMask, &gcv);
rect.x = rect.y = 0;
rect.width = DisplayWidth(dpy, screen_num);
rect.height = DisplayHeight(dpy, screen_num);
wa.override_redirect = 1;
wa.background_pixmap = ParentRelative;
wa.event_mask = ExposureMask | ButtonPressMask | PointerMotionMask
| SubstructureRedirectMask | SubstructureNotifyMask;
transient = XCreateWindow(dpy, root, 0, 0, rect.width, rect.height,
0, DefaultDepth(dpy, screen_num),
CopyFromParent, DefaultVisual(dpy,
screen_num),
CWOverrideRedirect | CWBackPixmap |
CWEventMask, &wa);
XSync(dpy, False);
transient_gc = XCreateGC(dpy, transient, 0, 0);
XDefineCursor(dpy, transient, normal_cursor);
XDefineCursor(dpy, root, normal_cursor);
XSelectInput(dpy, root, ROOT_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().
*/
static 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))
return 0;
fprintf(stderr, "%s", "wmiiwm: fatal error");
return x_error_handler(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()
{
int i;
XWindowChanges wc;
for (i = 0; clients && clients[i]; i++) {
Client *c = clients[i];
Frame *f = c->frame;
if (f) {
gravitate(c, tab_height(f), border_width(f), 1);
XReparentWindow(dpy, c->win, root,
rect_of_frame(f)->x + c->rect.x,
rect_of_frame(f)->y + c->rect.y);
wc.border_width = c->border;
XConfigureWindow(dpy, c->win, CWBorderWidth, &wc);
}
}
XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
}
static void
run()
{
/* init */
init_event_hander();
if (!(defaults[WM_CTL] = ixp_create(ixps, "/ctl"))) {
perror("wmiiwm: cannot connect IXP server");
exit(1);
}
defaults[WM_CTL]->after_write = handle_after_write;
clients = 0;
frames = 0;
detached = 0;
pages = 0;
layouts = 0;
sel = 0;
sel_client = 0;
init_atoms();
init_cursors();
init_defaults();
font = blitz_getfont(dpy, defaults[WM_FONT]->content);
init_lock_modifiers(dpy, &valid_mask, &num_lock_mask);
init_screen();
init_layouts();
scan_wins();
/* main event loop */
run_server_with_fd_support(ixps, ConnectionNumber(dpy), check_event,
0);
cleanup();
deinit_server(ixps);
XCloseDisplay(dpy);
}
int
main(int argc, char *argv[])
{
int i;
int checkwm = 0;
char *sockfile = 0;
/* command line args */
if (argc > 1) {
for (i = 1; (i < argc) && (argv[i][0] == '-'); i++) {
switch (argv[i][1]) {
case 'v':
fprintf(stdout, "%s", version[0]);
exit(0);
break;
case 'c':
checkwm = 1;
break;
case 's':
if (i + 1 < argc)
sockfile = argv[++i];
else
usage();
break;
default:
usage();
break;
}
}
}
dpy = XOpenDisplay(0);
if (!dpy) {
fprintf(stderr, "%s", "wmiiwm: cannot open display\n");
exit(1);
}
screen_num = DefaultScreen(dpy);
root = RootWindow(dpy, screen_num);
/* 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(dpy, root, ROOT_MASK);
XSync(dpy, False);
if (other_wm_running) {
fprintf(stderr,
"wmiiwm: another window manager is already running\n");
exit(1);
}
if (checkwm) {
XCloseDisplay(dpy);
exit(0);
}
XSetErrorHandler(0);
x_error_handler = XSetErrorHandler(wmii_error_handler);
ixps = wmii_setup_server(sockfile);
run();
return 0;
}

291
cmd/wm/wm.h Normal file
View File

@ -0,0 +1,291 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <stdio.h>
#include <X11/Xutil.h>
#include "cext.h"
#include "wmii.h"
/* array indexes of page file pointers */
enum {
P_PREFIX,
P_AREA_PREFIX,
P_SEL_AREA,
P_CTL,
P_MODE,
P_AUTO_DESTROY,
P_LAST
};
/* array indexes of area file pointers */
enum {
A_PREFIX,
A_SEL_FRAME,
A_CTL,
A_SIZE,
A_LAYOUT,
A_LAST
};
/* array indexes of frame file pointers */
enum {
F_PREFIX,
F_CLIENT_PREFIX,
F_SEL_CLIENT,
F_CTL,
F_SIZE,
F_BORDER,
F_TAB,
F_HANDLE_INC,
F_LOCKED,
F_SEL_BG_COLOR,
F_SEL_FG_COLOR,
F_SEL_BORDER_COLOR,
F_NORM_BG_COLOR,
F_NORM_FG_COLOR,
F_NORM_BORDER_COLOR,
F_EVENT_B2PRESS,
F_EVENT_B3PRESS,
F_EVENT_B4PRESS,
F_EVENT_B5PRESS,
F_LAST
};
/* array indexes of client file pointers */
enum {
C_PREFIX,
C_NAME,
C_CLASS,
C_INSTANCE,
C_LAST
};
/* array indexes of wm file pointers */
enum {
WM_CTL,
WM_DETACHED_FRAME,
WM_DETACHED_CLIENT,
WM_TRANS_COLOR,
WM_SEL_BG_COLOR,
WM_SEL_BORDER_COLOR,
WM_SEL_FG_COLOR,
WM_NORM_BG_COLOR,
WM_NORM_BORDER_COLOR,
WM_NORM_FG_COLOR,
WM_FONT,
WM_PAGE_SIZE,
WM_BORDER,
WM_TAB,
WM_HANDLE_INC,
WM_LOCKED,
WM_SNAP_VALUE,
WM_SEL_PAGE,
WM_LAYOUT,
WM_EVENT_PAGE_UPDATE,
WM_EVENT_CLIENT_UPDATE,
WM_EVENT_B1PRESS,
WM_EVENT_B2PRESS,
WM_EVENT_B3PRESS,
WM_EVENT_B4PRESS,
WM_EVENT_B5PRESS,
WM_LAST
};
#define PROTO_DEL 1
#define BORDER_WIDTH 3
#define LAYOUT "column"
#define GAP 5
#define ROOT_MASK (SubstructureRedirectMask | SubstructureNotifyMask | ButtonPressMask | ButtonReleaseMask)
#define CLIENT_MASK (SubstructureNotifyMask | PropertyChangeMask | EnterWindowMask)
typedef struct Page Page;
typedef struct Layout Layout;
typedef struct Area Area;
typedef struct Frame Frame;
typedef struct Client Client;
/* new layout interface:
* /page/[1..n]/0/ floating space
* /page/[1..n]/[1..n]/ layout space
*/
struct Page {
Area **areas;
unsigned int sel;
File *files[P_LAST];
};
struct Layout {
char *name;
void (*init) (Area *); /* called when layout is initialized */
void (*deinit) (Area *); /* called when layout is uninitialized */
void (*arrange) (Area *); /* called when area is resized */
void (*attach) (Area *, Client *); /* called on attach */
void (*detach) (Area *, Client *, int, int); /* called on detach */
void (*resize) (Frame *, XRectangle *, XPoint * pt); /* called after resize */
};
struct Area {
Layout *layout;
Page *page;
Frame **frames;
unsigned int sel;
XRectangle rect;
void *aux; /* free pointer */
File *files[A_LAST];
};
struct Frame {
Area *area;
Window win;
GC gc;
XRectangle rect;
Cursor cursor;
Client **clients;
int sel;
void *aux; /* free pointer */
File *files[F_LAST];
};
struct Client {
int proto;
unsigned int border;
Window win;
Window trans;
XRectangle rect;
XSizeHints size;
Frame *frame;
File *files[C_LAST];
};
#define SELFRAME(x) (x && x->areas[x->sel]->frames ? x->areas[x->sel]->frames[x->areas[x->sel]->sel] : 0)
#define ISSELFRAME(x) (pages && SELFRAME(pages[sel]) == x)
/* global variables */
Display *dpy;
IXPServer *ixps;
int screen_num;
Window root;
Window transient;
XRectangle rect;
Client **detached;
Page **pages;
unsigned int sel;
Frame **frames;
Client **clients;
XFontStruct *font;
XColor xorcolor;
GC xorgc;
GC transient_gc;
Layout **layouts;
Atom wm_state;
Atom wm_change_state;
Atom wm_protocols;
Atom wm_delete;
Atom motif_wm_hints;
Atom net_wm_desktop;
Cursor normal_cursor;
Cursor resize_cursor;
Cursor move_cursor;
Cursor drag_cursor;
Cursor w_cursor;
Cursor e_cursor;
Cursor n_cursor;
Cursor s_cursor;
Cursor nw_cursor;
Cursor ne_cursor;
Cursor sw_cursor;
Cursor se_cursor;
/* default file pointers */
File *defaults[WM_LAST];
unsigned int valid_mask, num_lock_mask;
/* area.c */
void focus_frame(Frame * f, int raise, int up, int down);
void destroy_area(Area *a);
void free_area(Area *a);
/* client.c */
Client *alloc_client(Window w);
void _init_client(Client * c, XWindowAttributes * wa);
void free_client(Client * c);
void configure_client(Client * c);
void handle_client_property(Client * c, XPropertyEvent * e);
void close_client(Client * c);
void draw_client(Client * c);
void draw_clients(Frame * f);
void gravitate(Client * c, unsigned int tabh, unsigned int bw, int invert);
int manage_class_instance(Client * c);
void grab_client(Client * c, unsigned long mod, unsigned int button);
void ungrab_client(Client * c, unsigned long mod, unsigned int button);
void hide_client(Client * c);
void show_client(Client * c);
void reparent_client(Client * c, Window w, int x, int y);
/* core.c */
unsigned int tab_height(Frame *f);
unsigned int border_width(Frame *f);
void invoke_core_event(File * f);
void run_action(File * f, void *obj, Action * acttbl);
void scan_wins();
Client *win_to_client(Window w);
int win_proto(Window w);
int win_state(Window w);
void handle_after_write(IXPServer * s, File * f);
void focus_page(Page * p, int raise, int down);
void detach(Frame * f, int client_destroyed);
int comp_obj(void *f1, void *f2);
void destroy_page(Page * p);
void set_client_state(Client * c, int state);
/* frame.c */
Frame *win_to_frame(Window w);
Frame *alloc_frame(XRectangle * r, int add_frame_border, int floating);
void free_frame(Frame * f);
void resize_frame(Frame * f, XRectangle * r, XPoint * pt, int ignore_layout);
void draw_frame(Frame * f);
void handle_frame_buttonpress(XButtonEvent * e, Frame * f);
void attach_client(Client * c);
void attach_Cliento_frame(Frame * f, Client * c);
void detach_client_from_frame(Client * c, int unmapped, int destroyed);
void draw_tab(Frame * f, char *text, int x, int y, int w, int h, int sel);
void focus_client(Client * c, int raise, int up);
int is_managed_frame(Frame * f);
XRectangle *rect_of_frame(Frame * f);
/* event.c */
void init_event_hander();
void check_event(Connection * c);
/* mouse.c */
void mouse_resize(Frame * f, Align align);
void mouse_move(Frame * f);
Cursor cursor_for_motion(Frame * f, int x, int y);
Align cursor_to_align(Cursor cursor);
Align xy_to_align(XRectangle * rect, int x, int y);
void drop_move(Frame * f, XRectangle * new, XPoint * pt);
/* page.c */
Page *alloc_page(char *autodestroy);
void free_page(Page * p);
void focus_area(Area *a, int raise, int up, int down);
XRectangle *rectangles(unsigned int *num);
void hide_page(Page * p);
void show_page(Page * p);
void attach_Frameo_page(Page * p, Frame * f, int managed);
void detach_frame_from_page(Frame * f, int ignore_focus_and_destroy);
void draw_page(Page * p);
Layout *get_layout(char *name);
Frame *select_floating(Page * p, Frame * f, char *what);
/* layout.c */
void init_layouts();

43
cmd/wm/wmii Normal file
View File

@ -0,0 +1,43 @@
#!9PREFIX/bin/rc
# window manager improved 2 wrapper
if(! ~ $#* 0) {
exec wmiiwm $*
}
if(! wmiiwm -c) {
exit 1 # wmiiwm is already running or $DISPLAY is unset
}
WMII_CONFDIR=CONFPREFIX/wmii-3
OLD_PATH=$PATH
PATH=$HOME/.wmii-3:$WMII_CONFDIR:9PREFIX/bin:$PATH
WMII_IDENT=`{date -n}^'-'^$pid
WMIR_SOCKET=/tmp/.ixp-$USER/wmifs-$WMII_IDENT
# start window manager and utilities:
mkdir -p /tmp/.ixp-$USER
wmiiwm -s /tmp/.ixp-$USER/wmiiwm-$WMII_IDENT &
wmiiwmpid=$apid
wmifs -s /tmp/.ixp-$USER/wmifs-$WMII_IDENT &
wmikeys -s /tmp/.ixp-$USER/wmikeys-$WMII_IDENT &
wmibar -s /tmp/.ixp-$USER/wmibar-$WMII_IDENT &
wmimenu -s /tmp/.ixp-$USER/wmimenu-$WMII_IDENT &
sleep 1
# mount ixp file systems:
wmir write /ctl 'bind /wm /tmp/.ixp-'^$USER/wmiiwm-$WMII_IDENT
wmir write /ctl 'bind /bar /tmp/.ixp-'^$USER/wmibar-$WMII_IDENT
wmir write /ctl 'bind /menu /tmp/.ixp-'^$USER/wmimenu-$WMII_IDENT
wmir write /ctl 'bind /keys /tmp/.ixp-'^$USER/wmikeys-$WMII_IDENT
# display the wmii introduction if necessary:
if(mkdir $HOME/.wmii-3 >[2]/dev/null) {
welcome &
}
# run configuration:
wmirc &
# wait for wmiiwm's termination:
wait $wmiiwmpid

95
cmd/wm/wmii.1 Normal file
View File

@ -0,0 +1,95 @@
.TH WMII 1 wmii-3
.SH NAME
wmii \- window manager improved 2
.SH DESCRIPTION
.SS Overview
.BR wmii (1)
is a script that launches the
.B wmii
window manager and its various utilities and takes care that they are
configured for use.
.SS Actions
An action is a script written for the rc shell, 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-3 to $HOME/.wmii-3 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
extern <cmd>
clean the environment and execute the given command
.TP 2
kmode <mode>
activate shortcuts of the given mode
.TP 2
quit
quit wmii
.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
wmirc
configure wmii
.SS Configuration
If you feel the need to change the default configuration, then customize (as
described above) the
.B wmirc
action. This action is executed at the end of the
.BR wmii (1)
script and does all the work of setting up the window manager, the key
bindings, the bar labels, etc.
.SH FILES
.TP
/tmp/.ixp-$USER
Directory where the socket files are stored.
.TP
CONFPREFIX/wmii-3
Global action directory.
.TP
$HOME/.wmii-3
User-specific action directory. Actions are first searched here.
.SH ENVIRONMENT
.TP
HOME, USER
See the section
.B FILES
above.
.P
The following variables are set and exported within
.BR wmii (1)
and thus can be used in actions.
.TP
OLD_PATH
PATH as it was before
.BR wmii (1)
added the local and global action directory and the location where the 9base
tools reside. The
.B wmire
action resets PATH and removes OLD_PATH before executing a command.
.TP
WMII_CONFDIR
Global action directory.
.TP
WMII_IDENT
Unique identifier for the
.BR wmii (1)
session. Part of the socket file names.
.TP
WMIR_SOCKET
Socket file of
.BR wmifs (1).
Used by
.BR wmir (1).
.SH SEE ALSO
.BR wmibar (1),
.BR wmifs (1),
.BR wmiiwm (1),
.BR wmikeys (1),
.BR wmimenu (1),
.BR wmir (1)

171
cmd/wm/wmiiwm.1 Normal file
View File

@ -0,0 +1,171 @@
.de FN
\fI\|\\$1\|\fP\\$2
..
.TH wmii 1
.SH NAME
wmii \- window manager improved 2 for X11
.SH DESCRIPTION
.B wmii
is a dynamic window manager for the X Window System.
It provides a synthesis of conventional, tiled and tabbed window
management based on dynamic layouts.
Several roots of these window management capabilities have been
introduced by the Ion and LarsWM window managers.
Apart from this, it implements a socket-based fileserver,
which is accessed to configure and interoperate with wmii. The idea
behind this file-based approach is derived from the plan9
operating system and can be found in the Acme programming environment.
.B wmii
consists of the core window manager itself and several utilities, such
as
.BR wmibar (1),
.BR wmifs (1),
.BR wmimenu (1),
.BR wmikeys (1),
.BR wmiplumb (1),
.BR wmir (1)
and
.BR wmiwarp (1).
.SH SYNOPSIS
.B wmii
.RB [ \-s
.IR socketfile ]
.br
.B wmii
.RB \-v
.SH OPTIONS
.TP
.BI \-s " socketfile"
lets you override the default socketfile which
.B wmii
should use for connecting fileserver clients.
.TP
.B \-v
prints version information to stderr, then exits.
.SH FILES
.TP
.FN /tmp/.ixp-$USER/wmii\-$WMII_IDENT
this file is the default socket file used by
.B wmii.
.TP
.FN $HOME/.wmii/rc [start|stop]
this file is executed when
.B wmii
starts up or shuts down. If it is not present,
.B wmii
executes
.FN $WMII_CONFDIR/rc [start|stop]
as fallback.
The rc script is used to customize and setup
.B wmii
beside its components to match the users needs.
It can be a simple shell script, a native binary or whatever executable.
The only condition is, that it understands the command line arguments
.IR start
, which is provided while startup and
.IR stop
, which is provided on shutdown.
For details about the shipped default rc configuration system, see
.BR wmii.rc (5).
.SH STRUCTURE
The structure of
.B wmii
consists of following objects which are described in more detail.
.TP
.B Display
The display is a running X server which consists of at least one
.B Screen (Monitor),
.B Keyboard,
and the
.B Mouse.
Applications with X11 support can connect to such X server display in
order to be used.
.TP
.B Screen (Monitor)
A screen is the physical device which displays a part or the whole
display driven by the X server. Each screen consists of a root window
which matches the physical conditions the X server is configured to
drive the graphics adaptor and the connected screen, such as color
depth and resolution.
.TP
.B (Root) Window
A window is a rectangular area which is drawn by the X server. A root
window is the complete drawable area of a screen. Root windows are
top-level windows which are always at the most-background position. If
you have set a wallpaper, the root window displays the wallpaper.
All other windows are children of the root window. Thus, windows of X
clients, frames surrounding them and bars are all windows.
.TP
.B Client
A client is the window provided by X applications (clients) without the
surrounding frame. There're X clients supported, such as xmms, which
request not to be surrounded by a frame, those are called borderless.
.TP
.B Frame
A frame is a parent window of a client or of nested frames in a layout.
Mostly a frame consists of a border and a titlebar. The titlebar
provides tabs, if the frame contains nested frames, otherwise it shows the title
of the surrounded client.
.TP
.B Layout
A layout defines how to arrange nested frames of a frame. See
CUSTOMIZATION section for further details about how such definitions look
like.
.TP
.B Page
A page is a container of the size of the root window which contains
nested frames. It can be compared to workspaces in other window
managers, but with the exception, that a page behaves very similiar to a
frame, except that it reuses the root window as frame window.
.TP
.B Action
An action is an internal interface command or an external process call
in order to reach a specific window management result, ie resizing a
frame or launching a terminal.
.SH CUSTOMIZATION
.B wmii
is customized through the rc script, which manipulates the namespace its
fileserver provides to other processes. This namespace can be accessed
using the
.BR wmir (1)
utility. The default rc script uses this utility to setup wmii and its
components.
.P
A namespace is a filesystem structure consisting of files
and directories, which is specified by its underlying fileserver
process, like
.B wmii.
Such
namespaces can be bound (mounted) to userdefined namespaces using the
.BR wmifs (1)
utility. In the default rc script the namespace provided by wmii, is
bound to
.FN /wmii
.P
There are four actions provided by the
.BR wmir (1)
utility to manipulate your namespace:
.BR create ,
.BR remove ,
.BR write ,
and
.BR read .
.P
.SH AUTHOR
Copyright \(co 2003 - 2005 by Anselm R. Garbe <garbeam (at) gmail (dot) com>
.SH SEE ALSO
.BR wmibar (1),
.BR wmifs (1),
.BR wmimenu (1),
.BR wmikeys (1),
.BR wmiplumb (1),
.BR wmir (1),
.BR wmiwarp (1),
.BR wmii.rc (5).

332
cmd/wm/wmiiwm.h.O Normal file
View File

@ -0,0 +1,332 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <stdio.h>
#include <X11/Xutil.h>
#include "cext.h"
#include "wmii.h"
/* array indexes of page file pointers */
typedef enum {
P_PREFIX,
P_FLOATING_PREFIX,
P_FLOATING_SELECTED,
P_FLOATING_LAYOUT,
P_MANAGED_PREFIX,
P_MANAGED_SELECTED,
P_MANAGED_LAYOUT,
P_MANAGED_SIZE,
P_CTL,
P_NAME,
P_MODE,
P_AUTO_DESTROY,
P_LAST
} PageIndexes;
/* array indexes of frame file pointers */
typedef enum {
F_PREFIX,
F_NAME,
F_CLIENT_PREFIX,
F_CLIENT_SELECTED,
F_CTL,
F_SIZE,
F_BORDER_W,
F_TAB_H,
F_HANDLE_INC,
F_LOCKED,
F_SELECTED_BG_COLOR,
F_SELECTED_TEXT_FONT,
F_SELECTED_FG_COLOR,
F_SELECTED_BORDER_COLOR,
F_NORMAL_BG_COLOR,
F_NORMAL_TEXT_FONT,
F_NORMAL_FG_COLOR,
F_NORMAL_BORDER_COLOR,
F_EVENT_B2PRESS,
F_EVENT_B3PRESS,
F_EVENT_B4PRESS,
F_EVENT_B5PRESS,
F_LAST
} FrameIndexes;
/* array indexes of client file pointers */
typedef enum {
C_PREFIX,
C_ID,
C_NAME,
C_CLASS,
C_INSTANCE,
C_LAST
} ClientIndexes;
/* array indexes of core file pointers */
typedef enum {
CORE_CTL,
CORE_DETACHED_FRAME,
CORE_DETACHED_CLIENT,
CORE_TRANS_COLOR,
CORE_PAGER_SEL_BG_COLOR,
CORE_PAGER_NORM_BG_COLOR,
CORE_PAGER_SEL_BORDER_COLOR,
CORE_PAGER_NORM_BORDER_COLOR,
CORE_PAGER_SEL_TEXT_COLOR,
CORE_PAGER_SEL_TEXT_FONT,
CORE_PAGER_NORM_TEXT_COLOR,
CORE_PAGER_NORM_TEXT_FONT,
CORE_PAGER_SEL_CLIENT_COLOR,
CORE_PAGER_NORM_CLIENT_COLOR,
CORE_PAGER_SEL_CTEXT_COLOR,
CORE_PAGER_SEL_CTEXT_FONT,
CORE_PAGER_NORM_CTEXT_COLOR,
CORE_PAGER_NORM_CTEXT_FONT,
CORE_PAGER_SEL_CBORDER_COLOR,
CORE_PAGER_NORM_CBORDER_COLOR,
CORE_PAGE_MANAGED_SIZE,
CORE_SNAP_VALUE,
CORE_PAGE_SELECTED,
CORE_PAGE_LAYOUT,
CORE_PAGE_TILE_WIDTH,
CORE_EVENT_PAGE_UPDATE,
CORE_EVENT_CLIENT_UPDATE,
CORE_LAST
} CoreIndexes;
#define PROTO_DEL 1
#define DEFAULT_BORDER_W "3"
#define DEFAULT_TAB_H "16"
#define DEFAULT_LAYOUT "tiled"
#define GAP 5
#define ROOT_MASK (SubstructureRedirectMask | SubstructureNotifyMask | ButtonPressMask | ButtonReleaseMask)
#define CLIENT_MASK (SubstructureNotifyMask | PropertyChangeMask | EnterWindowMask)
typedef struct Layout Layout;
typedef struct LayoutImpl LayoutImpl;
typedef struct Frame Frame;
typedef struct Client Client;
typedef struct Page Page;
/* new layout interface:
* /page/[1..n]/0/ floating space
* /page/[1..n]/[1..n]/ layout space
*
* CORE:
* Page **pages;
* Client **clients; -- for event lookup
* Frames **frames; -- for event lookup
*
* PAGE:
* int sel_layout;
* Layout **layout; -- layout[0] == floating
*
* LAYOUT:
* Frame **stack;
* Frame **frames;
*
* FRAME:
* Client **stack;
* Client **clients;
*
* CLIENT:
* Frame *frame;
*
*/
struct LayoutImpl {
char *name;
void (*init) (Page *, int argc, char **argv); /* called on new layout */
void (*deinit) (Page *); /* called when layout gets
* released */
void (*arrange) (Page *); /* called when area is
* resized */
void (*attach) (Page *, Client *); /* called on attach */
void (*detach) (Page *, Client *, int, int); /* called on detach */
void (*resize) (Frame *, XRectangle *, XPoint * pt); /* called after resize */
void (*aux) (Frame *, char *); /* auxillary extension
* for layout specific
* stuff */
};
struct Layout {
char *name;
void (*init) (Page *); /* called on new layout */
void (*deinit) (Page *); /* called when layout gets
* released */
void (*arrange) (Page *); /* called when area is
* resized */
void (*manage) (Frame *); /* called on attach */
void (*unmanage) (Frame *); /* called on detach */
void (*resize) (Frame *, XRectangle *, XPoint * pt);
/* called after resize/move */
Frame *(*select) (Frame *, char *); /* called to select a
* frame */
};
struct Page {
Client **clients;
Frame **floating;
Frame **managed;
Frame **floating_stack;
Frame **managed_stack;
XRectangle managed_rect;
File *files[P_LAST];
Layout *layout;
void *aux; /* free pointer for layout backends */
};
struct Frame {
int id;
int floating;
Window win;
GC gc;
XRectangle managed_rect;
XRectangle floating_rect;
XRectangle rect;
Cursor cursor;
Client **clients;
int sel;
File *files[F_LAST];
Page *page;
Page *before_max;
void *aux; /* free pointer for layout backends */
};
struct Client {
int id;
int proto;
unsigned int border;
Window win;
Window trans;
XRectangle rect;
XSizeHints size;
Frame *frame;
File *files[C_LAST];
};
/* global variables */
Display *dpy;
IXPServer *ixps;
int screen_num;
Window root;
Window transient;
XRectangle rect;
Client **detached;
Page **pages;
int sel_page;
Frame **frames;
Client **clients;
XColor xorcolor;
GC xorgc;
GC transient_gc;
Client *sel;
Layout **layouts;
Atom wm_state;
Atom wm_change_state;
Atom wm_protocols;
Atom wm_delete;
Atom motif_wm_hints;
Atom net_wm_desktop;
Cursor normal_cursor;
Cursor resize_cursor;
Cursor move_cursor;
Cursor drag_cursor;
Cursor w_cursor;
Cursor e_cursor;
Cursor n_cursor;
Cursor s_cursor;
Cursor nw_cursor;
Cursor ne_cursor;
Cursor sw_cursor;
Cursor se_cursor;
/* default file pointers */
File *defaults[F_LAST];
File *core_files[CORE_LAST];
unsigned int valid_mask, num_lock_mask;
/* client.c */
Client *alloc_client(Window w);
void _init_client(Client * c, XWindowAttributes * wa);
void free_client(Client * c);
void configure_client(Client * c);
void handle_client_property(Client * c, XPropertyEvent * e);
void close_client(Client * c);
void draw_client(Client * c);
void draw_clients(Frame * f);
void gravitate(Client * c, unsigned int tabh, unsigned int bw, int invert);
int manage_class_instance(Client * c);
void grab_client(Client * c, unsigned long mod, unsigned int button);
void ungrab_client(Client * c, unsigned long mod, unsigned int button);
void hide_client(Client * c);
void show_client(Client * c);
void reparent_client(Client * c, Window w, int x, int y);
/* core.c */
void invoke_core_event(File * f);
void run_action(File * f, void *obj, Action * acttbl);
void scan_wins();
Client *win_to_client(Window w);
Frame *win_to_frame(Window w);
int win_proto(Window w);
int win_state(Window w);
void handle_after_write(IXPServer * s, File * f);
void focus_page(Page * p, int raise, int down);
void detach(Frame * f, int client_destroyed);
int comp_obj(void *f1, void *f2);
void destroy_page(Page * p);
void set_client_state(Client * c, int state);
/* frame.c */
Frame *alloc_frame(XRectangle * r, int add_frame_border, int floating);
void free_frame(Frame * f);
void
resize_frame(Frame * f, XRectangle * r, XPoint * pt,
int ignore_layout);
void draw_frame(Frame * f);
void handle_frame_buttonpress(XButtonEvent * e, Frame * f);
void attach_client(Client * c);
void attach_Cliento_frame(Frame * f, Client * c);
void detach_client_from_frame(Client * c, int unmapped, int destroyed);
void draw_tab(Frame * f, char *text, int x, int y, int w, int h, int sel);
void focus_client(Client * c, int raise, int up);
int is_managed_frame(Frame * f);
XRectangle *rect_of_frame(Frame * f);
/* event.c */
void init_event_hander();
void check_event(Connection * c);
/* mouse.c */
void mouse_resize(Frame * f, Align align);
void mouse_move(Frame * f);
Cursor cursor_for_motion(Frame * f, int x, int y);
Align cursor_to_align(Cursor cursor);
Align xy_to_align(XRectangle * rect, int x, int y);
void drop_move(Frame * f, XRectangle * new, XPoint * pt);
/* page.c */
Page *alloc_page(char *autodestroy);
void free_page(Page * p);
Frame *get_selected(Page * p);
int is_selected(Frame * f);
XRectangle *rectangles(unsigned int *num);
void hide_page(Page * p);
void show_page(Page * p);
void attach_Frameo_page(Page * p, Frame * f, int managed);
void detach_frame_from_page(Frame * f, int ignore_focus_and_destroy);
void draw_page(Page * p);
void focus_frame(Frame * f, int raise, int up, int down);
Layout *get_layout(char *name);
int is_managed_mode(Page * p);
void toggle_frame(Frame * f);
Frame *select_floating(Page * p, Frame * f, char *what);
/* layout.c */
void init_layouts();

43
cmd/wmibar.1 Normal file
View File

@ -0,0 +1,43 @@
.TH WMIBAR 1 wmii-3
.SH NAME
wmibar \- window manager improved 2 bar
.SH SYNOPSIS
.B wmibar
.B \-s
.I socketfile
.SH DESCRIPTION
.SS Overview
.B wmibar
is a generic and highly customizable bar for the X Window System,
originally designed for
.BR wmii (1).
It supports arbitrary sized labels with arbitrary styles on a per label
basis and with button click events.
Like wmii,
.B wmibar
also implements a socket-based fileserver, which is accessed to configure and
interoperate with other components.
.SS Options
.TP
.BI \-s " socketfile"
specifies the socketfile that
.B wmibar
should create.
.TP
.B \-v
prints version information to stderr, then exits.
.SS Customization
.B wmibar
is customized through manipulating its filesystem namespace.
In the default setup of
.BR wmii (1)
the namespace of
.B wmibar
can be found in /bar.
.SH SEE ALSO
.BR wmifs (1),
.BR wmii (1),
.BR wmiiwm (1),
.BR wmikeys (1),
.BR wmimenu (1),
.BR wmir (1)

550
cmd/wmibar.c Normal file
View File

@ -0,0 +1,550 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <X11/Xatom.h>
#include <X11/cursorfont.h>
#include <X11/Xproto.h>
#include <X11/Xutil.h>
#include "wmii.h"
#include <cext.h>
/* array indexes for file pointers */
typedef enum {
B_CTL,
B_NEW,
B_EXPANDABLE,
B_GEOMETRY,
B_FONT,
B_FG_COLOR,
B_BORDER_COLOR,
B_BG_COLOR,
B_LAST
} BarIndexes;
typedef struct {
File *root;
Draw d;
} Item;
static IXPServer *ixps = 0;
static Display *dpy;
static GC gc;
static Window win;
static XRectangle rect;
static XRectangle brect;
static int screen_num;
static int displayed = 0;
static char *sockfile = 0;
static File *files[B_LAST];
static Item **items = 0;
static unsigned int id = 0;
static Pixmap pmap;
static Draw zero_draw = {0};
static void draw_bar(void *obj, char *arg);
static void quit(void *obj, char *arg);
static void display(void *obj, char *arg);
static void reset(void *obj, char *arg);
static void _destroy(void *obj, char *arg);
static void handle_after_write(IXPServer * s, File * f);
static Action acttbl[] = {
{"quit", quit},
{"display", display},
{"update", draw_bar},
{"reset", reset},
{"destroy", _destroy},
{0, 0}
};
static char *version[] = {
"wmibar - window manager improved bar - " VERSION "\n"
" (C)opyright MMIV-MMV Anselm R. Garbe\n", 0
};
static void
usage()
{
fprintf(stderr, "%s",
"usage: wmibar -s <socket file> [-v] [<x>,<y>,<width>,<height>]\n"
" -s socket file\n" " -v version info\n");
exit(1);
}
/**
* <path>/data "<txt value>"
* <path>/fgcolor "#RRGGBBAA"
* <path>/bgcolor "#RRGGBBAA"
* <path>/bordercolor "#RRGGBBAA"
* <path>/b1press "<command>"
* <path>/b2press "<command>"
* <path>/b3press "<command>"
* <path>/b4press "<command>"
* <path>/b5press "<command>"
*/
static void
create_label(char *path)
{
File *f;
char file[MAX_BUF];
int i;
snprintf(file, MAX_BUF, "%s/data", path);
f = ixp_create(ixps, file);
f->after_write = handle_after_write;
snprintf(file, MAX_BUF, "%s/fgcolor", path);
wmii_create_ixpfile(ixps, file, files[B_FG_COLOR]->content);
snprintf(file, MAX_BUF, "%s/bgcolor", path);
wmii_create_ixpfile(ixps, file, files[B_BG_COLOR]->content);
snprintf(file, MAX_BUF, "%s/bordercolor", path);
wmii_create_ixpfile(ixps, file, files[B_BORDER_COLOR]->content);
for (i = 1; i < 6; i++) { /* 5 buttons events */
snprintf(file, MAX_BUF, "%s/b%dpress", path, i);
ixp_create(ixps, file);
}
}
static void
_destroy(void *obj, char *arg)
{
char buf[512];
if (!arg)
return;
snprintf(buf, sizeof(buf), "/%s", arg);
ixps->remove(ixps, buf);
draw_bar(0, 0);
}
static void
reset(void *obj, char *arg)
{
int i;
char buf[512];
for (i = 0; i < id; i++) {
snprintf(buf, sizeof(buf), "/%d", i + 1);
ixps->remove(ixps, buf);
}
id = 0;
draw_bar(0, 0);
}
static void
quit(void *obj, char *arg)
{
ixps->runlevel = SHUTDOWN;
}
static void
display(void *obj, char *arg)
{
if (!arg)
return;
displayed = _strtonum(arg, 0, 1);
if (displayed) {
XMapRaised(dpy, win);
draw_bar(0, 0);
} else {
XUnmapWindow(dpy, win);
XSync(dpy, False);
}
}
static void
init_draw_label(char *path, Draw * d)
{
char buf[MAX_BUF];
File *f;
/* text stuff */
snprintf(buf, MAX_BUF, "%s/data", path);
f = ixp_walk(ixps, buf);
d->data = f->content;
/* style stuff */
snprintf(buf, MAX_BUF, "%s/fgcolor", path);
f = ixp_walk(ixps, buf);
d->fg = blitz_loadcolor(dpy, screen_num, f->content);
snprintf(buf, MAX_BUF, "%s/bgcolor", path);
f = ixp_walk(ixps, buf);
d->bg = blitz_loadcolor(dpy, screen_num, f->content);
snprintf(buf, MAX_BUF, "%s/bordercolor", path);
f = ixp_walk(ixps, buf);
d->border = blitz_loadcolor(dpy, screen_num, f->content);
}
static void
init_item(char *path, Item * i)
{
i->d = zero_draw;
i->root = ixp_walk(ixps, path);
i->d.gc = gc;
i->d.drawable = pmap;
i->d.rect = brect;
i->d.rect.y = 0;
init_draw_label(path, &i->d);
}
static int
comp_str(const void *s1, const void *s2)
{
return strcmp(*(char **) s1, *(char **) s2);
}
static void
draw()
{
unsigned int n = 0, i, w, xoff = 0;
XFontStruct *font;
unsigned expandable = 0;
char buf[32];
if (!items)
return;
n = count_items((void **) items);
font = blitz_getfont(dpy, files[B_FONT]->content);
expandable = _strtonum(files[B_EXPANDABLE]->content, 1, id);
snprintf(buf, sizeof(buf), "/%d", expandable);
if (!ixp_walk(ixps, buf))
expandable = 0;
w = 0;
/* precalc */
for (i = 0; expandable && items[i]; i++)
if (i + 1 != expandable) {
items[i]->d.rect.width = brect.height;
if (items[i]->d.data) {
if (!strncmp(items[i]->d.data, "%m:", 3))
/* meter */
items[i]->d.rect.width = brect.height / 2;
else
items[i]->d.rect.width +=
XTextWidth(font, items[i]->d.data, strlen(items[i]->d.data));
}
w += items[i]->d.rect.width;
}
if (!expandable || w > brect.width) {
/* failsafe mode, give all labels same width */
w = brect.width / n;
for (i = 0; items[i]; i++)
items[i]->d.rect.width = w;
items[i - 1]->d.rect.width = brect.width - items[i - 1]->d.rect.x;
} else
items[expandable - 1]->d.rect.width = brect.width - w;
for (i = 0; items[i]; i++) {
items[i]->d.font = font;
items[i]->d.rect.x = xoff;
xoff += items[i]->d.rect.width;
if (items[i]->d.data && !strncmp(items[i]->d.data, "%m:", 3))
blitz_drawmeter(dpy, &items[i]->d);
else
blitz_drawlabel(dpy, &items[i]->d);
}
XCopyArea(dpy, pmap, win, gc, 0, 0, brect.width, brect.height, 0, 0);
XSync(dpy, False);
XFreeFont(dpy, font);
}
static void
draw_bar(void *obj, char *arg)
{
File *label = 0;
unsigned int i = 0, n = 0;
Item *item;
char buf[512];
if (!displayed)
return;
if (items) {
for (i = 0; items[i]; i++) {
free(items[i]);
}
free(items);
}
items = 0;
snprintf(buf, sizeof(buf), "%s", "/1");
label = ixp_walk(ixps, buf);
if (!label) {
Draw d = {0};
/* default stuff */
d.gc = gc;
d.drawable = pmap;
d.rect.width = brect.width;
d.rect.height = brect.height;
d.bg = blitz_loadcolor(dpy, screen_num, files[B_BG_COLOR]->content);
d.fg = blitz_loadcolor(dpy, screen_num, files[B_FG_COLOR]->content);
d.border = blitz_loadcolor(dpy, screen_num, files[B_BORDER_COLOR]->content);
blitz_drawlabelnoborder(dpy, &d);
} else {
File *f;
char **paths = 0;
/*
* take order into account, directory names are used in
* alphabetical order
*/
n = 0;
for (f = label; f; f = f->next)
n++;
paths = emalloc(sizeof(char *) * n);
i = 0;
for (f = label; f; f = f->next)
paths[i++] = f->name;
qsort(paths, n, sizeof(char *), comp_str);
for (i = 0; i < n; i++) {
snprintf(buf, sizeof(buf), "/%s", paths[i]);
item = emalloc(sizeof(Item));
items = (Item **) attach_item_end((void **) items, item, sizeof(Item *));
init_item(buf, item);
}
draw();
free(paths);
}
}
static Item *
get_item_for_file(File * f)
{
int i;
for (i = 0; items && items[i]; i++)
if (items[i]->root == f)
return items[i];
return 0;
}
static void
handle_buttonpress(XButtonPressedEvent * e)
{
File *p;
char buf[MAX_BUF];
char path[512];
int i;
for (i = 0; items && items[i]; i++) {
if (blitz_ispointinrect(e->x, e->y, &items[i]->d.rect)) {
path[0] = '\0';
wmii_get_ixppath(items[i]->root, path, sizeof(path));
snprintf(buf, MAX_BUF, "%s/b%upress", path, e->button);
if ((p = ixp_walk(ixps, buf)))
if (p->content)
spawn(dpy, p->content);
return;
}
}
}
static void
check_event(Connection * e)
{
XEvent ev;
while (XPending(dpy)) {
XNextEvent(dpy, &ev);
switch (ev.type) {
case ButtonPress:
handle_buttonpress(&ev.xbutton);
break;
case Expose:
if (ev.xexpose.count == 0) {
/* XRaiseWindow(dpy, win); */
draw_bar(0, 0);
}
break;
default:
break;
}
}
}
static void
update_geometry(char *size)
{
blitz_strtorect(dpy, &rect, &brect, size);
if (!brect.width)
brect.width = DisplayWidth(dpy, screen_num);
if (!brect.height)
brect.height = 20;
}
static void
handle_after_write(IXPServer * s, File * f)
{
int i;
size_t len;
Item *item;
char buf[512];
buf[0] = '\0';
if (!strncmp(f->name, "data", 5)) {
if ((item = get_item_for_file(f->parent))) {
wmii_get_ixppath(f->parent, buf, sizeof(buf));
init_draw_label(buf, &item->d);
draw();
}
} else if (files[B_GEOMETRY] == f) {
char *geom = files[B_GEOMETRY]->content;
if (geom && strrchr(geom, ',')) {
update_geometry(geom);
XMoveResizeWindow(dpy, win, brect.x, brect.y,
brect.width, brect.height);
XSync(dpy, False);
pmap = XCreatePixmap(dpy, win, brect.width, brect.height,
DefaultDepth(dpy, screen_num));
XSync(dpy, False);
draw_bar(0, 0);
}
} else if (files[B_CTL] == f) {
for (i = 0; acttbl[i].name; i++) {
len = strlen(acttbl[i].name);
if (!strncmp(acttbl[i].name, (char *) f->content, len)) {
if (strlen(f->content) > len) {
acttbl[i].func(0, &((char *) f->content)[len + 1]);
} else {
acttbl[i].func(0, 0);
}
break;
}
}
}
check_event(0);
}
static void
handle_before_read(IXPServer * s, File * f)
{
char buf[64];
if (f == files[B_GEOMETRY]) {
snprintf(buf, sizeof(buf), "%d,%d,%d,%d", brect.x, brect.y,
brect.width, brect.height);
if (f->content)
free(f->content);
f->content = strdup(buf);
f->size = strlen(buf);
} else if (f == files[B_NEW]) {
snprintf(buf, sizeof(buf), "%d", ++id);
if (f->content)
free(f->content);
f->content = strdup(buf);
f->size = strlen(buf);
create_label(buf);
draw_bar(0, 0);
}
}
static void
run(char *geom)
{
XSetWindowAttributes wa;
XGCValues gcv;
/* init */
if (!(files[B_CTL] = ixp_create(ixps, "/ctl"))) {
perror("wmibar: cannot connect IXP server");
exit(1);
}
files[B_CTL]->after_write = handle_after_write;
files[B_NEW] = ixp_create(ixps, "/new");
files[B_NEW]->before_read = handle_before_read;
files[B_FONT] = wmii_create_ixpfile(ixps, "/font", BLITZ_FONT);
files[B_BG_COLOR] = wmii_create_ixpfile(ixps, "/bgcolor", BLITZ_NORM_BG_COLOR);
files[B_FG_COLOR] = wmii_create_ixpfile(ixps, "/fgcolor", BLITZ_NORM_FG_COLOR);
files[B_BORDER_COLOR] = wmii_create_ixpfile(ixps, "/bordercolor", BLITZ_NORM_BORDER_COLOR);
files[B_GEOMETRY] = ixp_create(ixps, "/geometry");
files[B_GEOMETRY]->before_read = handle_before_read;
files[B_GEOMETRY]->after_write = handle_after_write;
files[B_EXPANDABLE] = ixp_create(ixps, "/expandable");
wa.override_redirect = 1;
wa.background_pixmap = ParentRelative;
wa.event_mask = ExposureMask | ButtonPressMask | SubstructureRedirectMask | SubstructureNotifyMask;
brect.x = brect.y = brect.width = brect.height = 0;
rect.x = rect.y = 0;
rect.width = DisplayWidth(dpy, screen_num);
rect.height = DisplayHeight(dpy, screen_num);
update_geometry(geom);
win = XCreateWindow(dpy, RootWindow(dpy, screen_num), brect.x, brect.y,
brect.width, brect.height, 0, DefaultDepth(dpy,
screen_num),
CopyFromParent, DefaultVisual(dpy, screen_num),
CWOverrideRedirect | CWBackPixmap | CWEventMask,
&wa);
XDefineCursor(dpy, win, XCreateFontCursor(dpy, XC_left_ptr));
XSync(dpy, False);
gcv.function = GXcopy;
gcv.graphics_exposures = False;
gc = XCreateGC(dpy, win, 0, 0);
pmap =
XCreatePixmap(dpy, win, brect.width, brect.height,
DefaultDepth(dpy, screen_num));
/* main event loop */
run_server_with_fd_support(ixps, ConnectionNumber(dpy),
check_event, 0);
deinit_server(ixps);
XFreePixmap(dpy, pmap);
XFreeGC(dpy, gc);
XCloseDisplay(dpy);
}
static int
dummy_error_handler(Display * dpy, XErrorEvent * err)
{
return 0;
}
int
main(int argc, char *argv[])
{
char geom[64];
int i;
/* command line args */
for (i = 1; (i < argc) && (argv[i][0] == '-'); i++) {
switch (argv[i][1]) {
case 'v':
fprintf(stdout, "%s", version[0]);
exit(0);
break;
case 's':
if (i + 1 < argc)
sockfile = argv[++i];
else
usage();
break;
default:
usage();
break;
}
}
dpy = XOpenDisplay(0);
if (!dpy) {
fprintf(stderr, "%s", "wmibar: cannot open display\n");
exit(1);
}
XSetErrorHandler(dummy_error_handler);
screen_num = DefaultScreen(dpy);
geom[0] = '\0';
if (argc > i)
_strlcpy(geom, argv[i], sizeof(geom));
ixps = wmii_setup_server(sockfile);
run(geom);
return 0;
}

472
cmd/wmibar2.c Normal file
View File

@ -0,0 +1,472 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <X11/Xatom.h>
#include <X11/cursorfont.h>
#include <X11/Xproto.h>
#include <X11/Xutil.h>
#include "../libixp2/ixp.h"
#include "blitz.h"
#include "cext.h"
/*
* filesystem specification
* / Droot
* /display Fdisplay 'top', 'bottom', 'none'
* /font Ffont <xlib font name>
* /new Fnew returns id of new item
* /default/ Ditem
* /default/bNpress Fevent <command, gets executed>
* /default/bgcolor Fcolor <#RRGGBB, #RGB>
* /default/fgcolor Fcolor <#RRGGBB, #RGB>
* /default/bordercolor Fcolor <#RRGGBB, #RGB>
* /1/ Ditem
* /1/data Fdata <arbitrary data which gets displayed>
* /1/bNpress Fevent <command, gets executed>
* /1/bgcolor Fcolor <#RRGGBB, #RGB>
* /1/fgcolor Fcolor <#RRGGBB, #RGB>
* /1/bordercolor Fcolor <#RRGGBB, #RGB> ...
*/
enum { /* 8-bit qid.path.type */
Droot,
Ditem,
Fdisplay,
Fnew,
Fdata, /* data to display */
Fevent,
Fcolor,
Ffont
};
#define NONE (u16)0xffff
typedef struct {
char *name;
u8 type;
} QFile;
static QFile qfilelist[] = {
{"display", Fdisplay},
{"font", Ffont},
{"new", Fnew},
{"data", Fdata},
{"bgcolor", Fcolor},
{"fgcolor", Fcolor},
{"bordercolor", Fcolor},
{"b1press", Fevent},
{"b2press", Fevent},
{"b3press", Fevent},
{"b4press", Fevent},
{"b5press", Fevent},
{0, 0},
};
typedef struct {
u32 fid;
Qid qid;
} Map;
typedef struct {
int id;
char text[256];
int value;
unsigned long bg;
unsigned long fg;
unsigned long border[4];
XFontStruct *font;
char event[5][256];
} Item;
static Item **items = 0;
static char *sockfile = 0;
static pid_t mypid = 0;
static IXPServer srv = {0};
static Qid root_qid;
static Display *dpy;
static int screen_num;
static char *align = 0;
static char *font = 0;
/*
static GC gc;
static Window win;
static XRectangle geom;
static int mapped = 0;
static Pixmap pmap;
static Draw zero_draw = { 0 };
*/
static char *version[] = {
"wmibar - window manager improved bar - " VERSION "\n"
" (C)opyright MMIV-MMV Anselm R. Garbe\n", 0
};
static void
usage()
{
fprintf(stderr, "%s %d",
"usage: wmibar -s <socket file> [-v]\n"
" -s socket file\n" " -v version info\n",
NONE);
exit(1);
}
static int
dummy_error_handler(Display * dpy, XErrorEvent * err)
{
return 0;
}
static void
exit_cleanup()
{
if (mypid == getpid())
unlink(sockfile);
}
static u64
make_qpath(u8 type, u16 item, u16 file)
{
return ((u64) file << 24) | ((u64) item << 8) | (u64) type;
}
static u8
qpath_type(u64 path)
{
return path & 0xff;
}
static u16
qpath_item(u64 path)
{
return (path >> 8) & 0xffff;
}
/*
static u16
qpath_file(u64 path)
{
return (path >> 24) & 0xffff;
}
*/
static Map *
fid_to_map(Map ** maps, u32 fid)
{
u32 i;
for (i = 0; maps && maps[i]; i++)
if (maps[i]->fid == fid)
return maps[i];
return nil;
}
static int
qfile_index(char *name, u16 * index)
{
int i;
for (i = 0; qfilelist[i].name; i++)
if (!strncmp(name, qfilelist[i].name, strlen(qfilelist[i].name))) {
*index = i;
return TRUE;
}
return FALSE;
}
static int
make_qid(Qid * dir, char *wname, Qid * new)
{
u16 idx;
const char *errstr;
if (dir->type != IXP_QTDIR)
return FALSE;
new->version = 0;
if (!qfile_index(wname, &idx)) {
new->type = IXP_QTDIR;
if (!strncmp(wname, "..", 3)) {
*new = root_qid;
return TRUE;
} else if (!strncmp(wname, "default", 8)) {
new->path = make_qpath(Ditem, 0, NONE);
return TRUE;
}
/* check if wname is a number, otherwise file not found */
idx = (u16) __strtonum(wname, 1, 0xffff, &errstr);
if (errstr || count_items((void **) items) < idx)
return FALSE;
/* found */
new->path = make_qpath(Ditem, idx, NONE);
} else {
new->type = IXP_QTFILE;
new->path =
make_qpath(qfilelist[idx].type, qpath_item(dir->path), idx);
}
return TRUE;
}
static int
attach(IXPServer * s, IXPConn * c)
{
Map *map = emalloc(sizeof(Map));
fprintf(stderr, "attaching %d %s %s\n", s->fcall.afid, s->fcall.uname, s->fcall.aname);
map->qid = root_qid;
map->fid = s->fcall.fid;
c->aux = (Map **) attach_item_begin((void **) c->aux, map, sizeof(Map *));
s->fcall.id = RATTACH;
s->fcall.qid = root_qid;
return TRUE;
}
static int
walk(IXPServer * s, IXPConn * c)
{
u16 nwqid = 0;
Qid qid;
Map *map;
fprintf(stderr, "%s", "walking\n");
if (!(map = fid_to_map(c->aux, s->fcall.fid))) {
s->errstr = "no directory associated with fid";
return FALSE;
}
if (s->fcall.fid != s->fcall.newfid
&& (fid_to_map(c->aux, s->fcall.newfid))) {
s->errstr = "fid alreay in use";
return FALSE;
}
if (s->fcall.nwname) {
qid = map->qid;
for (nwqid = 0; (nwqid < s->fcall.nwname)
&& make_qid(&qid, s->fcall.wname[nwqid],
&s->fcall.wqid[nwqid]); nwqid++)
qid = s->fcall.wqid[nwqid];
if (!nwqid) {
s->errstr = "file not found";
return FALSE;
}
}
/*
* following condition is required by 9P, a fid will only be valid if
* the walk was complete
*/
if (nwqid == s->fcall.nwname) {
if (s->fcall.fid == s->fcall.newfid) {
c->aux =
(Map **) detach_item((void **) c->aux, map, sizeof(Map *));
free(map);
}
map = emalloc(sizeof(Map));
map->qid = qid;
map->fid = s->fcall.newfid;
c->aux =
(Map **) attach_item_begin((void **) c->aux, map,
sizeof(Map *));
}
s->fcall.id = RWALK;
s->fcall.nwqid = nwqid;
return TRUE;
}
static int
_open(IXPServer * s, IXPConn * c)
{
Map *map = fid_to_map(c->aux, s->fcall.fid);
fprintf(stderr, "%s", "opening\n");
if (!map) {
s->errstr = "invalid fid";
return FALSE;
}
if ((s->fcall.mode != IXP_OREAD) && (s->fcall.mode != IXP_OWRITE)) {
s->errstr = "mode not supported";
return FALSE;
}
s->fcall.id = ROPEN;
s->fcall.qid = map->qid;
s->fcall.iounit =
s->fcall.maxmsg - (sizeof(u8) + sizeof(u16) + 2 * sizeof(u32));
return TRUE;
}
static int
_read(IXPServer * s, IXPConn * c)
{
Map *map = fid_to_map(c->aux, s->fcall.fid);
Stat stat = {0};
u8 *p;
fprintf(stderr, "%s", "reading\n");
if (!map) {
s->errstr = "invalid fid";
return FALSE;
}
stat.mode = 0xff;
stat.atime = stat.mtime = time(0);
_strlcpy(stat.uid, getenv("USER"), sizeof(stat.uid));
_strlcpy(stat.gid, getenv("USER"), sizeof(stat.gid));
_strlcpy(stat.muid, getenv("USER"), sizeof(stat.muid));
fprintf(stderr, "%d\n", qpath_item(map->qid.path));
switch (qpath_type(map->qid.path)) {
default:
case Droot:
p = s->fcall.data;
_strlcpy(stat.name, "display", sizeof(stat.name));
stat.length = strlen(align);
make_qid(&root_qid, "display", &stat.qid);
stat.size = ixp_sizeof_stat(&stat);
s->fcall.count = stat.size;
p = ixp_enc_stat(p, &stat);
_strlcpy(stat.name, "font", sizeof(stat.name));
stat.length = strlen(font);
make_qid(&root_qid, "font", &stat.qid);
stat.size = ixp_sizeof_stat(&stat);;
s->fcall.count += stat.size;
p = ixp_enc_stat(p, &stat);
_strlcpy(stat.name, "new", sizeof(stat.name));
stat.length = 0;
make_qid(&root_qid, "new", &stat.qid);
stat.size = ixp_sizeof_stat(&stat);;
s->fcall.count += stat.size;
p = ixp_enc_stat(p, &stat);
s->fcall.id = RREAD;
fprintf(stderr, "%d msize\n", s->fcall.count);
break;
case Ditem:
break;
case Fdisplay:
break;
case Fnew:
break;
case Fdata:
break;
case Fevent:
break;
case Fcolor:
break;
case Ffont:
break;
}
return TRUE;
}
static int
_write(IXPServer * s, IXPConn * c)
{
return FALSE;
}
static int
clunk(IXPServer * s, IXPConn * c)
{
Map *map = fid_to_map(c->aux, s->fcall.fid);
if (!map) {
s->errstr = "invalid fid";
return FALSE;
}
c->aux = (Map **) detach_item((void **) c->aux, map, sizeof(Map *));
free(map);
s->fcall.id = RCLUNK;
return TRUE;
}
static void
freeconn(IXPServer * s, IXPConn * c)
{
Map **maps = c->aux;
if (maps) {
int i;
for (i = 0; maps[i]; i++)
free(maps[i]);
free(maps);
}
}
static IXPTFunc funcs[] = {
{TVERSION, ixp_server_tversion},
{TATTACH, attach},
{TWALK, walk},
{TOPEN, _open},
{TREAD, _read},
{TWRITE, _write},
{TCLUNK, clunk},
{0, 0}
};
int
main(int argc, char *argv[])
{
int i;
Item *item;
/* command line args */
for (i = 1; (i < argc) && (argv[i][0] == '-'); i++) {
switch (argv[i][1]) {
case 'v':
fprintf(stdout, "%s", version[0]);
exit(0);
break;
case 's':
if (i + 1 < argc)
sockfile = argv[++i];
else
usage();
break;
default:
usage();
break;
}
}
dpy = XOpenDisplay(0);
if (!dpy) {
fprintf(stderr, "%s", "wmibar: cannot open display\n");
exit(1);
}
XSetErrorHandler(dummy_error_handler);
screen_num = DefaultScreen(dpy);
if (!ixp_server_init(&srv, sockfile, funcs, freeconn)) {
fprintf(stderr, "wmibar: fatal: %s\n", srv.errstr);
exit(1);
}
root_qid.type = IXP_QTDIR;
root_qid.version = 0;
root_qid.path = make_qpath(Droot, NONE, NONE);
mypid = getpid();
atexit(exit_cleanup);
/* default item settings */
item = emalloc(sizeof(Item));
item->id = 0;
item->text[0] = '\0';
item->value = 0;
align = "bottom";
font = "fixed";
ixp_server_loop(&srv);
if (srv.errstr) {
fprintf(stderr, "wmibar: fatal: %s\n", srv.errstr);
ixp_server_deinit(&srv);
exit(1);
}
ixp_server_deinit(&srv);
return 0;
}

38
cmd/wmifs.1 Normal file
View File

@ -0,0 +1,38 @@
.TH WMIFS 1 wmii-3
.SH NAME
wmifs \- window manager improved 2 filesystem
.SH SYNOPSIS
.B wmifs
.B \-s
.I socketfile
.SH DESCRIPTION
.SS Overview
.B wmifs
is a socket-based fileserver which is used for binding other fileserver
filesystems to a specific namespace. It routes filesystem accesses to
the specific fileserver and behaves to the outside as one master
filesystem.
.SS Options
.TP
.BI \-s " socketfile"
specifies the socketfile that
.B wmifs
should create.
.TP
.B \-v
prints version information to stderr, then exits.
.SS Customization
.B wmifs
is customized through manipulating its filesystem namespace.
In the default setup of
.BR wmii (1)
the namespace of
.B wmifs
can be found in /.
.SH SEE ALSO
.BR wmibar (1),
.BR wmii (1),
.BR wmiiwm (1),
.BR wmikeys (1),
.BR wmimenu (1),
.BR wmir (1)

417
cmd/wmifs.c Normal file
View File

@ -0,0 +1,417 @@
/*
* (C)opyright MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "wmii.h"
#include <cext.h>
/* array indexes for file pointers */
typedef enum {
F_CTL,
F_LAST
} FsIndexes;
typedef struct Route Route;
struct Route {
File dest;
int src;
};
typedef struct Bind Bind;
struct Bind {
IXPClient *client;
Route route[MAX_CONN * MAX_OPEN_FILES];
File *mount;
char *prefix;
};
static Bind zero_bind = {0};
static Display *dpy;
static IXPServer *ixps;
static char *sockfile = 0;
static File *files[F_LAST];
static Bind **bindings = 0;
static void quit(void *obj, char *arg);
static void bind(void *obj, char *arg);
static void unbind(void *obj, char *arg);
static Bind *path_to_bind(char *path);
static Action acttbl[] = {
{"quit", quit},
{"unbind", unbind},
{"bind", bind},
{0, 0}
};
static char *version[] = {
"wmifs - window manager improved filesystem - " VERSION "\n"
" (C)opyright MMV Anselm R. Garbe\n", 0
};
static void
usage()
{
fprintf(stderr,
"usage: wmifs -s <socket file> [-v]\n"
" -s socket file\n" " -v version info\n");
exit(1);
}
static void
quit(void *obj, char *arg)
{
int i;
for (i = 0; bindings && bindings[i]; i++) {
if (bindings[i]->mount) {
bindings[i]->mount->content = 0;
ixp_remove(ixps, bindings[i]->prefix);
if (ixps->errstr)
fprintf(stderr, "wmifs: error on quit: remove %s: %s\n",
bindings[i]->prefix, ixps->errstr);
}
/* free stuff */
if (bindings[i]->prefix)
free(bindings[i]->prefix);
free(bindings[i]);
}
free(bindings);
bindings = 0;
ixps->runlevel = SHUTDOWN;
}
static void
_unbind(Bind * b)
{
bindings =
(Bind **) detach_item((void **) bindings, b, sizeof(Bind *));
if (b->mount) {
b->mount->content = 0;
ixp_remove(ixps, b->prefix);
if (ixps->errstr)
fprintf(stderr, "wmifs: error on _unbind: remove %s: %s\n",
b->prefix, ixps->errstr);
}
/* free stuff */
deinit_client(b->client);
free(b->prefix);
free(b);
}
static void
unbind(void *obj, char *arg)
{
Bind *b = path_to_bind(arg);
if (!b) {
fprintf(stderr, "wmifs: unbind: '%s' no such path\n", arg);
return;
}
_unbind(b);
}
static void
bind(void *obj, char *arg)
{
Bind *b = 0;
char cmd[1024];
char *sfile;
if (!arg)
return;
_strlcpy(cmd, arg, sizeof(cmd));
sfile = strchr(cmd, ' ');
if (!sfile) {
fprintf(stderr,
"wmifs: bind: '%s' without socket argument, ignoring\n",
arg);
return; /* shortcut with empty argument */
}
*sfile = '\0';
sfile++;
if (*sfile == '\0') {
fprintf(stderr,
"wmifs: bind: '%s' without socket argument, ignoring\n",
arg);
return; /* shortcut with empty argument */
}
b = emalloc(sizeof(Bind));
*b = zero_bind;
b->client = init_client(sfile);
if (!b->client) {
fprintf(stderr,
"wmifs: bind: cannot connect to server '%s', ignoring\n",
sfile);
free(b);
return;
}
b->prefix = strdup(cmd);
b->mount = ixp_create(ixps, b->prefix);
b->mount->content = b->mount; /* shall be a directory */
bindings =
(Bind **) attach_item_end((void **) bindings, b, sizeof(Bind *));
}
static void
handle_after_write(IXPServer * s, File * f)
{
int i;
size_t len;
for (i = 0; acttbl[i].name; i++) {
len = strlen(acttbl[i].name);
if (!strncmp(acttbl[i].name, (char *) f->content, len)) {
if (strlen(f->content) > len) {
acttbl[i].func(0, &((char *) f->content)[len + 1]);
} else {
acttbl[i].func(0, 0);
}
break;
}
}
}
static Bind *
path_to_bind(char *path)
{
int i;
for (i = 0; bindings && bindings[i]; i++)
if (!strncmp
(bindings[i]->prefix, path, strlen(bindings[i]->prefix)))
return bindings[i];
return 0;
}
static Bind *
fd_to_bind(int fd, int *client_fd)
{
File *f = fd_to_file(ixps, fd);
int i, j;
if (!f)
return 0;
for (i = 0; bindings && bindings[i]; i++) {
for (j = 0; j < MAX_CONN * MAX_OPEN_FILES; j++) {
if (&bindings[i]->route[j].dest == f) {
*client_fd = bindings[i]->route[j].src;
return bindings[i];
}
}
}
return 0;
}
static File *
fixp_create(IXPServer * s, char *path)
{
Bind *b = path_to_bind(path);
size_t len;
if (!b) {
File *f = ixp_create(s, path);
return f;
}
/* route to b */
len = strlen(b->prefix);
b->client->create(b->client, path[len] == '\0' ? "/" : &path[len]);
if (b->client->errstr) {
if (!strcmp(b->client->errstr, DEAD_SERVER))
_unbind(b);
}
return 0;
}
static File *
fixp_open(IXPServer * s, char *path)
{
Bind *b = path_to_bind(path);
int fd;
size_t len;
if (!b) {
File *f = ixp_open(s, path);
return f;
}
/* route to b */
len = strlen(b->prefix);
fd = b->client->open(b->client, path[len] == '\0' ? "/" : &path[len]);
if (b->client->errstr) {
set_error(s, b->client->errstr);
if (!strcmp(b->client->errstr, DEAD_SERVER))
_unbind(b);
return 0;
}
b->route[fd].src = fd;
return &b->route[fd].dest;
}
static size_t
fixp_read(IXPServer * s, int fd, size_t offset, void *out_buf,
size_t out_buf_len)
{
int cfd;
Bind *b = fd_to_bind(fd, &cfd);
size_t result;
if (!b) {
result = ixp_read(s, fd, offset, out_buf, out_buf_len);
return result;
}
/* route to b */
result = seek_read(b->client, cfd, offset, out_buf, out_buf_len);
if (b->client->errstr) {
set_error(s, b->client->errstr);
if (!strcmp(b->client->errstr, DEAD_SERVER))
_unbind(b);
}
return result;
}
static void
fixp_write(IXPServer * s, int fd, size_t offset, void *content,
size_t in_len)
{
int cfd;
Bind *b = fd_to_bind(fd, &cfd);
if (!b) {
ixp_write(s, fd, offset, content, in_len);
return;
}
/* route to b */
seek_write(b->client, cfd, offset, content, in_len);
if (b->client->errstr) {
set_error(s, b->client->errstr);
if (!strcmp(b->client->errstr, DEAD_SERVER))
_unbind(b);
}
}
static void
fixp_close(IXPServer * s, int fd)
{
int cfd;
Bind *b = fd_to_bind(fd, &cfd);
if (!b) {
ixp_close(s, fd);
return;
}
/* route to b */
b->client->close(b->client, cfd);
if (b->client->errstr) {
set_error(s, b->client->errstr);
if (!strcmp(b->client->errstr, DEAD_SERVER))
_unbind(b);
}
}
static void
fixp_remove(IXPServer * s, char *path)
{
Bind *b = path_to_bind(path);
size_t len;
if (!b) {
ixp_remove(s, path);
return;
}
/* route to b */
len = strlen(b->prefix);
b->client->remove(b->client, path[len] == '\0' ? "/" : &path[len]);
if (b->client->errstr) {
set_error(s, b->client->errstr);
if (!strcmp(b->client->errstr, DEAD_SERVER))
_unbind(b);
}
}
static void
check_event(Connection * e)
{
XEvent ev;
while (XPending(dpy)) {
/*
* wmifs isn't interested in any X events, so just drop them
* all
*/
XNextEvent(dpy, &ev);
}
/* why check them? because X won't kill wmifs when X dies */
}
static void
run()
{
if (!(files[F_CTL] = ixp_create(ixps, "/ctl"))) {
perror("wmifs: cannot connect IXP server");
exit(1);
}
files[F_CTL]->after_write = handle_after_write;
/* routing functions */
ixps->create = fixp_create;
ixps->remove = fixp_remove;
ixps->open = fixp_open;
ixps->close = fixp_close;
ixps->read = fixp_read;
ixps->write = fixp_write;
/* main event loop */
run_server_with_fd_support(ixps, ConnectionNumber(dpy),
check_event, 0);
deinit_server(ixps);
}
int
main(int argc, char *argv[])
{
int i;
/* command line args */
for (i = 1; (i < argc) && (argv[i][0] == '-'); i++) {
switch (argv[i][1]) {
case 'v':
fprintf(stdout, "%s", version[0]);
exit(0);
break;
case 's':
if (i + 1 < argc)
sockfile = argv[++i];
else
usage();
break;
default:
usage();
break;
}
}
if (!getenv("HOME")) {
fprintf(stderr, "%s",
"wmifs: $HOME environment variable is not set\n");
usage();
}
/* just for the case X crashes/gets quit */
dpy = XOpenDisplay(0);
if (!dpy) {
fprintf(stderr, "%s", "wmifs: cannot open display\n");
exit(1);
}
ixps = wmii_setup_server(sockfile);
run();
return 0;
}

45
cmd/wmikeys.1 Normal file
View File

@ -0,0 +1,45 @@
.TH WMIKEYS 1 wmii-3
.SH NAME
wmikeys \- window manager improved 2 keys
.SH SYNOPSIS
.B wmikeys
.B \-s
.I socketfile
.SH DESCRIPTION
.SS Overview
.B wmikeys
is a generic, highly customizable, and efficient key grabbing utility for the
X Window System, originally designed for
.BR wmii (1).
It is similar to xbindkeys, but provides more flexibility and comes
with equivalent features like the ratpoison keyboard control allows.
It supports arbitrary, user defined shortcuts which are bound to
arbitrary actions.
Like wmii,
.B wmikeys
also implements a socket-based fileserver, which is accessed to configure and
interact with other components.
.SS Options
.TP
.BI \-s " socketfile"
specifies the socketfile that
.B wmikeys
should create.
.TP
.B \-v
prints version information to stderr, then exits.
.SS Customization
.B wmikeys
is customized through manipulating its filesystem namespace.
In the default setup of
.BR wmii (1)
the namespace of
.B wmikeys
can be found in /keys.
.SH SEE ALSO
.BR wmibar (1),
.BR wmifs (1),
.BR wmii (1),
.BR wmiiwm (1),
.BR wmimenu (1),
.BR wmir (1)

535
cmd/wmikeys.c Normal file
View File

@ -0,0 +1,535 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <X11/keysym.h>
#include <X11/Xatom.h>
#include <X11/cursorfont.h>
#include <X11/Xproto.h>
#include <X11/Xutil.h>
#include "wmii.h"
#include <cext.h>
/* array indexes for file pointers */
typedef enum {
K_CTL,
K_LOOKUP,
K_SIZE,
K_GRAB_KB,
K_TEXT_FONT,
K_TEXT_COLOR,
K_BG_COLOR,
K_BORDER_COLOR,
K_LAST
} KeyIndexes;
typedef struct Shortcut Shortcut;
struct Shortcut {
char name[MAX_BUF];
unsigned long mod;
KeyCode key;
Shortcut *next;
File *cmdfile;
};
static IXPServer *ixps = 0;
static Display *dpy;
static GC gc;
static Window win;
static Window root;
static XRectangle krect;
static XRectangle rect;
static int screen_num;
static char *sockfile = 0;
static Shortcut **shortcuts = 0;
static File *files[K_LAST];
static int grabkb = 0;
static unsigned int num_lock_mask, valid_mask;
static char buf[MAX_BUF];
static Shortcut zero_shortcut = {"", 0, 0, 0, 0};
static void grab_shortcut(Shortcut * s);
static void ungrab_shortcut(Shortcut * s);
static void draw_shortcut_box(char *text);
static void quit(void *obj, char *arg);
static Action acttbl[] = {
{"quit", quit},
{0, 0}
};
static char *version[] = {
"wmikeys - window manager improved keys - " VERSION "\n"
" (C)opyright MMIV-MMV Anselm R. Garbe\n", 0
};
static void
center()
{
krect.x = rect.width / 2 - krect.width / 2;
krect.y = rect.height / 2 - krect.height / 2;
}
static void
usage()
{
fprintf(stderr, "%s",
"usage: wmikeys [-s <socket file>] [-v] [<x>,<y>,<width>,<height>]\n"
" -s socket file (default: /tmp/.ixp-$USER/wmikeys-$WMII_IDENT)\n"
" -v version info\n");
exit(1);
}
/* grabs shortcut on all screens */
static void
grab_shortcut(Shortcut * s)
{
XGrabKey(dpy, s->key, s->mod, root,
True, GrabModeAsync, GrabModeAsync);
if (num_lock_mask) {
XGrabKey(dpy, s->key, s->mod | num_lock_mask, root,
True, GrabModeAsync, GrabModeAsync);
XGrabKey(dpy, s->key, s->mod | num_lock_mask | LockMask, root,
True, GrabModeAsync, GrabModeAsync);
}
XSync(dpy, False);
}
/*
* don't handle evil keys anymore, just define more shortcuts if you cannot
* live without evil key handling
*/
static void
ungrab_shortcut(Shortcut * s)
{
XUngrabKey(dpy, s->key, s->mod, root);
if (num_lock_mask) {
XUngrabKey(dpy, s->key, s->mod | num_lock_mask, root);
XUngrabKey(dpy, s->key, s->mod | num_lock_mask |
LockMask, root);
}
XSync(dpy, False);
}
static void
create_shortcut(File * f)
{
static char *chain[8];
char *k;
size_t i, toks;
Shortcut *s = 0, *r = 0;
_strlcpy(buf, f->name, sizeof(buf));
toks = tokenize(chain, 8, buf, ',');
for (i = 0; i < toks; i++) {
if (!s)
r = s = emalloc(sizeof(Shortcut));
else {
s->next = emalloc(sizeof(Shortcut));
s = s->next;
}
*s = zero_shortcut;
_strlcpy(s->name, chain[i], MAX_BUF);
k = strrchr(chain[i], '-');
if (k)
k++;
else
k = chain[i];
s->key = XKeysymToKeycode(dpy, XStringToKeysym(k));
s->mod = blitz_strtomod(chain[i]);
}
if (r) {
s->cmdfile = f;
shortcuts = (Shortcut **) attach_item_end((void **) shortcuts, r, sizeof(Shortcut *));
grab_shortcut(r);
}
}
static void
destroy_shortcut(Shortcut * s, int ungrab)
{
if (s->next)
destroy_shortcut(s->next, 0);
if (ungrab)
ungrab_shortcut(s);
free(s);
}
static void
next_keystroke(unsigned long *mod, KeyCode * key)
{
XEvent e;
KeySym sym;
*mod = 0;
do {
XMaskEvent(dpy, KeyPressMask, &e);
*mod |= e.xkey.state & valid_mask;
*key = (KeyCode) e.xkey.keycode;
sym = XKeycodeToKeysym(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(dpy, &client_win, &revert);
e.xkey.type = KeyPress;
e.xkey.time = CurrentTime;
e.xkey.window = client_win;
e.xkey.display = dpy;
e.xkey.state = mod;
e.xkey.keycode = key;
XSendEvent(dpy, client_win, True, KeyPressMask, &e);
e.xkey.type = KeyRelease;
XSendEvent(dpy, client_win, True, KeyReleaseMask, &e);
XSync(dpy, False);
}
static void
handle_shortcut_chain(Window w, Shortcut * processed, char *prefix, int grab)
{
unsigned long mod;
KeyCode key;
Shortcut *s = processed->next;
if (grab) {
XGrabKeyboard(dpy, w, True, GrabModeAsync,
GrabModeAsync, CurrentTime);
XMapRaised(dpy, win);
}
draw_shortcut_box(prefix);
next_keystroke(&mod, &key);
if ((processed->mod == mod) && (processed->key == key)) {
/* double shortcut */
emulate_key_press(mod, key);
} else if ((s->mod == mod) && (s->key == key)) {
if (s->cmdfile && s->cmdfile->content)
spawn(dpy, s->cmdfile->content);
else if (s->next) {
snprintf(buf, sizeof(buf), "%s/%s", prefix, s->name);
handle_shortcut_chain(w, s, buf, 0);
}
}
if (grab) {
XUngrabKeyboard(dpy, CurrentTime);
XUnmapWindow(dpy, win);
XSync(dpy, False);
}
}
static void
handle_shortcut_gkb(Window w, unsigned long mod, KeyCode key)
{
int i;
Shortcut *s;
if (!files[K_LOOKUP]->content)
return;
for (i = 0; shortcuts && shortcuts[i]; i++) {
s = shortcuts[i];
if ((s->mod == mod) && (s->key == key)) {
if (s->cmdfile && s->cmdfile->content)
spawn(dpy, s->cmdfile->content);
return;
}
}
XBell(dpy, 0);
}
static void
handle_shortcut(Window w, unsigned long mod, KeyCode key)
{
int i;
Shortcut *s;
if (!files[K_LOOKUP]->content)
return;
for (i = 0; shortcuts && shortcuts[i]; i++) {
s = shortcuts[i];
if ((s->mod == mod) && (s->key == key)) {
if (s->cmdfile && s->cmdfile->content) {
spawn(dpy, s->cmdfile->content);
return;
}
break;
}
}
if (s->next)
handle_shortcut_chain(w, s, s->name, 1);
}
static void
quit(void *obj, char *arg)
{
ixps->runlevel = SHUTDOWN;
}
static void
update()
{
int i;
File *f, *p;
if (!files[K_LOOKUP]->content)
return;
f = ixp_walk(ixps, files[K_LOOKUP]->content);
if (!f || !is_directory(f))
return; /* cannot update */
/* destroy existing shortcuts if any */
for (i = 0; shortcuts && shortcuts[i]; i++)
destroy_shortcut(shortcuts[i], 1);
free(shortcuts);
shortcuts = 0;
if (grabkb) {
XGrabKeyboard(dpy, root, True, GrabModeAsync,
GrabModeAsync, CurrentTime);
return;
}
/* create new shortcuts */
for (p = f->content; p; p = p->next)
create_shortcut(p);
}
/*
* Function assumes following fs-structure:
*
* /box/style/text-align "<align>"
* /box/style/text-font "<value>"
* /box/style/text-color "#RRGGBBAA"
* /box/style/bg-color "#RRGGBBAA"
*/
static void
draw_shortcut_box(char *text)
{
Draw d = {0};
d.font = blitz_getfont(dpy, files[K_TEXT_FONT]->content);
krect.width = XTextWidth(d.font, text, strlen(text)) + krect.height;
center();
XMoveResizeWindow(dpy, win, krect.x, krect.y, krect.width,
krect.height);
/* default stuff */
d.gc = gc;
d.drawable = win;
d.data = text;
d.rect.y = 0;
d.rect.width = krect.width;
d.rect.height = krect.height;
d.bg = blitz_loadcolor(dpy, screen_num, files[K_BG_COLOR]->content);
d.fg = blitz_loadcolor(dpy, screen_num, files[K_TEXT_COLOR]->content);
d.border =
blitz_loadcolor(dpy, screen_num, files[K_BORDER_COLOR]->content);
blitz_drawlabel(dpy, &d);
}
static void
check_event(Connection * c)
{
XEvent ev;
while (XPending(dpy)) {
XNextEvent(dpy, &ev);
switch (ev.type) {
case KeyPress:
ev.xkey.state &= valid_mask;
if (grabkb)
handle_shortcut_gkb(root, ev.xkey.state,
(KeyCode) ev.xkey.keycode);
else
handle_shortcut(root, ev.xkey.state,
(KeyCode) ev.xkey.keycode);
break;
case KeymapNotify:
update();
break;
default:
break;
}
}
}
static void
handle_after_write(IXPServer * s, File * f)
{
int i;
size_t len;
if (f == files[K_CTL]) {
for (i = 0; acttbl[i].name; i++) {
len = strlen(acttbl[i].name);
if (!strncmp(acttbl[i].name, (char *) f->content, len)) {
if (strlen(f->content) > len) {
acttbl[i].func(0, &((char *) f->content)[len + 1]);
} else {
acttbl[i].func(0, 0);
}
break;
}
}
} else if (files[K_SIZE] == f) {
char *size = files[K_SIZE]->content;
if (size && strrchr(size, ','))
blitz_strtorect(dpy, &rect, &krect, size);
} else if (f == files[K_GRAB_KB]) {
grabkb = _strtonum(files[K_GRAB_KB]->content, 0, 1);
if (!grabkb) {
XUngrabKeyboard(dpy, CurrentTime);
XUnmapWindow(dpy, win);
XSync(dpy, False);
} else
update();
} else if (f == files[K_LOOKUP]) {
update();
}
check_event(0);
}
static void
handle_before_read(IXPServer * s, File * f)
{
if (f != files[K_SIZE])
return;
snprintf(buf, sizeof(buf), "%d,%d,%d,%d", krect.x, krect.y,
krect.width, krect.height);
if (f->content)
free(f->content);
f->content = strdup(buf);
f->size = strlen(buf);
}
static void
run(char *size)
{
XSetWindowAttributes wa;
XGCValues gcv;
/* init */
if (!(files[K_CTL] = ixp_create(ixps, "/ctl"))) {
perror("wmikeys: cannot connect IXP server");
exit(1);
}
files[K_CTL]->after_write = handle_after_write;
files[K_LOOKUP] = ixp_create(ixps, "/lookup");
files[K_LOOKUP]->after_write = handle_after_write;
files[K_SIZE] = ixp_create(ixps, "/size");
files[K_SIZE]->before_read = handle_before_read;
files[K_SIZE]->after_write = handle_after_write;
files[K_GRAB_KB] = wmii_create_ixpfile(ixps, "/grab-keyb", "0");
files[K_GRAB_KB]->after_write = handle_after_write;
files[K_TEXT_FONT] = wmii_create_ixpfile(ixps, "/box/style/text-font", BLITZ_FONT);
files[K_TEXT_COLOR] = wmii_create_ixpfile(ixps, "/box/style/text-color", BLITZ_SEL_FG_COLOR);
files[K_BG_COLOR] = wmii_create_ixpfile(ixps, "/box/style/bg-color", BLITZ_SEL_BG_COLOR);
files[K_BORDER_COLOR] = wmii_create_ixpfile(ixps, "/box/style/border-color", BLITZ_SEL_BORDER_COLOR);
wa.override_redirect = 1;
wa.background_pixmap = ParentRelative;
wa.event_mask =
ExposureMask | SubstructureRedirectMask | SubstructureNotifyMask;
root = RootWindow(dpy, screen_num);
rect.x = rect.y = 0;
rect.width = DisplayWidth(dpy, screen_num);
rect.height = DisplayHeight(dpy, screen_num);
krect.x = krect.y = -1;
krect.width = krect.height = 0;
blitz_strtorect(dpy, &rect, &krect, size);
/* default is center position */
if (!krect.width) {
krect.width = 200;
}
if (!krect.height) {
krect.height = 20;
}
center();
init_lock_modifiers(dpy, &valid_mask, &num_lock_mask);
win = XCreateWindow(dpy, RootWindow(dpy, screen_num), krect.x, krect.y,
krect.width, krect.height, 0, DefaultDepth(dpy,
screen_num),
CopyFromParent, DefaultVisual(dpy, screen_num),
CWOverrideRedirect | CWBackPixmap | CWEventMask,
&wa);
XDefineCursor(dpy, win, XCreateFontCursor(dpy, XC_left_ptr));
XSync(dpy, False);
gcv.function = GXcopy;
gcv.graphics_exposures = False;
gc = XCreateGC(dpy, win, 0, 0);
/* main event loop */
run_server_with_fd_support(ixps, ConnectionNumber(dpy),
check_event, 0);
deinit_server(ixps);
XFreeGC(dpy, gc);
XCloseDisplay(dpy);
}
static int
dummy_error_handler(Display * dpy, XErrorEvent * err)
{
return 0;
}
int
main(int argc, char *argv[])
{
char size[64];
int i;
/* command line args */
for (i = 1; (i < argc) && (argv[i][0] == '-'); i++) {
switch (argv[i][1]) {
case 'v':
fprintf(stdout, "%s", version[0]);
exit(0);
break;
case 's':
if (i + 1 < argc)
sockfile = argv[++i];
else
usage();
break;
default:
usage();
break;
}
}
dpy = XOpenDisplay(0);
if (!dpy) {
fprintf(stderr, "%s", "wmikeys: cannot open display\n");
exit(1);
}
XSetErrorHandler(dummy_error_handler);
screen_num = DefaultScreen(dpy);
size[0] = '\0';
if (argc > i)
_strlcpy(size, argv[i], sizeof(size));
ixps = wmii_setup_server(sockfile);
run(size);
return 0;
}

42
cmd/wmimenu.1 Normal file
View File

@ -0,0 +1,42 @@
.TH WMIMENU 1 wmii-3
.SH NAME
wmimenu \- window manager improved 2 menu
.SH SYNOPSIS
.B wmimenu
.B \-s
.I socketfile
.SH DESCRIPTION
.SS Overview
.B wmimenu
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, which get executed.
Like wmii,
.B wmimenu
also implements a socket-based fileserver, which is accessed to configure and
interoperate with other components.
.SS Options
.TP
.BI \-s " socketfile"
specifies the socketfile that
.B wmimenu
should create.
.TP
.B \-v
prints version information to stderr, then exits.
.SS Customization
.B wmimenu
is customized through manipulating its filesystem namespace.
In the default setup of
.BR wmii (1)
the namespace of
.B wmimenu
can be found in /menu.
.SH SEE ALSO
.BR wmibar (1),
.BR wmifs (1),
.BR wmii (1),
.BR wmiiwm (1),
.BR wmikeys (1),
.BR wmir (1)

689
cmd/wmimenu.c Normal file
View File

@ -0,0 +1,689 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <time.h>
#include <X11/Xatom.h>
#include <X11/cursorfont.h>
#include <X11/Xproto.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include "wmii.h"
#include <cext.h>
/* array indexes for file pointers */
typedef enum {
M_CTL,
M_SIZE,
M_PRE_COMMAND,
M_COMMAND,
M_HISTORY,
M_LOOKUP,
M_TEXT_FONT,
M_SELECTED_BG_COLOR,
M_SELECTED_TEXT_COLOR,
M_SELECTED_BORDER_COLOR,
M_NORMAL_BG_COLOR,
M_NORMAL_TEXT_COLOR,
M_NORMAL_BORDER_COLOR,
M_RETARDED,
M_LAST
} InputIndexes;
enum {
OFF_NEXT, OFF_PREV, OFF_CURR, OFF_LAST
};
static IXPServer *ixps = 0;
static Display *dpy;
static GC gc;
static Window win;
static XRectangle rect;
static XRectangle mrect;
static int screen_num;
static char *sockfile = 0;
static File *files[M_LAST];
static File **items = 0;
static size_t item_size = 0;
static int item = 0;
static int offset[OFF_LAST];
static unsigned int cmdw = 0;
static File **history = 0;
static int sel_history = 0;
static Pixmap pmap;
static const int seek = 30; /* 30px */
static void check_event(Connection * c);
static void draw_menu(void);
static void handle_kpress(XKeyEvent * e);
static void set_text(char *text);
static void quit(void *obj, char *arg);
static void display(void *obj, char *arg);
static int update_items(char *prefix);
static Action acttbl[2] = {
{"quit", quit},
{"display", display}
};
static char *version[] = {
"wmimenu - window manager improved menu - " VERSION "\n"
" (C)opyright MMIV-MMV Anselm R. Garbe\n", 0
};
static void
usage()
{
fprintf(stderr, "%s",
"usage: wmimenu [-s <socket file>] [-r] [-v] [<x>,<y>,<width>,<height>]\n"
" -s socket file (default: /tmp/.ixp-$USER/wmimenu-%s-%s)\n"
" -v version info\n");
exit(1);
}
static void
add_history(char *cmd)
{
char buf[MAX_BUF];
snprintf(buf, MAX_BUF, "/history/%ld", (long) time(0));
history = (File **) attach_item_begin((void **) history,
wmii_create_ixpfile(ixps, buf,
cmd),
sizeof(File *));
}
static void
_exec(char *cmd)
{
char *rc = cmd;
if (!cmd || cmd[0] == '\0')
return;
if (items && items[0]) {
if ((item >= 0) && items[item] && items[item]->size)
rc = cmd = items[item]->content;
else if ((item == -1) && items[0]->size) /* autolight */
rc = cmd = items[0]->content;
}
add_history(cmd);
if (files[M_PRE_COMMAND]->content) {
size_t len = strlen(cmd) + files[M_PRE_COMMAND]->size + 2;
rc = emalloc(len);
snprintf(rc, len, "%s %s", (char *) files[M_PRE_COMMAND]->content,
cmd);
}
/* fallback */
spawn(dpy, rc);
/* cleanup */
if (files[M_PRE_COMMAND]->content)
free(rc);
}
static void
quit(void *obj, char *arg)
{
ixps->runlevel = SHUTDOWN;
}
static void
show()
{
set_text(0);
XMapRaised(dpy, win);
XSync(dpy, False);
update_items(files[M_COMMAND]->content);
draw_menu();
while (XGrabKeyboard
(dpy, RootWindow(dpy, screen_num), True, GrabModeAsync,
GrabModeAsync, CurrentTime) != GrabSuccess)
usleep(1000);
}
static void
hide()
{
XUngrabKeyboard(dpy, CurrentTime);
XUnmapWindow(dpy, win);
XSync(dpy, False);
}
static void
display(void *obj, char *arg)
{
if (!arg)
return;
if (_strtonum(arg, 0, 1))
show();
else
hide();
check_event(0);
}
void
set_text(char *text)
{
if (files[M_COMMAND]->content) {
free(files[M_COMMAND]->content);
files[M_COMMAND]->content = 0;
files[M_COMMAND]->size = 0;
}
if (text && strlen(text)) {
files[M_COMMAND]->content = strdup(text);
files[M_COMMAND]->size = strlen(text);
}
}
static void
update_offsets()
{
int i;
XFontStruct *font;
unsigned int w = cmdw + 2 * seek;
if (!items)
return;
font = blitz_getfont(dpy, files[M_TEXT_FONT]->content);
/* calc next offset */
for (i = offset[OFF_CURR]; items[i]; i++) {
w += XTextWidth(font, items[i]->content, strlen(items[i]->content)) + mrect.height;
if (w > mrect.width)
break;
}
offset[OFF_NEXT] = i;
w = cmdw + 2 * seek;
for (i = offset[OFF_CURR] - 1; i >= 0; i--) {
w += XTextWidth(font, items[i]->content, strlen(items[i]->content)) + mrect.height;
if (w > mrect.width)
break;
}
offset[OFF_PREV] = i + 1;
XFreeFont(dpy, font);
}
static int
update_items(char *pattern)
{
size_t plen = pattern ? strlen(pattern) : 0, size = 0, max = 0,
len;
int matched = pattern ? plen == 0 : 1;
XFontStruct *font;
File *f, *p, *maxitem = 0;
cmdw = 0;
item = -1;
offset[OFF_CURR] = offset[OFF_PREV] = offset[OFF_NEXT] = 0;
if (!files[M_LOOKUP]->content)
return 0;
f = ixp_walk(ixps, files[M_LOOKUP]->content);
if (!f || !is_directory(f))
return 0;
font = blitz_getfont(dpy, files[M_TEXT_FONT]->content);
/* build new items */
for (p = f->content; p; p = p->next) {
size++;
len = strlen(p->name);
if (max < len) {
maxitem = p;
max = len;
}
}
if (maxitem) {
if (files[M_RETARDED]->content)
free(files[M_RETARDED]->content);
files[M_RETARDED]->content = strdup(maxitem->name);
files[M_RETARDED]->size = max;
cmdw = XTextWidth(font, maxitem->name, max) + mrect.height;
}
if (size > item_size) {
/* stores always the biggest amount of items in memory */
if (items)
free((File **) items);
items = 0;
item_size = size;
if (item_size)
items = (File **) emalloc((item_size + 1) * sizeof(File *));
}
size = 0;
for (p = f->content; p; p = p->next) {
if (!p->content)
continue; /* ignore bogus files */
if (matched || !strncmp(pattern, p->name, plen)) {
items[size++] = p;
p->parent = 0; /* HACK to prevent doubled items */
}
}
for (p = f->content; p; p = p->next) {
if (!p->content)
continue; /* ignore bogus files */
if (p->parent && strstr(p->name, pattern))
items[size++] = p;
else
p->parent = f; /* restore HACK */
}
items[size] = 0;
update_offsets();
XFreeFont(dpy, font);
return size;
}
/* creates draw structs for menu mode drawing */
static void
draw_menu()
{
Draw d = {0};
unsigned int offx = 0;
int i = 0;
XFontStruct *font = blitz_getfont(dpy, files[M_TEXT_FONT]->content);
d.gc = gc;
d.drawable = pmap;
d.rect = mrect;
d.rect.x = 0;
d.rect.y = 0;
d.bg = blitz_loadcolor(dpy, screen_num, files[M_NORMAL_BG_COLOR]->content);
d.border = blitz_loadcolor(dpy, screen_num, files[M_NORMAL_BORDER_COLOR]->content);
blitz_drawlabelnoborder(dpy, &d);
/* print command */
d.align = WEST;
d.font = font;
d.fg = blitz_loadcolor(dpy, screen_num, files[M_NORMAL_TEXT_COLOR]->content);
d.data = files[M_COMMAND]->content;
if (cmdw && items && items[0])
d.rect.width = cmdw;
offx += d.rect.width;
blitz_drawlabelnoborder(dpy, &d);
d.align = CENTER;
if (items && items[0]) {
d.bg = blitz_loadcolor(dpy, screen_num, files[M_NORMAL_BG_COLOR]->content);
d.fg = blitz_loadcolor(dpy, screen_num, files[M_NORMAL_TEXT_COLOR]->content);
d.data = offset[OFF_CURR] ? "<" : 0;
d.rect.x = offx;
d.rect.width = seek;
offx += d.rect.width;
blitz_drawlabelnoborder(dpy, &d);
/* determine maximum items */
for (i = offset[OFF_CURR]; items[i] && (i < offset[OFF_NEXT]); i++) {
d.data = items[i]->name;
d.rect.x = offx;
d.rect.width = XTextWidth(d.font, d.data, strlen(d.data)) + mrect.height;
offx += d.rect.width;
if (i == item) {
d.bg = blitz_loadcolor(dpy, screen_num, files[M_SELECTED_BG_COLOR]->content);
d.fg = blitz_loadcolor(dpy, screen_num, files[M_SELECTED_TEXT_COLOR]->content);
d.border = blitz_loadcolor(dpy, screen_num, files[M_SELECTED_BORDER_COLOR]->content);
blitz_drawlabel(dpy, &d);
} else if (!i && item == -1) {
/* fg and bg are inverted */
d.fg = blitz_loadcolor(dpy, screen_num, files[M_SELECTED_BG_COLOR]->content);
d.bg = blitz_loadcolor(dpy, screen_num, files[M_SELECTED_TEXT_COLOR]->content);
d.border = blitz_loadcolor(dpy, screen_num, files[M_SELECTED_BORDER_COLOR]->content);
blitz_drawlabel(dpy, &d);
} else {
d.bg = blitz_loadcolor(dpy, screen_num, files[M_NORMAL_BG_COLOR]->content);
d.fg = blitz_loadcolor(dpy, screen_num, files[M_NORMAL_TEXT_COLOR]->content);
d.border = blitz_loadcolor(dpy, screen_num, files[M_NORMAL_BORDER_COLOR]->content);
blitz_drawlabelnoborder(dpy, &d);
}
}
d.bg = blitz_loadcolor(dpy, screen_num, files[M_NORMAL_BG_COLOR]->content);
d.fg = blitz_loadcolor(dpy, screen_num, files[M_NORMAL_TEXT_COLOR]->content);
d.data = items[i] ? ">" : 0;
d.rect.x = mrect.width - seek;
d.rect.width = seek;
blitz_drawlabelnoborder(dpy, &d);
}
XCopyArea(dpy, pmap, win, gc, 0, 0, mrect.width, mrect.height, 0, 0);
XSync(dpy, False);
XFreeFont(dpy, font);
}
static void
handle_kpress(XKeyEvent * e)
{
KeySym ksym;
char buf[32];
int idx, num;
static char text[4096];
size_t len = 0;
text[0] = '\0';
if (files[M_COMMAND]->content) {
_strlcpy(text, files[M_COMMAND]->content, sizeof(text));
len = strlen(text);
}
buf[0] = '\0';
num = XLookupString(e, buf, sizeof(buf), &ksym, 0);
if (IsFunctionKey(ksym) || IsKeypadKey(ksym)
|| IsMiscFunctionKey(ksym) || IsPFKey(ksym)
|| IsPrivateKeypadKey(ksym))
return;
/* first check if a control mask is omitted */
if (e->state & ShiftMask) {
if (ksym == XK_ISO_Left_Tab)
ksym = XK_Left;
} else if (e->state & ControlMask) {
switch (ksym) {
case XK_E:
case XK_e:
ksym = XK_End;
break;
case XK_H:
case XK_h:
ksym = XK_BackSpace;
break;
case XK_J:
case XK_j:
ksym = XK_Return;
break;
case XK_U:
case XK_u:
set_text(0);
update_items(0);
draw_menu();
return;
break;
default: /* ignore other control sequences */
return;
break;
}
}
switch (ksym) {
case XK_Left:
if (!items || !items[0])
return;
if (item > 0) {
item--;
set_text(items[item]->name);
} else
return;
break;
case XK_Right:
case XK_Tab:
if (!items || !items[0])
return;
if (items[item + 1]) {
item++;
set_text(items[item]->name);
} else
return;
break;
case XK_Down:
if (history) {
set_text(history[sel_history]->content);
idx = index_prev_item((void **) history, history[sel_history]);
if (idx >= 0)
sel_history = idx;
}
update_items(files[M_COMMAND]->content);
break;
case XK_Up:
if (history) {
set_text(history[sel_history]->content);
idx = index_next_item((void **) history, history[sel_history]);
if (idx >= 0)
sel_history = idx;
}
update_items(files[M_COMMAND]->content);
break;
case XK_Return:
if (items && items[0]) {
if (item >= 0)
_exec(items[item]->name);
else
_exec(items[0]->name);
} else if (text)
_exec(text);
case XK_Escape:
hide();
break;
case XK_BackSpace:
if (len) {
int size = 0;
size_t i = len;
for (size = 0; items && items[size]; size++);
if (i) {
do
text[--i] = '\0';
while (size && i && size == update_items(text));
}
set_text(text);
update_items(files[M_COMMAND]->content);
}
break;
default:
if ((num == 1) && !iscntrl((int) buf[0])) {
buf[num] = '\0';
if (len > 0)
_strlcat(text, buf, sizeof(text));
else
_strlcpy(text, buf, sizeof(text));
set_text(text);
update_items(files[M_COMMAND]->content);
}
}
if (items && item > 0) {
if (item < offset[OFF_CURR]) {
offset[OFF_CURR] = offset[OFF_PREV];
update_offsets();
} else if (item >= offset[OFF_NEXT]) {
offset[OFF_CURR] = offset[OFF_NEXT];
update_offsets();
}
}
draw_menu();
}
static void
check_event(Connection * c)
{
XEvent ev;
while (XPending(dpy)) {
XNextEvent(dpy, &ev);
switch (ev.type) {
case KeyPress:
handle_kpress(&ev.xkey);
break;
case Expose:
if (ev.xexpose.count == 0) {
draw_menu();
}
break;
default:
break;
}
}
}
static void
handle_after_write(IXPServer * s, File * f)
{
int i;
size_t len;
if (files[M_CTL] == f) {
for (i = 0; i < 2; i++) {
len = strlen(acttbl[i].name);
if (!strncmp(acttbl[i].name, (char *) f->content, len)) {
if (strlen(f->content) > len) {
acttbl[i].func(0, &((char *) f->content)[len + 1]);
} else {
acttbl[i].func(0, 0);
}
break;
}
}
} else if (files[M_SIZE] == f) {
char *size = files[M_SIZE]->content;
if (size && strrchr(size, ',')) {
blitz_strtorect(dpy, &rect, &mrect, size);
XFreePixmap(dpy, pmap);
XMoveResizeWindow(dpy, win, mrect.x, mrect.y,
mrect.width, mrect.height);
XSync(dpy, False);
pmap = XCreatePixmap(dpy, win, mrect.width, mrect.height,
DefaultDepth(dpy, screen_num));
XSync(dpy, False);
draw_menu();
}
} else if (files[M_COMMAND] == f) {
update_items(files[M_COMMAND]->content);
draw_menu();
}
check_event(0);
}
static void
handle_before_read(IXPServer * s, File * f)
{
char buf[64];
if (f != files[M_SIZE])
return;
snprintf(buf, sizeof(buf), "%d,%d,%d,%d", mrect.x, mrect.y,
mrect.width, mrect.height);
if (f->content)
free(f->content);
f->content = strdup(buf);
f->size = strlen(buf);
}
static void
run(char *size)
{
XSetWindowAttributes wa;
XGCValues gcv;
/* init */
if (!(files[M_CTL] = ixp_create(ixps, "/ctl"))) {
perror("wmimenu: cannot connect IXP server");
exit(1);
}
files[M_CTL]->after_write = handle_after_write;
files[M_SIZE] = ixp_create(ixps, "/size");
files[M_SIZE]->before_read = handle_before_read;
files[M_SIZE]->after_write = handle_after_write;
files[M_PRE_COMMAND] = ixp_create(ixps, "/precmd");
files[M_COMMAND] = ixp_create(ixps, "/cmd");
files[M_COMMAND]->after_write = handle_after_write;
files[M_HISTORY] = ixp_create(ixps, "/history");
add_history("");
files[M_LOOKUP] = ixp_create(ixps, "/lookup");
files[M_TEXT_FONT] = wmii_create_ixpfile(ixps, "/style/text-font", BLITZ_FONT);
files[M_SELECTED_BG_COLOR] = wmii_create_ixpfile(ixps, "/sel-style/bg-color", BLITZ_SEL_BG_COLOR);
files[M_SELECTED_TEXT_COLOR] = wmii_create_ixpfile(ixps, "/sel-style/text-color", BLITZ_SEL_FG_COLOR);
files[M_SELECTED_BORDER_COLOR] = wmii_create_ixpfile(ixps, "/sel-style/border-color", BLITZ_SEL_BORDER_COLOR);
files[M_NORMAL_BG_COLOR] = wmii_create_ixpfile(ixps, "/norm-style/bg-color", BLITZ_NORM_BG_COLOR);
files[M_NORMAL_TEXT_COLOR] = wmii_create_ixpfile(ixps, "/norm-style/text-color", BLITZ_NORM_FG_COLOR);
files[M_NORMAL_BORDER_COLOR] = wmii_create_ixpfile(ixps, "/norm-style/border-color", BLITZ_NORM_BORDER_COLOR);
files[M_RETARDED] = ixp_create(ixps, "/retarded");
wa.override_redirect = 1;
wa.background_pixmap = ParentRelative;
wa.event_mask = ExposureMask | ButtonPressMask | KeyPressMask
| SubstructureRedirectMask | SubstructureNotifyMask;
rect.x = rect.y = 0;
rect.width = DisplayWidth(dpy, screen_num);
rect.height = DisplayHeight(dpy, screen_num);
blitz_strtorect(dpy, &rect, &mrect, size);
if (!mrect.width)
mrect.width = DisplayWidth(dpy, screen_num);
if (!mrect.height)
mrect.height = 40;
win = XCreateWindow(dpy, RootWindow(dpy, screen_num), mrect.x, mrect.y,
mrect.width, mrect.height, 0, DefaultDepth(dpy,
screen_num),
CopyFromParent, DefaultVisual(dpy, screen_num),
CWOverrideRedirect | CWBackPixmap | CWEventMask,
&wa);
XDefineCursor(dpy, win, XCreateFontCursor(dpy, XC_xterm));
XSync(dpy, False);
/* window pixmap */
gcv.function = GXcopy;
gcv.graphics_exposures = False;
gc = XCreateGC(dpy, win, 0, 0);
pmap =
XCreatePixmap(dpy, win, mrect.width, mrect.height,
DefaultDepth(dpy, screen_num));
/* main event loop */
run_server_with_fd_support(ixps, ConnectionNumber(dpy),
check_event, 0);
deinit_server(ixps);
XFreePixmap(dpy, pmap);
XFreeGC(dpy, gc);
XCloseDisplay(dpy);
}
int
main(int argc, char *argv[])
{
char size[64];
int i;
/* command line args */
for (i = 1; (i < argc) && (argv[i][0] == '-'); i++) {
switch (argv[i][1]) {
case 'v':
fprintf(stdout, "%s", version[0]);
exit(0);
break;
case 's':
if (i + 1 < argc)
sockfile = argv[++i];
else
usage();
break;
default:
usage();
break;
}
}
dpy = XOpenDisplay(0);
if (!dpy) {
fprintf(stderr, "%s", "wmimenu: cannot open display\n");
exit(1);
}
screen_num = DefaultScreen(dpy);
size[0] = '\0';
if (argc > i)
_strlcpy(size, argv[i], sizeof(size));
ixps = wmii_setup_server(sockfile);
items = 0;
run(size);
return 0;
}

95
cmd/wmiplumb.c Normal file
View File

@ -0,0 +1,95 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
static char *version[] = {
"wmiplumb - window manager improved plumb - " VERSION "\n"
" (C)opyright MMIV-MMV Anselm R. Garbe\n", 0
};
static void
usage()
{
fprintf(stderr, "%s\n",
"usage: wmiplumb [-v]\n" " -v version info\n");
exit(1);
}
static void
print_sel(Display * dpy, Window w, XSelectionEvent * e)
{
Atom typeret;
int format;
unsigned long nitems, bytesleft;
unsigned char *data;
XGetWindowProperty(dpy, w, e->property, 0L, 4096L, False,
AnyPropertyType, &typeret, &format,
&nitems, &bytesleft, &data);
if (format == 8) {
int i;
for (i = 0; i < nitems; i++)
putchar(data[i]);
putchar('\n');
}
XDeleteProperty(dpy, w, e->property);
}
int
main(int argc, char **argv)
{
Display *dpy;
Atom xa_clip_string;
Window w;
XEvent ev;
int pdone = 0;
/* command line args */
if (argc > 1) {
if (!strncmp(argv[1], "-v", 3)) {
fprintf(stdout, "%s", version[0]);
exit(0);
} else
usage();
}
dpy = XOpenDisplay(0);
if (!dpy) {
fprintf(stderr, "%s", "wmiplumb: cannot open display\n");
exit(1);
}
xa_clip_string = XInternAtom(dpy, "PLUMB_STRING", False);
w = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 10, 10, 200, 200,
1, CopyFromParent, CopyFromParent);
while (1 && !pdone) {
XConvertSelection(dpy, XA_PRIMARY, XA_STRING, xa_clip_string,
w, CurrentTime);
XFlush(dpy);
XNextEvent(dpy, &ev);
switch (ev.type) {
case SelectionNotify:
if (ev.xselection.property != None)
print_sel(dpy, w, &ev.xselection);
else
putchar('\n');
XDestroyWindow(dpy, w);
XCloseDisplay(dpy);
pdone = 1;
break;
default:
break;
}
}
return 0;
/*
* XDestroyWindow(dpy, w); XCloseDisplay(dpy); return 1;
*/
}

71
cmd/wmir.1 Normal file
View File

@ -0,0 +1,71 @@
.TH WMIR 1 wmii-3
.SH NAME
wmir \- window manager improved 2 remote
.SH SYNOPSIS
.B wmir
.RB [ \-s
.IR socketfile ]
.I action
.I action_arg
[...]
.br
.B wmir
.RB [ \-s
.IR socketfile ]
.B \-f
.br
.B wmir
.B \-v
.SH DESCRIPTION
.SS Overview
.B wmir
is a client to access wmi* fileservers for the command line and
scripts. It can be used to configure wmii(1).
.SS Options
.TP
.BI \-s " socketfile"
lets you specify the socketfile to which
.B wmir
a connection will be established. If the environment variable
.B WMIR_SOCKET
is set and points to a socket file, wmir will use that file, if this
option is not supplied.
.TP
.B \-f
reads from stdin, useful for interactive wmir sessions or for
scripts that write/read a bunch of data, because this speeds things up
due to missing process creation/destruction, ie., a hack.
.TP
.B \-v
prints version information to stderr, then exits.
.TP
The syntax of the actions is as follows:
.TP
.B create <path/file> [<value>]
creates file
.TP
.B write <file> <value>
writes value to a file
.TP
.B read <path/file>
reads file or directory contents
.TP
.B remove <path/file>
removes file or directory tree
.SH ENVIRONMENT
.TP
WMIR_SOCKET
See above.
.SH EXAMPLES
.TP
$ wmir read /
This gives you an idea about what the wmii filesystem currently looks like.
.TP
$ wmir write /wm/ctl quit
.SH SEE ALSO
.BR wmibar (1),
.BR wmifs (1),
.BR wmii (1),
.BR wmiiwm (1),
.BR wmikeys (1),
.BR wmimenu (1)

228
cmd/wmir.c Normal file
View File

@ -0,0 +1,228 @@
/*
* (C)opyright MMIV-MMV 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"
static IXPClient *c;
static int exit_code = 0;
static char *version[] = {
"wmir - window manager improved remote - " VERSION "\n"
" (C)opyright MMIV-MMV Anselm R. Garbe\n", 0
};
static void
usage()
{
fprintf(stderr, "%s",
"usage: wmir [-s <socket file>] [-v] <action> <action_arg> [...]\n"
" -s socket file (default: $WMIR_SOCKET)\n"
" -f read actions from stdin\n"
" -v version info\n"
"actions:\n"
" create <file> [<content>] -- creates and optionally writes content to a file\n"
" write <file> <content> -- writes content to a file\n"
" read <directory/file> -- reads file or directory contents\n"
" remove <directory/file> -- removes file or directory, use with care!\n");
exit(1);
}
static void
perform(char *action, char *file, char *content)
{
size_t out_len = 0;
char output[2050];
int crt, fd = -1;
if (!action)
return;
crt = !strncmp(action, "create", 7);
if (!strncmp(action, "write", 6) || crt) {
if (!file)
return;
/* create file first */
if (crt) {
c->create(c, file);
if (c->errstr) {
fprintf(stderr, "wmir: error: create %s: %s\n", file,
c->errstr);
exit_code = 1;
return;
}
}
if (!content)
return;
fd = c->open(c, file);
if (c->errstr) {
fprintf(stderr, "wmir: error: open %s: %s\n", file, c->errstr);
exit_code = 1;
return;
}
c->write(c, fd, content, strlen(content));
if (c->errstr) {
fprintf(stderr, "wmir: error: write %s: %s\n", file,
c->errstr);
exit_code = 1;
if (!strncmp(c->errstr, DEAD_SERVER, strlen(DEAD_SERVER) + 1))
return;
}
} else if (!strncmp(action, "read", 5)) {
if (!file)
return;
fd = c->open(c, file);
if (c->errstr) {
fprintf(stderr, "wmir: error: open %s: %s\n", file, c->errstr);
exit_code = 1;
return;
}
do {
out_len = c->read(c, fd, output, 2048);
if (c->errstr) {
fprintf(stderr, "wmir: error: read %s: %s\n", file,
c->errstr);
exit_code = 1;
if (!strncmp
(c->errstr, DEAD_SERVER, strlen(DEAD_SERVER) + 1))
return;
break;
}
output[out_len] = '\0';
fprintf(stdout, "%s", output);
}
while (out_len == 2048);
fprintf(stdout, "%s", "\n");
} else if (!strncmp(action, "remove", 7)) {
if (!file)
return;
c->remove(c, file);
if (c->errstr) {
fprintf(stderr, "wmir: error: remove %s: %s\n", file,
c->errstr);
exit_code = 1;
return;
}
}
if (fd != -1) {
c->close(c, fd);
if (c->errstr) {
fprintf(stderr, "wmir: error: close %s: %s\n", file,
c->errstr);
exit_code = 1;
return;
}
}
}
int
main(int argc, char *argv[])
{
int i = 0, read_stdin = 0;
char line[4096], *p;
char *sockfile = getenv("WMIR_SOCKET");
/* command line args */
if (argc > 1) {
for (i = 1; (i < argc) && (argv[i][0] == '-'); i++) {
switch (argv[i][1]) {
case 'v':
fprintf(stderr, "%s", version[0]);
exit(0);
break;
case 's':
if (i + 1 < argc) {
sockfile = argv[++i];
} else {
usage();
}
break;
case 'f':
read_stdin = 1;
break;
default:
usage();
break;
}
}
}
if ((argc <= 1) || (!read_stdin && (i + 1) >= argc)) {
fprintf(stderr, "%s", "wmir: arguments: ");
for (i = 1; i < argc; i++)
fprintf(stderr, "%s, ", argv[i]);
fprintf(stderr, "%s", "\n");
usage();
}
if (!sockfile) {
fprintf(stderr, "%s",
"wmir: error: WMIR_SOCKET environment not set\n");
usage();
}
/* open socket */
if (!(c = init_client(sockfile))) {
fprintf(stderr, "wmir: cannot connect to server '%s'\n", sockfile);
exit(1);
}
if (read_stdin) {
/* simple shell */
char *action, *file, *content;
while (fgets(line, 4096, stdin)) {
p = line;
while (*p != '\0' && (*p == ' ' || *p == '\t'))
p++;
if (*p == '\0')
continue; /* empty line */
if (strncmp(p, "create ", 7) &&
strncmp(p, "write ", 6) &&
strncmp(p, "read ", 5) && strncmp(p, "remove ", 7))
continue;
action = p;
while (*p != '\0' && *p != ' ' && *p != '\t' && *p != '\n')
p++;
if (*p == '\0' || *p == '\n')
continue; /* ignore bogus command */
*p = '\0';
p++;
while (*p != '\0' && (*p == ' ' || *p == '\t'))
p++;
if (*p == '\0')
continue; /* ignore bogus command */
file = p;
while (*p != '\0' && *p != ' ' && *p != '\t' && *p != '\n')
p++;
if (*p == '\0' || *p == '\n') {
content = 0;
*p = '\0';
} else {
*p = '\0';
p++;
content = p;
}
if (file[0] == '\0')
continue;
if (content) {
static size_t len;
if ((len = strlen(content)))
content[len - 1] = '\0';
}
perform(action, file, content);
if (c->errstr)
fprintf(stderr, "wmir: error: read %s: %s\n", file,
c->errstr);
}
} else {
perform(argv[i], argv[i + 1], (i + 2) < argc ? argv[i + 2] : 0);
}
if (c->errstr) {
deinit_client(c);
exit_code = 1;
}
return exit_code;
}

252
cmd/wmir2.c Normal file
View File

@ -0,0 +1,252 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../libixp2/ixp.h"
#include <cext.h>
typedef struct {
char *name;
int (*cmd) (char **argv);
int min_argc;
} Command;
static int xcreate(char **argv);
static int xread(char **argv);
static int xwrite(char **argv);
static int xremove(char **argv);
static Command cmds[] = {
{"create", xcreate, 2},
{"read", xread, 1},
{"write", xwrite, 2},
{"remove", xremove, 1},
{0, 0}
};
static IXPClient c = {0};
static char *version[] = {
"wmir - window manager improved remote - " VERSION "\n"
" (C)opyright MMIV-MMV Anselm R. Garbe\n", 0
};
static void
usage()
{
fprintf(stderr, "%s",
"usage: wmir [-s <socket file>] [-v] <command> <args> [...]\n"
" -s socket file (default: $WMIR_SOCKET)\n"
" -f read commands from stdin\n"
" -v version info\n"
"commands:\n"
" create <file> <content> -- creates file\n"
" read <file/directory> -- reads file/directory contents\n"
" write <file> <content> -- writes content to a file\n"
" remove <file/directory> -- removes file\n");
exit(1);
}
static u32
write_data(u32 fid, u8 * data, u32 count)
{
u32 len, i, runs = count / c.fcall.iounit;
for (i = 0; i <= runs; i++) {
/* write */
len = count - (i * c.fcall.iounit);
if (len > c.fcall.iounit)
len = c.fcall.iounit;
if (ixp_client_write
(&c, fid, i * c.fcall.iounit, len,
&data[i * c.fcall.iounit]) != count) {
fprintf(stderr, "wmir: cannot write file: %s\n", c.errstr);
return 0;
}
}
return count;
}
static int
xcreate(char **argv)
{
u32 fid;
char *p = strrchr(argv[0], '/');
fid = c.root_fid << 2;
/* walk to bottom-most directory */
*p = 0;
if (!ixp_client_walk(&c, fid, argv[0])) {
fprintf(stderr, "wmir: cannot walk to %s: %s\n", argv[0],
c.errstr);
return 1;
}
/* create */
p++;
if (!ixp_client_create(&c, fid, p, (u32) 0xff, IXP_OWRITE)) {
fprintf(stderr, "wmir: cannot create file: %s\n", c.errstr);
return 1;
}
write_data(fid, (u8 *) argv[1], strlen(argv[1]));
return !ixp_client_close(&c, fid);
}
static int
xwrite(char **argv)
{
/* open */
u32 fid = c.root_fid << 2;
if (!ixp_client_open(&c, fid, argv[0], IXP_OWRITE)) {
fprintf(stderr, "wmir: cannot open file: %s\n", c.errstr);
return 1;
}
write_data(fid, (u8 *) argv[1], strlen(argv[1]));
return !ixp_client_close(&c, fid);
}
static void
print_directory(void *result, u32 msize)
{
void *p = result;
static Stat stat, zerostat = {0};
u32 len = 0;
do {
p = ixp_dec_stat(p, &stat);
len += stat.size + sizeof(u16);
if (stat.qid.type == IXP_QTDIR)
fprintf(stdout, "%s/\n", stat.name);
else
fprintf(stdout, "%s\n", stat.name);
stat = zerostat;
}
while (len < msize);
}
static int
xread(char **argv)
{
u32 count, fid = c.root_fid << 2;
int is_directory = FALSE;
static u8 result[IXP_MAX_MSG];
/* open */
if (!ixp_client_open(&c, fid, argv[0], IXP_OREAD)) {
fprintf(stderr, "wmir: cannot open file: %s\n", c.errstr);
return 1;
}
is_directory = !c.fcall.nwqid || (c.fcall.qid.type == IXP_QTDIR);
/* read */
if (!(count = ixp_client_read(&c, fid, 0, result, IXP_MAX_MSG))
&& c.errstr) {
fprintf(stderr, "wmir: cannot read file: %s\n", c.errstr);
return 1;
}
if (count) {
if (is_directory)
print_directory(result, count);
else {
u32 i;
for (i = 0; i < count; i++)
putchar(result[i]);
}
}
return !ixp_client_close(&c, fid);
}
static int
xremove(char **argv)
{
u32 fid;
/* remove */
fid = c.root_fid << 2;
if (!ixp_client_remove(&c, fid, argv[0])) {
fprintf(stderr, "wmir: cannot remove file: %s\n", c.errstr);
return 1;
}
return 0;
}
static int
perform_cmd(int argc, char **argv)
{
int i;
for (i = 0; cmds[i].name; i++)
if (!strncmp(cmds[i].name, argv[0], strlen(cmds[i].name))) {
if (cmds[i].min_argc <= argc)
return cmds[i].cmd(&argv[1]);
else
usage();
}
/* bogus command */
return 1;
}
int
main(int argc, char *argv[])
{
int i = 0, ret, read_stdin = 0;
char line[4096];
char *sockfile = getenv("WMIR_SOCKET");
/* command line args */
if (argc > 1) {
for (i = 1; (i < argc) && (argv[i][0] == '-'); i++) {
switch (argv[i][1]) {
case 'v':
fprintf(stderr, "%s", version[0]);
exit(0);
break;
case 's':
if (i + 1 < argc)
sockfile = argv[++i];
else
usage();
break;
case 'f':
read_stdin = 1;
break;
default:
usage();
break;
}
}
}
if ((argc <= 1) || (!read_stdin && (i + 1) >= argc)) {
fprintf(stderr, "%s", "wmir: arguments: ");
for (i = 1; i < argc; i++)
fprintf(stderr, "%s, ", argv[i]);
fprintf(stderr, "%s", "\n");
usage();
}
if (!sockfile) {
fprintf(stderr, "%s",
"wmir: error: WMIR_SOCKET environment not set\n");
usage();
}
/* open socket */
if (!ixp_client_init(&c, sockfile)) {
fprintf(stderr, "wmir: %s\n", c.errstr);
exit(1);
}
/* wether perform directly or read from stdin */
if (read_stdin) {
char *_argv[3];
int _argc;
while (fgets(line, 4096, stdin))
if ((_argc = tokenize(_argv, 3, line, ' '))) {
if ((ret = perform_cmd(_argc, _argv)))
break;
}
} else
ret = perform_cmd(argc - (i + 1), &argv[i]);
/* close socket */
ixp_client_deinit(&c);
return ret;
}

56
cmd/wmiwarp.c Normal file
View File

@ -0,0 +1,56 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
static char *version[] = {
"wmiwarp - window manager improved warp - " VERSION "\n"
" (C)opyright MMIV-MMV Anselm R. Garbe\n", 0
};
static void
usage()
{
fprintf(stderr,
"usage: wmiwarp [-v] <x>,<y>\n" " -v version info\n");
exit(1);
}
int
main(int argc, char **argv)
{
Display *dpy;
int x, y;
/* command line args */
if (argc != 2)
usage();
if (!strncmp(argv[1], "-v", 2)) {
fprintf(stdout, "%s", version[0]);
exit(0);
}
dpy = XOpenDisplay(0);
if (!dpy) {
fprintf(stderr, "%s", "wmiwarp: cannot open display\n");
exit(1);
}
if (!strncmp(argv[1], "center", 7)) {
x = DisplayWidth(dpy, DefaultScreen(dpy)) / 2;
y = DisplayHeight(dpy, DefaultScreen(dpy)) / 2;
} else if (sscanf(argv[1], "%d,%d", &x, &y) != 2) {
XCloseDisplay(dpy);
usage();
}
XWarpPointer(dpy, None, RootWindow(dpy, DefaultScreen(dpy)),
0, 0, 0, 0, x, y);
XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
XCloseDisplay(dpy);
return 0;
}

28
config.mk Normal file
View File

@ -0,0 +1,28 @@
# Customize to fit your system
# paths
PREFIX = /usr/local
CONFPREFIX = ${PREFIX}/etc
MANPREFIX = ${PREFIX}/share/man
9PREFIX = ${PREFIX}/9
INCDIR = ${PREFIX}/include
LIBDIR = ${PREFIX}/lib
X11INC = /usr/X11R6/include
X11LIB = /usr/X11R6/lib
# includes and libs
INCLUDES = -I. -I${INCDIR} -I/usr/include -I${X11INC}
LIBS = -L${LIBDIR} -L/usr/lib -lc -lm -L${X11LIB} -lX11
VERSION = 3-current
# flags
CFLAGS = -O0 -g -Wall ${INCLUDES} -DVERSION=\"${VERSION}\"
LDFLAGS = -g ${LIBS}
# compiler
# Note: - under Solaris add -D__EXTENSIONS__ to CFLAGS
MAKE = make
AR = ar cr
CC = cc
RANLIB = ranlib

198
doc/NOTES Normal file
View File

@ -0,0 +1,198 @@
Development notes of wmii
===============================================
Last change: 2005-06-29, garbeam
Audience
--------
This document is intended to describe the architectural decisions and
concepts of the wmii development. It is primary written for developers.
Motivation
----------
There're several reasons for rewriting wmi:
1. Monolithic object-oriented design
wmi consisted of a single binary which included all components we know,
the window manager itself, three different bars, a shortcut box and the
slot. Monolithic designs provide some advantages (no shared memory
needed between the different components, special optimizations possible),
but they lead to high coupling (code overhead) and reduce maintainability.
2. The XTextProperty based remote interface.
Originally the idea of wmiremote was derived from larswm and is based on
XTextProperties, which suffer from reliability and correct order under
high system load (maybe due to bugs in X itself). But for the context of
wmi they just work and do their job fine. However, a guy called Uriel
came up one day in #wmi IRC channel and asked to generalize the
interface and change its property-based character to a file input/output
interface similar to the 9p protocol of plan9.
In long discussions we agreed and tried a prototypical implementation in
wmi, which can be found between revisions 640-735 of the wmi Subversion
repository.
3. The wmi configuration interface lacked on-the-fly changes
If a user wanted to change a setting, he had to restart wmi. As
workaround, which produced much complexity, some convenience actions like
create-workspace or bind-shortcut have been implemented, that the user
was able to change a subset of often needed things on-the-fly. Anyway, this
sucked a lot and made wmi less attractive. Within the above revisions we
also implemented prototypical support for on-the-fly changes in wmi.
4. Software law
However the prototypical implementation was very time-consuming without
stable and usable results (in wmi Subversion repository they can be
found in trunk/current/wmi). Thus we realized, that wmi itself was
a prototype only around end of September 2004. Originally the
prototypical changes between revisions 640-735 were intended to be used
in wmi-10, but they changed things to deep. Thus wmii was born.
Architecture
------------
To solve the above issues found in wmi are the requirements for wmii:
1. Modularized design
wmii separates all different components into different binaries. We
evaluated the dlopen() mechanism to load modules dynamically (like ion
does), but skipped that for stability reasons (one single invalid memory
access would crash the whole application in dlopen() world).
To share code between components we separated also two libraries,
which are initially written for wmii, and most wmii components link
statically against them.
The components and libraries of wmii look as follows:
* libixp (remote interface library)
* liblitz (non-wimp GUI-toolkit library)
* wmii (core WM)
* wmibar (a generic bar)
* wmiinput (input bar)
* wmir (remote interface client)
* wmikeys (shortcut handler)
The modularization has been chosen by two different point of views:
a) The separation of independent tasks, which means that a pager or
input bar should be independent from a window manager itself.
b) The separation of technically independent stuff, e.g. managing windows
is completely independent from shortcut handling - in X world at least.
The libixp is described below.
The liblitz contains primary all drawing code, which is used by
all separated components, and provides some util functions.
If you implement a function within a wmii component, first ask yourself,
if that function would make sense also in other components, if so,
export it to liblitz, the non-wimp GUI toolkit.
2. Remote interface
The remote interface is implemented with the new libixp library, which
is independent from all other parts of wmii and provides a client/server
API for a userland memory filesystem with a similar approach as 9p
server- and clients of the plan9 operating system.
An IXP server provides and manages a memory file system which is accessed
concurrently by several clients over a UNIX socket file. All above wmii
components, except wmir, are IXP servers. Since the IXP server
dispatches its connections with a select() based loop, it also
represents the X event loop for above components, because the
ConnectionNumber(X11) is the file descriptor of a connection to the X
server. If requested, the IXP server also selects() for file descriptors
provided by the server implementing developer and runs appropriate read()
and write() callbacks. In above components we need only a read()
callback for the ConnectionNumber, because the event handling of X is
single-directional and awakes if X writes data to that file descriptor.
The file system what the server manages, is capable to manage arbitrary
length paths, infinite files (as long as memory is available), and byte
content of files through following functions:
* create (creates file/path)
* remove (removes file/path)
* open (opens file)
* read (reads opened file)
* write (writes opened file)
* close (closes opened file)
Processes which implement such a file server can access the memory file
system explicitly, which is done by all above components for
performance reasons. Because the server handles all accesses
sequentially there is no race condition.
An IXP client, like wmir, accesses an IXP server remotely to perform
above functions. The wmir of wmii is not allowed to create or to remove
files/paths, because all components of wmii handle file creation and
destruction internally, because they are IXP servers and can do so.
But that is only a special case. In general each client is capable to
request the server to create or remove files.
Not allowing all IXP functions to wmir is easy to understand, because
the problematic of accidentally removals of important files,
which guarantees the functionality of wmii components, is prevented.
If for example an IXP client would request to remove / in wmii, wmii
would crash immediately, because it won't be able to read its needed
files.
As you might notice, libixp provides accessing shared memory through a
decent file input/output based interface between an IXP server and IXP
clients, which is used for configuration and scripting. To get further
details about IXP, start with libixp/libixp.h.
3. Configuration
The above described remote interface is used for configuration. The idea
is very similar to the procfs of Linux and other Unices which is
accessed to configure the kernel. The above IXP servers provide following
special file systems which are used for configuration and can be
explored with:
wmir -s $HOME/.wmii/ixp/<socket file> read <path>
e.g.
wmir -s $HOME/.wmii/ixp/wmii-:0 read /
Reading a path which is no file, returns the contents of a directory. In
IXP there's no differentiation between files and directories. Whether a
file contains content (byte data) or it contains files (you can compare
the term 'file' in IXP to inodes in UNIX world).
Note: By default all wmi components use a socket file in
$HOME/.wmii/ixp/<component>-$DISPLAY. That arose some issues with the
$DISPLAY variable, because it is on X startup mostly set to ':0'
(':<display number>'), but if wmii runs and you invoke terms it is set
to the screen which is focused (important for multihead configurations),
that means that a ':0.<screen number>' is appended. Thus reading simply
using $HOME/.wmii/ixp/<component>-$DISPLAY might fail, because of the
appended '.<screen number>' - keep that in mind!
Each server developer decides by itself how its file system should look
like to configure it. A very basic configuration interface filesystem
can be found in wmipager, the most complex one can be found in wmii.
4. Software law
Yes, wmi was implemented with C++, but wmii is implemented with C. Most
people are surprised about that fact. But there're several reasons for
this. Most are rarely rational, but there're arguable ones:
* C code compiles faster
* all dependencies (Xlib, libc, ...) are implemented with C and
don't provide an object oriented API
* C code produces smaller binaries
One design goal of wmii is to win the challenge of simplicity, which
means that our upper boundary of code size is 10.000 SLOC (including
above components, libwmii and libixp).
Style guide
-----------
We use the traditional Unix-style from Bell Labs in the source code.
We only comment things which are hard to understand or not obvious.
Technical Details
-----------------
The window management of wmii conforms to the ICCCM specification,
although client supplied icons and iconified window states are not
handled for simplicity reasons and because they don't fit well with the
tiled window management.
Apart from this, wmii conforms partly to the EWMH specification to fit
well with the requirements of more recent applications in the area of
the KDE and Gnome desktop, although to fulfill these hints are no primary
target of wmii's window management capabilities.

45
doc/RELEASE Normal file
View File

@ -0,0 +1,45 @@
Abbadon
Amare
Andariel
Andon
ArchDean
Beth
Bethanel
Boyd * (2)
Camael
Charon
Duma
Djibril
Elerial
Foucault
Frankie
Gabriel
Haniel
Israfiel
Izra'il
Japhkiel
Johab
Loki
Lucifer
Lycurgus
Munkar
Michael
Mikaal
Metathron
Melody
Naarai
Nakir
Raphael
Raziel
Satan
Sandalphon
Sophia
Sunya
Tini * (3)
Thanatos
Tothiel
Ultimus Maximus
Uriel * (1)
Yhprum
Zaphkiel
Zadkiel * (1.1)

61
doc/wmii.svg Normal file
View File

@ -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>

After

Width:  |  Height:  |  Size: 2.3 KiB

83
doc/wmii.tex Normal file
View File

@ -0,0 +1,83 @@
% (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}

8
extra/README Normal file
View File

@ -0,0 +1,8 @@
Scripts in this directory have been contributed by various different
people. For License information look into the source code of the
appropriate script.
Name: pkeys
Purpose: Print the current key bindings.
Requirements: 9base

6
extra/pkeys Normal file
View File

@ -0,0 +1,6 @@
#!/usr/local/9/bin/rc
mdir=/keys/mode/
for (mode in `{wmir read $mdir})
for (key in `{wmir read $mdir$mode})
echo $mode$key '->' `{wmir read $mdir$mode$key}

25
libcext/Makefile Normal file
View File

@ -0,0 +1,25 @@
# libcext - c extensions for wmii project
# (C)opyright MMIV-MMV Anselm R. Garbe
include ../config.mk
SRC = array.c emalloc.c estrdup.c strlcat.c strlcpy.c strtonum.c tokenize.c
OBJ = ${SRC:.c=.o}
.PREFIXES = .c .o
all: libcext
@echo built libcext
.c.o:
@echo CC $<
@${CC} -c ${CFLAGS} $<
libcext: ${OBJ}
@echo AR $@.a
@${AR} $@.a ${OBJ}
@${RANLIB} $@.a
clean:
rm -f libcext.a *.o

98
libcext/array.c Normal file
View File

@ -0,0 +1,98 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <stdlib.h>
#include "cext.h"
void **
attach_item_begin(void **old, void *item, size_t size_item)
{
int i, size_old;
void **result = 0;
for (size_old = 0; old && old[size_old]; size_old++);
result = emalloc(size_item * (size_old + 2));
result[0] = item;
for (i = 0; old && old[i]; i++)
result[i + 1] = old[i];
result[i + 1] = 0;
if (old)
free(old);
return result;
}
void **
attach_item_end(void **old, void *item, size_t size_item)
{
int i, size_old;
void **result = 0;
for (size_old = 0; old && old[size_old]; size_old++);
result = emalloc(size_item * (size_old + 2));
for (i = 0; old && old[i]; i++)
result[i] = old[i];
result[i++] = item;
result[i] = 0;
if (old)
free(old);
return result;
}
void **
detach_item(void **old, void *item, size_t size_item)
{
int size_old, i, j = 0;
void **result = 0;
for (size_old = 0; old && old[size_old]; size_old++);
if (size_old != 1) {
result = emalloc(size_item * size_old);
for (i = 0; old[i]; i++)
if (old[i] != item)
result[j++] = old[i];
result[j] = 0;
}
if (old)
free(old);
return result;
}
int
index_item(void **items, void *item)
{
int i = 0;
for (i = 0; items && items[i] && (items[i] != item); i++);
return items[i] ? i : -1;
}
int
count_items(void **items)
{
int i;
for (i = 0; items && items[i]; i++);
return i;
}
int
index_next_item(void **items, void *item)
{
int idx = index_item(items, item);
if (idx == -1)
return idx;
if (idx == count_items(items) - 1)
return 0;
else
return idx + 1;
}
int
index_prev_item(void **items, void *item)
{
int idx = index_item(items, item);
if (idx == -1)
return idx;
if (idx == 0)
return count_items(items) - 1;
else
return idx - 1;
}

45
libcext/cext.h Normal file
View File

@ -0,0 +1,45 @@
/*
* (C)opyright MMIV-MMV 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
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
/* array.c */
void **attach_item_end(void **old, void *item, size_t size_item);
void **attach_item_begin(void **old, void *item, size_t size_item);
void **detach_item(void **old, void *item, size_t size_item);
int index_item(void **items, void *item);
int index_next_item(void **items, void *item);
int index_prev_item(void **items, void *item);
int count_items(void **items);
/* emalloc.c */
void *emalloc(size_t size);
/* estrdup.c */
char *estrdup(const char *s);
/* strlcat.c */
size_t _strlcat(char *dst, const char *src, size_t siz);
/* strlcpy.c */
size_t _strlcpy(char *dst, const char *src, size_t siz);
/* strtonum.c */
long long
__strtonum(const char *numstr, long long minval,
long long maxval, const char **errstrp);
/* tokenize.c */
size_t tokenize(char **result, size_t reslen, char *str, char delim);

22
libcext/emalloc.c Normal file
View File

@ -0,0 +1,22 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <stdlib.h>
#include <stdio.h>
#include "cext.h"
void *
emalloc(size_t size)
{
void *res = malloc(size);
if (!res) {
fprintf(stderr, "fatal: could not malloc() %d bytes\n",
(int) size);
exit(1);
}
return res;
}

21
libcext/estrdup.c Normal file
View File

@ -0,0 +1,21 @@
/*
* (C)opyright MMIV-MMV 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"
char *
estrdup(const char *s)
{
char *tmp;
tmp = (char *) emalloc(strlen(s) + 1);
strcpy(tmp, (char *) s);
return tmp;
}

55
libcext/strlcat.c Normal file
View File

@ -0,0 +1,55 @@
/*
* 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.
*/
size_t
_strlcat(char *dst, const char *src, size_t siz)
{
register char *d = dst;
register const char *s = src;
register size_t n = siz;
size_t 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 */
}

47
libcext/strlcpy.c Normal file
View File

@ -0,0 +1,47 @@
/*
* 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.
*/
size_t
_strlcpy(char *dst, const char *src, size_t siz)
{
register char *d = dst;
register const char *s = src;
register size_t 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 */
}

68
libcext/strtonum.c Normal file
View File

@ -0,0 +1,68 @@
/*
* Copyright (c) 2004 Ted Unangst and Todd Miller
* All rights reserved.
*
* 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 <errno.h>
#include <limits.h>
#include <stdlib.h>
#define INVALID 1
#define TOOSMALL 2
#define TOOLARGE 3
long long
__strtonum(const char *numstr, long long minval, long long maxval,
const char **errstrp)
{
long long ll = 0;
char *ep;
int error = 0;
struct errval {
const char *errstr;
int err;
} ev[4] = {
{
NULL, 0
}, {
"invalid", EINVAL
}, {
"too small", ERANGE
}, {
"too large", ERANGE
},
};
ev[0].err = errno;
errno = 0;
if (minval > maxval)
error = INVALID;
else {
ll = strtoll(numstr, &ep, 10);
if (numstr == ep || *ep != '\0')
error = INVALID;
else if (errno == ERANGE || ll < minval)
error = TOOSMALL;
else if (errno == ERANGE || ll > maxval)
error = TOOLARGE;
}
if (errstrp != NULL)
*errstrp = ev[error].errstr;
errno = ev[error].err;
if (error)
ll = 0;
return (ll);
}

34
libcext/tokenize.c Normal file
View File

@ -0,0 +1,34 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <string.h>
#include "cext.h"
size_t
tokenize(char **result, size_t reslen, char *str, char delim)
{
char *p, *n;
size_t i = 0;
if (!str)
return 0;
for (n = str; *n == ' '; 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 (strlen(p))
result[i++] = p;
return i; /* number of tokens */
}

26
libixp/Makefile Normal file
View File

@ -0,0 +1,26 @@
# libixp - lib ixp protocol
# (C)opyright MMIV-MMV Anselm R. Garbe
include ../config.mk
CFLAGS += -I ../libcext
LDFLAGS += -L../libcext -lcext
SRC = client.c message.c ramfs.c server.c
OBJ = ${SRC:.c=.o}
all: libixp
@echo built libixp
.c.o:
@echo CC $<
@${CC} -c ${CFLAGS} $<
libixp: ${OBJ}
@echo AR $@.a
@${AR} $@.a ${OBJ}
@${RANLIB} $@.a
clean:
rm -f libixp.a *.o

302
libixp/client.c Normal file
View File

@ -0,0 +1,302 @@
/*
* (C)opyright MMIV-MMV 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 "ixp.h"
#include <cext.h>
static IXPClient zero_client = {0};
static size_t offsets[MAX_CONN * MAX_OPEN_FILES][2]; /* set of possible fd's */
static void
check_error(IXPClient * c, void *msg)
{
ResHeader h;
if (c->errstr)
free(c->errstr);
c->errstr = 0;
memcpy(&h, msg, sizeof(ResHeader));
if (h.res == RERROR)
c->errstr = strdup((char *) msg + sizeof(ResHeader));
}
static void
handle_dead_server(IXPClient * c)
{
if (c->errstr)
free(c->errstr);
c->errstr = strdup(DEAD_SERVER);
if (c->fd) {
shutdown(c->fd, SHUT_RDWR);
close(c->fd);
}
c->fd = -1;
}
static void *
poll_server(IXPClient * c, void *request, size_t req_len,
size_t * out_len)
{
size_t num = 0;
void *result = 0;
int r, header = 0;
if (c->errstr)
free(c->errstr);
c->errstr = 0;
/* first send request */
while (num < req_len) {
FD_ZERO(&c->wr);
FD_SET(c->fd, &c->wr);
r = select(c->fd + 1, 0, &c->wr, 0, 0);
if (r == -1 && errno == EINTR)
continue;
if (r < 0) {
perror("ixp: client: select");
if (result)
free(result);
handle_dead_server(c);
return 0;
} else if (r > 0) {
if (!header) {
/* write header first */
r = write(c->fd, &req_len, sizeof(size_t));
if (r != sizeof(size_t)) {
if (result)
free(result);
handle_dead_server(c);
return 0;
}
header = 1;
}
r = write(c->fd, ((char *) request) + num, req_len - num);
if (r < 1) {
perror("ixp: client: write");
if (result)
free(result);
handle_dead_server(c);
return 0;
}
num += r;
}
}
free(request); /* cleanup */
num = 0;
header = 0;
/* now wait for response */
do {
FD_ZERO(&c->rd);
FD_SET(c->fd, &c->rd);
r = select(c->fd + 1, &c->rd, 0, 0, 0);
if (r == -1 && errno == EINTR)
continue;
if (r < 0) {
perror("ixp: client: select");
if (result)
free(result);
handle_dead_server(c);
return 0;
} else if (r > 0) {
if (!header) {
r = read(c->fd, out_len, sizeof(size_t));
if (r != sizeof(size_t)) {
if (result)
free(result);
handle_dead_server(c);
return 0;
}
result = emalloc(*out_len);
header = 1;
}
r = read(c->fd, ((char *) result) + num, *out_len - num);
if (r < 1) {
perror("ixp: client: read");
if (result)
free(result);
handle_dead_server(c);
return 0;
}
num += r;
}
}
while (num != *out_len);
/* error checking */
if (result)
check_error(c, result);
if (c->errstr) {
free(result);
result = 0;
}
return result;
}
static void
cixp_create(IXPClient * c, char *path)
{
ResHeader h;
size_t req_len, res_len;
void *req = tcreate_message(path, &req_len);
void *result = poll_server(c, req, req_len, &res_len);
if (!result)
return;
memcpy(&h, result, sizeof(ResHeader));
free(result);
}
static int
cixp_open(IXPClient * c, char *path)
{
ResHeader h;
size_t req_len, res_len;
void *req = topen_message(path, &req_len);
void *result = poll_server(c, req, req_len, &res_len);
if (!result)
return -1;
memcpy(&h, result, sizeof(ResHeader));
free(result);
offsets[h.fd][0] = offsets[h.fd][1] = 0;
return h.fd;
}
static size_t
cixp_read(IXPClient * c, int fd, void *out_buf, size_t out_buf_len)
{
size_t len;
len = seek_read(c, fd, offsets[fd][0], out_buf, out_buf_len);
if (c->errstr)
return 0;
if (len == out_buf_len)
offsets[fd][0] += len;
return len;
}
size_t
seek_read(IXPClient * c, int fd, size_t offset,
void *out_buf, size_t out_buf_len)
{
ResHeader h;
size_t req_len, res_len;
void *req = tread_message(fd, offset, out_buf_len, &req_len);
void *result = poll_server(c, req, req_len, &res_len);
if (!result)
return -1;
memcpy(&h, result, sizeof(ResHeader));
memcpy(out_buf, ((char *) result) + sizeof(ResHeader), h.buf_len);
free(result);
return h.buf_len;
}
static void
cixp_write(IXPClient * c, int fd, void *content, size_t in_len)
{
seek_write(c, fd, offsets[fd][1], content, in_len);
if (!c->errstr)
offsets[fd][1] += in_len;
}
void
seek_write(IXPClient * c, int fd, size_t offset, void *content,
size_t in_len)
{
ResHeader h;
size_t req_len, res_len;
void *req = twrite_message(fd, offset, content, in_len, &req_len);
void *result = poll_server(c, req, req_len, &res_len);
if (!result)
return;
memcpy(&h, result, sizeof(ResHeader));
free(result);
}
static void
cixp_close(IXPClient * c, int fd)
{
ResHeader h;
size_t req_len, res_len;
void *req = tclose_message(fd, &req_len);
void *result = poll_server(c, req, req_len, &res_len);
if (!result)
return;
memcpy(&h, result, sizeof(ResHeader));
free(result);
offsets[fd][0] = offsets[fd][1] = 0;
}
static void
cixp_remove(IXPClient * c, char *path)
{
ResHeader h;
size_t req_len, res_len;
void *req = tremove_message(path, &req_len);
void *result = poll_server(c, req, req_len, &res_len);
if (!result)
return;
memcpy(&h, result, sizeof(ResHeader));
free(result);
}
IXPClient *
init_client(char *sockfile)
{
struct sockaddr_un addr = {0};
socklen_t su_len;
/* init */
IXPClient *c = (IXPClient *) emalloc(sizeof(IXPClient));
*c = zero_client;
c->create = cixp_create;
c->open = cixp_open;
c->read = cixp_read;
c->write = cixp_write;
c->close = cixp_close;
c->remove = cixp_remove;
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, sockfile, sizeof(addr.sun_path));
su_len = sizeof(struct sockaddr) + strlen(addr.sun_path);
if ((c->fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
free(c);
return 0;
}
if (connect(c->fd, (struct sockaddr *) & addr, su_len)) {
close(c->fd);
free(c);
return 0;
}
return c;
}
void
deinit_client(IXPClient * c)
{
if (c->fd) {
shutdown(c->fd, SHUT_RDWR);
close(c->fd);
}
c->fd = -1;
free(c);
}

188
libixp/ixp.h Normal file
View File

@ -0,0 +1,188 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#define MAX_CONN 32
#define MAX_OPEN_FILES 16
#define DEAD_SERVER "server closed connection unexpectedly"
#define MAX_SEEN_SHUTDOWN 3
#define _MAX(x,y) ((x) > (y) ? (x) : (y))
/* 9P message types */
enum {
TVERSION = 100,
RVERSION,
TAUTH = 102,
RAUTH,
TATTACH = 104,
RATTACH,
TERROR = 106,
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,
};
typedef struct ResHeader ResHeader;
typedef struct ReqHeader ReqHeader;
typedef struct Connection Connection;
typedef struct File File;
typedef struct IXPClient IXPClient;
typedef struct IXPServer IXPServer;
typedef int IXPRequest;
typedef int IXPResponse;
typedef enum {
HALT, SHUTDOWN, RUNNING
} IXPRunlevel;
struct ReqHeader {
IXPRequest req;
int fd;
size_t offset;
size_t buf_len;
};
struct ResHeader {
IXPResponse res;
int fd;
size_t buf_len;
};
/** Definition of a connection to IXP */
struct Connection {
IXPServer *s; /* !< server for this connection */
int index; /* !< index inside server */
int fd; /* !< file descriptor */
int mode; /* 0 for reading, 1 for writing */
int header;
void *data;
size_t len;
size_t remain;
void (*read) (Connection *);
void (*write) (Connection *);
File *files[MAX_OPEN_FILES];
int seen[MAX_OPEN_FILES];
};
struct File {
char *name;
void *content;
size_t size;
int lock;
int bind;
File *parent;
File *next;
/* introduced IXPServer in signature for IXPServer->errstr */
void (*after_write) (IXPServer * s, File *);
void (*before_read) (IXPServer * s, File *);
};
struct IXPClient {
int fd;
fd_set rd, wr;
char *errstr; /* 0 if succes, CHECK AFTER EACH of following
* operations */
/* returns fd if path exists */
void (*create) (IXPClient *, char *path);
void (*remove) (IXPClient *, char *path);
int (*open) (IXPClient *, char *path);
void (*close) (IXPClient *, int fd);
size_t(*read) (IXPClient *, int fd, void *out_buf,
size_t out_buf_len);
void (*write) (IXPClient *, int fd, void *content, size_t in_len);
};
struct IXPServer {
char *sockfile;
IXPRunlevel runlevel;
Connection conn[MAX_CONN];
fd_set rd, wr; /* socks to wakeup while select() */
int nfds; /* number of file descriptors */
File *root;
char *errstr; /* 0 if succes, CHECK AFTER EACH of following
* operations */
File *(*create) (IXPServer *, char *);
File *(*open) (IXPServer *, char *);
size_t(*read) (IXPServer *, int, size_t, void *, size_t);
void (*write) (IXPServer *, int, size_t, void *, size_t);
void (*close) (IXPServer *, int);
void (*remove) (IXPServer *, char *);
};
/* client.c, implements client stub functions for fs access */
IXPClient *init_client(char *sockfile);
void deinit_client(IXPClient * c);
size_t
seek_read(IXPClient * c, int fd, size_t offset, void *out_buf,
size_t out_buf_len);
void
seek_write(IXPClient * c, int fd, size_t offset,
void *content, size_t in_len);
/* message.c */
void *tcreate_message(char *path, size_t * msg_len);
void *topen_message(char *path, size_t * msg_len);
void *
tread_message(int fd, size_t offset, size_t buf_len,
size_t * msg_len);
void *
twrite_message(int fd, size_t offset, void *msg, size_t in_len,
size_t * msg_len);
void *tclose_message(int fd, size_t * msg_len);
void *tremove_message(char *path, size_t * msg_len);
void *rcreate_message(size_t * msg_len);
void *ropen_message(int fd, size_t * msg_len);
void *rread_message(void *content, size_t content_len, size_t * msg_len);
void *rwrite_message(size_t * msg_len);
void *rclose_message(size_t * msg_len);
void *rremove_message(size_t * msg_len);
void *rerror_message(char *errstr, size_t * msg_len);
/* ramfs.c */
int is_directory(File * f);
File *ixp_walk(IXPServer * s, char *path);
File *ixp_create(IXPServer * s, char *path);
File *ixp_open(IXPServer * s, char *path);
size_t
ixp_read(IXPServer * s, int fd, size_t offset, void *out_buf,
size_t out_buf_len);
void
ixp_write(IXPServer * s, int fd, size_t offset,
void *content, size_t in_len);
void ixp_close(IXPServer * s, int fd);
void ixp_remove(IXPServer * s, char *path);
void ixp_remove_file(IXPServer * s, File * f);
/* server.c, uses fs directly for manipulation */
IXPServer *init_server(char *sockfile, void (*cleanup) (void));
void deinit_server(IXPServer * s);
File *fd_to_file(IXPServer * s, int fd);
void run_server(IXPServer * s);
void run_server_with_fd_support(IXPServer * s, int fd, void (*fd_read) (Connection *), /* callback for read on
* fd */
void (*fd_write) (Connection *)); /* callback for write on
* fd */
void set_error(IXPServer * s, char *errstr);

189
libixp/message.c Normal file
View File

@ -0,0 +1,189 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <stdlib.h>
#include <string.h>
#include "ixp.h"
#include <cext.h>
/* request messages ------------------------------------------------ */
void *
tcreate_message(char *path, size_t * msg_len)
{
char *msg;
ReqHeader h;
*msg_len = sizeof(ReqHeader) + strlen(path) + 1;
msg = emalloc(*msg_len);
h.req = TCREATE;
memcpy(msg, &h, sizeof(ReqHeader));
memcpy(msg + sizeof(ReqHeader), path, strlen(path) + 1);
return msg;
}
void *
topen_message(char *path, size_t * msg_len)
{
char *msg;
ReqHeader h;
*msg_len = sizeof(ReqHeader) + strlen(path) + 1;
msg = emalloc(*msg_len);
h.req = TOPEN;
memcpy(msg, &h, sizeof(ReqHeader));
memcpy(msg + sizeof(ReqHeader), path, strlen(path) + 1);
return msg;
}
void *
tread_message(int fd, size_t offset, size_t buf_len,
size_t * msg_len)
{
char *msg;
ReqHeader h;
*msg_len = sizeof(ReqHeader);
msg = emalloc(*msg_len);
h.req = TREAD;
h.fd = fd;
h.offset = offset;
h.buf_len = buf_len;
memcpy(msg, &h, sizeof(ReqHeader));
return msg;
}
void *
twrite_message(int fd, size_t offset, void *content,
size_t content_len, size_t * msg_len)
{
char *msg;
ReqHeader h;
*msg_len = sizeof(ReqHeader) + content_len;
msg = emalloc(*msg_len);
h.req = TWRITE;
h.fd = fd;
h.offset = offset;
h.buf_len = content_len;
memcpy(msg, &h, sizeof(ReqHeader));
memcpy(msg + sizeof(ReqHeader), content, content_len);
return msg;
}
void *
tclose_message(int fd, size_t * msg_len)
{
char *msg;
ReqHeader h;
*msg_len = sizeof(ReqHeader);
msg = emalloc(*msg_len);
h.req = TCLUNK;
h.fd = fd;
memcpy(msg, &h, sizeof(ReqHeader));
return msg;
}
void *
tremove_message(char *path, size_t * msg_len)
{
char *msg;
ReqHeader h;
*msg_len = sizeof(ReqHeader) + strlen(path) + 1;
msg = emalloc(*msg_len);
h.req = TREMOVE;
memcpy(msg, &h, sizeof(ReqHeader));
memcpy(msg + sizeof(ReqHeader), path, strlen(path) + 1);
return msg;
}
/* response messages ----------------------------------------------- */
void *
rcreate_message(size_t * msg_len)
{
char *msg;
ResHeader h;
*msg_len = sizeof(ResHeader);
msg = emalloc(*msg_len);
h.res = RCREATE;
memcpy(msg, &h, sizeof(ResHeader));
return msg;
}
void *
ropen_message(int fd, size_t * msg_len)
{
char *msg;
ResHeader h;
*msg_len = sizeof(ResHeader);
msg = emalloc(*msg_len);
h.res = ROPEN;
h.fd = fd;
memcpy(msg, &h, sizeof(ResHeader));
return msg;
}
void *
rread_message(void *content, size_t content_len, size_t * msg_len)
{
char *msg;
ResHeader h;
*msg_len = sizeof(ResHeader) + content_len;
msg = emalloc(*msg_len);
h.res = RREAD;
h.buf_len = content_len;
memcpy(msg, &h, sizeof(ResHeader));
memmove(msg + sizeof(ResHeader), content, content_len);
return msg;
}
void *
rwrite_message(size_t * msg_len)
{
char *msg;
ResHeader h;
*msg_len = sizeof(ResHeader);
msg = emalloc(*msg_len);
h.res = RWRITE;
memcpy(msg, &h, sizeof(ResHeader));
return msg;
}
void *
rclose_message(size_t * msg_len)
{
char *msg;
ResHeader h;
*msg_len = sizeof(ResHeader);
msg = emalloc(*msg_len);
h.res = RCLUNK;
memcpy(msg, &h, sizeof(ResHeader));
return msg;
}
void *
rremove_message(size_t * msg_len)
{
char *msg;
ResHeader h;
*msg_len = sizeof(ResHeader);
msg = emalloc(*msg_len);
h.res = RREMOVE;
memcpy(msg, &h, sizeof(ResHeader));
return msg;
}
void *
rerror_message(char *errstr, size_t * msg_len)
{
char *msg;
size_t len = strlen(errstr) + 1;
ResHeader h;
*msg_len = sizeof(ResHeader) + len;
msg = emalloc(*msg_len);
h.res = RERROR;
memcpy(msg, &h, sizeof(ResHeader));
memmove(msg + sizeof(ResHeader), errstr, len);
return msg;
}

322
libixp/ramfs.c Normal file
View File

@ -0,0 +1,322 @@
/*
* (C)opyright MMIV-MMV 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"
#include <cext.h>
static File zero_file = {0};
int
is_directory(File * f)
{
return !f->size && f->content;
}
File *
ixp_create(IXPServer * s, char *path)
{
File *f, *p;
char *buf = strdup(path);
char *tok, *tok_ptr;
if (!path)
return 0;
/* cannot create a directory with empty name */
if (buf[strlen(buf) - 1] == '/')
return 0;
tok = strtok_r(buf, "/", &tok_ptr);
p = s->root;
f = p->content;
/* first determine the path as it exists already */
while (f && tok) {
if (!strcmp(f->name, tok)) {
tok = strtok_r(0, "/", &tok_ptr);
p = f;
if (tok && is_directory(f))
f = f->content;
else
break;
continue;
}
f = f->next;
}
if (p->size && tok) {
free(buf);
return 0; /* cannot create subdirectory, if file has
* content */
}
/* only create missing parts, if file is directory */
while (tok) {
f = (File *) emalloc(sizeof(File));
*f = zero_file;
f->name = strdup(tok);
f->parent = p;
if (p->content) {
p = p->content;
while (p->next) {
p = p->next;
}
p->next = f;
} else {
p->content = f;
}
p = f;
tok = strtok_r(0, "/", &tok_ptr);
}
free(buf);
return f;
}
static int
comp_file_name(const void *f1, const void *f2)
{
File *f = (File *) f1;
File *p = (File *) f2;
return strcmp(*(char **) f->name, *(char **) p->name);
}
static char *
_ls(File * f)
{
File *p;
char *result = 0;
size_t size = 1; /* for \n */
int num = 0, i;
File **tmp;
for (p = f; p; p = p->next)
num++;
tmp = emalloc(sizeof(File *) * num);
i = 0;
for (p = f; p; p = p->next) {
size += strlen(p->name) + 1;
if (is_directory(p))
size++;
tmp[i++] = p;
}
qsort(tmp, num, sizeof(char *), comp_file_name);
result = emalloc(size);
result[0] = '\0';
for (i = 0; i < num; i++) {
strncat(result, tmp[i]->name, size);
if (is_directory(tmp[i]))
strncat(result, "/\n", size);
else
strncat(result, "\n", size);
}
free(tmp);
return result;
}
File *
ixp_open(IXPServer * s, char *path)
{
File *f;
f = ixp_walk(s, path);
if (!f) {
set_error(s, "file does not exist");
return 0;
}
f->lock++;
return f;
}
void
ixp_close(IXPServer * s, int fd)
{
File *f = fd_to_file(s, fd);
if (!f)
set_error(s, "invalid file descriptor");
else if (f->lock > 0)
f->lock--;
}
size_t
ixp_read(IXPServer * s, int fd, size_t offset, void *out_buf,
size_t out_buf_len)
{
File *f = fd_to_file(s, fd);
void *result = 0;
size_t len = 0, res_len = 0;
if (!f) {
set_error(s, "invalid file descriptor");
return 0;
}
/* callback */
if (f->before_read)
f->before_read(s, f);
if (is_directory(f)) {
result = _ls(f->content);
res_len = strlen(result);
} else if (f->size) {
result = f->content;
res_len = f->size;
}
if (offset > res_len) {
set_error(s, "invalid offset when reading file");
if (is_directory(f))
free(result);
return 0;
}
if (result) {
len = res_len - offset;
if (len > out_buf_len)
len = out_buf_len;
memcpy(out_buf, (char *) result + offset, len);
if (is_directory(f))
free(result);
}
return len;
}
void
ixp_write(IXPServer * s, int fd, size_t offset, void *content,
size_t in_len)
{
File *f = fd_to_file(s, fd);
if (!f) {
set_error(s, "invalid file descriptor");
return;
}
if (is_directory(f)) {
/* we cannot write to directories */
set_error(s, "cannot write to a directory");
return;
}
if (in_len) {
/* offset 0 flushes the file */
if (!offset || (offset + in_len > f->size)) {
f->content = realloc(f->content, offset + in_len + 1);
f->size = offset + in_len;
}
memcpy((char *) f->content + offset, content, in_len);
/* internal EOF character */
((char *) f->content)[f->size] = '\0';
} else if (!offset) {
/* blank file */
if (f->content)
free(f->content);
f->content = 0;
f->size = 0;
}
/* callback */
if (f->after_write)
f->after_write(s, f);
}
static void
_ixp_remove(IXPServer * s, File * f)
{
if (!f)
return;
if (f->next) {
_ixp_remove(s, f->next);
if (s->errstr)
return;
}
if (f->lock) {
set_error(s, "cannot remove opened file");
return; /* a file is opened, so stop removing tree */
}
if (!f->bind && is_directory(f)) {
_ixp_remove(s, f->content);
if (s->errstr)
return;
}
if (f->content && f->size) {
free(f->content);
}
if (f != s->root) {
if (f->name) {
free(f->name);
}
free(f);
}
}
void
ixp_remove_file(IXPServer * s, File * f)
{
File *p, *n;
set_error(s, 0);
if (!f) {
set_error(s, "file does not exist");
return;
}
if (f->lock) {
set_error(s, "cannot remove opened file");
return;
}
/* detach */
p = f->parent;
n = f->next;
f->next = 0;
if (p) {
if (p->content == f)
p->content = n;
else {
p = p->content;
while (p && (p->next != f))
p = p->next;
if (p)
p->next = n;
}
}
/* remove now */
_ixp_remove(s, f);
}
void
ixp_remove(IXPServer * s, char *path)
{
ixp_remove_file(s, ixp_walk(s, path));
}
File *
ixp_walk(IXPServer * s, char *path)
{
File *f = 0;
File *n;
char *buf;
char *tok, *tok_ptr;
if (!path) {
return 0;
}
buf = strdup(path);
tok = strtok_r(buf, "/", &tok_ptr);
f = s->root->content;
if (!tok && buf[0] == '/') {
f = s->root;
}
while (f && tok) {
n = f->next;
if (!strcmp(f->name, tok)) {
tok = strtok_r(0, "/", &tok_ptr);
if (tok && f->size)
return 0;
if (!tok)
break;
f = f->content;
continue;
}
f = n;
}
if (f && (path[strlen(path) - 1] == '/') && !is_directory(f))
f = 0;
free(buf);
return f;
}

475
libixp/server.c Normal file
View File

@ -0,0 +1,475 @@
/*
* (C)opyright MMIV-MMV 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 <signal.h>
#include <string.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include "ixp.h"
#include <cext.h>
static File zero_file = {0};
static IXPServer zero_server = {0};
static Connection zero_conn = {0};
static int user_fd = -1;
void
set_error(IXPServer * s, char *errstr)
{
if (s->errstr)
free(s->errstr);
if (errstr)
s->errstr = strdup(errstr);
else
s->errstr = 0;
}
File *
fd_to_file(IXPServer * s, int fd)
{
int cidx = fd / MAX_CONN;
int fidx = fd - (cidx * MAX_CONN);
return s->conn[cidx].files[fidx];
}
static void
handle_ixp_create(Connection * c)
{
c->s->create(c->s, ((char *) c->data) + sizeof(ReqHeader));
free(c->data);
c->data = c->s->errstr ?
rerror_message(c->s->errstr, &c->len) : rcreate_message(&c->len);
c->remain = c->len;
}
static void
handle_ixp_open(Connection * c)
{
int i;
/* seek next free slot */
for (i = 0; (i < MAX_OPEN_FILES) && c->files[i]; i++);
if (i == MAX_OPEN_FILES) {
fprintf(stderr, "%s",
"ixp: server: maximum of open files, try again later.\n");
free(c->data);
c->data =
rerror_message("maximum open files reached, close files first",
&c->len);
c->remain = c->len;
return;
}
c->files[i] = c->s->open(c->s, ((char *) c->data) + sizeof(ReqHeader));
c->seen[i] = MAX_SEEN_SHUTDOWN;
free(c->data);
c->data = c->s->errstr ?
rerror_message(c->s->errstr,
&c->len) : ropen_message(i + MAX_CONN * c->index,
&c->len);
c->remain = c->len;
}
static void
handle_ixp_read(Connection * c, ReqHeader * h)
{
int fidx = h->fd - (c->index * MAX_CONN);
void *data = 0;
size_t out_len;
data = emalloc(h->buf_len);
out_len = c->s->read(c->s, h->fd, h->offset, data, h->buf_len);
free(c->data);
if (c->s->errstr) {
c->data = rerror_message(c->s->errstr, &c->len);
if (c->files[fidx] && c->files[fidx]->lock > 0)
c->files[fidx]->lock--;
} else
c->data = rread_message(data, out_len, &c->len);
c->remain = c->len;
free(data);
}
static void
handle_ixp_write(Connection * c, ReqHeader * h)
{
int fidx = h->fd - (c->index * MAX_CONN);
c->s->write(c->s, h->fd, h->offset,
((char *) c->data) + sizeof(ReqHeader), h->buf_len);
free(c->data);
if (c->s->errstr) {
c->data = rerror_message(c->s->errstr, &c->len);
if (c->files[fidx] && c->files[fidx]->lock > 0)
c->files[fidx]->lock--;
} else
c->data = rwrite_message(&c->len);
c->remain = c->len;
}
static void
handle_ixp_close(Connection * c, ReqHeader * h)
{
int fidx = h->fd - (c->index * MAX_CONN);
c->s->close(c->s, h->fd);
c->files[fidx] = 0;
free(c->data);
if (c->s->errstr) {
c->data = rerror_message(c->s->errstr, &c->len);
if (c->files[fidx] && c->files[fidx]->lock > 0)
c->files[fidx]->lock--;
} else
c->data = rclose_message(&c->len);
c->remain = c->len;
}
static void
handle_ixp_remove(Connection * c)
{
c->s->remove(c->s, ((char *) c->data) + sizeof(ReqHeader));
free(c->data);
c->data = c->s->errstr ?
rerror_message(c->s->errstr, &c->len) : rremove_message(&c->len);
c->remain = c->len;
}
static void
check_ixp_request(Connection * c)
{
ReqHeader h;
/* check pending request */
if (c->s->errstr)
set_error(c->s, 0);
memcpy(&h, c->data, sizeof(ReqHeader));
switch (h.req) {
case TCREATE:
handle_ixp_create(c);
break;
case TREMOVE:
handle_ixp_remove(c);
break;
case TOPEN:
handle_ixp_open(c);
break;
case TCLUNK:
handle_ixp_close(c, &h);
break;
case TREAD:
handle_ixp_read(c, &h);
break;
case TWRITE:
handle_ixp_write(c, &h);
break;
default:
fprintf(stderr, "%s", "ixp: server: invalid request\n");
free(c->data);
c->len = c->remain = 0;
break;
}
}
static void
update_conns(IXPServer * s)
{
int i;
FD_ZERO(&s->rd);
FD_ZERO(&s->wr);
for (i = 0; i < MAX_CONN; i++) {
if (s->conn[i].fd >= 0) {
s->nfds = _MAX(s->nfds, s->conn[i].fd);
if (s->conn[i].read && !s->conn[i].mode
&& (!s->conn[i].len || s->conn[i].remain)) {
FD_SET(s->conn[i].fd, &s->rd);
}
if (s->conn[i].write && s->conn[i].mode && s->conn[i].remain) {
FD_SET(s->conn[i].fd, &s->wr);
}
}
}
}
static void
close_conn(Connection * c)
{
int i;
/* shutdown connection and cleanup open files */
shutdown(c->fd, SHUT_RDWR);
close(c->fd);
c->fd = -1;
c->mode = 0;
for (i = 0; i < MAX_OPEN_FILES; i++) {
if (c->files[i]) {
if (c->files[i]->lock > 0)
c->files[i]->lock--;
c->files[i] = 0;
}
}
}
static void
read_conn(Connection * c)
{
size_t r;
if (!c->header) {
r = read(c->fd, &c->len, sizeof(size_t));
if (r != sizeof(size_t)) {
close_conn(c);
return;
}
c->remain = c->len;
c->data = emalloc(c->len);
c->header = 1;
}
r = read(c->fd, ((char *) c->data) + c->len - c->remain, c->remain);
if (r < 1) {
close_conn(c);
return;
}
c->remain -= r;
if (c->remain == 0) {
/* check IXP request */
c->mode = 1; /* next mode is response */
check_ixp_request(c);
c->header = 0;
}
}
static void
write_conn(Connection * c)
{
size_t r;
if (!c->header) {
r = write(c->fd, &c->len, sizeof(size_t));
if (r != sizeof(size_t)) {
close_conn(c);
}
c->header = 1;
}
r = write(c->fd, ((char *) c->data) + c->len - c->remain, c->remain);
if (r < 1) {
close_conn(c);
return;
}
c->remain -= r;
if (c->remain == 0) {
c->len = 0;
c->mode = 0;
c->header = 0;
}
}
static void
new_conn(Connection * c)
{
int r, i;
socklen_t l;
struct sockaddr_un name = {0};
l = sizeof(name);
if ((r = accept(c->fd, (struct sockaddr *) & name, &l)) < 0) {
perror("ixp: server: cannot accept connection");
return;
}
if (c->s->runlevel == SHUTDOWN) {
fprintf(stderr, "%s",
"ixp: server: connection refused, server is shutting down.\n");
close(r);
return;
}
for (i = 0; i < MAX_CONN; i++) {
if (c->s->conn[i].fd == -1) { /* free connection */
c->s->conn[i] = zero_conn;
c->s->conn[i].s = c->s;
c->s->conn[i].index = i;
c->s->conn[i].fd = r;
c->s->conn[i].read = read_conn;
c->s->conn[i].write = write_conn;
break;
}
}
if (i == MAX_CONN) {
fprintf(stderr, "%s",
"ixp: server: connection refused, try again later.\n");
close(r);
}
}
static int
check_open_files(Connection * c)
{
int i;
for (i = 0; i < MAX_OPEN_FILES; i++) {
if (c->files[i] && c->seen[i]) {
c->seen[i]--;
return 1;
}
}
return 0;
}
static void
handle_socks(IXPServer * s)
{
int i, now = 1;
for (i = 0; i < MAX_CONN; i++) {
if (s->conn[i].fd >= 0) {
if (FD_ISSET(s->conn[i].fd, &s->rd) && s->conn[i].read) {
/* call back read handler */
s->conn[i].read(&s->conn[i]);
} else if (FD_ISSET(s->conn[i].fd, &s->wr) && s->conn[i].write) {
/* call back write handler */
s->conn[i].write(&s->conn[i]);
}
/*
* don't shutdown, if there're remaining bits or if
* still responses are sent or still opened files
*/
if ((s->runlevel == SHUTDOWN)
&& (check_open_files(&s->conn[i])
|| (s->conn[i].remain > 0)
|| s->conn[i].mode))
now = 0;
}
}
if ((s->runlevel == SHUTDOWN) && now)
s->runlevel = HALT; /* real stop */
}
IXPServer *
init_server(char *sockfile, void (*cleanup) (void))
{
int i;
struct sockaddr_un addr = {0};
int yes = 1;
socklen_t su_len;
IXPServer *s;
/* init */
s = (IXPServer *) emalloc(sizeof(IXPServer));
*s = zero_server;
s->sockfile = sockfile;
s->root = (File *) emalloc(sizeof(File));
s->runlevel = HALT; /* initially server is not running */
s->create = ixp_create;
s->remove = ixp_remove;
s->open = ixp_open;
s->close = ixp_close;
s->read = ixp_read;
s->write = ixp_write;
*s->root = zero_file;
s->root->name = strdup("");
for (i = 0; i < MAX_CONN; i++) {
s->conn[i].s = s;
s->conn[i].fd = -1;
s->conn[i].index = i;
}
signal(SIGPIPE, SIG_IGN);
if ((s->conn[0].fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
perror("ixp: server: socket");
free(s);
return 0;
}
if (setsockopt(s->conn[0].fd, SOL_SOCKET, SO_REUSEADDR,
(char *) &yes, sizeof(yes)) < 0) {
perror("ixp: server: setsockopt");
close(s->conn[0].fd);
free(s);
return 0;
}
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, sockfile, sizeof(addr.sun_path));
su_len = sizeof(struct sockaddr) + strlen(addr.sun_path);
if (bind(s->conn[0].fd, (struct sockaddr *) & addr, su_len) < 0) {
perror("ixp: server: cannot bind socket");
close(s->conn[0].fd);
free(s);
return 0;
}
chmod(sockfile, S_IRWXU);
if (listen(s->conn[0].fd, MAX_CONN) < 0) {
perror("ixp: server: cannot listen on socket");
close(s->conn[0].fd);
free(s);
return 0;
}
s->conn[0].read = new_conn;
/* register to cleanup function, to unlink sockfile */
if (cleanup)
atexit(cleanup);
return s;
}
void
run_server_with_fd_support(IXPServer * s, int fd,
void (*fd_read) (Connection *),
void (*fd_write) (Connection *))
{
s->conn[1] = zero_conn;
s->conn[1].index = 1;
s->conn[1].s = s;
s->conn[1].fd = user_fd = fd;
s->conn[1].read = fd_read;
s->conn[1].write = fd_write;
run_server(s);
}
void
run_server(IXPServer * s)
{
int r, i;
s->runlevel = RUNNING;
/* main loop */
while (s->runlevel != HALT) {
update_conns(s);
r = select(s->nfds + 1, &s->rd, &s->wr, 0, 0);
if (r == -1 && errno == EINTR)
continue;
if (r < 0) {
perror("ixp: server: select");
break; /* allow cleanups in IXP using app */
} else if (r > 0) {
handle_socks(s);
}
}
/* shut down server */
for (i = MAX_CONN - 1; i >= 0; i--) {
if (s->conn[i].fd >= 0 && s->conn[i].fd != user_fd) {
close(s->conn[i].fd);
}
}
}
void
deinit_server(IXPServer * s)
{
unlink(s->sockfile);
ixp_remove(s, "/");
free(s);
}

25
libixp2/Makefile Normal file
View File

@ -0,0 +1,25 @@
# libixp - lib ixp protocol
# (C)opyright MMIV-MMV Anselm R. Garbe
include ../config.mk
CFLAGS += -I ../libcext
SRC = client.c convert.c message.c server.c socket.c transport.c
OBJ = ${SRC:.c=.o}
all: libixp
@echo built libixp2
.c.o:
@echo CC $<
@${CC} -c ${CFLAGS} $<
libixp: ${OBJ}
@echo AR $@.a
@${AR} $@.a ${OBJ}
@${RANLIB} $@.a
clean:
rm -f libixp.a *.o

191
libixp2/client.c Normal file
View File

@ -0,0 +1,191 @@
/*
* (C)opyright MMIV-MMV 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 "cext.h"
#include "ixp.h"
static u8 msg[IXP_MAX_MSG];
static int
do_fcall(IXPClient * c)
{
u32 msize = ixp_fcall_to_msg(&c->fcall, msg, IXP_MAX_MSG);
c->errstr = 0;
if (ixp_send_message(c->fd, msg, msize, &c->errstr) != msize)
return FALSE;
if (!ixp_recv_message(c->fd, msg, IXP_MAX_MSG, &c->errstr))
return FALSE;
if (!(msize = ixp_msg_to_fcall(msg, IXP_MAX_MSG, &c->fcall))) {
c->errstr = "received bad message";
return FALSE;
}
if (c->fcall.id == RERROR) {
c->errstr = c->fcall.errstr;
return FALSE;
}
return TRUE;
}
int
ixp_client_init(IXPClient * c, char *sockfile)
{
if ((c->fd = ixp_connect_sock(sockfile)) < 0) {
c->errstr = "cannot connect server";
return FALSE;
}
/* version */
c->fcall.id = TVERSION;
c->fcall.tag = IXP_NOTAG;
c->fcall.maxmsg = IXP_MAX_MSG;
_strlcpy(c->fcall.version, IXP_VERSION, sizeof(c->fcall.version));
if (!do_fcall(c)) {
ixp_client_deinit(c);
return FALSE;
}
if (strncmp(c->fcall.version, IXP_VERSION, strlen(IXP_VERSION))) {
c->errstr = "9P versions differ";
ixp_client_deinit(c);
return FALSE; /* we cannot handle this version */
}
c->root_fid = getpid();
/* attach */
c->fcall.id = TATTACH;
c->fcall.tag = IXP_NOTAG;
c->fcall.fid = c->root_fid;
c->fcall.afid = IXP_NOFID;
_strlcpy(c->fcall.uname, getenv("USER"), sizeof(c->fcall.uname));
c->fcall.aname[0] = '\0';
if (!do_fcall(c)) {
ixp_client_deinit(c);
return FALSE;
}
c->root_qid = c->fcall.qid;
return TRUE;
}
int
ixp_client_remove(IXPClient * c, u32 newfid, char *filepath)
{
if (!ixp_client_walk(c, newfid, filepath))
return FALSE;
/* remove */
c->fcall.id = TREMOVE;
c->fcall.tag = IXP_NOTAG;
c->fcall.fid = newfid;
return do_fcall(c);
}
int
ixp_client_create(IXPClient * c, u32 dirfid, char *name, u32 perm,
u8 mode)
{
/* create */
c->fcall.id = TCREATE;
c->fcall.tag = IXP_NOTAG;
c->fcall.fid = dirfid;
_strlcpy(c->fcall.name, name, sizeof(c->fcall.name));
c->fcall.perm = perm;
c->fcall.mode = mode;
return do_fcall(c);
}
int
ixp_client_walk(IXPClient * c, u32 newfid, char *filepath)
{
/* walk */
c->fcall.id = TWALK;
c->fcall.fid = c->root_fid;
c->fcall.newfid = newfid;
if (filepath) {
_strlcpy(c->fcall.name, filepath, sizeof(c->fcall.name));
c->fcall.nwname =
tokenize((char **) c->fcall.wname, IXP_MAX_WELEM,
c->fcall.name, '/');
}
return do_fcall(c);
}
int
ixp_client_open(IXPClient * c, u32 newfid, char *filepath, u8 mode)
{
if (!ixp_client_walk(c, newfid, filepath))
return FALSE;
/* open */
c->fcall.id = TOPEN;
c->fcall.tag = IXP_NOTAG;
c->fcall.fid = newfid;
c->fcall.mode = mode;
return do_fcall(c);
}
u32
ixp_client_read(IXPClient * c, u32 fid, u64 offset, void *result,
u32 res_len)
{
u32 bytes =
c->fcall.maxmsg - (sizeof(u8) + sizeof(u16) + 2 * sizeof(u32) +
sizeof(u64));
/* read */
c->fcall.id = TREAD;
c->fcall.tag = IXP_NOTAG;
c->fcall.fid = fid;
c->fcall.offset = offset;
c->fcall.count = res_len < bytes ? res_len : bytes;
if (!do_fcall(c))
return 0;
memcpy(result, c->fcall.data, c->fcall.count);
return c->fcall.count;
}
u32
ixp_client_write(IXPClient * c, u32 fid, u64 offset, u32 count,
u8 * data)
{
if (count >
c->fcall.maxmsg - (sizeof(u8) + sizeof(u16) + 2 * sizeof(u32) +
sizeof(u64))) {
c->errstr = "message size exceeds buffer size";
return 0;
}
/* write */
c->fcall.id = TWRITE;
c->fcall.tag = IXP_NOTAG;
c->fcall.fid = fid;
c->fcall.offset = offset;
c->fcall.count = count;
memcpy(c->fcall.data, data, count);
if (!do_fcall(c))
return 0;
return c->fcall.count;
}
int
ixp_client_close(IXPClient * c, u32 fid)
{
/* clunk */
c->fcall.id = TCLUNK;
c->fcall.tag = IXP_NOTAG;
c->fcall.fid = fid;
return do_fcall(c);
}
void
ixp_client_deinit(IXPClient * c)
{
/* session finished, now shutdown */
if (c->fd) {
shutdown(c->fd, SHUT_RDWR);
close(c->fd);
}
}

187
libixp2/convert.c Normal file
View File

@ -0,0 +1,187 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <stdlib.h>
#include <string.h>
#include "ixp.h"
/* encode/decode stuff */
void *
ixp_enc_u8(u8 * msg, u8 val)
{
msg[0] = val;
return &msg[1];
}
void *
ixp_dec_u8(u8 * msg, u8 * val)
{
*val = msg[0];
return &msg[1];
}
void *
ixp_enc_u16(u8 * msg, u16 val)
{
msg[0] = val;
msg[1] = val >> 8;
return &msg[2];
}
void *
ixp_dec_u16(u8 * msg, u16 * val)
{
*val = msg[0] | (msg[1] << 8);
return &msg[2];
}
void *
ixp_enc_u32(u8 * msg, u32 val)
{
msg[0] = val;
msg[1] = val >> 8;
msg[2] = val >> 16;
msg[3] = val >> 24;
return &msg[4];
}
void *
ixp_dec_u32(u8 * msg, u32 * val)
{
*val = msg[0] | (msg[1] << 8) | (msg[2] << 16) | (msg[3] << 24);
return &msg[4];
}
void *
ixp_enc_u64(u8 * msg, u64 val)
{
msg[0] = val;
msg[1] = val >> 8;
msg[2] = val >> 16;
msg[3] = val >> 24;
msg[4] = val >> 32;
msg[5] = val >> 40;
msg[6] = val >> 48;
msg[7] = val >> 56;
return &msg[8];
}
void *
ixp_dec_u64(u8 * msg, u64 * val)
{
*val = (u64) msg[0] | ((u64) msg[1] << 8) |
((u64) msg[2] << 16) | ((u64) msg[3] << 24) |
((u64) msg[4] << 32) | ((u64) msg[5] << 40) |
((u64) msg[6] << 48) | ((u64) msg[7] << 56);
return &msg[8];
}
void *
ixp_enc_string(u8 * msg, const char *s)
{
u16 len = s ? strlen(s) : 0;
msg = ixp_enc_u16(msg, len);
if (s)
memcpy(msg, s, len);
return &msg[len];
}
void *
ixp_dec_string(u8 * msg, char *string, u16 stringlen, u16 * len)
{
msg = ixp_dec_u16(msg, len);
if (!(*len))
return msg;
if (*len > stringlen - 1)
/* might never happen if stringlen == IXP_MAX_MSG */
string[0] = '\0';
else {
memcpy(string, msg, *len);
string[*len] = 0;
}
return &msg[*len];
}
void *
ixp_enc_data(u8 * msg, u8 * data, u32 datalen)
{
memcpy(msg, data, datalen);
return &msg[datalen];
}
void *
ixp_dec_data(u8 * msg, u8 * data, u32 datalen)
{
memcpy(data, msg, datalen);
return &msg[datalen];
}
void *
ixp_enc_prefix(u8 * msg, u32 size, u8 id, u16 tag)
{
msg = ixp_enc_u32(msg, size);
msg = ixp_enc_u8(msg, id);
return ixp_enc_u16(msg, tag);
}
void *
ixp_dec_prefix(u8 * msg, u32 * size, u8 * id, u16 * tag)
{
msg = ixp_dec_u32(msg, size);
msg = ixp_dec_u8(msg, id);
return ixp_dec_u16(msg, tag);
}
void *
ixp_enc_qid(u8 * msg, Qid * qid)
{
msg = ixp_enc_u8(msg, qid->type);
msg = ixp_enc_u32(msg, qid->version);
return ixp_enc_u64(msg, qid->path);
}
void *
ixp_dec_qid(u8 * msg, Qid * qid)
{
msg = ixp_dec_u8(msg, &qid->type);
msg = ixp_dec_u32(msg, &qid->version);
return ixp_dec_u64(msg, &qid->path);
}
void *
ixp_enc_stat(u8 * msg, Stat * stat)
{
msg = ixp_enc_u16(msg, stat->size);
msg = ixp_enc_u16(msg, stat->type);
msg = ixp_enc_u32(msg, stat->dev);
msg = ixp_enc_qid(msg, &stat->qid);
msg = ixp_enc_u32(msg, stat->mode);
msg = ixp_enc_u32(msg, stat->atime);
msg = ixp_enc_u32(msg, stat->mtime);
msg = ixp_enc_u64(msg, stat->length);
msg = ixp_enc_string(msg, stat->name);
msg = ixp_enc_string(msg, stat->uid);
msg = ixp_enc_string(msg, stat->gid);
return ixp_enc_string(msg, stat->muid);
}
void *
ixp_dec_stat(u8 * msg, Stat * stat)
{
u16 len;
msg = ixp_dec_u16(msg, &stat->size);
msg = ixp_dec_u16(msg, &stat->type);
msg = ixp_dec_u32(msg, &stat->dev);
msg = ixp_dec_qid(msg, &stat->qid);
msg = ixp_dec_u32(msg, &stat->mode);
msg = ixp_dec_u32(msg, &stat->atime);
msg = ixp_dec_u32(msg, &stat->mtime);
msg = ixp_dec_u64(msg, &stat->length);
msg = ixp_dec_string(msg, stat->name, sizeof(stat->name), &len);
msg = ixp_dec_string(msg, stat->uid, sizeof(stat->uid), &len);
msg = ixp_dec_string(msg, stat->gid, sizeof(stat->gid), &len);
return ixp_dec_string(msg, stat->muid, sizeof(stat->muid), &len);
}

241
libixp2/ixp.h Normal file
View File

@ -0,0 +1,241 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <sys/types.h>
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
#ifndef nil
#define nil 0
#endif
#define IXP_VERSION "9P2000"
#define IXP_MAX_VERSION 32
#define IXP_MAX_ERROR 128
#define IXP_MAX_CONN 32
#define IXP_MAX_MSG 8192
#define IXP_MAX_FLEN 128
#define IXP_MAX_ULEN 32
#define IXP_MAX_STAT 64
#define IXP_MAX_WELEM 16 /* MAXWELEM */
#define IXP_MAX_TFUNCS 14
/* 9P message types */
enum {
TVERSION = 100,
RVERSION,
TAUTH = 102,
RAUTH,
TATTACH = 104,
RATTACH,
TERROR = 106,
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,
};
/* 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,
};
/* this should work on all architectures */
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
#define IXP_NOTAG (u16)~0U /* Dummy tag */
#define IXP_NOFID (u32)~0 /* No auth */
typedef struct {
u8 type;
u32 version;
u64 path;
} Qid;
/* stat structure */
typedef struct {
u16 size;
u16 type;
u32 dev;
Qid qid;
u32 mode;
u32 atime;
u32 mtime;
u64 length;
char name[IXP_MAX_FLEN];
char uid[IXP_MAX_ULEN];
char gid[IXP_MAX_ULEN];
char muid[IXP_MAX_ULEN];
} Stat;
typedef struct {
u8 id;
u16 tag;
u32 fid;
u32 maxmsg; /* Tversion, Rversion */
char version[IXP_MAX_VERSION]; /* Tversion, Rversion */
u16 oldtag; /* Tflush */
char errstr[IXP_MAX_ERROR]; /* Rerror */
Qid qid; /* Rattach, Ropen, Rcreate */
u32 iounit; /* Ropen, Rcreate */
Qid aqid; /* Rauth */
u32 afid; /* Tauth, Tattach */
char uname[IXP_MAX_ULEN]; /* Tauth, Tattach */
char aname[IXP_MAX_FLEN]; /* Tauth, Tattach */
u32 perm; /* Tcreate */
char name[IXP_MAX_FLEN]; /* Tcreate */
u8 mode; /* Tcreate, Topen */
u32 newfid; /* Twalk */
u16 nwname; /* Twalk */
char *wname[IXP_MAX_WELEM]; /* Twalk */
u16 nwqid; /* Rwalk */
Qid wqid[IXP_MAX_WELEM]; /* Rwalk */
u64 offset; /* Tread, Twrite */
u32 count; /* Tread, Twrite, Rread */
Stat stat; /* Rstat */
u16 nstat; /* Twstat, Rstat */
u8 data[IXP_MAX_MSG]; /* Twrite, Rread, Twstat,
* Rstat */
} Fcall;
typedef struct IXPServer IXPServer;
typedef struct IXPConn IXPConn;
typedef struct {
u8 id;
int (*tfunc) (IXPServer *, IXPConn *);
} IXPTFunc;
struct IXPConn {
int fd;
int dont_close;
void (*read) (IXPServer *, IXPConn *);
void *aux;
};
struct IXPServer {
int running;
IXPConn conn[IXP_MAX_CONN];
void (*freeconn) (IXPServer *, IXPConn *);
int maxfd;
fd_set rd;
IXPTFunc *funcs;
Fcall fcall;
char *errstr;
};
typedef struct {
int fd;
u32 root_fid;
Qid root_qid;
Fcall fcall;
char *errstr;
} IXPClient;
/* client.c */
int ixp_client_init(IXPClient * c, char *sockfile);
void ixp_client_deinit(IXPClient * c);
int ixp_client_remove(IXPClient * c, u32 newfid, char *filepath);
int
ixp_client_create(IXPClient * c, u32 dirfid, char *name, u32 perm,
u8 mode);
int ixp_client_walk(IXPClient * c, u32 newfid, char *filepath);
int ixp_client_open(IXPClient * c, u32 newfid, char *filepath, u8 mode);
u32
ixp_client_read(IXPClient * c, u32 fid, u64 offset, void *result,
u32 res_len);
u32
ixp_client_write(IXPClient * c, u32 fid, u64 offset, u32 count,
u8 * data);
int ixp_client_close(IXPClient * c, u32 fid);
/* convert.c */
void *ixp_enc_u8(u8 * msg, u8 val);
void *ixp_dec_u8(u8 * msg, u8 * val);
void *ixp_enc_u16(u8 * msg, u16 val);
void *ixp_dec_u16(u8 * msg, u16 * val);
void *ixp_enc_u32(u8 * msg, u32 val);
void *ixp_dec_u32(u8 * msg, u32 * val);
void *ixp_enc_u64(u8 * msg, u64 val);
void *ixp_dec_u64(u8 * msg, u64 * val);
void *ixp_enc_string(u8 * msg, const char *s);
void *ixp_dec_string(u8 * msg, char *string, u16 stringlen, u16 * len);
void *ixp_enc_data(u8 * msg, u8 * data, u32 datalen);
void *ixp_dec_data(u8 * msg, u8 * data, u32 datalen);
void *ixp_enc_prefix(u8 * msg, u32 size, u8 id, u16 tag);
void *ixp_dec_prefix(u8 * msg, u32 * size, u8 * id, u16 * tag);
void *ixp_enc_qid(u8 * msg, Qid * qid);
void *ixp_dec_qid(u8 * msg, Qid * qid);
void *ixp_enc_stat(u8 * msg, Stat * stat);
void *ixp_dec_stat(u8 * msg, Stat * stat);
/* message.c */
u16 ixp_sizeof_stat(Stat * stat);
u32 ixp_fcall_to_msg(Fcall * fcall, void *msg, u32 msglen);
u32 ixp_msg_to_fcall(void *msg, u32 msglen, Fcall * fcall);
/* server.c */
IXPConn *
ixp_server_add_conn(IXPServer * s, int fd, int dont_close,
void (*read) (IXPServer *, IXPConn *));
int ixp_server_tversion(IXPServer *, IXPConn * c);
void ixp_server_rm_conn(IXPServer * s, IXPConn * c);
void ixp_server_loop(IXPServer * s);
int ixp_server_init(IXPServer * s, char *sockfile, IXPTFunc * funcs,
void (*freeconn) (IXPServer *, IXPConn *));
void ixp_server_deinit(IXPServer * s);
/* socket.c */
int ixp_connect_sock(char *sockfile);
int ixp_accept_sock(int fd);
int ixp_create_sock(char *sockfile, char **errstr);
/* transport.c */
u32 ixp_send_message(int fd, void *msg, u32 msize, char **errstr);
u32 ixp_recv_message(int fd, void *msg, u32 msglen, char **errstr);

293
libixp2/message.c Normal file
View File

@ -0,0 +1,293 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <stdlib.h>
#include <string.h>
#include "ixp.h"
static u16
sizeof_string(const char *s)
{
return sizeof(u16) + strlen(s);
}
u16
ixp_sizeof_stat(Stat * stat)
{
return sizeof(Qid)
+ 2 * sizeof(u16)
+ 4 * sizeof(u32)
+ sizeof(u64)
+ sizeof_string(stat->name)
+ sizeof_string(stat->uid)
+ sizeof_string(stat->gid)
+ sizeof_string(stat->muid);
}
u32
ixp_fcall_to_msg(Fcall * fcall, void *msg, u32 msglen)
{
u32 i, msize = sizeof(u8) + sizeof(u16) + sizeof(u32);
void *p = msg;
switch (fcall->id) {
case TVERSION:
case RVERSION:
msize += sizeof(u32) + sizeof_string(fcall->version);
break;
case TAUTH:
msize +=
sizeof(u32) + sizeof_string(fcall->uname) +
sizeof_string(fcall->aname);
break;
case RAUTH:
case RATTACH:
msize += sizeof(Qid);
break;
case TATTACH:
msize +=
2 * sizeof(u32) + sizeof_string(fcall->uname) +
sizeof_string(fcall->aname);
break;
case RERROR:
msize += sizeof_string(fcall->errstr) + sizeof(u32);
break;
case RWRITE:
case TCLUNK:
case TREMOVE:
case TSTAT:
msize += sizeof(u32);
break;
case TWALK:
msize += sizeof(u16) + 2 * sizeof(u32);
for (i = 0; i < fcall->nwname; i++)
msize += sizeof_string(fcall->wname[i]);
break;
case TFLUSH:
msize += sizeof(u16);
break;
case RWALK:
msize += sizeof(u16) + fcall->nwqid * sizeof(Qid);
break;
case TOPEN:
msize += sizeof(u32) + sizeof(u8);
break;
case ROPEN:
case RCREATE:
msize += sizeof(Qid) + sizeof(u32);
break;
case TCREATE:
msize += sizeof(u8) + 2 * sizeof(u32) + sizeof_string(fcall->name);
break;
case TREAD:
msize += 2 * sizeof(u32) + sizeof(u64);
break;
case RREAD:
msize += sizeof(u32) + fcall->count;
break;
case TWRITE:
msize += 2 * sizeof(u32) + sizeof(u64) + fcall->count;
break;
case RSTAT:
msize += ixp_sizeof_stat(&fcall->stat);
break;
case TWSTAT:
msize += sizeof(u32) + ixp_sizeof_stat(&fcall->stat);
break;
default:
break;
}
if (msize > msglen)
return 0;
p = ixp_enc_prefix(p, msize, fcall->id, fcall->tag);
switch (fcall->id) {
case TVERSION:
case RVERSION:
p = ixp_enc_u32(p, fcall->maxmsg);
p = ixp_enc_string(p, fcall->version);
break;
case TAUTH:
p = ixp_enc_u32(p, fcall->afid);
p = ixp_enc_string(p, fcall->uname);
p = ixp_enc_string(p, fcall->aname);
break;
case RAUTH:
case RATTACH:
p = ixp_enc_qid(p, &fcall->qid);
break;
case TATTACH:
p = ixp_enc_u32(p, fcall->fid);
p = ixp_enc_u32(p, fcall->afid);
p = ixp_enc_string(p, fcall->uname);
p = ixp_enc_string(p, fcall->aname);
break;
case RERROR:
p = ixp_enc_string(p, fcall->errstr);
break;
case TFLUSH:
p = ixp_enc_u16(p, fcall->oldtag);
break;
case TWALK:
p = ixp_enc_u32(p, fcall->fid);
p = ixp_enc_u32(p, fcall->newfid);
p = ixp_enc_u16(p, fcall->nwname);
for (i = 0; i < fcall->nwname; i++)
p = ixp_enc_string(p, fcall->wname[i]);
break;
case RWALK:
p = ixp_enc_u16(p, fcall->nwqid);
for (i = 0; i < fcall->nwqid; i++)
p = ixp_enc_qid(p, &fcall->wqid[i]);
break;
case TOPEN:
p = ixp_enc_u32(p, fcall->fid);
p = ixp_enc_u8(p, fcall->mode);
break;
case ROPEN:
case RCREATE:
p = ixp_enc_qid(p, &fcall->qid);
p = ixp_enc_u32(p, fcall->iounit);
break;
case TCREATE:
p = ixp_enc_u32(p, fcall->fid);
p = ixp_enc_string(p, fcall->name);
p = ixp_enc_u32(p, fcall->perm);
p = ixp_enc_u8(p, fcall->mode);
break;
case TREAD:
p = ixp_enc_u32(p, fcall->fid);
p = ixp_enc_u64(p, fcall->offset);
p = ixp_enc_u32(p, fcall->count);
break;
case RREAD:
p = ixp_enc_u32(p, fcall->count);
p = ixp_enc_data(p, fcall->data, fcall->count);
break;
case TWRITE:
p = ixp_enc_u32(p, fcall->fid);
p = ixp_enc_u64(p, fcall->offset);
p = ixp_enc_u32(p, fcall->count);
p = ixp_enc_data(p, fcall->data, fcall->count);
break;
case RWRITE:
p = ixp_enc_u32(p, fcall->count);
break;
case TCLUNK:
case TREMOVE:
case TSTAT:
p = ixp_enc_u32(p, fcall->fid);
break;
case RSTAT:
p = ixp_enc_stat(p, &fcall->stat);
break;
case TWSTAT:
p = ixp_enc_u32(p, fcall->fid);
p = ixp_enc_stat(p, &fcall->stat);
break;
}
return msize;
}
u32
ixp_msg_to_fcall(void *msg, u32 msglen, Fcall * fcall)
{
u32 i, msize;
u16 len;
void *p = ixp_dec_prefix(msg, &msize, &fcall->id, &fcall->tag);
if (msize > msglen) /* bad message */
return 0;
switch (fcall->id) {
case TVERSION:
case RVERSION:
p = ixp_dec_u32(p, &fcall->maxmsg);
p = ixp_dec_string(p, fcall->version, sizeof(fcall->version),
&len);
break;
case TAUTH:
p = ixp_dec_u32(p, &fcall->afid);
p = ixp_dec_string(p, fcall->uname, sizeof(fcall->uname), &len);
p = ixp_dec_string(p, fcall->aname, sizeof(fcall->aname), &len);
break;
case RAUTH:
case RATTACH:
p = ixp_dec_qid(p, &fcall->qid);
break;
case TATTACH:
p = ixp_dec_u32(p, &fcall->fid);
p = ixp_dec_u32(p, &fcall->afid);
p = ixp_dec_string(p, fcall->uname, sizeof(fcall->uname), &len);
p = ixp_dec_string(p, fcall->aname, sizeof(fcall->aname), &len);
break;
case RERROR:
p = ixp_dec_string(p, fcall->errstr, sizeof(fcall->errstr), &len);
break;
case TFLUSH:
p = ixp_dec_u16(p, &fcall->oldtag);
break;
case TWALK:
p = ixp_dec_u32(p, &fcall->fid);
p = ixp_dec_u32(p, &fcall->newfid);
p = ixp_dec_u16(p, &fcall->nwname);
for (i = 0; i < fcall->nwname; i++)
p = ixp_dec_string(p, fcall->wname[i], IXP_MAX_FLEN, &len);
break;
case RWALK:
p = ixp_dec_u16(p, &fcall->nwqid);
for (i = 0; i < fcall->nwqid; i++)
p = ixp_dec_qid(p, &fcall->wqid[i]);
break;
case TOPEN:
p = ixp_dec_u32(p, &fcall->fid);
p = ixp_dec_u8(p, &fcall->mode);
break;
case ROPEN:
case RCREATE:
p = ixp_dec_qid(p, &fcall->qid);
p = ixp_dec_u32(p, &fcall->iounit);
break;
case TCREATE:
p = ixp_dec_u32(p, &fcall->fid);
p = ixp_dec_string(p, fcall->name, sizeof(fcall->name), &len);
p = ixp_dec_u32(p, &fcall->perm);
p = ixp_dec_u8(p, &fcall->mode);
break;
case TREAD:
p = ixp_dec_u32(p, &fcall->fid);
p = ixp_dec_u64(p, &fcall->offset);
p = ixp_dec_u32(p, &fcall->count);
break;
case RREAD:
p = ixp_dec_u32(p, &fcall->count);
p = ixp_dec_data(p, fcall->data, fcall->count);
break;
case TWRITE:
p = ixp_dec_u32(p, &fcall->fid);
p = ixp_dec_u64(p, &fcall->offset);
p = ixp_dec_u32(p, &fcall->count);
p = ixp_dec_data(p, fcall->data, fcall->count);
break;
case RWRITE:
p = ixp_dec_u32(p, &fcall->count);
break;
case TCLUNK:
case TREMOVE:
case TSTAT:
p = ixp_dec_u32(p, &fcall->fid);
break;
case RSTAT:
p = ixp_dec_stat(p, &fcall->stat);
break;
case TWSTAT:
p = ixp_dec_u32(p, &fcall->fid);
p = ixp_dec_stat(p, &fcall->stat);
break;
}
return msize;
}

201
libixp2/server.c Normal file
View File

@ -0,0 +1,201 @@
/*
* (C)opyright MMIV-MMV 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"
#include "cext.h"
static IXPConn zero_conn = {-1, 0, 0, 0};
static u8 msg[IXP_MAX_MSG];
static IXPConn *
next_free_conn(IXPServer * s)
{
int i;
for (i = 0; i < IXP_MAX_CONN; i++)
if (s->conn[i].fd < 0)
return &s->conn[i];
return nil;
}
static void
prepare_select(IXPServer * s)
{
int i;
FD_ZERO(&s->rd);
for (i = 0; i < IXP_MAX_CONN; i++) {
if (s->conn[i].fd >= 0) {
if (s->maxfd < s->conn[i].fd)
s->maxfd = s->conn[i].fd;
if (s->conn[i].read)
FD_SET(s->conn[i].fd, &s->rd);
}
}
}
void
ixp_server_rm_conn(IXPServer * s, IXPConn * c)
{
if (!c->dont_close) {
shutdown(c->fd, SHUT_RDWR);
close(c->fd);
}
if (s->freeconn)
s->freeconn(s, c);
*c = zero_conn;
}
static IXPConn *
init_conn(IXPConn * c, int fd, int dont_close,
void (*read) (IXPServer *, IXPConn *))
{
*c = zero_conn;
c->fd = fd;
c->dont_close = dont_close;
c->read = read;
return c;
}
IXPConn *
ixp_server_add_conn(IXPServer * s, int fd, int dont_close,
void (*read) (IXPServer *, IXPConn *))
{
IXPConn *c = next_free_conn(s);
if (!c)
return nil;
return init_conn(c, fd, dont_close, read);
}
static void
handle_conns(IXPServer * s)
{
int i;
for (i = 0; i < IXP_MAX_CONN; i++) {
if (s->conn[i].fd >= 0) {
if (FD_ISSET(s->conn[i].fd, &s->rd) && s->conn[i].read)
/* call back read handler */
s->conn[i].read(s, &s->conn[i]);
}
}
}
static void
server_client_read(IXPServer * s, IXPConn * c)
{
u32 i, msize;
s->errstr = 0;
if (!(msize = ixp_recv_message(c->fd, msg, IXP_MAX_MSG, &s->errstr))) {
ixp_server_rm_conn(s, c);
return;
}
fprintf(stderr, "msize=%d\n", msize);
if ((msize = ixp_msg_to_fcall(msg, IXP_MAX_MSG, &s->fcall))) {
for (i = 0; s->funcs && s->funcs[i].id; i++) {
if (s->funcs[i].id == s->fcall.id) {
if (!s->funcs[i].tfunc(s, c))
break;
msize = ixp_fcall_to_msg(&s->fcall, msg, s->fcall.maxmsg);
fprintf(stderr, "msize=%d\n", msize);
if (ixp_send_message(c->fd, msg, msize, &s->errstr) != msize)
break;
return;
}
}
}
if (!s->errstr)
s->errstr = "function not supported";
s->fcall.id = RERROR;
_strlcpy(s->fcall.errstr, s->errstr, sizeof(s->fcall.errstr));
msize = ixp_fcall_to_msg(&s->fcall, msg, IXP_MAX_MSG);
if (ixp_send_message(c->fd, msg, msize, &s->errstr) != msize)
ixp_server_rm_conn(s, c);
}
static void
server_read(IXPServer * s, IXPConn * c)
{
int fd;
IXPConn *new = next_free_conn(s);
if (new && ((fd = ixp_accept_sock(c->fd)) >= 0))
init_conn(new, fd, 0, server_client_read);
}
void
ixp_server_loop(IXPServer * s)
{
int r;
s->running = TRUE;
s->errstr = 0;
/* 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) {
s->errstr = "fatal select error";
break; /* allow cleanups in IXP using app */
} else if (r > 0)
handle_conns(s);
}
}
int
ixp_server_tversion(IXPServer * s, IXPConn * c)
{
fprintf(stderr, "got version %s (%s) %d (%d)\n", s->fcall.version, IXP_VERSION,
s->fcall.maxmsg, IXP_MAX_MSG);
if (strncmp(s->fcall.version, IXP_VERSION, strlen(IXP_VERSION))) {
s->errstr = "9P versions differ";
return FALSE;
}
else if (s->fcall.maxmsg > IXP_MAX_MSG)
s->fcall.maxmsg = IXP_MAX_MSG;
s->fcall.id = RVERSION;
return TRUE;
}
int
ixp_server_init(IXPServer * s, char *sockfile, IXPTFunc * funcs,
void (*freeconn) (IXPServer *, IXPConn *))
{
int fd, i;
s->funcs = funcs;
s->freeconn = freeconn;
s->errstr = 0;
if (!sockfile) {
s->errstr = "no socket file provided or invalid directory";
return FALSE;
}
if ((fd = ixp_create_sock(sockfile, &s->errstr)) < 0)
return FALSE;
for (i = 0; i < IXP_MAX_CONN; i++)
s->conn[i] = zero_conn;
ixp_server_add_conn(s, fd, 0, server_read);
return TRUE;
}
void
ixp_server_deinit(IXPServer * s)
{
int i;
/* shut down server */
for (i = 0; i < IXP_MAX_CONN; i++)
if (s->conn[i].fd >= 0)
ixp_server_rm_conn(s, &s->conn[i]);
}

86
libixp2/socket.c Normal file
View File

@ -0,0 +1,86 @@
/*
* (C)opyright MMIV-MMV 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 <sys/un.h>
#include <unistd.h>
#include "cext.h"
#include "ixp.h"
int
ixp_connect_sock(char *sockfile)
{
int fd = 0;
struct sockaddr_un addr = {0};
socklen_t su_len;
/* init */
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, sockfile, 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;
}
int
ixp_accept_sock(int fd)
{
socklen_t su_len;
struct sockaddr_un addr = {0};
su_len = sizeof(struct sockaddr);
return accept(fd, (struct sockaddr *) & addr, &su_len);
}
int
ixp_create_sock(char *sockfile, 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, sockfile, sizeof(addr.sun_path));
su_len = sizeof(struct sockaddr) + strlen(addr.sun_path);
if (bind(fd, (struct sockaddr *) & addr, su_len) < 0) {
*errstr = "cannot bind socket";
close(fd);
return -1;
}
chmod(sockfile, S_IRWXU);
if (listen(fd, IXP_MAX_CONN) < 0) {
*errstr = "cannot listen on socket";
close(fd);
return -1;
}
return fd;
}

78
libixp2/transport.c Normal file
View File

@ -0,0 +1,78 @@
/*
* (C)opyright MMIV-MMV 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"
#include <cext.h>
u32
ixp_send_message(int fd, void *msg, u32 msize, char **errstr)
{
u32 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 = "cannot send message";
return 0;
}
num += r;
}
return num;
}
static u32
ixp_recv_data(int fd, void *msg, u32 msize, char **errstr)
{
u32 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 = "cannot receive data";
return 0;
}
num += r;
}
return num;
}
u32
ixp_recv_message(int fd, void *msg, u32 msglen, char **errstr)
{
u32 msize;
/* receive header */
if (ixp_recv_data(fd, msg, sizeof(u32), errstr) != sizeof(u32))
return 0;
ixp_dec_u32(msg, &msize);
if (msize > msglen) {
*errstr = "message size exceeds buffer size";
return 0;
}
/* receive message */
if (ixp_recv_data(fd, msg + sizeof(u32), msize - sizeof(u32), errstr)
!= msize - sizeof(u32))
return 0;
return msize;
}

25
liblitz/Makefile Normal file
View File

@ -0,0 +1,25 @@
# liblitz - non wimp GUI toolkit
# (C)opyright MMIV-MMV Anselm R. Garbe
include ../config.mk
CFLAGS += -I../libixp -I../libcext
LDFLAGS += -L../libixp -lixp -L../libcext -lcext
SRC = draw.c geometry.c kb.c mouse.c util.c
OBJ = ${SRC:.c=.o}
all: liblitz
@echo built liblitz
.c.o:
@echo CC $<
@${CC} -c ${CFLAGS} $<
liblitz: ${OBJ}
@echo AR $@.a
@${AR} $@.a ${OBJ}
@${RANLIB} $@.a
clean:
rm -f liblitz.a *.o

64
liblitz/blitz.h Normal file
View File

@ -0,0 +1,64 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <X11/Xlib.h>
#define BLITZ_FONT "fixed"
#define BLITZ_SEL_FG_COLOR "#eeeeee"
#define BLITZ_SEL_BG_COLOR "#506070"
#define BLITZ_SEL_BORDER_COLOR "#708090"
#define BLITZ_NORM_FG_COLOR "#bbbbbb"
#define BLITZ_NORM_BG_COLOR "#222222"
#define BLITZ_NORM_BORDER_COLOR "#000000"
typedef enum {
CENTER, WEST, NWEST, NORTH, NEAST, EAST,
SEAST, SOUTH, SWEST
} Align;
typedef struct Draw Draw;
struct Draw {
Drawable drawable;
GC gc;
unsigned long bg;
unsigned long fg;
unsigned long border;
Align align;
XFontStruct *font;
XRectangle rect; /* relative rect */
XRectangle *notch; /* relative notch rect */
char *data;
};
/* draw.c */
XFontStruct *blitz_getfont(Display * dpy, char *fontstr);
unsigned long blitz_loadcolor(Display * dpy, int mon, char *colstr);
void blitz_drawlabel(Display * dpy, Draw * r);
void blitz_drawmeter(Display * dpy, Draw * r);
void blitz_drawlabelnoborder(Display * dpy, Draw * r);
/* geometry.c */
int
blitz_strtorect(Display * dpy, XRectangle * root, XRectangle * r,
char *val);
int blitz_ispointinrect(int x, int y, XRectangle * r);
int blitz_distance(XRectangle * origin, XRectangle * target);
void
blitz_getbasegeometry(void **items, unsigned int *size,
unsigned int *cols, unsigned int *rows);
/* mouse.c */
char *blitz_buttontostr(unsigned int button);
unsigned int blitz_strtobutton(char *val);
/* kb.c */
char *blitz_modtostr(unsigned long mod);
unsigned long blitz_strtomod(char *val);
/* util.c */
long long
_strtonum(const char *numstr, long long minval,
long long maxval);

181
liblitz/draw.c Normal file
View File

@ -0,0 +1,181 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <stdio.h>
#include <string.h>
#include "blitz.h"
#include <cext.h>
XFontStruct *
blitz_getfont(Display * dpy, char *fontstr)
{
XFontStruct *font;
font = XLoadQueryFont(dpy, fontstr);
if (!font) {
font = XLoadQueryFont(dpy, "fixed");
if (!font) {
fprintf(stderr, "%s", "wmii: error, cannot load fixed font\n");
return 0;
}
}
return font;
}
unsigned long
blitz_loadcolor(Display * dpy, int mon, char *colstr)
{
XColor color;
char col[8];
_strlcpy(col, colstr, sizeof(col));
col[7] = '\0';
XAllocNamedColor(dpy, DefaultColormap(dpy, mon), col, &color, &color);
return color.pixel;
}
static void
draw_bg(Display * dpy, Draw * d)
{
XRectangle rect[4];
XSetForeground(dpy, d->gc, d->bg);
if (!d->notch) {
XFillRectangles(dpy, d->drawable, d->gc, &d->rect, 1);
return;
}
rect[0] = d->rect;
rect[0].height = d->notch->y;
rect[1] = d->rect;
rect[1].y = d->notch->y;
rect[1].width = d->notch->x;
rect[1].height = d->notch->height;
rect[2].x = d->notch->x + d->notch->width;
rect[2].y = d->notch->y;
rect[2].width = d->rect.width - (d->notch->x + d->notch->width);
rect[2].height = d->notch->height;
rect[3] = d->rect;
rect[3].y = d->notch->y + d->notch->height;
rect[3].height = d->rect.height - (d->notch->y + d->notch->height);
XFillRectangles(dpy, d->drawable, d->gc, rect, 4);
}
static void
_draw_border(Display * dpy, Draw * d)
{
XPoint points[5];
XSetLineAttributes(dpy, d->gc, 1, LineSolid, CapButt, JoinMiter);
XSetForeground(dpy, d->gc, d->border);
points[0].x = d->rect.x;
points[0].y = d->rect.y;
points[1].x = d->rect.width - 1;
points[1].y = 0;
points[2].x = 0;
points[2].y = d->rect.height - 1;
points[3].x = -(d->rect.width - 1);
points[3].y = 0;
points[4].x = 0;
points[4].y = -(d->rect.height - 1);
XDrawLines(dpy, d->drawable, d->gc, points, 5, CoordModePrevious);
}
static void
draw_text(Display * dpy, Draw * d)
{
unsigned int x, y, w, h, shortened = FALSE;
size_t len = 0;
static char text[2048];
if (!d->data)
return;
len = strlen(d->data);
_strlcpy(text, d->data, sizeof(text));
XSetFont(dpy, d->gc, d->font->fid);
h = d->font->ascent + d->font->descent;
y = d->rect.y + d->rect.height / 2 - h / 2 + d->font->ascent;
/* shorten text if necessary */
while (len && (w = XTextWidth(d->font, text, len)) > d->rect.width) {
text[len - 1] = '\0';
len--;
shortened = TRUE;
}
if (w > d->rect.width)
return;
/* mark shortened info in the string */
if (shortened) {
if (len > 3)
text[len - 3] = '.';
if (len > 2)
text[len - 2] = '.';
if (len > 1)
text[len - 1] = '.';
}
switch (d->align) {
case WEST:
x = d->rect.x + h / 2;
break;
case EAST:
x = d->rect.x + d->rect.width - (h / 2 + w);
break;
default: /* CENTER */
x = d->rect.x + (d->rect.width - w) / 2;
break;
}
XSetBackground(dpy, d->gc, d->bg);
/*
* uncomment, if you want get an shadow effect XSetForeground(dpy,
* d->gc, BlackPixel(dpy, DefaultScreen(dpy))); XDrawString(dpy,
* d->drawable, d->gc, x + 1, y + 1, text, len);
*/
XSetForeground(dpy, d->gc, d->fg);
XDrawString(dpy, d->drawable, d->gc, x, y, text, len);
}
/* draws meter */
void
blitz_drawmeter(Display * dpy, Draw * d)
{
unsigned int offy, mh, val, w = d->rect.width - 4;
if (!d->data || strncmp(d->data, "%m:", 3))
return;
val = _strtonum(&d->data[3], 0, 100);
draw_bg(dpy, d);
_draw_border(dpy, d);
/* draw bg gradient */
mh = ((d->rect.height - 4) * val) / 100;
offy = d->rect.y + d->rect.height - 2 - mh;
XSetForeground(dpy, d->gc, d->fg);
XFillRectangle(dpy, d->drawable, d->gc, d->rect.x + 2, offy, w, mh);
}
static void
_draw_label(Display * dpy, Draw * d)
{
draw_bg(dpy, d);
if (d->data)
draw_text(dpy, d);
}
/* draws label */
void
blitz_drawlabel(Display * dpy, Draw * d)
{
_draw_label(dpy, d);
_draw_border(dpy, d);
}
void
blitz_drawlabelnoborder(Display * dpy, Draw * d)
{
_draw_label(dpy, d);
}

222
liblitz/geometry.c Normal file
View File

@ -0,0 +1,222 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include "blitz.h"
#include <cext.h>
static int
strtoalign(Align * result, char *val)
{
/*
* note, resize allows syntax like "east-20", this we cannot do
* include zero termination in strncmp checking!
*/
*result = CENTER;
if (!strncmp(val, "west", 4))
*result = WEST;
else if (!strncmp(val, "nwest", 5))
*result = NWEST;
else if (!strncmp(val, "north", 5))
*result = NORTH;
else if (!strncmp(val, "neast", 5))
*result = NEAST;
else if (!strncmp(val, "east", 4))
*result = EAST;
else if (!strncmp(val, "seast", 5))
*result = SEAST;
else if (!strncmp(val, "south", 5))
*result = SOUTH;
else if (!strncmp(val, "swest", 5))
*result = SWEST;
else if (!strncmp(val, "center", 6))
*result = CENTER;
else
return FALSE;
return TRUE;
}
/**
* Basic Syntax: <x>,<y>,<width>,<height>
* Each component can be of following format:
* <...> = [+|-]0..n|<alignment>[[+|-]0..n]
*/
int
blitz_strtorect(Display * dpy, XRectangle * root, XRectangle * r,
char *val)
{
char buf[64];
char *x, *y, *w, *h;
char *p;
int sx, sy, sw, sh;
if (!val)
return FALSE;
sx = sy = sw = sh = 0;
x = y = w = h = 0;
_strlcpy(buf, val, sizeof(buf));
x = strtok_r(buf, ",", &p);
if (x) {
y = strtok_r(0, ",", &p);
if (y) {
w = strtok_r(0, ",", &p);
if (w) {
h = strtok_r(0, "", &p);
}
}
}
if (x && (sx = (x[0] >= '0') && (x[0] <= '9')))
r->x = _strtonum(x, 0, 65535);
if (y && (sy = (y[0] >= '0') && (y[0] <= '9')))
r->y = _strtonum(y, 0, 65535);
if (w && (sw = (w[0] >= '0') && (w[0] <= '9')))
r->width = _strtonum(w, 0, 65535);
if (h && (sh = (h[0] >= '0') && (h[0] <= '9')))
r->height = _strtonum(h, 0, 65535);
if (!sx && !sw && x && w
&& x[0] != '-' && x[0] != '+' && w[0] != '-' && w[0] != '+') {
Align ax, aw;
strtoalign(&ax, x);
strtoalign(&aw, w);
if ((ax == CENTER) && (aw == EAST)) {
r->x = root->x + root->width / 2;
r->width = root->width / 2;
} else {
r->x = root->x;
if (aw == CENTER) {
r->width = root->width / 2;
} else {
r->width = root->width;
}
}
} else if (!sx && x && x[0] != '-' && x[0] != '+') {
Align ax;
strtoalign(&ax, x);
if (ax == CENTER) {
r->x = root->x + (root->width / 2) - (r->width / 2);
} else if (ax == EAST) {
r->x = root->x + root->width - r->width;
} else {
r->x = root->x;
}
} else if (!sw && w && w[0] != '-' && w[0] != '+') {
Align aw;
strtoalign(&aw, w);
if (aw == CENTER) {
r->width = (root->width / 2) - r->x;
} else {
r->width = root->width - r->x;
}
}
if (!sy && !sh && y && h
&& y[0] != '-' && y[0] != '+' && h[0] != '-' && h[0] != '+') {
Align ay, ah;
strtoalign(&ay, y);
strtoalign(&ah, h);
if ((ay == CENTER) && (ah == SOUTH)) {
r->y = root->y + root->height / 2;
r->height = root->height / 2;
} else {
r->y = root->y;
if (ah == CENTER) {
r->height = root->height / 2;
} else {
r->height = root->height;
}
}
} else if (!sy && y && y[0] != '-' && y[0] != '+') {
Align ay;
strtoalign(&ay, y);
if (ay == CENTER) {
r->y = root->y + (root->height / 2) - (r->height / 2);
} else if (ay == SOUTH) {
r->y = root->y + root->height - r->height;
} else {
r->y = root->y;
}
} else if (!sh && h && h[0] != '-' && h[0] != '+') {
Align ah;
strtoalign(&ah, h);
if (ah == CENTER) {
r->height = (root->height / 2) - r->y;
} else {
r->height = root->height - r->y;
}
}
/* now do final calculations */
if (x) {
p = strchr(x, '-');
if (p)
r->x -= _strtonum(++p, 0, 65535);
p = strchr(x, '+');
if (p)
r->x += _strtonum(++p, 0, 65535);
}
if (y) {
p = strchr(y, '-');
if (p)
r->y -= _strtonum(++p, 0, 65535);
p = strchr(y, '+');
if (p)
r->y += _strtonum(++p, 0, 65535);
}
if (w) {
p = strchr(w, '-');
if (p)
r->width -= _strtonum(++p, 0, 65535);
p = strchr(w, '+');
if (p)
r->width += _strtonum(++p, 0, 65535);
}
if (h) {
p = strchr(h, '-');
if (p)
r->height -= _strtonum(++p, 0, 65535);
p = strchr(h, '+');
if (p)
r->height += _strtonum(++p, 0, 65535);
}
return TRUE;
}
int
blitz_ispointinrect(int x, int y, XRectangle * r)
{
return (x >= r->x) && (x <= r->x + r->width)
&& (y >= r->y) && (y <= r->y + r->height);
}
int
blitz_distance(XRectangle * origin, XRectangle * target)
{
int ox = origin->x + origin->width / 2;
int oy = origin->y + origin->height / 2;
int tx = target->x + target->width / 2;
int ty = target->y + target->height / 2;
return (int) sqrt((double) (((ox - tx) * (ox - tx)) +
((oy - ty) * (oy - ty))));
}
void
blitz_getbasegeometry(void **items, unsigned int *size,
unsigned int *cols, unsigned int *rows)
{
float sq, dummy;
*size = count_items((void **) items);
sq = sqrt(*size);
if (modff(sq, &dummy) < 0.5)
*rows = floor(sq);
else
*rows = ceil(sq);
*cols = ((*rows) * (*rows)) < (*size) ? *rows + 1 : *rows;
}

55
liblitz/kb.c Normal file
View File

@ -0,0 +1,55 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <string.h>
#include "blitz.h"
#include <cext.h>
/* free the result manually! */
char *
blitz_modtostr(unsigned long mod)
{
char result[60];
result[0] = '\0';
if (mod & ShiftMask)
_strlcat(result, "S-", sizeof(result));
if (mod & ControlMask)
_strlcat(result, "C-", sizeof(result));
if (mod & Mod1Mask)
_strlcat(result, "M-", sizeof(result));
if (mod & Mod2Mask)
_strlcat(result, "M2-", sizeof(result));
if (mod & Mod3Mask)
_strlcat(result, "M3-", sizeof(result));
if (mod & Mod4Mask)
_strlcat(result, "WIN-", sizeof(result));
if (mod & Mod5Mask)
_strlcat(result, "M5-", sizeof(result));
return estrdup(result);
}
unsigned long
blitz_strtomod(char *val)
{
unsigned long mod = 0;
if (strstr(val, "S-"))
mod |= ShiftMask;
if (strstr(val, "C-"))
mod |= ControlMask;
if (strstr(val, "M-"))
mod |= Mod1Mask;
if (strstr(val, "M2-"))
mod |= Mod2Mask;
if (strstr(val, "M3-"))
mod |= Mod3Mask;
if (strstr(val, "WIN-"))
mod |= Mod4Mask;
if (strstr(val, "M5-"))
mod |= Mod5Mask;
return mod;
}

31
liblitz/mouse.c Normal file
View File

@ -0,0 +1,31 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "blitz.h"
#include <cext.h>
/* free the result manually! */
char *
blitz_buttontostr(unsigned int button)
{
char result[8];
result[0] = '\0';
snprintf(result, 8, "Button%ud", button - Button1);
return estrdup(result);
}
unsigned int
blitz_strtobutton(char *val)
{
unsigned int res = 0;
if (val && strlen(val) > 6 && !strncmp(val, "Button", 6))
res = _strtonum(&val[6], 1, 5) + Button1;
return res;
}

21
liblitz/util.c Normal file
View File

@ -0,0 +1,21 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <stdio.h>
#include "cext.h"
#include "blitz.h"
long long
_strtonum(const char *numstr, long long minval, long long maxval)
{
const char *errstr;
long long ret = __strtonum(numstr, minval, maxval, &errstr);
if (errstr)
fprintf(stderr,
"liblitz: cannot convert '%s' into integer: %s\n",
numstr, errstr);
return ret;
}

26
libwmii/Makefile Normal file
View File

@ -0,0 +1,26 @@
# libwmii - mediator lib for wmii
# (C)opyright MMIV-MMV Anselm R. Garbe
include ../config.mk
CFLAGS += -I../liblitz -I../libixp -I../libcext
LDFLAGS += -L../liblitz -llitz -L../libixp -lixp -L../libcext -lcext
SRC = ixputil.c spawn.c util.c wm.c
OBJ = ${SRC:.c=.o}
all: libwmii
@echo built libwmii
.c.o:
@echo CC $<
@${CC} -c ${CFLAGS} $<
libwmii: ${OBJ}
@echo AR $@.a
@${AR} $@.a ${OBJ}
@${RANLIB} $@.a
clean:
rm -f libwmii.a *.o

102
libwmii/ixputil.c Normal file
View File

@ -0,0 +1,102 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "wmii.h"
#include <cext.h>
static pid_t mypid;
static char *mysockfile;
/* convenience stuff ----------------------------------------------- */
File *
wmii_create_ixpfile(IXPServer * s, char *key, char *val)
{
File *f = ixp_create(s, key);
if (f && !is_directory(f)) {
size_t l = val ? strlen(val) : 0;
f->content = l ? strdup(val) : 0;
f->size = l;
return f;
}
/* forbidden, file is directory */
return 0;
}
void
wmii_get_ixppath(File * f, char *path, size_t size)
{
char buf[512];
buf[0] = '\0';
if (path)
_strlcpy(buf, path, sizeof(buf));
snprintf(path, size, "%s/", f->name);
if (buf[0] != '\0')
_strlcat(path, buf, size);
if (f->parent)
wmii_get_ixppath(f->parent, path, size);
}
void
wmii_move_ixpfile(File * f, File * to_parent)
{
File *p = f->parent;
File *fil = p->content;
/* detach */
if (p->content == f)
p->content = fil->next;
else {
while (fil->next != f)
fil = fil->next;
fil->next = f->next;
}
f->next = 0;
/* attach */
if (!to_parent->content)
to_parent->content = f;
else {
for (fil = to_parent->content; fil->next; fil = fil->next);
fil->next = f;
}
f->parent = to_parent;
}
static void
exit_cleanup()
{
if (mypid == getpid())
unlink(mysockfile);
}
IXPServer *
wmii_setup_server(char *sockfile)
{
IXPServer *s;
if (!sockfile) {
fprintf(stderr, "%s\n", "libwmii: no socket file provided");
exit(1);
}
mysockfile = sockfile;
mypid = getpid();
s = init_server(sockfile, exit_cleanup);
if (!s) {
perror("libwmii: cannot initialize IXP server");
exit(1);
}
return s;
}

31
libwmii/spawn.c Normal file
View File

@ -0,0 +1,31 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "wmii.h"
#include <cext.h>
void
spawn(void *dpy, char *cmd)
{
/* the questionable double-fork is done to catch all zombies */
if (fork() == 0) {
if (fork() == 0) {
setsid();
close(ConnectionNumber(dpy));
execlp("rc", "rc", "-c", cmd, (char *) 0);
perror("failed");
exit(1);
}
exit(0);
}
wait(0);
}

14
libwmii/util.c Normal file
View File

@ -0,0 +1,14 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include "wmii.h"
void
swap(void **p1, void **p2)
{
void *tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}

95
libwmii/wm.c Normal file
View File

@ -0,0 +1,95 @@
/*
* (C)opyright MMIV-MMV 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 <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include "blitz.h"
#include <cext.h>
int
property(Display * dpy, Window w, Atom a, Atom t, long l,
unsigned char **prop)
{
Atom real;
int format;
unsigned long res, extra;
int status;
status = XGetWindowProperty(dpy, w, a, 0L, l, False, t, &real, &format,
&res, &extra, prop);
if (status != Success || *prop == 0) {
return 0;
}
if (res == 0) {
free((void *) *prop);
}
return res;
}
void
win_prop(Display * dpy, Window w, Atom a, char *res, int len)
{
unsigned char *prop;
if (property(dpy, w, a, XA_STRING, 100L, &prop)) {
_strlcpy(res, (char *) prop, len);
XFree(prop);
}
res[len - 1] = '\0';
XSync(dpy, False);
}
void
send_message(Display * dpy, Window w, Atom a, long value)
{
XEvent e;
e.type = ClientMessage;
e.xclient.window = w;
e.xclient.message_type = a;
e.xclient.format = 32;
e.xclient.data.l[0] = value;
e.xclient.data.l[1] = CurrentTime;
XSendEvent(dpy, w, False, NoEventMask, &e);
XSync(dpy, False);
}
#define NUM_MASKS 8
void
init_lock_modifiers(Display * dpy, unsigned int *valid_mask,
unsigned int *num_lock_mask)
{
XModifierKeymap *modmap;
KeyCode num_lock;
static int masks[NUM_MASKS] = {
ShiftMask, LockMask, ControlMask, Mod1Mask,
Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
};
int i;
*num_lock_mask = 0;
modmap = XGetModifierMapping(dpy);
num_lock = XKeysymToKeycode(dpy, XStringToKeysym("Num_Lock"));
if (modmap && modmap->max_keypermod > 0) {
int max = NUM_MASKS * 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);
}

39
libwmii/wmii.h Normal file
View File

@ -0,0 +1,39 @@
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include "ixp.h"
#include "blitz.h"
#define MAX_ID 20
#define MAX_BUF 128
typedef struct Action Action;
struct Action {
char *name;
void (*func) (void *obj, char *);
};
/* ixputil.c */
File *wmii_create_ixpfile(IXPServer * s, char *key, char *val);
void wmii_get_ixppath(File * f, char *path, size_t size);
void wmii_move_ixpfile(File * f, File * to_parent);
IXPServer *wmii_setup_server(char *sockfile);
/* spawn.c */
void spawn(void *dpy, char *cmd);
/* util.c */
void swap(void **p1, void **p2);
/* wm.c */
int
property(Display * dpy, Window w, Atom a, Atom t, long l,
unsigned char **prop);
void win_prop(Display * dpy, Window w, Atom a, char *res, int len);
void send_message(Display * dpy, Window w, Atom a, long value);
void
init_lock_modifiers(Display * dpy, unsigned int *valid_mask,
unsigned int *num_lock_mask);

15
rc/extern Normal file
View File

@ -0,0 +1,15 @@
#!9PREFIX/bin/rc
# clean the environment and execute the given command
PATH=$OLD_PATH
OLD_PATH=()
wmiiwmpid=()
apid=()
home=()
ifs=()
pid=()
prompt=()
rcname=()
status=()
exec $*

11
rc/kmode Normal file
View File

@ -0,0 +1,11 @@
#!9PREFIX/bin/rc
# activate shortcuts of the given mode
wmir write /keys/grab-keyb 0
wmir write /keys/lookup /mode/$1
plab_cmd=`{wmir read /wm/event/page-update | sed -e 's/\\//'}
if (! ~ $#plab_cmd 0) eval $"plab_cmd
#wmir write /bar/$plab/data \
#`{wmir read /bar/$plab/data | sed -e 's/.$/'^`{echo $2 | cut -b1}^'/'}

4
rc/quit Normal file
View File

@ -0,0 +1,4 @@
#!9PREFIX/bin/rc
# quit wmii
wmir write /wm/ctl quit

30
rc/status Normal file
View File

@ -0,0 +1,30 @@
#!9PREFIX/bin/rc
# periodically print date and load average to the bar
PIDFILE=/tmp/.ixp-$USER/statuspid-$WMII_IDENT
if(test -r $PIDFILE) {
kill -2 `{cat $PIDFILE} >[2]/dev/null
}
echo $pid >$PIDFILE
label=`{wmir read /bar/new}
wmir write /bar/$label/b1press 'wmir write /wm/ctl ''select prev'''
wmir write /bar/$label/b3press 'wmir write /wm/ctl ''select next'''
wmir write /bar/$label/b4press 'wmir write /wm/ctl ''select prev'''
wmir write /bar/$label/b5press 'wmir write /wm/ctl ''select next'''
# install signal handler for artificial sigexit:
fn sigint {
if(test -f $PIDFILE && ~ `{cat $PIDFILE} $pid)
rm -f $PIDFILE
wmir write /bar/ctl 'destroy '^$label
exit
}
text=fnord
while(wmir write /bar/$label/data $"text >[2]/dev/null) {
# if you need formatted date, use /bin/date instead
text=(`{date} `{uptime | sed 's/.*://; s/,//g'})
sleep 10
}

65
rc/welcome Normal file
View File

@ -0,0 +1,65 @@
#!9PREFIX/bin/rc
# display a welcome message that contains the wmii tutorial
xmessage -file - <<END
Welcome to wmii, the non-wimp environment of the WMI Project.
This is a small step by step tutorial, intended to make you a little bit
familiar with the wmii window manager.
NOTE: Some characters in the shortcuts that are mentioned in the following
text have a special meaning. S is the Shift key, C is the Ctrl key, M is
the Meta key (usually Alt), and the hyphen means that you have to press the
surrounding keys at the same time.
Let's go!
- Start a terminal by pressing M-t. This assumes that you have xterm(1)
installed.
- Open the actions menu: C-M-a
Type 'term' and press Enter. You've now used wmimenu(1) to accomplish what
wmikeys(1) did before when you pressed M-t: executing the term action.
Notice how the tiled layout arranges the windows.
- Switch between the three windows: M-j, M-k
You can also use M-Tab instead of M-j. If you prefer the mouse, then
just move the pointer above the desired window; no click is required.
- Create a new page: C-M-y
IMPORTANT: this text will then no longer be visible. In order to continue
reading you'll now often have to go back to the first page. Use M-h
or M-l for cycling through the existing pages. The digit in the left
corner at the bottom indicates which page you're on. You can directly
select a specific page as well: M-1, etc. Make sure that you are on the
second page before performing the next steps.
- Apply the float layout to the current page: S-M-f
Watch the leftmost bar label: the 't', which stood for 'tiled', has now
turned into an 'f', for 'float'.
- Open the programs menu: C-M-p
Type 'xclock' and press Enter.
- Resize the xclock window: left-click on the border and, while holding the
button down, move the cursor around.
- Move the xclock window: left-click on the title bar and, while holding
the button down, move the cursor around.
- Remove the current page by opening the actions menu and selecting the premove
action. The xclock window will be automatically detached and we'll land on
the first page.
- Attach the xclock window again: M-a
- Select one of the terminals and close it: C-M-c
Close xclock as well.
- Swap the remaining terminal with the left master tile by moving the terminal
window somewhere to the left. Afterwards, swap them once again, but this
time by pressing M-return.
- We'll now have a look at the internal filesystem used by wmii. Executing
wmir read /
in the shell of the terminal will list all the files in the root directory.
Files ending with a slash are directories. As you can see, / contains a
"normal" file and four directories. These directories correspond to the
four main components of wmii: wmiiwm, wmibar, wmikeys, and wmimenu. If you
are curious, you can now dig deeper into the directory trees. For instance,
wmir read /bar/1/b1press
will show you which command gets executed when you left-click on the page
label in the bar.
We hope that these steps gave you an idea of how wmii works. Of course, there
is much more to say. You should now read the wmii(1) man page and the FAQ at
<http://wmii.de>.
END

218
rc/wmirc Normal file
View File

@ -0,0 +1,218 @@
#!9PREFIX/bin/rc
# configure wmii
TAB_HEIGHT=16
BAR_HEIGHT=16
BORDER_WIDTH=3
SELECTED_BG_COLOR='#307080'
SELECTED_FG_COLOR='#fefefe'
SELECTED_BORDER_COLOR='#5090a0'
NORMAL_BG_COLOR='#004050'
NORMAL_FG_COLOR='#cccccc'
NORMAL_BORDER_COLOR='#206070'
TEXT_FONT='fixed'
MODKEY=M
NORTHKEY=k
SOUTHKEY=j
WESTKEY=h
EASTKEY=l
nl='
'
fn kbind {
wmir create /keys/mode/$1/$2 $3
}
fn selstyle {
wmir write $1/text-font $TEXT_FONT
wmir write $1/text-color $SELECTED_FG_COLOR
wmir write $1/bg-color $SELECTED_BG_COLOR
wmir write $1/border-color $SELECTED_BORDER_COLOR
}
fn normstyle {
wmir write $1/text-font $TEXT_FONT
wmir write $1/text-color $NORMAL_FG_COLOR
wmir write $1/bg-color $NORMAL_BG_COLOR
wmir write $1/border-color $NORMAL_BORDER_COLOR
}
fn items {
ifs=:$nl { dirs=`{echo $2} }
{
for(dir in $dirs) {
for(file in $dir/*) {
if(! ~ $file $dir^'/*' && ! test -d $file && test -x $file) {
file=`{basename $file}
echo create /menu/items/$1/$"file $"file
}
}
}
} | sort | wmir -f &
}
fn frameconf {
wmir write $1/event/b3press 'wmir write /wm/page/sel/ctl toggle'
wmir write $1/event/b2press 'wmir write /wm/ctl close'
normstyle $1/norm-style
selstyle $1/sel-style
wmir write $1/tab-height $TAB_HEIGHT
if(~ $2 refresh)
wmir write $1/size +0,+0,+0,+0 # causes refresh
}
fn framesconf {
for(frame in `{wmir read $1 | grep '^[0-9]'})
frameconf $1/$frame $2
}
# WMIBAR CONFIGURATION
fn barsucks {
wmir write /bar/$1/b1press 'wmir write /wm/ctl ''select prev'''
wmir write /bar/$1/b3press 'wmir write /wm/ctl ''select next'''
wmir write /bar/$1/b4press 'wmir write /wm/ctl ''select prev'''
wmir write /bar/$1/b5press 'wmir write /wm/ctl ''select next'''
}
wmir write /bar/ctl reset
wmir write /bar/font $TEXT_FONT
wmir write /bar/fgcolor $NORMAL_FG_COLOR
wmir write /bar/bgcolor $NORMAL_BG_COLOR
wmir write /bar/bordercolor $NORMAL_BORDER_COLOR
plab=`{wmir read /bar/new}
wmir write /bar/$plab/b1press 'wmir write /wm/ctl pager'
clab=`{wmir read /bar/new}
wmir write /bar/$clab/fgcolor $SELECTED_FG_COLOR
wmir write /bar/$clab/bgcolor $SELECTED_BG_COLOR
wmir write /bar/$clab/bordercolor $SELECTED_BG_COLOR
barsucks $clab
wmir write /bar/expandable 2
wmir write /bar/geometry 0,south,east,$BAR_HEIGHT
wmir write /bar/ctl 'display 1'
# WMIIWM CONFIGURATION
# default layout (tiled, max, grid or vsplit):
wmir write /wm/default/page/layout tiled
# width of the left frame in tiled layout in percent:
wmir write /wm/default/page/tile-width 60
wmir write /wm/default/core/trans-color $SELECTED_BG_COLOR
wmir write /wm/default/frame/handle-inc 1
# some broken WIMP apps:
wmir create /wm/default/client/'xmms:*'/manage 0
wmir create /wm/default/client/'Gimp:*'/manage 0
wmir write /wm/default/frame/border-width $BORDER_WIDTH
wmir write /wm/default/page/size '0,0,east,south-'^$BAR_HEIGHT
wmir write /wm/event/client-update \
'text=`{wmir read /wm/page/sel/mode/sel/client/sel/name} \
wmir write /bar/'^$clab^'/data $"text'
wmir write /wm/event/page-update \
'text=`{wmir read /wm/page/sel/name} ^ \
`{wmir read /wm/page/sel/mode/name|awk ''{print substr($0,0,1)}''} ^ \
`{wmir read /keys/lookup|awk ''{print substr($0,7,1)}''} \
wmir write /bar/'^$plab^'/data $"text'
for(page in `{wmir read /wm/page | grep '^[0-9]'}) {
framesconf /wm/page/$page/floating refresh
framesconf /wm/page/$page/managed refresh
}
frameconf /wm/default/frame norefresh
for(i in norm-style norm-style/client)
normstyle /wm/default/core/pager/$i
for(i in sel-style sel-style/client)
selstyle /wm/default/core/pager/$i
wmir write /wm/page/sel/managed/size 0,0,east,south-$BAR_HEIGHT >[2]/dev/null
# WMIKEYS CONFIGURATION
kbind bare $MODKEY-Escape 'kmode normal'
kbind move Escape 'kmode normal'
kbind move $MODKEY-C-r 'kmode resize'
kbind move $NORTHKEY 'wmir write /wm/page/sel/mode/sel/size -0,-30,-0,-0'
kbind move $SOUTHKEY 'wmir write /wm/page/sel/mode/sel/size +0,+30,+0,+0'
kbind move $WESTKEY 'wmir write /wm/page/sel/mode/sel/size -40,-0,-0,-0'
kbind move $EASTKEY 'wmir write /wm/page/sel/mode/sel/size +40,+0,+0,+0'
kbind move S-$NORTHKEY 'wmir write /wm/page/sel/mode/sel/size -0,north,-0,-0'
kbind move S-$SOUTHKEY 'wmir write /wm/page/sel/mode/sel/size +0,south-'^$BAR_HEIGHT^',+0,+0'
kbind move S-$WESTKEY 'wmir write /wm/page/sel/mode/sel/size west,-0,-0,-0'
kbind move S-$EASTKEY 'wmir write /wm/page/sel/mode/sel/size east,+0,+0,+0'
kbind resize Escape 'kmode normal'
kbind resize $MODKEY-C-m 'kmode move'
kbind resize $NORTHKEY 'wmir write /wm/page/sel/mode/sel/size +0,+0,+0,-30'
kbind resize $SOUTHKEY 'wmir write /wm/page/sel/mode/sel/size +0,+0,+0,+30'
kbind resize $WESTKEY 'wmir write /wm/page/sel/mode/sel/size +0,+0,-40,+0'
kbind resize $EASTKEY 'wmir write /wm/page/sel/mode/sel/size +0,+0,+40,+0'
kbind normal $MODKEY-C-b 'kmode bare'
kbind normal $MODKEY-C-m 'kmode move'
kbind normal $MODKEY-C-r 'kmode resize'
kbind normal $MODKEY-C-a 'wmir write /menu/precmd ''''; wmir write /menu/lookup /items/actions; wmir write /menu/ctl ''display 1'''
kbind normal $MODKEY-C-p 'wmir write /menu/precmd extern; wmir write /menu/lookup /items/programs; wmir write /menu/ctl ''display 1'''
kbind normal $MODKEY-C-c 'wmir write /wm/ctl close'
kbind normal $MODKEY-C-q,y quit
kbind normal $MODKEY-C-w,y wmirc
kbind normal $MODKEY-t 'extern xterm ''+sb'' -bg ''#003040'' -fg ''#dddddd'' -cr ''#408090'' -sl 4000'
kbind normal $MODKEY-d 'wmir write /wm/ctl detach'
kbind normal $MODKEY-a 'wmir write /wm/ctl attach'
kbind normal $MODKEY-S-a 'wmir write /wm/ctl icons'
kbind normal $MODKEY-S-space 'wmir write /wm/page/sel/ctl toggle'
kbind normal $MODKEY-Return 'wmir write /wm/page/sel/ctl ''select zoomed'''
kbind normal $MODKEY-C-y 'wmir write /wm/ctl new'
kbind normal $MODKEY-u 'wmir write /wm/page/sel/mode/sel/locked 0'
kbind normal $MODKEY-S-u 'wmir write /wm/page/sel/mode/sel/locked 1'
kbind normal $MODKEY-m 'wmir write /wm/ctl togglemax'
kbind normal $MODKEY-S-t 'wmir write /wm/page/sel/managed/name tiled'
kbind normal $MODKEY-S-g 'wmir write /wm/page/sel/managed/name grid'
kbind normal $MODKEY-S-v 'wmir write /wm/page/sel/managed/name vsplit'
kbind normal $MODKEY-S-f 'wmir write /wm/page/sel/managed/name float'
kbind normal $MODKEY-S-m 'wmir write /wm/page/sel/managed/name max'
kbind normal $MODKEY-$WESTKEY 'wmir write /wm/ctl ''select prev'''
kbind normal $MODKEY-$EASTKEY 'wmir write /wm/ctl ''select next'''
kbind normal $MODKEY-Tab 'wmir write /wm/page/sel/ctl ''select next'''
kbind normal $MODKEY-$SOUTHKEY 'wmir write /wm/page/sel/ctl ''select next'''
kbind normal $MODKEY-$NORTHKEY 'wmir write /wm/page/sel/ctl ''select prev'''
kbind normal $MODKEY-S-Tab 'wmir write /wm/page/sel/mode/sel/ctl ''select next'''
kbind normal $MODKEY-S-$SOUTHKEY 'wmir write /wm/page/sel/mode/sel/ctl ''select next'''
kbind normal $MODKEY-S-$NORTHKEY 'wmir write /wm/page/sel/mode/sel/ctl ''select prev'''
kbind normal $MODKEY-space 'wmir write /wm/page/sel/ctl ''select toggled'''
kbind normal $MODKEY-S-p 'wmir write /wm/ctl pager'
kbind normal $MODKEY-S-0 'wmir write /wm/ctl ''select 10'''
for(i in 1 2 3 4 5 6 7 8 9) {
kbind normal $MODKEY-S-$i 'wmir write /wm/ctl ''select '^$i^''''
}
wmir write /keys/size center,center,100,$BAR_HEIGHT
selstyle /keys/box/style
kmode normal
# WMIMENU CONFIGURATION
items actions $WMII_CONFDIR:$HOME/.wmii-3
wmir create /menu/items/actions/rmpage 'wmir write /wm/ctl destroy'
items programs $OLD_PATH
wmir write /menu/size 0,south,east,$BAR_HEIGHT
normstyle /menu/style >[2]/dev/null
normstyle /menu/norm-style >[2]/dev/null
selstyle /menu/sel-style >[2]/dev/null
# MISC
xsetroot -mod 2 2 -fg '#003040' -bg '#004050'
status &