commit caaad63c0d40c92b4256cd9d0fc92a2a716e7068
Author: garbeam <garbeam@wmii.de>
Date: Fri, 18 Nov 2005 17:54:58 +0200
added initial files
Diffstat:
| CHANGES | | | 92 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| LICENSE | | | 22 | ++++++++++++++++++++++ |
| Makefile | | | 70 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| README | | | 86 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| cmd/Makefile | | | 86 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| cmd/wm/Makefile | | | 15 | +++++++++++++++ |
| cmd/wm/Makefile.wm | | | 25 | +++++++++++++++++++++++++ |
| cmd/wm/area.c | | | 67 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| cmd/wm/client.c | | | 366 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| cmd/wm/core.c | | | 582 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| cmd/wm/event.c | | | 329 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| cmd/wm/frame.c | | | 659 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| cmd/wm/layout_column.c | | | 393 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| cmd/wm/mklayout.sh | | | 12 | ++++++++++++ |
| cmd/wm/mouse.c | | | 620 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| cmd/wm/old/layout_grid.c | | | 316 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| cmd/wm/old/layout_max.c | | | 115 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| cmd/wm/old/layout_tiled.c | | | 176 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| cmd/wm/old/layout_vsplit.c | | | 223 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| cmd/wm/page.c | | | 463 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| cmd/wm/wm.c | | | 290 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| cmd/wm/wm.h | | | 291 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| cmd/wm/wmii | | | 43 | +++++++++++++++++++++++++++++++++++++++++++ |
| cmd/wm/wmii.1 | | | 95 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| cmd/wm/wmiiwm.1 | | | 171 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| cmd/wm/wmiiwm.h.O | | | 332 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| cmd/wmibar.1 | | | 43 | +++++++++++++++++++++++++++++++++++++++++++ |
| cmd/wmibar.c | | | 550 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| cmd/wmibar2.c | | | 472 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| cmd/wmifs.1 | | | 38 | ++++++++++++++++++++++++++++++++++++++ |
| cmd/wmifs.c | | | 417 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| cmd/wmikeys.1 | | | 45 | +++++++++++++++++++++++++++++++++++++++++++++ |
| cmd/wmikeys.c | | | 535 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| cmd/wmimenu.1 | | | 42 | ++++++++++++++++++++++++++++++++++++++++++ |
| cmd/wmimenu.c | | | 689 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| cmd/wmiplumb.c | | | 95 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| cmd/wmir.1 | | | 71 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| cmd/wmir.c | | | 228 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| cmd/wmir2.c | | | 252 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| cmd/wmiwarp.c | | | 56 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| config.mk | | | 28 | ++++++++++++++++++++++++++++ |
| doc/NOTES | | | 198 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| doc/RELEASE | | | 45 | +++++++++++++++++++++++++++++++++++++++++++++ |
| doc/wmii.svg | | | 61 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| doc/wmii.tex | | | 83 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| extra/README | | | 8 | ++++++++ |
| extra/pkeys | | | 6 | ++++++ |
| libcext/Makefile | | | 25 | +++++++++++++++++++++++++ |
| libcext/array.c | | | 98 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| libcext/cext.h | | | 45 | +++++++++++++++++++++++++++++++++++++++++++++ |
| libcext/emalloc.c | | | 22 | ++++++++++++++++++++++ |
| libcext/estrdup.c | | | 21 | +++++++++++++++++++++ |
| libcext/strlcat.c | | | 55 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| libcext/strlcpy.c | | | 47 | +++++++++++++++++++++++++++++++++++++++++++++++ |
| libcext/strtonum.c | | | 68 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| libcext/tokenize.c | | | 34 | ++++++++++++++++++++++++++++++++++ |
| libixp/Makefile | | | 26 | ++++++++++++++++++++++++++ |
| libixp/client.c | | | 302 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| libixp/ixp.h | | | 188 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| libixp/message.c | | | 189 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| libixp/ramfs.c | | | 322 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| libixp/server.c | | | 475 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| libixp2/Makefile | | | 25 | +++++++++++++++++++++++++ |
| libixp2/client.c | | | 191 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| libixp2/convert.c | | | 187 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| libixp2/ixp.h | | | 241 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| libixp2/message.c | | | 293 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| libixp2/server.c | | | 201 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| libixp2/socket.c | | | 86 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| libixp2/transport.c | | | 78 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| liblitz/Makefile | | | 25 | +++++++++++++++++++++++++ |
| liblitz/blitz.h | | | 64 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| liblitz/draw.c | | | 181 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| liblitz/geometry.c | | | 222 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| liblitz/kb.c | | | 55 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| liblitz/mouse.c | | | 31 | +++++++++++++++++++++++++++++++ |
| liblitz/util.c | | | 21 | +++++++++++++++++++++ |
| libwmii/Makefile | | | 26 | ++++++++++++++++++++++++++ |
| libwmii/ixputil.c | | | 102 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| libwmii/spawn.c | | | 31 | +++++++++++++++++++++++++++++++ |
| libwmii/util.c | | | 14 | ++++++++++++++ |
| libwmii/wm.c | | | 95 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| libwmii/wmii.h | | | 39 | +++++++++++++++++++++++++++++++++++++++ |
| rc/extern | | | 15 | +++++++++++++++ |
| rc/kmode | | | 11 | +++++++++++ |
| rc/quit | | | 4 | ++++ |
| rc/status | | | 30 | ++++++++++++++++++++++++++++++ |
| rc/welcome | | | 65 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| rc/wmirc | | | 218 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
89 files changed, 14369 insertions(+), 0 deletions(-)
diff --git a/CHANGES b/CHANGES
@@ -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
diff --git a/LICENSE b/LICENSE
@@ -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.
diff --git a/Makefile b/Makefile
@@ -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"
diff --git a/README b/README
@@ -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
diff --git a/cmd/Makefile b/cmd/Makefile
@@ -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
diff --git a/cmd/wm/Makefile b/cmd/wm/Makefile
@@ -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.*
diff --git a/cmd/wm/Makefile.wm b/cmd/wm/Makefile.wm
@@ -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}
diff --git a/cmd/wm/area.c b/cmd/wm/area.c
@@ -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);
+}
+
diff --git a/cmd/wm/client.c b/cmd/wm/client.c
@@ -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;
+}
diff --git a/cmd/wm/core.c b/cmd/wm/core.c
@@ -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);
+}
diff --git a/cmd/wm/event.c b/cmd/wm/event.c
@@ -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);
+}
diff --git a/cmd/wm/frame.c b/cmd/wm/frame.c
@@ -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 = ¬ch;
+
+ 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;
+ }
+ }
+}
diff --git a/cmd/wm/layout_column.c b/cmd/wm/layout_column.c
@@ -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)
+{
+}
diff --git a/cmd/wm/mklayout.sh b/cmd/wm/mklayout.sh
@@ -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
diff --git a/cmd/wm/mouse.c b/cmd/wm/mouse.c
@@ -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;
+ }
+ }
+}
diff --git a/cmd/wm/old/layout_grid.c b/cmd/wm/old/layout_grid.c
@@ -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;
+}
diff --git a/cmd/wm/old/layout_max.c b/cmd/wm/old/layout_max.c
@@ -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;
+}
diff --git a/cmd/wm/old/layout_tiled.c b/cmd/wm/old/layout_tiled.c
@@ -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;
+}
diff --git a/cmd/wm/old/layout_vsplit.c b/cmd/wm/old/layout_vsplit.c
@@ -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;
+}
diff --git a/cmd/wm/page.c b/cmd/wm/page.c
@@ -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);
+ }
+ }
+}
+
diff --git a/cmd/wm/wm.c b/cmd/wm/wm.c
@@ -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;
+}
diff --git a/cmd/wm/wm.h b/cmd/wm/wm.h
@@ -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();
diff --git a/cmd/wm/wmii b/cmd/wm/wmii
@@ -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
diff --git a/cmd/wm/wmii.1 b/cmd/wm/wmii.1
@@ -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)
diff --git a/cmd/wm/wmiiwm.1 b/cmd/wm/wmiiwm.1
@@ -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).
diff --git a/cmd/wm/wmiiwm.h.O b/cmd/wm/wmiiwm.h.O
@@ -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();
diff --git a/cmd/wmibar.1 b/cmd/wmibar.1
@@ -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)
diff --git a/cmd/wmibar.c b/cmd/wmibar.c
@@ -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;
+}
diff --git a/cmd/wmibar2.c b/cmd/wmibar2.c
@@ -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;
+}
diff --git a/cmd/wmifs.1 b/cmd/wmifs.1
@@ -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)
diff --git a/cmd/wmifs.c b/cmd/wmifs.c
@@ -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;
+}
diff --git a/cmd/wmikeys.1 b/cmd/wmikeys.1
@@ -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)
diff --git a/cmd/wmikeys.c b/cmd/wmikeys.c
@@ -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;
+}
diff --git a/cmd/wmimenu.1 b/cmd/wmimenu.1
@@ -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)
diff --git a/cmd/wmimenu.c b/cmd/wmimenu.c
@@ -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;
+}
diff --git a/cmd/wmiplumb.c b/cmd/wmiplumb.c
@@ -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;
+ */
+}
diff --git a/cmd/wmir.1 b/cmd/wmir.1
@@ -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)
diff --git a/cmd/wmir.c b/cmd/wmir.c
@@ -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;
+}
diff --git a/cmd/wmir2.c b/cmd/wmir2.c
@@ -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;
+}
diff --git a/cmd/wmiwarp.c b/cmd/wmiwarp.c
@@ -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;
+}
diff --git a/config.mk b/config.mk
@@ -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
diff --git a/doc/NOTES b/doc/NOTES
@@ -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.
diff --git a/doc/RELEASE b/doc/RELEASE
@@ -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)
diff --git a/doc/wmii.svg b/doc/wmii.svg
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ sodipodi:docbase="/home/zahod"
+ sodipodi:docname="wmii9.svg"
+ inkscape:version="0.41"
+ sodipodi:version="0.32"
+ version="1.0"
+ x="0.0000000"
+ y="0.0000000"
+ width="240.00000px"
+ height="120.00000px"
+ id="svg2">
+ <metadata
+ id="metadata1296">
+ <rdf:RDF
+ id="RDF1298">
+ <cc:Work
+ id="Work1300"
+ rdf:about="">
+ <dc:format
+ id="format1302">image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage"
+ id="type1304" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ inkscape:current-layer="svg2"
+ inkscape:window-y="0"
+ inkscape:window-x="0"
+ inkscape:cy="50.733714"
+ inkscape:cx="133.35719"
+ inkscape:zoom="1.6877337"
+ inkscape:window-height="538"
+ inkscape:window-width="640"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ borderopacity="1.0"
+ bordercolor="#666666"
+ pagecolor="#ffffff"
+ id="base" />
+ <defs
+ id="defs3" />
+ <g
+ transform="translate(0.000000,-932.3622)"
+ id="layer1">
+ <path
+ d="M 0.0000000,971.36227 L 26.999997,971.36227 L 26.999997,1025.3623 L 54.000004,1025.3623 L 54.000004,971.28555 L 81.310128,971.28555 L 81.310128,1025.3623 L 108.00001,1025.3623 L 108.00001,971.36227 L 240.00000,971.36227 L 240.00000,1052.3622 L 213.00001,1052.3622 L 213.00001,998.36218 L 186.00001,998.36218 L 186.00001,1052.3622 L 159.00001,1052.3622 L 159.00001,998.46222 L 132.01646,998.46222 L 132.01646,1052.3622 L 0.0000000,1052.3622 L 0.0000000,971.36227 z M 159.00001,932.36216 L 186.00001,932.36216 L 186.00001,959.36220 L 159.00001,959.36220 L 159.00001,932.36216 z M 213.00001,932.36216 L 240.00000,932.36216 L 240.00000,959.36220 L 213.00001,959.36220 L 213.00001,932.36216 z "
+ style="fill:#010101"
+ id="path1366" />
+ </g>
+</svg>
diff --git a/doc/wmii.tex b/doc/wmii.tex
@@ -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}
diff --git a/extra/README b/extra/README
@@ -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
diff --git a/extra/pkeys b/extra/pkeys
@@ -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}
diff --git a/libcext/Makefile b/libcext/Makefile
@@ -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
diff --git a/libcext/array.c b/libcext/array.c
@@ -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;
+}
diff --git a/libcext/cext.h b/libcext/cext.h
@@ -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);
diff --git a/libcext/emalloc.c b/libcext/emalloc.c
@@ -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;
+}
diff --git a/libcext/estrdup.c b/libcext/estrdup.c
@@ -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;
+}
diff --git a/libcext/strlcat.c b/libcext/strlcat.c
@@ -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 */
+}
diff --git a/libcext/strlcpy.c b/libcext/strlcpy.c
@@ -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 */
+}
diff --git a/libcext/strtonum.c b/libcext/strtonum.c
@@ -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);
+}
diff --git a/libcext/tokenize.c b/libcext/tokenize.c
@@ -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 */
+}
diff --git a/libixp/Makefile b/libixp/Makefile
@@ -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
diff --git a/libixp/client.c b/libixp/client.c
@@ -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);
+}
diff --git a/libixp/ixp.h b/libixp/ixp.h
@@ -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);
diff --git a/libixp/message.c b/libixp/message.c
@@ -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;
+}
diff --git a/libixp/ramfs.c b/libixp/ramfs.c
@@ -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;
+}
diff --git a/libixp/server.c b/libixp/server.c
@@ -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);
+}
diff --git a/libixp2/Makefile b/libixp2/Makefile
@@ -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
diff --git a/libixp2/client.c b/libixp2/client.c
@@ -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);
+ }
+}
diff --git a/libixp2/convert.c b/libixp2/convert.c
@@ -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);
+}
diff --git a/libixp2/ixp.h b/libixp2/ixp.h
@@ -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);
diff --git a/libixp2/message.c b/libixp2/message.c
@@ -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;
+}
diff --git a/libixp2/server.c b/libixp2/server.c
@@ -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]);
+}
diff --git a/libixp2/socket.c b/libixp2/socket.c
@@ -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;
+}
diff --git a/libixp2/transport.c b/libixp2/transport.c
@@ -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;
+}
diff --git a/liblitz/Makefile b/liblitz/Makefile
@@ -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
diff --git a/liblitz/blitz.h b/liblitz/blitz.h
@@ -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);
diff --git a/liblitz/draw.c b/liblitz/draw.c
@@ -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);
+}
diff --git a/liblitz/geometry.c b/liblitz/geometry.c
@@ -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;
+}
diff --git a/liblitz/kb.c b/liblitz/kb.c
@@ -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;
+}
diff --git a/liblitz/mouse.c b/liblitz/mouse.c
@@ -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;
+}
diff --git a/liblitz/util.c b/liblitz/util.c
@@ -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;
+}
diff --git a/libwmii/Makefile b/libwmii/Makefile
@@ -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
diff --git a/libwmii/ixputil.c b/libwmii/ixputil.c
@@ -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;
+}
diff --git a/libwmii/spawn.c b/libwmii/spawn.c
@@ -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);
+}
diff --git a/libwmii/util.c b/libwmii/util.c
@@ -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;
+}
diff --git a/libwmii/wm.c b/libwmii/wm.c
@@ -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);
+}
diff --git a/libwmii/wmii.h b/libwmii/wmii.h
@@ -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);
diff --git a/rc/extern b/rc/extern
@@ -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 $*
diff --git a/rc/kmode b/rc/kmode
@@ -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}^'/'}
diff --git a/rc/quit b/rc/quit
@@ -0,0 +1,4 @@
+#!9PREFIX/bin/rc
+# quit wmii
+
+wmir write /wm/ctl quit
diff --git a/rc/status b/rc/status
@@ -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
+}
diff --git a/rc/welcome b/rc/welcome
@@ -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
diff --git a/rc/wmirc b/rc/wmirc
@@ -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 &